code-423n4 / 2022-09-quickswap-findings

0 stars 0 forks source link

Gas Optimizations #289

Open code423n4 opened 2 years ago

code423n4 commented 2 years ago

1)++i costs less gas than i++, especially when it’s used in for-loops (--i/i-- too)

Saves 6 gas PER LOOP

File: 2022-09-quickswap\src\core\contracts\libraries\DataStorage.sol 307,49: for (uint256 i = 0; i < secondsAgos.length; i++) {

2)Use custom errors rather than revert()/require() strings to save gas

File: 2022-09-quickswap\src\core\contracts\AlgebraFactory.sol

109,5: require(uint256(alpha1) + uint256(alpha2) + uint256(baseFee) <= type(uint16).max, 'Max fee exceeded'); 110,5: require(gamma1 != 0 && gamma2 != 0 && volumeGamma != 0, 'Gammas must be > 0');

File: 2022-09-quickswap\src\core\contracts\AlgebraPool.sol

60,5: require(topTick < TickMath.MAX_TICK + 1, 'TUM'); 61,5: require(topTick > bottomTick, 'TLU'); 62,5: require(bottomTick > TickMath.MIN_TICK - 1, 'TLM');

194,5: require(globalState.price == 0, 'AI'); 224,7: require(currentLiquidity > 0, 'NP'); // Do not recalculate the empty ranges

434,5: require(liquidityDesired > 0, 'IL');

469,5: require(liquidityActual > 0, 'IIL2'); 474,7: require((amount0 = uint256(amount0Int)) <= receivedAmount0, 'IIAM2'); 475,7: require((amount1 = uint256(amount1Int)) <= receivedAmount1, 'IIAM2'); 608,7: require(balance0Before.add(uint256(amount0)) <= balanceToken0(), 'IIA'); 614,7: require(balance1Before.add(uint256(amount1)) <= balanceToken1(), 'IIA'); 636,5: require(globalState.unlocked, 'LOK'); 641,7: require((amountRequired = int256(balanceToken0().sub(balance0Before))) > 0, 'IIA'); 645,7: require((amountRequired = int256(balanceToken1().sub(balance1Before))) > 0, 'IIA'); 731,7: require(unlocked, 'LOK'); 733,7: require(amountRequired != 0, 'AS'); 739,9: require(limitSqrtPrice < currentPrice && limitSqrtPrice > TickMath.MIN_SQRT_RATIO, 'SPL'); 743,9: require(limitSqrtPrice > currentPrice && limitSqrtPrice < TickMath.MAX_SQRT_RATIO, 'SPL'); 898,5: require(_liquidity > 0, 'L'); 921,5: require(balance0Before.add(fee0) <= paid0, 'F0'); 935,5: require(balance1Before.add(fee1) <= paid1, 'F1');

File: 2022-09-quickswap\src\core\contracts\DataStorageOperator.sol 27,5: require(msg.sender == pool, 'only pool can call this');

45,5: require(uint256(_feeConfig.alpha1) + uint256(_feeConfig.alpha2) + uint256(_feeConfig.baseFee) <= type(uint16).max, 'Max fee exceeded'); 46,5: require(_feeConfig.gamma1 != 0 && _feeConfig.gamma2 != 0 && _feeConfig.volumeGamma != 0, 'Gammas must be > 0');

File: 2022-09-quickswap\src\core\contracts\base\PoolState.sol 41,5: require(globalState.unlocked, 'LOK');

File: 2022-09-quickswap\src\core\contracts\libraries\DataStorage.sol 238,5: require(lteConsideringOverflow(self[oldestIndex].blockTimestamp, target, time), 'OLD');

File: 2022-09-quickswap\src\core\contracts\libraries\TickTable.sol 15,5: require(tick % Constants.TICK_SPACING == 0, 'tick is not spaced'); // ensure that the tick is spaced

3) USING > 0 COSTS MORE GAS THAN != 0 WHEN USED ON A UINT IN A REQUIRE() STATEMENT

This change saves 6 gas per instance

File: 2022-09-quickswap\src\core\contracts\AlgebraPool.sol 224,32: require(currentLiquidity > 0, 'NP'); // Do not recalculate the empty ranges

469,29: require(liquidityActual > 0, 'IIL2');

641,78: require((amountRequired = int256(balanceToken0().sub(balance0Before))) > 0, 'IIA'); 645,78: require((amountRequired = int256(balanceToken1().sub(balance1Before))) > 0, 'IIA');

898,24: require(_liquidity > 0, 'L');

File: 2022-09-quickswap\src\core\contracts\libraries\FullMath.sol 114,27: require(denominator > 0);

File: 2022-09-quickswap\src\core\contracts\libraries\PriceMovementMath.sol 52,19: require(price > 0); 53,23: require(liquidity > 0);

4) ++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

File: 2022-09-quickswap\src\core\contracts\libraries\DataStorage.sol 307,49: for (uint256 i = 0; i < secondsAgos.length; i++) {

5)It costs more gas to initialize variables to zero than to let the default of zero be applied

If a variable is not set/initialized, it is assumed to have the default value (0 for uint, false for bool, address(0) for address…). Explicitly initializing it with its default value is an anti-pattern and wastes gas.

