Platform: Code4rena
Start Date: 10/11/2023
Pot Size: $28,000 USDC
Total HM: 5
Participants: 185
Period: 5 days
Judge: 0xDjango
Id: 305
League: ETH
Rank: 114/185
Findings: 1
Award: $4.66
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Krace
Also found by: 0xDING99YA, 0xrugpull_detector, Aamir, AlexCzm, Aymen0909, Banditx0x, Bauer, CatsSecurity, GREY-HAWK-REACH, Madalad, Phantasmagoria, QiuhaoLi, Ruhum, SBSecurity, SandNallani, SpicyMeatball, T1MOH, TheSchnilch, adam-idarrha, adriro, almurhasan, ast3ros, ayden, bronze_pickaxe, btk, chaduke, ck, crack-the-kelp, critical-or-high, deth, gumgumzum, jasonxiale, joaovwfreire, ke1caM, m_Rassska, mahdirostami, mahyar, max10afternoon, osmanozdemir1, peanuts, pep7siup, peter, ptsanev, qpzm, rouhsamad, rvierdiiev, spark, twcctop, ubl4nk, wisdomn_, zach, zhaojie
4.6614 USDC - $4.66
https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTDepositPool.sol#L95-L110 https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTOracle.sol#L52-L79
This is the formula to calculate how much rseth to mint from the amount of LST, in LRTDepositPool.getRsETHAmountToMint()
// calculate rseth amount to mint based on asset amount and asset exchange rate rsethAmountToMint = (amount * lrtOracle.getAssetPrice(asset)) / lrtOracle.getRSETHPrice();
lrtOracle.getAssetPrice(asset)
returns the ETH price of the asset, using the asset/ETH oracle and returning latestAnswer (in 18 decimal places), LRTOracle.getAssetPrice()
function getAssetPrice(address asset) public view onlySupportedAsset(asset) returns (uint256) { return IPriceFetcher(assetPriceOracle[asset]).getAssetPrice(asset); }
lrtOracle.getRSETHPrice()
gets the price of rsETH. It takes the totalETHInPool and divides by the rsETHSupply. If there is no supply yet, return 1 ether.
function getRSETHPrice() external view returns (uint256 rsETHPrice) { if (rsEthSupply == 0) { return 1 ether; } return totalETHInPool / rsEthSupply;
From all these, we know that the rsETH price is dependent on every LST asset deposited. Let's look at what potential issue can a first depositor surface.
Assume that there is no deposits, and no rsETH minted. The LST used is rETH, with a ratio of 1:1.09, rETH:ETH. (1 rETH is worth 1.09 ETH)
depositAsset()
getRsETHAmountToMint()
is called.lrtOracle.getAssetPrice(rETH)
returns 1.09e18.lrtOracle.getRSETHPrice()
returns 1e18 since rsETH supply == 0.Next, a normal user deposits 1e18 rETH through depositAsset()
getRsETHAmountToMint()
is called.lrtOracle.getAssetPrice(rETH)
returns 1.09e18.getRSETHPrice()
calculation is executedTotalAssetDeposits
is 1e18 + 1 wei of rETH. (user deposit of 1e18 + malicious user deposit of 1 wei)totalETHinPool
is (1e18 + 1) * 1.09e18 = 1.09e36totalETHinPool / rsEthSupply
= 1.09e36 / 1.09 = 1e36for (uint16 asset_idx; asset_idx < supportedAssetCount;) { address asset = supportedAssets[asset_idx]; uint256 assetER = getAssetPrice(asset); uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr).getTotalAssetDeposits(asset); totalETHInPool += totalAssetAmt * assetER; unchecked { ++asset_idx; }
rETH/ETH latestAnswer returns 18 decimals.
https://etherscan.io/address/0x536218f9e9eb48863970252233c8f271f554c2d0#readContract
First depositor breaks the entire accounting of rsETH mints. Subsequent depositors, if not careful, will lose their funds when converting LST to rsETH.
VSCode
Either make sure the first user cannot deposit such a dust amount. Or make sure the protocol mints the first few rsETH supply, so that the first depositor problem can be mitigated.
Invalid Validation
#0 - c4-pre-sort
2023-11-16T22:39:23Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-11-16T22:39:34Z
raymondfam marked the issue as duplicate of #42
#2 - c4-judge
2023-12-01T17:02:48Z
fatherGoose1 changed the severity to 3 (High Risk)
#3 - c4-judge
2023-12-01T17:06:34Z
fatherGoose1 marked the issue as satisfactory