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: 25/113
Findings: 3
Award: $111.57
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: 0xSmartContract
Also found by: 0xDecorativePineapple, 0xNazgul, 0xmatt, Jeiwan, Trust, berndartmueller, brgltd, catchup, ch13fd357r0y3r, cryptonue, ladboy233, minhtrng, neko_nyaa, rbserver, rvierdiiev, s3cunda
35.4829 USDC - $35.48
Detailed description of the impact of this finding.
Lack of authorization for initialize function in Algebra Pool allow anyone to set the initialized price
function initialize(uint160 initialPrice) external override {
then mint position or swap at invalid price at the cost of user.
Provide direct links to all referenced, code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
let look into the initialize function
/// @inheritdoc IAlgebraPoolActions function initialize(uint160 initialPrice) external override { require(globalState.price == 0, 'AI'); // getTickAtSqrtRatio checks validity of initialPrice inside int24 tick = TickMath.getTickAtSqrtRatio(initialPrice); uint32 timestamp = _blockTimestamp(); IDataStorageOperator(dataStorageOperator).initialize(timestamp, tick); globalState.price = initialPrice; globalState.unlocked = true; globalState.tick = tick; emit Initialize(initialPrice, tick); }
anyone one can set the the initialized price as long as the price pass the math check in
int24 tick = TickMath.getTickAtSqrtRatio(initialPrice);
as demonstrate in the test case
describe('#initialize', () => { it('fails if already initialized', async () => { await pool.initialize(encodePriceSqrt(1, 1)) await expect(pool.initialize(encodePriceSqrt(1, 1))).to.be.reverted })
when the initial price is set,
the price is passed into
globalState.price
the globalState.price is used extensively
_calculateSwapAndLock() in swap UpdatePositionCache in mint function.
A malicious actor could monitor the transaction when a new pool is deployed,
calculate a price, and front run others to set the initial price,
then call
function mint
to mint an invalid amount of position
the mint would compute amount0int and amount1int based on the init price user set.
(, int256 amount0Int, int256 amount1Int) = _updatePositionTicksAndFees(recipient, bottomTick, topTick, int256(liquidityActual).toInt128());
Hardhat, Manual Review
We recommend the project set the initialized price based on the liquid provided in the constructor of the contract to remove the need to call the initialize function separately at the risk of front running.
#0 - 0xean
2022-10-02T22:03:42Z
dupe of #84
🌟 Selected for report: 0xNazgul
Also found by: 0x1f8b, 0x52, 0xDecorativePineapple, 0xSmartContract, 0xmatt, Aeros, Aymen0909, Bnke0x0, Chom, CodingNameKiki, Deivitto, DimitarDimitrov, IllIllI, JC, Jeiwan, Lambda, Matin, Migue, Mukund, Ocean_Sky, Olivierdem, RaymondFam, RockingMiles, Rolezn, Ruhum, Satyam_Sharma, Shinchan, Tomo, Trabajo_de_mates, V_B, Waze, __141345__, a12jmx, ajtra, asutorufos, aysha, brgltd, bulej93, carrotsmuggler, catchup, cccz, chrisdior4, cryptonue, cryptphi, d3e4, defsec, delfin454000, durianSausage, erictee, fatherOfBlocks, gogo, kaden, karanctf, ladboy233, lukris02, mahdikarimi, martin, mics, natzuu, oyc_109, p_crypt0, pedr02b2, rbserver, reassor, rotcivegaf, rvierdiiev, sikorico, slowmoses, sorrynotsorry, tnevler, trustindistrust
52.0694 USDC - $52.07
the contract uses the solidity version 0.7.6
Use a solidity version of at least 0.8.0 to get overflow protection without SafeMath Use a solidity version of at least 0.8.2 to get simple 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
User will not know which steps goes wrong when transaction reverted.
require(msg.sender == owner);
require(tokenA != tokenB);
require(token0 != address(0));
require(poolByPair[token0][token1] == address(0));
require(owner != _owner);
require(farmingAddress != _farmingAddress);
require(vaultAddress != _vaultAddress);
require(msg.sender == IAlgebraFactory(factory).owner());
require(_lower.initialized);
require(_upper.initialized);
require((_blockTimestamp() - lastLiquidityAddTimestamp) >= _liquidityCooldown);
require((communityFee0 <= Constants.MAX_COMMUNITY_FEE) && (communityFee1 <= Constants.MAX_COMMUNITY_FEE));
require(msg.sender == IAlgebraFactory(factory).farmingAddress());
require(newLiquidityCooldown <= Constants.MAX_LIQUIDITY_COOLDOWN && liquidityCooldown != newLiquidityCooldown);
require(msg.sender == factory);
require(msg.sender == owner);
require(_factory != address(0));
require(factory == address(0));
require(msg.sender == factory || msg.sender == IAlgebraFactory(factory).owner());
there is nested function, we recommand move the internal function out and create a seperate function
returns ( uint160 resultPrice, uint256 input, uint256 output, uint256 feeAmount ) { function(uint160, uint160, uint128) pure returns (uint256) getAmountA = zeroToOne ? getTokenADelta01 : getTokenADelta10;
🌟 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
24.0179 USDC - $24.02
https://github.com/code-423n4/2022-01-xdefi-findings/issues/128
See this issue which describes the fact that there is a larger deployment gas cost, but with enough runtime calls, the change ends up being cheaper
require(gamma1 != 0 && gamma2 != 0 && volumeGamma != 0, 'Gammas must be > 0');
require(limitSqrtPrice < currentPrice && limitSqrtPrice > TickMath.MIN_SQRT_RATIO, 'SPL');
require(limitSqrtPrice > currentPrice && limitSqrtPrice < TickMath.MAX_SQRT_RATIO, 'SPL');
require((communityFee0 <= Constants.MAX_COMMUNITY_FEE) && (communityFee1 <= Constants.MAX_COMMUNITY_FEE));
require(newLiquidityCooldown <= Constants.MAX_LIQUIDITY_COOLDOWN && liquidityCooldown != newLiquidityCooldown);
require(_feeConfig.gamma1 != 0 && _feeConfig.gamma2 != 0 && _feeConfig.volumeGamma != 0, 'Gammas must be > 0');
pool = address(new AlgebraPool{salt: keccak256(abi.encode(token0, token1))}());