Wrong precision scaling on ThunderLoan::deposit::mintAmount & ThunderLoanUpgraded::deposit::mintAmount & ThunderLoan::deposit::redeem & ThunderLoanUpgraded::deposit::redeem & AssetToken::updateExchangeRate because we're using both the liquidity token and the asset token and the fee and the totalSupply as though they had the same decimals value which isn't forcibly the case for all erc20 tokens on ethereum.
This problem of lack of precision scaling can be found at multiple locations in the codebase.
ThunderLoan::deposit::mintAmount when converting from liquidity token to asset token
ThunderLoanUpgraded::deposit::mintAmount when converting from liquidity token to asset token
ThunderLoan::redeem::amountUnderlying when converting from asset token to liquidity token
ThunderLoanUpgraded::redeem::amountUnderlying when converting from asset token to liquidity token
AssetToken::updateExchangeRate::fee when determining the new exchange rate. The fee being in a different precision is added onto the totalSupply as though they both were of the same decimal precision. Which they aren't for certain tokens like say USDC.
For ThunderLoan::deposit/ThunderLoanUpgraded::deposit
mintAmount, the amount of assetToken to be minted for the amount of liquidity token deposited is derived from the expression
uint256 mintAmount = (amount * assetToken.EXCHANGE_RATE_PRECISION()) / exchangeRate
The exaplanation on AssetToken::s_exchangeRate says that, s_exchangeRate is the underlying per asset exchange rate.
ie: s_exchangeRate = 2
means 1 asset token is worth 2 underlying tokens.
Using the initial values of both:
AssetToken::s_exchangeRate = 1e18 and AssetToken::EXCHANGE_RATE_PRECISION = 1e18
this effectively means that, if we deposit 1 unit of liquidity token ( ie amount = 1 ) we expect to be minted 1 unit of assetToken. There exist ERC20 tokens with different decimal values. For example:
USDT on eth having a decimal value of 6
USDC on eth having a decimal value of 6
ZIL on eth having a decimal value of 12
Using one such token as the liquidity token means that, the mintAmount calculated above will be wrong as can be seen in the below calculatioon.
using the above s_exchangeRate and EXCHANGE_RATE_PRECISION values. If I deposit 1 USDC ( decimals = 1e6 ), then I expect to receive 1 assetToken (decimals = 1e18)
uint256 mintAmount = (1e6 * 1e18)/1e18;
ergo,
uint256 mintAmount = 1e6; // < 1e18
We can clearly see that, the LP will be minted less tokens than expected.
test/unit/ThunderLoanTest.t.sol:testDepositMintsAssetAndUpdatesBalance clearly shows us that, the LP is minted the same numerical value of liquidity he provided regardless of the decimals of the liquidity token he's providing. Herein lies the problem.
For ThunderLoan::redeem/ThunderLoanUpgraded::redeem
Applying a logic similar to the above for the redeem functions, we notice that, amountUnderlying has a precision of 18 decimals but it's being transfered on the liquidity token as though the liquidity token has 18 decimals of precision as well. Which we've already established, it doesn't always have 18 decimals of precision. This effectively means that, if the liquidity token is say USDC, we'll be sending the LP more tokens than he initially wanted to redeem and at the expense of the other liquidity providers.
For AssetToken::updateExchangeRate::fee
Applying a logic similar to the above for the updateExchangeRate, we notice that, newExchangeRate is smaller or greater than should have been because we're taking fee which can of a precision greater than or lesser than that of AssetToken and adding to the totalSupply() of AssetToken as though they both have the same precisions. Using USDC as our liquidity token, then newExchangeRate will be less that what is expected.
For tokens with a precision greater than that of assetToken
LP is minted more assetToken than expected for the amount of liquidity he deposits.
During redemption, LP is provided with lesser liquidity tokens than was supposed to.
During updateExchangeRate, the fee is greater than expected
For tokens with a precision lower than that of assetToken
LP is minted less assetToken than expected for the amount of liquidity he deposits.
During redemption, LP is provided with more liquidity tokens than was supposed to.
During updateExchangeRate, the fee is lesser than expected
Manual review
We should scale the precision of mintAmount to that of the assetToken before doing the minting.
This test assumes `s_precisionFee == s_exchangeRate == 1e18`
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.