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: 82/113
Findings: 1
Award: $35.48
🌟 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
The initialize-function of an AlgebraPool can be frontrun to set an arbitrary initial price. This will negatively affect the first person to add liquidity to the pool.
There are no restrictions on the AlgebraPool.initialize(price)
function and it is not called as part of the deployment process.
So a malicious user (call her Eve) could look on the mempool for a sequence of deploy
->initialize
->mint
transactions of someone (call her Alice) who deploys a pool for DAI and her own Token A and wants to add liquidity at the same time (for example by using a deploy script, but would work the same if she just manually sends transactions over multiple blocks). Alice wants an initial ratio of 1:10 (1 DAI can be exchanged for 10 A tokens). Now Eve could frontrun the initialize
tx and set the ratio to 1:100 instead, so that Alices mint
transaction would cause her to add more A tokens than she intended to add, which Eve could then buy for much less DAI than she would have had to pay for else.
The following small snippet demonstrates the difference between initializing price with a ratio of 1:10 vs 1:100 (code mostly from AlgebraPool.spec.ts
, just added the frontrun test case):
//... describe('#mint', () => { it('fails if not initialized', async () => { await expect(mint(wallet.address, -tickSpacing, tickSpacing, 1)).to.be.revertedWith('LOK') }) describe('frontrun initialization', () => { beforeEach('initialize the pool at price of 100:1', async () => { await pool.initialize(encodePriceSqrt(1, 100)) await mint(wallet.address, minTick, maxTick, 3161) }) it('has disadvantageous token ratio', async () => { expect(await token0.balanceOf(pool.address)).to.eq(31610) expect(await token1.balanceOf(pool.address)).to.eq(317) }) }) describe('after initialization', () => { beforeEach('initialize the pool at price of 10:1', async () => { await pool.initialize(encodePriceSqrt(1, 10)) await mint(wallet.address, minTick, maxTick, 3161) }) describe('success cases', () => { it('initial balances', async () => { expect(await token0.balanceOf(pool.address)).to.eq(9996) expect(await token1.balanceOf(pool.address)).to.eq(1000) }) //... }
Manual Review
Options:
#0 - 0xean
2022-10-02T22:34:55Z
dupe of #84