Platform: Code4rena
Start Date: 26/09/2022
Pot Size: $50,000 USDC
Total HM: 13
Participants: 113
Period: 5 days
Judge: 0xean
Total Solo HM: 6
Id: 166
League: ETH
Rank: 86/113
Findings: 1
Award: $25.78
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0x1f8b, 0x5rings, 0xNazgul, 0xRoxas, 0xSmartContract, 0xbepresent, 0xmatt, Aeros, Amithuddar, Awesome, Aymen0909, B2, Bnke0x0, ChristianKuri, CodingNameKiki, Deivitto, Diraco, Fitraldys, HardlyCodeMan, JC, Mukund, Noah3o6, Olivierdem, RaymondFam, ReyAdmirado, RockingMiles, Rolezn, Ruhum, Saintcode_, Shinchan, SnowMan, TomJ, Tomio, Tomo, V_B, Waze, __141345__, ajtra, asutorufos, aysha, beardofginger, bobirichman, brgltd, bulej93, c3phas, ch0bu, cryptonue, defsec, delfin454000, dharma09, durianSausage, emrekocak, erictee, fatherOfBlocks, francoHacker, gianganhnguyen, gogo, imare, kaden, karanctf, ladboy233, lukris02, m_Rassska, martin, medikko, mics, natzuu, oyc_109, peiw, rbserver, ret2basic, rotcivegaf, saian, shark, slowmoses, tnevler, trustindistrust, zeesaw, zishansami
25.776 USDC - $25.78
Total of 8 issues found
 
When there are multiple conditions in require statement, break down the require statement into multiple require statements instead of using && can save gas.
Total of 6 instances found.
./DataStorageOperator.sol:46: require(_feeConfig.gamma1 != 0 && _feeConfig.gamma2 != 0 && _feeConfig.volumeGamma != 0, 'Gammas must be > 0'); ./AlgebraPool.sol:739: require(limitSqrtPrice < currentPrice && limitSqrtPrice > TickMath.MIN_SQRT_RATIO, 'SPL'); ./AlgebraPool.sol:743: require(limitSqrtPrice > currentPrice && limitSqrtPrice < TickMath.MAX_SQRT_RATIO, 'SPL'); ./AlgebraPool.sol:953: require((communityFee0 <= Constants.MAX_COMMUNITY_FEE) && (communityFee1 <= Constants.MAX_COMMUNITY_FEE)); ./AlgebraPool.sol:968: require(newLiquidityCooldown <= Constants.MAX_LIQUIDITY_COOLDOWN && liquidityCooldown != newLiquidityCooldown); ./AlgebraFactory.sol:110: require(gamma1 != 0 && gamma2 != 0 && volumeGamma != 0, 'Gammas must be > 0');
Break down into several require statement. For example,
require(_feeConfig.gamma1 != 0); require(_feeConfig.gamma2 != 0); require(_feeConfig.volumeGamma != 0, 'Gammas must be > 0');
 
Certain function is defined even though it is called only once. Inline it instead to where it is called to avoid usage of extra gas.
Total of 6 instances found.
getMostSignificantBit() of TickTable.sol This function called only once at line 92 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/libraries/TickTable.sol#L46 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/libraries/TickTable.sol#L92
computeAddress() of AlgebraFactory.sol This function called only once at line 65 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraFactory.sol#L122 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraFactory.sol#L65
_recalculatePosition() of AlgebraPool.sol This function called only once at line 342 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#L215 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#L342
getNewPriceAfterInput() of PriceMovementMath.sol This function called only once at line 163 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/libraries/PriceMovementMath.sol#L20 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/libraries/PriceMovementMath.sol#L163
getNewPriceAfterOutput() of PriceMovementMath.sol This function called only once at line 182 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/libraries/PriceMovementMath.sol#L36 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/libraries/PriceMovementMath.sol#L182
_volatilityOnRange() of DataStorage.sol This function called only once at line 81 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/libraries/DataStorage.sol#L32 https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/libraries/DataStorage.sol#L81
I recommend to not define above functions and instead inline it at place it is called.
 
The MUL and DIV opcodes cost 5 gas but SHL and SHR only costs 3 gas. Since MUL/DIV and SHL/SHR result the same, use cheaper bit shifting.
Total of 1 instance found.
./AdaptiveFee.sol:88: res += (xLowestDegree * gHighestDegree) / 2;
Use bit shifting instead of multiplication/division. Example:
uint256 center = upper - (upper - lower) / 2; Good: uint256 center = upper - (upper - lower) >> 2;
 
It is cheaper gas to use calldata than memory if the function parameter is read only. Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory. More details on following link. link: https://docs.soliditylang.org/en/v0.8.15/types.html#data-location
Total of 1 instance found.
./DataStorageOperator.sol:90: uint32[] memory secondsAgos,
Change memory to calldata
 
