code-423n4 / 2022-06-badger-findings

0 stars 0 forks source link

Gas Optimizations #57

Open code423n4 opened 2 years ago

code423n4 commented 2 years ago

G01 For Loop Optimisations

When incrementing i in for loops there is no chance of overflow so unchecked can be used to save gas. I ran a simple test in remix and found deployment savings of 31,653 gas and on each function call saved ~141 gas per iteration.

contract Test {
    function loopTest() external {
        for (uint256 i; i < 1; ++i) {
        Deployment Cost: 125,637, Cost on function call: 24,601
        vs
        for (uint256 i; i < 1; ) {
        // for loop body
        unchecked { ++i }
        Deployment Cost: 93,984, Cost on function call: 24,460
        }
    }
}

In for loops pre increments can also be used to save a small amount of gas per iteraition. I ran a test in remix using a for loop and found the deployment savings of 497 gas and ~5 gas per iteration.

contract Test {
    function loopTest() external {
        for (uint256 i; i < 1; i++) {
        (Deployment cost: 118,408, Cost on function call: 24,532)
        vs
        for (uint256 i; i < 1; ++i) {
        (Deployment cost: 117,911, Cost on function call: 24,527)
        }
    }
}

Instances where unchecked and pre Increments can be implemented in for loops: MyStrategy.sol#L118 MyStrategy.sol#L153 - is already pre increment MyStrategy.sol#L300 MyStrategy.sol#L317

G02 Long Revert Strings

Keeping revert strings <= 32 bytes in length will save gas. I ran a test in remix and found the savings for a single short revert string vs long string to be 9,377 gas in deployment cost and 18 gas on function call.

contract Test {
    uint256 a;
    function check() external {
        require(a != 0, "short error message"); 
        (Deployment cost: 114,799, Cost on function call: 23,392)   
        vs 
        require(a != 0, "A longer Error Message over 32 bytes in     
        length"); 
        (Deployment cost: 124,176, Cost on function call: 23,410)   
    }
}

I recommend shortenning the following revert strings to <= 32 bytes in length: MyStrategy.sol#L186

G03 && in Require Functions

If optimising for running costs over deployment costs you can seperate && in require functions into 2 parts. I ran a basic test in remix and it cost an extra 234 gas to deploy but will save ~9 gas everytime the require function is called.

contract Test {
    uint256 a = 0;
    uint256 b = 1;

    function test() external {
        require(a == 0 && b > a) 
        (Deployment cost: 123,291, Cost on function call: 29,371)
        vs
        require(a == 0);
        require(b > a);
        (Deployment cost: 123,525, Cost on function call: 29,362)
    }
}

MyStrategy.sol#L185

GalloDaSballo commented 2 years ago

Ack