sherlock-audit / 2024-02-rio-network-core-protocol-judging

4 stars 4 forks source link

Aymen0909 - Users might receive less funds from the queued withdrawals #364

Closed sherlock-admin4 closed 8 months ago

sherlock-admin4 commented 8 months ago

Aymen0909

medium

Users might receive less funds from the queued withdrawals

Summary

Users will receive less funds when claiming withdrawals from an epoch that was queued into EigenLayer through queueCurrentEpochSettlement if other users request withdrawals in that same epoch.

Vulnerability Detail

For a user to withdraw funds from the protocol, they must first request a withdrawal using the requestWithdrawal function, which will queue the withdrawal in the current epoch by calling withdrawalQueue().queueWithdrawal.

All the queued withdrawals will be processed when the rebalance function is called. There are two ways to do so (after invoking _processUserWithdrawalsForCurrentEpoch under the hood):

The _processUserWithdrawalsForCurrentEpoch function will handle all the withdrawal logic in the rebalancing call:

function _processUserWithdrawalsForCurrentEpoch(address asset, uint256 sharesOwed) internal {
    IRioLRTWithdrawalQueue withdrawalQueue_ = withdrawalQueue();
    (uint256 assetsSent, uint256 sharesSent) = depositPool().transferMaxAssetsForShares(
        asset,
        sharesOwed,
        address(withdrawalQueue_)
    );
    uint256 sharesRemaining = sharesOwed - sharesSent;

    // Exit early if all pending withdrawals were paid from the deposit pool.
    if (sharesRemaining == 0) {
        withdrawalQueue_.settleCurrentEpoch(asset, assetsSent, sharesSent);
        return;
    }

    address strategy = assetRegistry().getAssetStrategy(asset);
    bytes32 aggregateRoot = OperatorOperations.queueWithdrawalFromOperatorsForUserSettlement(
        operatorRegistry(),
        strategy,
        sharesRemaining
    );

    withdrawalQueue_.queueCurrentEpochSettlement(asset, assetsSent, sharesSent, aggregateRoot);
}

In this rebalance process, there is a scenario in which users may receive less funds than expected for their queued withdrawal shares. Let's illustrate that scenario:

if (epochWithdrawals.aggregateRoot != bytes32(0)) revert WITHDRAWALS_ALREADY_QUEUED_FOR_EPOCH();
amountOut = userSummary.sharesOwed.mulDiv(epochWithdrawals.assetsReceived, epochWithdrawals.sharesOwed);

Impact

Users will receive less funds when claiming withdrawals from an epoch that was queued into EigenLayer through queueCurrentEpochSettlement if other users request withdrawals in that same epoch after it was queued.

Code Snippet

https://github.com/sherlock-audit/2024-02-rio-network-core-protocol/blob/main/rio-sherlock-audit/contracts/restaking/RioLRTCoordinator.sol#L245-L267

https://github.com/sherlock-audit/2024-02-rio-network-core-protocol/blob/main/rio-sherlock-audit/contracts/restaking/RioLRTWithdrawalQueue.sol#L130-L145

https://github.com/sherlock-audit/2024-02-rio-network-core-protocol/blob/main/rio-sherlock-audit/contracts/restaking/RioLRTWithdrawalQueue.sol#L177-L209

https://github.com/sherlock-audit/2024-02-rio-network-core-protocol/blob/main/rio-sherlock-audit/contracts/restaking/RioLRTWithdrawalQueue.sol#L104

Tool used

Manual Review

Recommendation

To address this issue, the simplest method is to increment the epoch currentEpochsByAsset when RioLRTWithdrawalQueue.queueCurrentEpochSettlement is called. Requests for withdrawals made after queuing will be recorded in the next epoch.

Duplicate of #4