code-423n4 / 2022-05-velodrome-findings

0 stars 0 forks source link

Gas Optimizations #184

Open code423n4 opened 2 years ago

code423n4 commented 2 years ago

1. !=0 is cheaper than >0

2. Initilization to default values can be omitted.

uint variables are set to zero by default, initializing them to zero (for storage variables) causes extra gas.

3. Use solidity custom errors to save gas

solidity 0.8.4 introduces custom errors which are cheaper than using revert strings in terms of gas Use the custom error patterns to reduce gas cost.

for eg.


  // Before
  require(condition, "Revert strings");

  // After
  error CustomError();
  if (!condition) {
    revert CustomError();
  }

more details can be found here

4. using prefix increment in loops can save some gas

Prefix increment operation(++i) is more gas effiecient than postfix increment(i++), while few instance of postfix increment doesn't makes much difference in overall gas, in loops these increments are called a large number of times resulting in significant gas savings.

// Before
for (uint i = 0; i < _prices.length; i++) {
}

// After
for (uint i = 0; i < _prices.length; ++i) {
}

Also, since the loop increment starts from zero and only increases, there is no chance of overflow. So the increment can also be added inside a unchecked block so save further gas

for (uint i = 0; i < _prices.length;) {
  unchecked {
    ++i;
  }
}

The instances of prefix increment can be found as follows

❯ grep -rn './contracts' -e 'for.*i++'
./contracts/contracts/Pair.sol:257:        for (uint i = 0; i < _prices.length; i++) {
./contracts/contracts/Pair.sol:389:        for (uint i = 0; i < 255; i++) {
./contracts/contracts/VotingEscrow.sol:1146:        for (uint i = 0; i < _tokenIds.length; i++) {
./contracts/contracts/VotingEscrow.sol:1193:        for (uint i = 0; i < _tokenIds.length; i++) {
./contracts/contracts/VotingEscrow.sol:1225:                for (uint i = 0; i < srcRepOld.length; i++) {
./contracts/contracts/VotingEscrow.sol:1249:                for (uint i = 0; i < dstRepOld.length; i++) {
./contracts/contracts/VotingEscrow.sol:1295:                for (uint i = 0; i < srcRepOld.length; i++) {
.
.
.

5. array length in loops can be cached instead of calculating in every iteration

The loop bounds are calculated with array.length which are calculated in every loop iterations which can result in high gas. The array length can be cached instead of calculating in every loop iteration to save gas.

// Before
for (uint i = 0; i < _prices.length; i++) {
}

// After
uint len = _prices.length;
for (uint i = 0; i < len; i++) {
}

The instances where this pattern can be applied is found as follows

❯ grep -rn './contracts' -e 'for.*.length'
./contracts/contracts/Pair.sol:257:        for (uint i = 0; i < _prices.length; i++) {
./contracts/contracts/Pair.sol:276:        for (; i < length; i+=window) {
./contracts/contracts/VotingEscrow.sol:1146:        for (uint i = 0; i < _tokenIds.length; i++) {
./contracts/contracts/VotingEscrow.sol:1193:        for (uint i = 0; i < _tokenIds.length; i++) {
./contracts/contracts/VotingEscrow.sol:1225:                for (uint i = 0; i < srcRepOld.length; i++) {
./contracts/contracts/VotingEscrow.sol:1249:                for (uint i = 0; i < dstRepOld.length; i++) {
./contracts/contracts/VotingEscrow.sol:1295:                for (uint i = 0; i < srcRepOld.length; i++) {
./contracts/contracts/VotingEscrow.sol:1320:                for (uint i = 0; i < dstRepOld.length; i++) {
./contracts/contracts/Minter.sol:57:        for (uint i = 0; i < claimants.length; i++) {
./contracts/contracts/Gauge.sol:353:        for (uint i = 0; i < tokens.length; i++) {
./contracts/contracts/Gauge.sol:426:        for (uint i; i < length; i++) {
.
.
.
GalloDaSballo commented 2 years ago

1. !=0 is cheaper than >0

Not anymore

2. Initilization to default values can be omitted.

Valid but these are deployment costs

3. Use solidity custom errors to save gas

Valid but in lack of gas golfing I can't give it points

4. using prefix increment in loops can save some gas

Saves 5 gas

7 * 5 = 35

5. array length in loops can be cached instead of calculating in every iteration

Saves 6 gas per instance 9 * 6 (2 seem to not be valid) = 54

Report is really concise, which is nice, grep seems to be missing tons of these so I recommend the warden to do more manual searches

Total Gas saved: 89