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: 66/103
Findings: 1
Award: $40.26
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Jeiwan
Also found by: 0xxm, 9svR6w, BAHOZ, Bobface, CRYP70, Chom, HE1M, Junnon, RaymondFam, UNCHAIN, __141345__, bytehat, carlitox477, caventa, cccz, chaduke, hansfriese, hihen, koxuan, mauricio1802, minhquanym, minhtrng, nicobevi, obront, shung, unforgiven, wait
40.2564 USDC - $40.26
https://github.com/code-423n4/2022-12-caviar/blob/0212f9dc3b6a418803dbfacda0e340e059b8aae2/src/Pair.sol#L417-L428 https://github.com/code-423n4/2022-12-caviar/blob/0212f9dc3b6a418803dbfacda0e340e059b8aae2/src/Pair.sol#L76-L80
The add
function in Pair.sol allows users to add liquidity to the contract. One of the parameters of this function, called minLpTokenAmount
, is used to calculate the necessary amount of liquidity to prevent slippage using the addQuote
function. However, minLpTokenAmount
does not prevent users from inputting the wrong amount of baseToken
and fractionalToken
, which may result in the loss of user funds.
the addQuote
is used to calculate amount of LP Token will be received by user. the function calculate using Math.min
between LP calculation of baseToken
and fractionalToken
. this is the code snippets
function addQuote(uint256 baseTokenAmount, uint256 fractionalTokenAmount) public view returns (uint256) { uint256 lpTokenSupply = lpToken.totalSupply(); if (lpTokenSupply > 0) { // calculate amount of lp tokens as a fraction of existing reserves uint256 baseTokenShare = (baseTokenAmount * lpTokenSupply) / baseTokenReserves(); uint256 fractionalTokenShare = (fractionalTokenAmount * lpTokenSupply) / fractionalTokenReserves(); return Math.min(baseTokenShare, fractionalTokenShare); } else { // if there is no liquidity then init return Math.sqrt(baseTokenAmount * fractionalTokenAmount); } }
This function will return the minimum value between baseTokenShare
and fractionalTokenShare
. Consider the following scenario:
addQuote
function will return the same amount of LP Tokens regardless of whether Bob adds 100 Fractional Tokens or just 30 Fractional Tokens.for code POC add this following code to Add.t.sol:
function testAddWrongAmount() public { // arrange uint256 minLpTokenAmount = Math.sqrt(baseTokenAmount * fractionalTokenAmount); p.add(baseTokenAmount, fractionalTokenAmount, minLpTokenAmount); // initial add uint256 lpTokenSupplyBefore = lpToken.totalSupply(); //Babe Balance Calculation //Babe Base Token Amount = 100 uint256 babeUsdAmount = baseTokenAmount; // Babe Fractional Token Amount = 100 uint256 babeFractionalAmount = baseTokenAmount; // Babe Receive the same amount of LP token as received by address(this) uint256 expectedLpTokenAmount = lpTokenSupplyBefore; //According to sponsor this method use to calculate minLpTokenAmount in Offchain. minLpTokenAmount = p.addQuote(babeUsdAmount, babeFractionalAmount); //Give babe token for the transaction deal(address(usd), babe, babeUsdAmount, true); deal(address(p), babe, babeFractionalAmount, true); // act vm.startPrank(babe); usd.approve(address(p), type(uint256).max); uint256 lpTokenAmount = p.add(babeUsdAmount, babeFractionalAmount, minLpTokenAmount); vm.stopPrank(); // assert assertEq(lpToken.balanceOf(address(this)), lpToken.balanceOf(babe), "Babe balance should same as address(this)" ); assertEq(lpToken.balanceOf(babe), expectedLpTokenAmount, "Should have minted lp tokens" ); assertEq(lpToken.totalSupply() - lpTokenSupplyBefore, expectedLpTokenAmount, "Should have increased lp supply" ); // Babe removing LP vm.startPrank(babe); p.remove(lpTokenAmount,0,0); vm.stopPrank(); // address(this) removing LP p.remove(lpTokenAmount,0,0); // these 2 user have same amount of base token assertEq(usd.balanceOf(address(this)), usd.balanceOf(babe), "Base Token Balance of Babe should same as balance of address(this)" ); // these 2 users have same amount of fractional token assertEq(p.balanceOf(address(this)), p.balanceOf(babe), "Fractional Token Balance of Babe should same as balance of address(this)" ); }
The add
function needs to verify that the composition of baseTokenAmount
and fractionalTokenAmount
is equal to the price of the token. There is already a price
function within the contract to assist with this check. If the contract needs to allow for a certain amount of price fluctuation, a parameter can be added and compared with the current price.
example of mitigation
//let say there's toleration of 1% slippage require(baseTokenAmount < fractionalTokenAmount * price() * 100 / 10000)
#0 - jraynaldi3
2022-12-20T00:58:07Z
i'm sorry, the code inside example of mitigation is wrong because i use 100 instead of 10100 which represent 101%. it should be like this instead
//let say there's toleration of 1% slippage require( baseTokenAmount < fractionalTokenAmount * price() * 10100 / 10000 && baseTokenAmount > fractionalTokenAmount * price() * 9900 / 10000 )
#1 - c4-judge
2022-12-28T12:34:30Z
berndartmueller marked the issue as duplicate of #376
#2 - c4-judge
2023-01-10T09:01:39Z
berndartmueller changed the severity to 3 (High Risk)
#3 - c4-judge
2023-01-10T09:01:43Z
berndartmueller marked the issue as satisfactory