Kelp DAO | rsETH - circlelooper's results

A collective DAO designed to unlock liquidity, DeFi and higher rewards for restaked assets through liquid restaking.

General Information

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

Kelp DAO

Findings Distribution

Researcher Performance

Rank: 15/185

Findings: 3

Award: $490.08

QA:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Labels

bug
3 (High Risk)
insufficient quality report
partial-50
upgraded by judge
duplicate-584

Awards

451.2859 USDC - $451.29

External Links

Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTDepositPool.sol#L116-L144

Vulnerability details

Impact

User may deposit low value asset in order to get high value asset, high value asset depositor will suffer loss.

Proof of Concept

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.

Tools Used

Manual Review

Please consider to save user's depositing asset, so user can withdraw the same asset.

Assessed type

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)

Awards

36.0335 USDC - $36.03

Labels

bug
3 (High Risk)
satisfactory
sufficient quality report
upgraded by judge
duplicate-62

External Links

Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/LRTDepositPool.sol#L119-L144

Vulnerability details

Impact

Incorrect amount of RSETH is minted to depositor.

Proof of Concept

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:

  1. Alice deposits 2 ether asset when RSETH supply is 0, she will get 2 ether RSETH
  2. Alice deposits 1 ether asset when RSETH supply is 0, she gets 1 ether RSETH, then she deposits again with 1 ether asset, she will only get get 0.5 ether RSETH because the 1 ether asset is transferred to the contract firstly, the asset amount in the contract is 2 ether.

Tools Used

Manual Review

Please consider to mint first then transfer the asset to the contract.

Assessed type

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)

Awards

2.7592 USDC - $2.76

Labels

bug
downgraded by judge
grade-b
QA (Quality Assurance)
sufficient quality report
duplicate-294
Q-86

External Links

Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/f751d7594051c0766c7ecd1e68daeb0661e43ee3/src/NodeDelegator.sol#L94-L116

Vulnerability details

Impact

Queued withdrawal asset in EigenLayer is ignored when calculate the asset balance.

Proof of Concept

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.

  1. First, user should call function 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;
  2. Secondly, user calls function 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.

Tools Used

Manual Review

Please consider to add queued withdrawal asset balance to total asset balance in the calculation.

Assessed type

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

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter