20,000 USDC
View results
Submission Details
Severity: gas
Valid

Gas Report

VULN 1

[GAS] Use != 0 instead of > 0 for unsigned integer comparison


Proof of concept

Found in line 63 at 2023-07-beedle/src/Staking.sol:

    if (totalSupply > 0) {

Found in line 67 at 2023-07-beedle/src/Staking.sol:

            if (_diff > 0) {

Found in line 69 at 2023-07-beedle/src/Staking.sol:

                if (_ratio > 0) {

Found in line 83 at 2023-07-beedle/src/Staking.sol:

    if (_supplied > 0) {

Found in line 87 at 2023-07-beedle/src/Staking.sol:

        if (_delta > 0) {

Mitigation

Use != 0 instead of > 0 for unsigned integer comparison.

VULN 2

[GAS] Use shift Right/Left instead of division/multiplication if possible


Proof of concept

Found in line 265 at 2023-07-beedle/src/Lender.sol:

        uint256 fees = (debt * borrowerFee) / 10000;

Found in line 561 at 2023-07-beedle/src/Lender.sol:

        uint256 govFee = (borrowerFee * loan.collateral) / 10000;

Found in line 650 at 2023-07-beedle/src/Lender.sol:

            uint256 fee = (borrowerFee * (debt - debtToPay)) / 10000;

Found in line 724 at 2023-07-beedle/src/Lender.sol:

    interest = (l.interestRate * l.debt * timeElapsed) / 10000 / 365 days;

Found in line 725 at 2023-07-beedle/src/Lender.sol:

    fees = (lenderFee * interest) / 10000;

Mitigation

Use shift Right/Left instead of division/multiplication if possible.

VULN 3

[GAS] ++i/i++ should be unchecked{++i}/unchecked{i++} and ++i costs less gas than i++


Proof of concept

Found in line 233 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < borrows.length; i++) {

Found in line 293 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < loanIds.length; i++) {

Found in line 359 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < loanIds.length; i++) {

Found in line 438 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < loanIds.length; i++) {

Found in line 549 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < loanIds.length; i++) {

Found in line 592 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < refinances.length; i++) {

Mitigation

++i/i++ should be unchecked{++i}/unchecked{i++} when it is not possible for them to overflow, as is the case when used in for and while-loops. Moreover ++i costs less gas than i++, especially when its used in for-loops (--i/i-- too).

VULN 4

[GAS] Don’t initialize variables with default value


Proof of concept

Found in line 233 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < borrows.length; i++) {

Found in line 293 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < loanIds.length; i++) {

Found in line 359 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < loanIds.length; i++) {

Found in line 438 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < loanIds.length; i++) {

Found in line 549 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < loanIds.length; i++) {

Found in line 592 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < refinances.length; i++) {

Found in line 14 at 2023-07-beedle/src/Staking.sol:

uint256 public balance = 0;

Found in line 16 at 2023-07-beedle/src/Staking.sol:

uint256 public index = 0;

Mitigation

In such cases, initializing the variables with default values would be unnecessary and can be considered a waste of gas. Additionally, initializing variables with default values can sometimes lead to unnecessary storage operations, which can increase gas costs. For example, if you have a large array of variables, initializing them all with default values can result in a lot of unnecessary storage writes, which can increase the gas costs of your contract.

VULN 5

[GAS] Use Custom Errors


Proof of concept

Found in line 27 at 2023-07-beedle/src/Fees.sol:

    require(_profits != WETH, "not allowed");

Found in line 11 at 2023-07-beedle/src/utils/Ownable.sol:

    require(msg.sender == owner, "UNAUTHORIZED");

Mitigation

Instead of using error strings, to reduce deployment and runtime cost, you should use Custom Errors. This would save both deployment and runtime cost.

VULN 6

[GAS] Cache array length outside of loop


Proof of concept

Found in line 233 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < borrows.length; i++) {

Found in line 293 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < loanIds.length; i++) {

Found in line 359 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < loanIds.length; i++) {

Found in line 438 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < loanIds.length; i++) {

Found in line 549 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < loanIds.length; i++) {

Found in line 592 at 2023-07-beedle/src/Lender.sol:

    for (uint256 i = 0; i < refinances.length; i++) {

Mitigation

If not cached, the solidity compiler will always read the length of the array during each iteration. That is, if it is a storage array, this is an extra sload operation (100 additional extra gas for each iteration except for the first) and if it is a memory array, this is an extra mload operation (3 additional gas for each iteration except for the first).

VULN 7

[GAS] Use assembly to check for address(0)


Proof of concept

Found in line 167 at 2023-07-beedle/src/Lender.sol:

    if (pools[poolId].lender == address(0)) {

Found in line 240 at 2023-07-beedle/src/Lender.sol:

        if (pool.lender == address(0)) revert PoolConfig();

Mitigation

Using assembly to check for the zero address can result in significant gas savings compared to using a Solidity expression, especially if the check is performed frequently or in a loop. However, it's important to note that using assembly can make the code less readable and harder to maintain, so it should be used judiciously and with caution.

VULN 8

[GAS] Usage of uints/ints smaller than 32 bytes (256 bits) incurs overhead


Proof of concept

Found in line 8 at 2023-07-beedle/src/interfaces/ISwapRouter.sol:

    uint24 fee;

Mitigation

When using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size. https://docs.soliditylang.org/en/v0.8.11/internals/layout_in_storage.html. Each operation involving a uint8 costs an extra 22-28 gas (depending on whether the other operand is also a variable of type uint8) as compared to ones involving uint256, due to the compiler having to clear the higher bits of the memory word before operating on the uint8, as well as the associated stack operations of doing so. Use a larger size then downcast where needed.

VULN 9

[GAS] += Costs More Gas Than = + For State Variables


Proof of concept

Found in line 263 at 2023-07-beedle/src/Lender.sol:

        pools[poolId].outstandingLoans += debt;

Found in line 388 at 2023-07-beedle/src/Lender.sol:

        pools[poolId].outstandingLoans += totalDebt;

Found in line 490 at 2023-07-beedle/src/Lender.sol:

    pools[poolId].outstandingLoans += totalDebt;

Found in line 637 at 2023-07-beedle/src/Lender.sol:

        pools[poolId].outstandingLoans += debt;

Found in line 41 at 2023-07-beedle/src/Staking.sol:

    balances[msg.sender] += _amount;

Found in line 89 at 2023-07-beedle/src/Staking.sol:

          claimable[recipient] += _share;

Mitigation

When you use the += operator on a state variable, the EVM has to perform three operations: load the current value of the state variable, add the new value to it, and then store the result back in the state variable. On the other hand, when you use the = operator and then add the values separately, the EVM only needs to perform two operations: load the current value of the state variable and add the new value to it. Better use = + For State Variables.

VULN 10

[GAS] Multiple Address Mappings Can Be Combined Into A Single Mapping Of An Address To A Struct, Where Appropriate


Proof of concept

Found in line 23 and 24 at 2023-07-beedle/src/Staking.sol:

/// @notice mapping of user claimable rewards

mapping(address => uint256) public claimable;

Mitigation

Saves a storage slot for the mapping. Depending on the circumstances and sizes of types, can avoid a Gsset (20000 gas) per mapping combined. Reads and subsequent writes can also be cheaper when a function requires both values and they both fit in the same storage slot.

VULN 11

[GAS] Use hardcode address instead address(this)


Proof of concept

Found in line 154 at 2023-07-beedle/src/Lender.sol:

            address(this),

Found in line 189 at 2023-07-beedle/src/Lender.sol:

        address(this),

Found in line 273 at 2023-07-beedle/src/Lender.sol:

            address(this),

Found in line 319 at 2023-07-beedle/src/Lender.sol:

            address(this),

Found in line 644 at 2023-07-beedle/src/Lender.sol:

                address(this),

Found in line 665 at 2023-07-beedle/src/Lender.sol:

                address(this),

Found in line 39 at 2023-07-beedle/src/Staking.sol:

    TKN.transferFrom(msg.sender, address(this), _amount);

Found in line 57 at 2023-07-beedle/src/Staking.sol:

    balance = WETH.balanceOf(address(this));

Found in line 62 at 2023-07-beedle/src/Staking.sol:

    uint256 totalSupply = TKN.balanceOf(address(this));

Found in line 64 at 2023-07-beedle/src/Staking.sol:

        uint256 _balance = WETH.balanceOf(address(this));

Found in line 28 at 2023-07-beedle/src/Fees.sol:

    uint256 amount = IERC20(_profits).balanceOf(address(this));

Found in line 35 at 2023-07-beedle/src/Fees.sol:

            recipient: address(this),

Found in line 43 at 2023-07-beedle/src/Fees.sol:

    IERC20(WETH).transfer(staking, IERC20(WETH).balanceOf(address(this)));

Mitigation

Instead of using address(this), it is more gas-efficient to pre-calculate and use the hardcoded address. Foundry’s script.sol and solmate’s LibRlp.sol contracts can help achieve this.

VULN 12

[GAS] Functions guaranteed to revert when called by normal users can be marked payable


Proof of concept

Found in line 36 at 2023-07-beedle/src/Beedle.sol:

function mint(address to, uint256 amount) external onlyOwner {

Found in line 84 at 2023-07-beedle/src/Lender.sol:

function setLenderFee(uint256 _fee) external onlyOwner {

Found in line 92 at 2023-07-beedle/src/Lender.sol:

function setBorrowerFee(uint256 _fee) external onlyOwner {

Found in line 100 at 2023-07-beedle/src/Lender.sol:

function setFeeReceiver(address _feeReceiver) external onlyOwner {

Mitigation

If a function modifier or require such as onlyOwner/onlyX is used, the function will revert if a normal user tries to pay the function. Marking the function as payable will lower the gas cost for legitimate callers because the compiler will not include checks for whether a payment was provided. The extra opcodes avoided are CALLVALUE(2), DUP1(3), ISZERO(3), PUSH2(3), JUMPI(10), PUSH1(3), DUP1(3), REVERT(0), JUMPDEST(1), POP(2) which costs an average of about 21 gas per call to the function, in addition to the extra deployment cost.

VULN 13

[GAS] require() or revert() statements that check input arguments should be at the top of the function (Also restructured some if’s)


Proof of concept

Found in line 11 at 2023-07-beedle/src/utils/Ownable.sol:

    require(msg.sender == owner, "UNAUTHORIZED");

Mitigation

Checks that involve constants should come before checks that involve state variables, function calls, and calculations. By doing these checks first, the function is able to revert before wasting alot of gas in a function that may ultimately revert in the unhappy case.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.