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: 30/103
Findings: 1
Award: $184.33
🌟 Selected for report: 0
🚀 Solo Findings: 0
184.3311 USDC - $184.33
https://github.com/masaun/2022-12-caviar/blob/main/src/Pair.sol#L63-L99 https://github.com/masaun/2022-12-caviar/blob/main/src/Pair.sol#L275-L286
In case a ERC20 token that has smaller number of decimals (eg. USDC
) is used as the baseToken
, this vulnerability lead to miscalculation of LpTokens
distributed. As a result, a liquidity provider receives smaller amount of LpTokens
than they expected.
The number of decimals of ERC20 tokens are different depends on ERC20 token.
For example, unlike WETH that is a 18 decimals token, USDC
is a 6 decimals
token.
In the Pair.sol, there is possibility that USDC
is used as a base token
.
USDC
is possibly used as a base token
is described like this:
The core AMM is a stripped down version of UNI-V2 that handles swapping between a
base token
(such as WETH orUSDC
) and a fractional NFT token.
However, there is no input validation to check the number of decimals of baseToken
and there is no code in order to adjust baseTokenAmount
depends on the number of decimals of baseToken
on the add()
and nftAdd()
functions like below:
add()
function: Pair.sol#L63-L99function add(uint256 baseTokenAmount, uint256 fractionalTokenAmount, uint256 minLpTokenAmount) public payable returns (uint256 lpTokenAmount) { // *** Checks *** // // check the token amount inputs are not zero require(baseTokenAmount > 0 && fractionalTokenAmount > 0, "Input token amount is zero"); // check that correct eth input was sent - if the baseToken equals address(0) then native ETH is used require(baseToken == address(0) ? msg.value == baseTokenAmount : msg.value == 0, "Invalid ether input"); // calculate the lp token shares to mint lpTokenAmount = addQuote(baseTokenAmount, fractionalTokenAmount); // check that the amount of lp tokens outputted is greater than the min amount require(lpTokenAmount >= minLpTokenAmount, "Slippage: lp token amount out"); // *** Effects *** // // transfer fractional tokens in _transferFrom(msg.sender, address(this), fractionalTokenAmount); // *** Interactions *** // // mint lp tokens to sender lpToken.mint(msg.sender, lpTokenAmount); // transfer base tokens in if the base token is not ETH if (baseToken != address(0)) { // transfer base tokens in ERC20(baseToken).safeTransferFrom(msg.sender, address(this), baseTokenAmount); } emit Add(baseTokenAmount, fractionalTokenAmount, lpTokenAmount); }
nftAdd()
function: Pair.sol#L275-L286function nftAdd( uint256 baseTokenAmount, uint256[] calldata tokenIds, uint256 minLpTokenAmount, bytes32[][] calldata proofs ) public payable returns (uint256 lpTokenAmount) { // wrap the incoming NFTs into fractional tokens uint256 fractionalTokenAmount = wrap(tokenIds, proofs); // add liquidity using the fractional tokens and base tokens lpTokenAmount = add(baseTokenAmount, fractionalTokenAmount, minLpTokenAmount); }
Currently, baseTokenAmount
seems to be adjusted for the 18 decimal tokens only.
Thus, if the 6 decimals token such as USDC
is used as a baseToken
, its baseTokenAmount
is dealt as a 18 decimal token.
Let's say a liquidity provider try to add 1 USDC as baseTokenAmount
to liquidity and transfer it to the Pool. Its 1 USDC is dealt as 0.0000000000001 USDC
.
This lead to miscalculation of LpTokens
distributed when a liquidity provider does adding liquidity.
As a result, a liquidity provider above receives smaller amount of LpTokens
than they expected.
baseToken
used.USDC
that is a 6 decimals token) is used as a baseToken
to the first line of the add()
function or the nftAdd()
function.
_baseTokenAmount
that is adjusted depends on the number of decimals of baseToken used for calculating LpToken distributed like below is one of way that I recommend.// @dev - Calculate the difference of number of decimals between the standard token (18 decimals) and the baseToken. uint256 differenceOfDecimals = 18 - baseToken.decimals(); // @dev - Adjust baseTokenAmount for dealing with the standard token (18 decimals) uint256 _baseTokenAmount = baseTokenAmount * 10 ** (baseToken.decimals() + differenceOfDecimals);
#0 - c4-judge
2022-12-28T12:38:23Z
berndartmueller marked the issue as duplicate of #53
#1 - c4-judge
2023-01-10T09:31:34Z
berndartmueller changed the severity to 2 (Med Risk)
#2 - c4-judge
2023-01-10T09:31:38Z
berndartmueller marked the issue as satisfactory
#3 - C4-Staff
2023-01-25T12:23:03Z
CloudEllie marked the issue as duplicate of #141