By storing an array's length as a variable before the for-loop, can save 3 gas per iteration.
Total of 1 instance found.
./DataStorage.sol:307: for (uint256 i = 0; i < secondsAgos.length; i++) {
Store array's length as a variable before looping it. For example, I suggest changing it to
uint256 len = secondsAgosLength; for (uint256 i = 0; i < len; i++) {
 
Prefix increments/decrements (++i or --i) costs cheaper gas than postfix increment/decrements (i++ or i--).
Total of 1 instance found.
./DataStorage.sol:307: for (uint256 i = 0; i < secondsAgos.length; i++) {
Change it to postfix increments/decrements. It saves 6 gas per loop.
 
!= 0 costs less gas when optimizer is enabled and is used for unsigned integers in require statement.
Total of 8 instances found.
./AlgebraPool.sol:224: require(currentLiquidity > 0, 'NP'); // Do not recalculate the empty ranges ./AlgebraPool.sol:434: require(liquidityDesired > 0, 'IL'); ./AlgebraPool.sol:454: if (amount0 > 0) require((receivedAmount0 = balanceToken0() - receivedAmount0) > 0, 'IIAM'); ./AlgebraPool.sol:455: if (amount1 > 0) require((receivedAmount1 = balanceToken1() - receivedAmount1) > 0, 'IIAM'); ./AlgebraPool.sol:469: require(liquidityActual > 0, 'IIL2'); ./AlgebraPool.sol:898: require(_liquidity > 0, 'L'); ./PriceMovementMath.sol:52: require(price > 0); ./PriceMovementMath.sol:53: require(liquidity > 0);
I suggest changing > 0 to != 0
 
Custom errors from Solidity 0.8.4 are cheaper than revert strings. I suggest implementing custom errors to save gas. Reference: https://blog.soliditylang.org/2021/04/21/custom-errors/
Total of 32 instances found.
./TickManager.sol:96: require(liquidityTotalAfter < Constants.MAX_LIQUIDITY_PER_TICK + 1, 'LO'); ./DataStorageOperator.sol:27: require(msg.sender == pool, 'only pool can call this'); ./DataStorageOperator.sol:45: require(uint256(_feeConfig.alpha1) + uint256(_feeConfig.alpha2) + uint256(_feeConfig.baseFee) <= type(uint16).max, 'Max fee exceeded'); ./DataStorageOperator.sol:46: require(_feeConfig.gamma1 != 0 && _feeConfig.gamma2 != 0 && _feeConfig.volumeGamma != 0, 'Gammas must be > 0'); ./AlgebraPool.sol:60: require(topTick < TickMath.MAX_TICK + 1, 'TUM'); ./AlgebraPool.sol:61: require(topTick > bottomTick, 'TLU'); ./AlgebraPool.sol:62: require(bottomTick > TickMath.MIN_TICK - 1, 'TLM'); ./AlgebraPool.sol:194: require(globalState.price == 0, 'AI'); ./AlgebraPool.sol:224: require(currentLiquidity > 0, 'NP'); // Do not recalculate the empty ranges ./AlgebraPool.sol:434: require(liquidityDesired > 0, 'IL'); ./AlgebraPool.sol:454: if (amount0 > 0) require((receivedAmount0 = balanceToken0() - receivedAmount0) > 0, 'IIAM'); ./AlgebraPool.sol:455: if (amount1 > 0) require((receivedAmount1 = balanceToken1() - receivedAmount1) > 0, 'IIAM'); ./AlgebraPool.sol:469: require(liquidityActual > 0, 'IIL2'); ./AlgebraPool.sol:474: require((amount0 = uint256(amount0Int)) <= receivedAmount0, 'IIAM2'); ./AlgebraPool.sol:475: require((amount1 = uint256(amount1Int)) <= receivedAmount1, 'IIAM2'); ./AlgebraPool.sol:608: require(balance0Before.add(uint256(amount0)) <= balanceToken0(), 'IIA'); ./AlgebraPool.sol:614: require(balance1Before.add(uint256(amount1)) <= balanceToken1(), 'IIA'); ./AlgebraPool.sol:636: require(globalState.unlocked, 'LOK'); ./AlgebraPool.sol:641: require((amountRequired = int256(balanceToken0().sub(balance0Before))) > 0, 'IIA'); ./AlgebraPool.sol:645: require((amountRequired = int256(balanceToken1().sub(balance1Before))) > 0, 'IIA'); ./AlgebraPool.sol:731: require(unlocked, 'LOK'); ./AlgebraPool.sol:733: require(amountRequired != 0, 'AS'); ./AlgebraPool.sol:739: require(limitSqrtPrice < currentPrice && limitSqrtPrice > TickMath.MIN_SQRT_RATIO, 'SPL'); ./AlgebraPool.sol:743: require(limitSqrtPrice > currentPrice && limitSqrtPrice < TickMath.MAX_SQRT_RATIO, 'SPL'); ./AlgebraPool.sol:898: require(_liquidity > 0, 'L'); ./AlgebraPool.sol:921: require(balance0Before.add(fee0) <= paid0, 'F0'); ./AlgebraPool.sol:935: require(balance1Before.add(fee1) <= paid1, 'F1'); ./TickTable.sol:15: require(tick % Constants.TICK_SPACING == 0, 'tick is not spaced'); // ensure that the tick is spaced ./PoolState.sol:41: require(globalState.unlocked, 'LOK'); ./AlgebraFactory.sol:109: require(uint256(alpha1) + uint256(alpha2) + uint256(baseFee) <= type(uint16).max, 'Max fee exceeded'); ./AlgebraFactory.sol:110: require(gamma1 != 0 && gamma2 != 0 && volumeGamma != 0, 'Gammas must be > 0'); ./DataStorage.sol:238: require(lteConsideringOverflow(self[oldestIndex].blockTimestamp, target, time), 'OLD');