As an example: for (uint256 i = 0; i < numIterations; ++i) { should be replaced with for (uint256 i; i < numIterations; ++i) {

File: 2022-09-quickswap\src\core\contracts\libraries\DataStorage.sol 307,49: for (uint256 i = 0; i < secondsAgos.length; i++) {

6).LENGTH SHOULD NOT BE LOOKED UP IN EVERY LOOP OF A FOR-LOOP The overheads outlined below are PER LOOP,

storage arrays incur a Gwarmaccess (100 gas) memory arrays use MLOAD (3 gas) calldata arrays use CALLDATALOAD (3 gas) Caching the length changes each of these to a DUP (3 gas), and gets rid of the extra DUP needed to store the stack offset

File: 2022-09-quickswap\src\core\contracts\libraries\DataStorage.sol 307,49: for (uint256 i = 0; i < secondsAgos.length; i++) {

7)X = X + Y IS CHEAPER THAN X += Y

File: 2022-09-quickswap\src\core\contracts\AlgebraPool.sol 257,23: _position.fees0 += fees0; 258,23: _position.fees1 += fees1; 804,24: amountRequired += step.output.toInt256(); // increase remaining output amount (since its negative) 811,28: communityFeeAmount += delta; 814,54: if (currentLiquidity > 0) cache.totalFeeGrowth += FullMath.mulDiv(step.feeAmount, Constants.Q128, currentLiquidity); 931,28: totalFeeGrowth0Token += FullMath.mulDiv(paid0 - fees0, Constants.Q128, _liquidity); 945,28: totalFeeGrowth1Token += FullMath.mulDiv(paid1 - fees1, Constants.Q128, _liquidity);

File: 2022-09-quickswap\src\core\contracts\libraries\AdaptiveFee.sol 84,9: res += xLowestDegree gHighestDegree; 88,9: res += (xLowestDegree gHighestDegree) / 2; 92,9: res += (xLowestDegree gHighestDegree) / 6; 96,9: res += (xLowestDegree gHighestDegree) / 24; 100,9: res += (xLowestDegree gHighestDegree) / 120; 104,9: res += (xLowestDegree gHighestDegree) / 720; 107,9: res += (xLowestDegree g) / 5040 + (xLowestDegree x) / (40320);

File: 2022-09-quickswap\src\core\contracts\libraries\DataStorage.sol 79,25: last.tickCumulative += int56(tick) delta; 80,40: last.secondsPerLiquidityCumulative += ((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)); // just timedelta if liquidity == 0 81,31: last.volatilityCumulative += uint88(_volatilityOnRange(delta, prevTick, tick, last.averageTick, averageTick)); // always fits 88 bits 83,39: last.volumePerLiquidityCumulative += volumePerLiquidity; 251,33: beforeOrAt.tickCumulative += ((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / timepointTimeDelta) targetDelta; 252,48: beforeOrAt.secondsPerLiquidityCumulative += uint160( 255,39: beforeOrAt.volatilityCumulative += ((atOrAfter.volatilityCumulative - beforeOrAt.volatilityCumulative) / timepointTimeDelta) * targetDelta; 256,47: beforeOrAt.volumePerLiquidityCumulative +=

File: 2022-09-quickswap\src\core\contracts\libraries\TickTable.sol 100,12: tick += 1; 112,14: tick += int24(getSingleSignificantBit(-_row & _row)); // least significant bit 115,14: tick += int24(255 - bitNumber);

8)USE A MORE RECENT VERSION OF SOLIDITY

Use a solidity version of at least 0.8.2 to get compiler automatic inlining

Use a solidity version of at least 0.8.3 to get better struct packing and cheaper multiple storage reads

Use a solidity version of at least 0.8.4 to get custom errors, which are cheaper at deployment than revert()/require() strings

Use a solidity version of at least 0.8.10 to have external calls skip contract existence checks if the external call has a return value

File: 2022-09-quickswap\src\core\contracts\AlgebraFactory.sol 2,1: pragma solidity =0.7.6;

File: 2022-09-quickswap\src\core\contracts\AlgebraPool.sol 2,1: pragma solidity =0.7.6;

File: 2022-09-quickswap\src\core\contracts\AlgebraPoolDeployer.sol 2,1: pragma solidity =0.7.6;

File: 2022-09-quickswap\src\core\contracts\DataStorageOperator.sol 2,1: pragma solidity =0.7.6;

File: 2022-09-quickswap\src\core\contracts\base\PoolImmutables.sol 2,1: pragma solidity =0.7.6;

File: 2022-09-quickswap\src\core\contracts\base\PoolState.sol 2,1: pragma solidity =0.7.6;

File: 2022-09-quickswap\src\core\contracts\libraries\AdaptiveFee.sol 2,1: pragma solidity =0.7.6;

File: 2022-09-quickswap\src\core\contracts\libraries\Constants.sol 2,1: pragma solidity =0.7.6;

File: 2022-09-quickswap\src\core\contracts\libraries\DataStorage.sol 2,1: pragma solidity =0.7.6;

File: 2022-09-quickswap\src\core\contracts\libraries\TickManager.sol 2,1: pragma solidity =0.7.6;

File: 2022-09-quickswap\src\core\contracts\libraries\TickTable.sol 2,1: pragma solidity =0.7.6;

File: 2022-09-quickswap\src\core\contracts\libraries\TokenDeltaMath.sol 2,1: pragma solidity =0.7.6;

9)Using private rather than public for constants, saves gas

If needed, the value can be read from the verified contract source code. Savings are due to the compiler not having to create non-payable getter functions for deployment calldata, and not adding another entry to the method ID table

File: 2022-09-quickswap\src\core\contracts\libraries\DataStorage.sol 12,10: uint32 public constant WINDOW = 1 days;