QuickSwap and StellaSwap contest - ladboy233's results

A concentrated liquidity DEX with dynamic fees.

General Information

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

QuickSwap and StellaSwap

Findings Distribution

Researcher Performance

Rank: 25/113

Findings: 3

Award: $111.57

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

35.4829 USDC - $35.48

Labels

bug
duplicate
2 (Med Risk)

External Links

Lines of code

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#L193

Vulnerability details

Impact

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.

Proof of Concept

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

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/test/AlgebraPool.spec.ts#L121

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.

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#L472

(, int256 amount0Int, int256 amount1Int) = _updatePositionTicksAndFees(recipient, bottomTick, topTick, int256(liquidityActual).toInt128());

Tools Used

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

Solidity version is not update-to-date

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

require statement does not have reason of failure

User will not know which steps goes wrong when transaction reverted.

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraFactory.sol#:43

require(msg.sender == owner);

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraFactory.sol#:60

require(tokenA != tokenB);

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraFactory.sol#:62

require(token0 != address(0));

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraFactory.sol#:63

require(poolByPair[token0][token1] == address(0));

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraFactory.sol#:78

require(owner != _owner);

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraFactory.sol#:85

require(farmingAddress != _farmingAddress);

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraFactory.sol#:92

require(vaultAddress != _vaultAddress);

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#:55

require(msg.sender == IAlgebraFactory(factory).owner());

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#:122

require(_lower.initialized);

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#:134

require(_upper.initialized);

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#:229

require((_blockTimestamp() - lastLiquidityAddTimestamp) >= _liquidityCooldown);

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#:953

require((communityFee0 <= Constants.MAX_COMMUNITY_FEE) && (communityFee1 <= Constants.MAX_COMMUNITY_FEE));

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#:960

require(msg.sender == IAlgebraFactory(factory).farmingAddress());

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#:968

require(newLiquidityCooldown <= Constants.MAX_LIQUIDITY_COOLDOWN && liquidityCooldown != newLiquidityCooldown);

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPoolDeployer.sol#:22

require(msg.sender == factory);

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPoolDeployer.sol#:27

require(msg.sender == owner);

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPoolDeployer.sol#:37

require(_factory != address(0));

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPoolDeployer.sol#:38

require(factory == address(0));

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/DataStorageOperator.sol#:43

require(msg.sender == factory || msg.sender == IAlgebraFactory(factory).owner());

nested function make it diffucult to read and reason about the contract

there is nested function, we recommand move the internal function out and create a seperate function

https://github.com/code-423n4/2022-09-quickswap/blob/main/src/core/contracts/libraries/PriceMovementMath.sol/#L153

returns ( uint160 resultPrice, uint256 input, uint256 output, uint256 feeAmount ) { function(uint160, uint160, uint128) pure returns (uint256) getAmountA = zeroToOne ? getTokenADelta01 : getTokenADelta10;

Splitting require() statements that use && saves gas

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

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraFactory.sol#:110

require(gamma1 != 0 && gamma2 != 0 && volumeGamma != 0, 'Gammas must be > 0');

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#:739

require(limitSqrtPrice < currentPrice && limitSqrtPrice > TickMath.MIN_SQRT_RATIO, 'SPL');

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#:743

require(limitSqrtPrice > currentPrice && limitSqrtPrice < TickMath.MAX_SQRT_RATIO, 'SPL');

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#:953

require((communityFee0 <= Constants.MAX_COMMUNITY_FEE) && (communityFee1 <= Constants.MAX_COMMUNITY_FEE));

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPool.sol#:968

require(newLiquidityCooldown <= Constants.MAX_LIQUIDITY_COOLDOWN && liquidityCooldown != newLiquidityCooldown);

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/DataStorageOperator.sol#:46

require(_feeConfig.gamma1 != 0 && _feeConfig.gamma2 != 0 && _feeConfig.volumeGamma != 0, 'Gammas must be > 0');

ABI.ENCODE() IS LESS EFFICIENT THAN ABI.ENCODEPACKED()

https://github.com/code-423n4/2022-09-quickswap/blob/15ea643c85ed936a92d2676a7aabf739b210af39/src/core/contracts/AlgebraPoolDeployer.sol#:51

pool = address(new AlgebraPool{salt: keccak256(abi.encode(token0, token1))}());
AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter