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: 15/185
Findings: 3
Award: $490.08
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: m_Rassska
Also found by: 0xDING99YA, Aamir, Bauchibred, CatsSecurity, HChang26, PENGUN, SBSecurity, adriro, almurhasan, anarcheuz, circlelooper, d3e4, deth, jayfromthe13th
451.2859 USDC - $451.29
User may deposit low value asset in order to get high value asset, high value asset depositor will suffer loss.
User deposits asset and receives RSETH, when withdraw (in the future when it's implemented), it's highly likely that user will not get the same asset back, for that the contract simply mints RSETH to user, but not save the asset user deposits.
The assets used to deposit have different prices, so users are tempted to deposit low value asset in order to withdraw high value asset. High value asset depositors will suffer loss if they are unable to withdraw the same asset as they deposit.
Manual Review
Please consider to save user's depositing asset, so user can withdraw the same asset.
MEV
#0 - c4-pre-sort
2023-11-16T03:32:11Z
raymondfam marked the issue as insufficient quality report
#1 - c4-pre-sort
2023-11-16T03:32:46Z
raymondfam marked the issue as duplicate of #284
#2 - c4-pre-sort
2023-11-18T01:30:53Z
raymondfam marked the issue as duplicate of #584
#3 - c4-judge
2023-12-01T17:09:01Z
fatherGoose1 marked the issue as unsatisfactory: Invalid
#4 - c4-judge
2023-12-08T17:44:03Z
fatherGoose1 marked the issue as partial-50
#5 - fatherGoose1
2023-12-08T17:44:26Z
The core impact is correct, but very little explanation or recommended mitigation steps are provided.
#6 - c4-judge
2023-12-08T18:55:04Z
fatherGoose1 changed the severity to 3 (High Risk)
🌟 Selected for report: T1MOH
Also found by: 0x1337, 0xNaN, 0xepley, 0xluckhu, 0xmystery, 7siech, Aamir, AlexCzm, Aymen0909, DanielArmstrong, GREY-HAWK-REACH, HChang26, Jiamin, Juntao, QiuhaoLi, Ruhum, SBSecurity, Varun_05, Weed0607, adam-idarrha, adriro, ast3ros, ayden, circlelooper, crack-the-kelp, crunch, cryptothemex, deepplus, mahdirostami, max10afternoon, osmanozdemir1, rouhsamad, rvierdiiev, trachev, xAriextz, zhaojie
36.0335 USDC - $36.03
Incorrect amount of RSETH is minted to depositor.
To mint RSETH, user is required to transfer asset to LRTDepositPool contract.
function depositAsset( address asset, uint256 depositAmount ) external whenNotPaused nonReentrant onlySupportedAsset(asset) { // checks if (depositAmount == 0) { revert InvalidAmount(); } if (depositAmount > getAssetCurrentLimit(asset)) { revert MaximumDepositLimitReached(); } if (!IERC20(asset).transferFrom(msg.sender, address(this), depositAmount)) { revert TokenTransferFailed(); } // interactions uint256 rsethAmountMinted = _mintRsETH(asset, depositAmount); emit AssetDeposit(asset, depositAmount, rsethAmountMinted); }
The amount of RSETH to be minted is calculated in function getRsETHAmountToMint:
function getRsETHAmountToMint( address asset, uint256 amount ) public view override returns (uint256 rsethAmountToMint) { // setup oracle contract address lrtOracleAddress = lrtConfig.getContract(LRTConstants.LRT_ORACLE); ILRTOracle lrtOracle = ILRTOracle(lrtOracleAddress); // calculate rseth amount to mint based on asset amount and asset exchange rate rsethAmountToMint = (amount * lrtOracle.getAssetPrice(asset)) / lrtOracle.getRSETHPrice(); }
The amount is thus determined by deposit asset amount, asset price and RSETH price. Let's examine the RSETH price in getRSETHPrice.
function getRSETHPrice() external view returns (uint256 rsETHPrice) { address rsETHTokenAddress = lrtConfig.rsETH(); uint256 rsEthSupply = IRSETH(rsETHTokenAddress).totalSupply(); if (rsEthSupply == 0) { return 1 ether; } uint256 totalETHInPool; address lrtDepositPoolAddr = lrtConfig.getContract(LRTConstants.LRT_DEPOSIT_POOL); address[] memory supportedAssets = lrtConfig.getSupportedAssetList(); uint256 supportedAssetCount = supportedAssets.length; for (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; } } return totalETHInPool / rsEthSupply; }
If there is no RSETH has been minted, then RSETH price is 1e18, otherwise the price is determined by the asset amount in the contract and the RSETH supply. Recall that when user deposits, the asset is transferred to the contract firstly, this is wrong and incorrect RSETH is minted to user. Consider the following 2 scenarios:
Manual Review
Please consider to mint first then transfer the asset to the contract.
Math
#0 - c4-pre-sort
2023-11-16T03:33:14Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-11-16T03:33:21Z
raymondfam marked the issue as duplicate of #62
#2 - c4-judge
2023-11-29T21:21:03Z
fatherGoose1 marked the issue as satisfactory
#3 - c4-judge
2023-12-01T19:00:05Z
fatherGoose1 changed the severity to 2 (Med Risk)
#4 - c4-judge
2023-12-04T15:31:41Z
fatherGoose1 changed the severity to 3 (High Risk)
🌟 Selected for report: m_Rassska
Also found by: 0x1337, 0xAadi, 0xHelium, 0xLeveler, 0xblackskull, 0xbrett8571, 0xepley, 0xffchain, 0xluckhu, 0xmystery, 0xrugpull_detector, 0xvj, ABAIKUNANBAEV, Aamir, AerialRaider, Amithuddar, Bauchibred, Bauer, CatsSecurity, Cryptor, Daniel526, Draiakoo, Eigenvectors, ElCid, GREY-HAWK-REACH, Inspecktor, Juntao, King_, LinKenji, Madalad, MaslarovK, Matin, MatricksDeCoder, McToady, Noro, PENGUN, Pechenite, Phantasmagoria, RaoulSchaffranek, SBSecurity, SandNallani, Shaheen, Soul22, Stormreckson, T1MOH, Tadev, TeamSS, TheSchnilch, Topmark, Tumelo_Crypto, Udsen, Yanchuan, ZanyBonzy, _thanos1, adeolu, adriro, alexfilippov314, almurhasan, amaechieth, anarcheuz, ayden, baice, bareli, boredpukar, bronze_pickaxe, btk, cartlex_, catellatech, chaduke, cheatc0d3, circlelooper, codynhat, crack-the-kelp, critical-or-high, debo, deepkin, desaperh, dipp, eeshenggoh, evmboi32, ge6a, gesha17, glcanvas, gumgumzum, hals, hihen, hunter_w3b, jasonxiale, joaovwfreire, ke1caM, leegh, lsaudit, marchev, merlinboii, niser93, osmanozdemir1, paritomarrr, passion, pep7siup, phoenixV110, pipidu83, poneta, ro1sharkm, rouhsamad, rvierdiiev, sakshamguruji, seerether, shealtielanz, soliditytaker, spark, squeaky_cactus, stackachu, supersizer0x, tallo, taner2344, turvy_fuzz, twcctop, ubl4nk, wisdomn_, xAriextz, zach, zhaojie, zhaojohnson, ziyou-
2.7592 USDC - $2.76
Queued withdrawal asset in EigenLayer is ignored when calculate the asset balance.
Kelp's total asset balance is composed of 3 parts: asset lying in deposit pool, asset lying in NodeDelegators and asset staked in EigenLayer:
(uint256 assetLyingInDepositPool, uint256 assetLyingInNDCs, uint256 assetStakedInEigenLayer) = getAssetDistributionData(asset);
When calculate the asset staked in Eigenlayer, Kelp calls EigenLayer strategy contract's function userUnderlyingView
:
/// @dev Returns the balance of an asset that the node delegator has deposited into the strategy /// @param asset the asset to get the balance of /// @return stakedBalance the balance of the asset function getAssetBalance(address asset) external view override returns (uint256) { address strategy = lrtConfig.assetStrategy(asset); return IStrategy(strategy).userUnderlyingView(address(this)); }
This function converts user's share to the underlying asset, when user deposit or withdraw, user's share will be updated. However, 2 steps are required to withdraw from EigenLayer.
queueWithdrawal
to queue a withdrawal of the given amount of shares from each of the respective given strategies, in this function, user's share will be deducted;completeQueuedWithdrawal
or completeQueuedWithdrawals
to complete the specified queuedWithdrawal
, in these functions, user's asset will be returned.It's possible that a user deposits after a withdrawal is queued but before the withdrawal is completed, if that is the case, the total asset balance is calculated without the queued asset, because the total asset balance will affect the RSETH price, which would in turn affect the minted amount when user deposits, so wrong amount of RSETH will be minted.
Manual Review
Please consider to add queued withdrawal asset balance to total asset balance in the calculation.
Other
#0 - c4-pre-sort
2023-11-16T03:33:35Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2023-11-16T03:33:52Z
raymondfam marked the issue as duplicate of #197
#2 - c4-pre-sort
2023-11-16T03:34:19Z
raymondfam marked the issue as not a duplicate
#3 - c4-pre-sort
2023-11-16T03:35:29Z
raymondfam marked the issue as duplicate of #294
#4 - c4-judge
2023-12-01T17:41:34Z
fatherGoose1 marked the issue as unsatisfactory: Invalid
#5 - c4-judge
2023-12-06T18:19:18Z
fatherGoose1 changed the severity to QA (Quality Assurance)
#6 - c4-judge
2023-12-08T18:52:34Z
fatherGoose1 marked the issue as grade-b