EigenLayer Contest - btk's results

Enabling restaking of staked Ether, to be used as cryptoeconomic security for decentralized protocols and applications.

General Information

Platform: Code4rena

Start Date: 27/04/2023

Pot Size: $90,500 USDC

Total HM: 4

Participants: 43

Period: 7 days

Judge: GalloDaSballo

Id: 233

League: ETH

EigenLayer

Findings Distribution

Researcher Performance

Rank: 40/43

Findings: 1

Award: $71.60

QA:
grade-b

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

Labels

bug
grade-b
QA (Quality Assurance)
Q-07

Awards

71.6048 USDC - $71.60

External Links

Total Low issues
RiskIssues DetailsNumber
[L-01]MAX_STAKER_STRATEGY_LIST_LENGTH is not implemented correctltly1
[L-02]_setWithdrawalDelayBlocks() lacks sanity checks1

[L-01] MAX_STAKER_STRATEGY_LIST_LENGTH is not implemented correctltly

Impact

The _addShares() function in the StrategyManager.sol contract is currently implemented in a way that causes it to revert when the depositor's strategy list is equal to MAX_STAKER_STRATEGY_LIST_LENGTH. This is not correct, as it should only revert when the list is greater than max length.

Proof of Concept

The stakerStrategyList mapping has a maximum length of MAX_STAKER_STRATEGY_LIST_LENGTH. Therefore, the _addShares() function should only revert when stakerStrategyList[depositor].length > MAX_STAKER_STRATEGY_LIST_LENGTH:

    function _addShares(address depositor, IStrategy strategy, uint256 shares) internal {
        // sanity checks on inputs
        require(depositor != address(0), "StrategyManager._addShares: depositor cannot be zero address");
        require(shares != 0, "StrategyManager._addShares: shares should not be zero!");

        // if they dont have existing shares of this strategy, add it to their strats
        if (stakerStrategyShares[depositor][strategy] == 0) {
            require(
                stakerStrategyList[depositor].length < MAX_STAKER_STRATEGY_LIST_LENGTH,
                "StrategyManager._addShares: deposit would exceed MAX_STAKER_STRATEGY_LIST_LENGTH"
            );
            stakerStrategyList[depositor].push(strategy);
        }

        // add the returned shares to their existing shares for this strategy
        stakerStrategyShares[depositor][strategy] += shares;

        // if applicable, increase delegated shares accordingly
        delegation.increaseDelegatedShares(depositor, strategy, shares);
    }

As you can see in the code, the function currently throws when the depositor tries to add the 32nd strategy to their list, which is not consistent with the maximum length of the list.

Note that while this issue may not directly impact users of EigenLayer, it could have an impact on protocols built on top of EigenLayer.

Tools Used

Manual Review

We recommend to update the _addShares() function as follows:

    function _addShares(address depositor, IStrategy strategy, uint256 shares) internal {
        // sanity checks on inputs
        require(depositor != address(0), "StrategyManager._addShares: depositor cannot be zero address");
        require(shares != 0, "StrategyManager._addShares: shares should not be zero!");

        // if they dont have existing shares of this strategy, add it to their strats
        if (stakerStrategyShares[depositor][strategy] == 0) {
            require(
                stakerStrategyList[depositor].length <= MAX_STAKER_STRATEGY_LIST_LENGTH,
                "StrategyManager._addShares: deposit would exceed MAX_STAKER_STRATEGY_LIST_LENGTH"
            );
            stakerStrategyList[depositor].push(strategy);
        }

        // add the returned shares to their existing shares for this strategy
        stakerStrategyShares[depositor][strategy] += shares;

        // if applicable, increase delegated shares accordingly
        delegation.increaseDelegatedShares(depositor, strategy, shares);
    }

This updated function will only revert when the depositor's strategy list exceeds MAX_STAKER_STRATEGY_LIST_LENGTH.

[L-02] _setWithdrawalDelayBlocks() lacks sanity checks

Impact

The _setWithdrawalDelayBlocks() function lacks sanity checks to prevent the withdrawalDelayBlocks variable from being set to 0, which could allow for malicious withdrawals to be executed instantly.

Proof of Concept

The withdrawalDelayBlocks variable is used to enforce a 7-day delay window for completing queued withdrawals in the StrategyManager.sol contract. This delay is measured in blocks and can be adjusted by the contract owner, but is subject to a maximum value of MAX_WITHDRAWAL_DELAY_BLOCKS. However, the function is missing a MIN_WITHDRAWAL_DELAY_BLOCKS check, which means that the delay could be set to 0.

    function _setWithdrawalDelayBlocks(uint256 _withdrawalDelayBlocks) internal {
        require(_withdrawalDelayBlocks <= MAX_WITHDRAWAL_DELAY_BLOCKS, "StrategyManager.setWithdrawalDelay: _withdrawalDelayBlocks too high");
        emit WithdrawalDelayBlocksSet(withdrawalDelayBlocks, _withdrawalDelayBlocks);
        withdrawalDelayBlocks = _withdrawalDelayBlocks;
    }
Tools Used

Manual Review

To make the contract more robust, a MIN_WITHDRAWAL_DELAY_BLOCKS check should be added to the _setWithdrawalDelayBlocks() function to ensure that the withdrawalDelayBlocks variable is not set to 0. This will provide an additional layer of protection against malicious withdrawals.

#0 - GalloDaSballo

2023-06-01T17:59:49Z

[L-01] MAX_STAKER_STRATEGY_LIST_LENGTH is not implemented correctltly R

_setWithdrawalDelayBlocks L

#1 - c4-judge

2023-06-02T09:38:49Z

GalloDaSballo 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