Kelp DAO | rsETH - MaslarovK'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: 140/185

Findings: 1

Award: $2.76

QA:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

2.7592 USDC - $2.76

Labels

bug
downgraded by judge
grade-b
insufficient quality report
QA (Quality Assurance)
edited-by-warden
duplicate-69
Q-122

External Links

Lines of code

https://github.com/code-423n4/2023-11-kelp/blob/main/src/LRTDepositPool.sol#L183-L197 https://github.com/code-423n4/2023-11-kelp/blob/main/src/NodeDelegator.sol#L74-L89

Vulnerability details

Impact

The function attempts to transfer a specified amount of an asset to a contract. However, there is no check to ensure that the current contract has sufficient balance of the asset before attempting the transfer. If the specified amount is greater than the contract's current balance of the asset, the transfer will fail and the entire transaction will revert. Even though there is a check which reverts if the transfer fails, the transaction will revert before this, when the external call fails due to insufficient amount of tokens and the gas used to this moment will still be consumed.

Proof of Concept

I made slight change in the test below which shows the possibility of transfer attempt with amount bigger than the current balance:

function test_TransferAssetToNodeDelegator() external {
        // deposit 3 ether rETH
        vm.startPrank(alice);
        rETH.approve(address(lrtDepositPool), 3 ether);
        lrtDepositPool.depositAsset(address(rETH), 3 ether);
        vm.stopPrank();

        uint256 indexOfNodeDelegatorContractOneInNDArray;
        address[] memory nodeDelegatorArray = lrtDepositPool.getNodeDelegatorQueue();
        for (uint256 i = 0; i < nodeDelegatorArray.length; i++) {
            if (lrtDepositPool.nodeDelegatorQueue(i) == nodeDelegatorContractOne) {
                indexOfNodeDelegatorContractOneInNDArray = i;
                break;
            }
        }

        // transfer 4 ether rETH to node delegator contract one
        vm.startPrank(manager);
        lrtDepositPool.transferAssetToNodeDelegator(indexOfNodeDelegatorContractOneInNDArray, address(rETH), 4 ether);
        vm.stopPrank();

        assertEq(rETH.balanceOf(address(lrtDepositPool)), 2 ether, "Asset amount in lrtDepositPool is incorrect");
        assertEq(rETH.balanceOf(nodeDelegatorContractOne), 1 ether, "Asset is not transferred to node delegator");
    }

resulting in this: [FAIL. Reason: ERC20: transfer amount exceeds balance] test_TransferAssetToNodeDelegator() (gas: 232868)

Tools Used

Manual Review

I recommend to add the following check to ensure that the contract has sufficient balance before attempting the transfer.

uint256 balance = IERC20(asset).balanceOf(address(this));
require(balance >= amount, "Insufficient balance");

Assessed type

Token-Transfer

#0 - c4-pre-sort

2023-11-15T21:52:47Z

raymondfam marked the issue as insufficient quality report

#1 - c4-pre-sort

2023-11-15T21:52:51Z

raymondfam marked the issue as primary issue

#2 - c4-pre-sort

2023-11-15T21:53:34Z

raymondfam marked the issue as duplicate of #69

#3 - c4-judge

2023-11-29T20:58:12Z

fatherGoose1 changed the severity to QA (Quality Assurance)

#4 - c4-judge

2023-11-29T20:58:45Z

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