Liquid Staking

Stakelink
DeFiHardhatOracle
50,000 USDC
View results
Submission Details
Severity: low
Valid

Incorrect `nextGroupTotalUnbonded` Calculation in `FundFlowController::_getVaultUpdateData` Includes Non-grouped Vaults, Leading to Potential Withdrawal and Deposit Errors

Summary

The calculation for nextGroupTotalUnbonded in FundFlowController::_getVaultUpdateData incorrectly includes non-grouped vaults in its computation. The purpose of this function is to return data necessary to execute a vault group update for a strategy, excluding vaults that are not in the group. However, by including all vaults, both grouped and non-grouped, in the calculation, the returned nextGroupTotalUnbonded value becomes inaccurate. This leads to several downstream issues in the protocol, especially in the updateVaultGroups function, where this value is used for further calculations affecting withdrawals and deposits.

Vulnerability Details

The _getVaultUpdateData function is intended to calculate and return data only for grouped vaults, but it mistakenly includes non-grouped vaults in its calculation of nextGroupTotalUnbonded. Here’s the relevant code section:

function _getVaultUpdateData(
IVaultControllerStrategy _vcs,
uint256 _nextUnbondedVaultGroup
) internal view returns (uint256[] memory, uint256, uint256) {
address[] memory vaults = _vcs.getVaults();
(, , , uint64 depositIndex) = _vcs.globalVaultState();
(
uint256 curGroupTotalDepositRoom,
uint256[] memory curGroupVaultsToUnbond
) = _getTotalDepositRoom(
vaults,
numVaultGroups,
curUnbondedVaultGroup,
_vcs.vaultMaxDeposits(),
depositIndex
);
uint256 nextGroupTotalUnbonded = _getTotalUnbonded(
vaults,
numVaultGroups,
_nextUnbondedVaultGroup
);
return (curGroupVaultsToUnbond, curGroupTotalDepositRoom, nextGroupTotalUnbonded);
}

The issue lies in the fact that _vcs.getVaults() returns all vaults, both grouped and non-grouped, and passes them into _getTotalUnbonded. This causes all vaults to be factored into the totalUnbonded calculation:

function _getTotalUnbonded(
address[] memory _vaults,
uint256 _numVaultGroups,
uint256 _vaultGroup
) internal view returns (uint256) {
uint256 totalUnbonded;
for (uint256 i = _vaultGroup; i < _vaults.length; i += _numVaultGroups) {
if (!IVault(_vaults[i]).claimPeriodActive() || IVault(_vaults[i]).isRemoved()) continue;
totalUnbonded += IVault(_vaults[i]).getPrincipalDeposits();
}
return totalUnbonded;
}

The calculation erroneously includes non-grouped vaults, which can receive deposits once grouped vaults are full. These non-group vaults are only added to a group when the group deposit index reaches the next vault in the list. As a result, the totalUnbonded value returned is incorrect.

In contrast, the _getTotalDepositRoom function, called earlier in _getVaultUpdateData, correctly uses the depositIndex to ensure that non-grouped vaults are not included in its calculation:

function _getTotalDepositRoom() {
// ...
for (uint256 i = _vaultGroup; i < _depositIndex; i += _numVaultGroups) {
if (IVault(_vaults[i]).isRemoved()) continue;
uint256 principalDeposits = IVault(_vaults[i]).getPrincipalDeposits();
totalDepositRoom += _vaultMaxDeposits - principalDeposits;
if (principalDeposits != 0) {
nonEmptyVaults[numNonEmptyVaults] = i;
numNonEmptyVaults++;
}
}
// ...
}

Because _getVaultUpdateData is used in the updateVaultGroups function, this error leads to an incorrect nextGroupOpVaultsTotalUnbonded value, which affects the updates to both operatorVCS and communityVCS. This miscalculation can lead to several accounting issues, ultimately causing deposit and withdrawal functions to malfunction.

If the totalUnbonded value is higher than it should be, withdrawals could be blocked when the requested amount exceeds the incorrect totalUnbonded value.

Impact

The incorrect totalUnbonded value returned by _getVaultUpdateData leads to misaligned accounting in the updateVaultGroups function. This can cause:

  • Blocked withdrawals if the system calculates an inflated totalUnbonded value that exceeds the available unbonded balance.

  • Potentially malfunctioning deposit and withdrawal functions due to misalignment between grouped and non-grouped vault calculations.

These issues disrupt the protocol’s core operations, negatively impacting user experience and the protocol’s financial integrity.

Tools Used

Manual

Recommendations

Modify the _getTotalUnbonded function to exclude non-grouped vaults from its calculation by introducing a similar mechanism as used in _getTotalDepositRoom, ensuring that only grouped vaults are considered. This will align the nextGroupTotalUnbonded value with the actual vault state and prevent future miscalculations.

Updates

Lead Judging Commences

inallhonesty Lead Judge
10 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

krisrenzo Submitter
10 months ago
inallhonesty Lead Judge
10 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

`nextGroupTotalUnbonded` Calculation in `FundFlowController::_getVaultUpdateData` Includes Non-grouped Vaults

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.