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: 179/185
Findings: 1
Award: $2.76
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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
https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L162-L176 https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L71-L89 https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L47-L58 https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTOracle.sol#L70
To add new node delegators, the LRTAdmin calls the addNodeDelegatorContractToQueue
which takes an array of node delegators and pushes them to the nodeDelegatorQueue
. The added node delegators aren't checked to be unique so nodeDelegatorQueue
may contain duplicate node delegator addresses.
If nodeDelegatorQueue
contains duplicates then LRTDepositPool:getAssetDistributionData
returns an incorrect, higher value for assetLyingInNDCs and assetStakedInEigenLayer. This would cause LRTDepositPool:getTotalAssetDeposits
to also return a higher value which affects both the LRTOracle:getRSETHPrice
and LRTDepositPool:getAssetCurrentLimit
functions.
A higher return value for LRTDepositPool:getAssetCurrentLimit
could prevent deposits before the real limit for the asset is reached.
LRTOracle:getRSETHPrice
could cause users to receive less rSETH tokens when depositing.
The test code below shows how duplicate node delegator addresses may be added and that LRTDepositPool:getAssetDistributionData
returns inflated values.
contract LRTDepositPoolGetAssetDistributionData is LRTDepositPoolTest { [code...] function test_DuplicateNodeDelegatorsIncorrectData() external { address nodeDelegatorContractOne = address(new MockNodeDelegator()); address nodeDelegatorContractTwo = address(new MockNodeDelegator()); address nodeDelegatorContractThree = address(new MockNodeDelegator()); address[] memory nodeDelegatorQueue = new address[](3); nodeDelegatorQueue[0] = nodeDelegatorContractOne; nodeDelegatorQueue[1] = nodeDelegatorContractTwo; nodeDelegatorQueue[2] = nodeDelegatorContractThree; vm.startPrank(admin); lrtDepositPool.addNodeDelegatorContractToQueue(nodeDelegatorQueue); vm.stopPrank(); // deposit 3 ether rETH vm.startPrank(alice); rETH.approve(address(lrtDepositPool), 3 ether); lrtDepositPool.depositAsset(rETHAddress, 3 ether); vm.stopPrank(); (uint256 assetLyingInDepositPool, uint256 assetLyingInNDCs, uint256 assetStakedInEigenLayer) = lrtDepositPool.getAssetDistributionData(rETHAddress); assertEq(assetLyingInDepositPool, 3 ether, "Asset lying in deposit pool is not set"); assertEq(assetLyingInNDCs, 0, "Asset lying in NDCs is not set"); assertEq(assetStakedInEigenLayer, 3 ether, "Asset staked in eigen layer is not set"); // Add duplicates vm.startPrank(admin); lrtDepositPool.addNodeDelegatorContractToQueue(nodeDelegatorQueue); vm.stopPrank(); (assetLyingInDepositPool, assetLyingInNDCs, assetStakedInEigenLayer) = lrtDepositPool.getAssetDistributionData(rETHAddress); assertEq(assetLyingInDepositPool, 3 ether, "Asset lying in deposit pool is not set"); assertEq(assetLyingInNDCs, 0, "Asset lying in NDCs is not set"); assertEq(assetStakedInEigenLayer, 6 ether, "Asset staked in eigen layer is not set"); } }
With inflated values returned, LRTDepositPool:getTotalAssetDeposits
also returns a higher value which causes LRTDepositPool:getAssetCurrentLimit
to either return a lower value than it should or revert due to underflow preventing further deposits.
function getAssetCurrentLimit(address asset) public view override returns (uint256) { return lrtConfig.depositLimitByAsset(asset) - getTotalAssetDeposits(asset); }
The incorrect returned value from LRTDepositPool:getTotalAssetDeposits
would also cause LRTOracle:getRESTHPrice
to have a higher totalETHInPool
value that it should, leading to a higher returned value.
function getRSETHPrice() external view returns (uint256 rsETHPrice) { [code...] for (uint16 asset_idx; asset_idx < supportedAssetCount;) { [code...] uint256 totalAssetAmt = ILRTDepositPool(lrtDepositPoolAddr).getTotalAssetDeposits(asset); totalETHInPool += totalAssetAmt * assetER; unchecked { ++asset_idx; } } return totalETHInPool / rsEthSupply; }
This causes LRTDepositPool:getRsETHAmountToMint
to be lower, leading to less shares for the user when depositing assets.
function _mintRsETH(address _asset, uint256 _amount) private returns (uint256 rsethAmountToMint) { (rsethAmountToMint) = getRsETHAmountToMint(_asset, _amount); address rsethToken = lrtConfig.rsETH(); // mint rseth for user IRSETH(rsethToken).mint(msg.sender, rsethAmountToMint); }
Manual
Foundry
Consider using a mapping to set check if a node delegator has been added.
Invalid Validation
#0 - c4-pre-sort
2023-11-16T21:50:58Z
raymondfam marked the issue as insufficient quality report
#1 - c4-pre-sort
2023-11-16T21:51:07Z
raymondfam marked the issue as duplicate of #36
#2 - c4-judge
2023-11-29T21:35:51Z
fatherGoose1 changed the severity to QA (Quality Assurance)
#3 - c4-judge
2023-11-29T21:44:17Z
fatherGoose1 marked the issue as grade-b