Liquid Staking

Stakelink
DeFiHardhatOracle
50,000 USDC
View results
Submission Details
Severity: medium
Invalid

Decreased Chainlink Staker Capacity Causes Premature Termination of the `deposit` Function in `CommunityVCS`

Summary

When CommunityVCS::deposit is called, it retrieves the maximum deposit limit for a staker from the Chainlink stakeController. If the new maxDeposits is greater than the previously recorded value, the totalDepositRoom for each vault group is updated. However, the function fails to account for cases where maxDeposits is decreased. This causes the recorded VaultGroups.totalDepositRoom to become outdated, resulting in inefficient use of vault capacity and premature termination of the deposit function.

Vulnerability Details

The CommunityVCS::deposit function updates the totalDepositRoom for each vault group when maxDeposits increases. Here's the relevant code:

function deposit(uint256 _amount, bytes calldata _data) external override onlyStakingPool {
(, uint256 maxDeposits) = getVaultDepositLimits();
// if vault deposit limit has changed in Chainlink staking contract, make adjustments
if (maxDeposits > vaultMaxDeposits) {
uint256 diff = maxDeposits - vaultMaxDeposits;
uint256 totalVaults = globalVaultState.depositIndex;
uint256 numVaultGroups = globalVaultState.numVaultGroups;
uint256 vaultsPerGroup = totalVaults / numVaultGroups;
uint256 remainder = totalVaults % numVaultGroups;
for (uint256 i = 0; i < numVaultGroups; ++i) {
uint256 numVaults = vaultsPerGroup;
if (i < remainder) {
numVaults += 1;
}
vaultGroups[i].totalDepositRoom += uint128(numVaults * diff);
}
vaultMaxDeposits = maxDeposits;
}
if (vaultDepositController == address(0)) revert VaultDepositControllerNotSet();
(bool success, ) = vaultDepositController.delegatecall(
abi.encodeWithSelector(VaultDepositController.deposit.selector, _amount, _data)
);
if (!success) revert DepositFailed();
}

The problem arises when maxDeposits is decreased. The totalDepositRoom value recorded for each vault group remains larger than the new, lower maxDeposits, causing an issue when deposits are processed in VaultDepositController::_depositToVaults. The check in _depositToVaults compares the outdated totalDepositRoom with the updated _maxDeposits, leading to premature termination of the deposit process:

for (uint256 i = 0; i < globalState.numVaultGroups; ++i) {
if (
(i != globalState.curUnbondedVaultGroup && groups[i].totalDepositRoom >= _maxDeposits) ||
(i == globalState.curUnbondedVaultGroup && groups[i].totalDepositRoom >= 2 * _maxDeposits)
) {
return _toDeposit - toDeposit;
}
}

This results in inefficient vault capacity utilization and prevents the correct amount of tokens from being deposited into the vaults. Furthermore, there is no function in the contract that can adjust the totalDepositRoom to the correct value when maxDeposits decreases, leaving the problem unresolved until the Chainlink staker limit increases again to the previous or higher value.

Impact

This issue leads to inefficient use of vault capacity, as the protocol unintentionally limits the amount of staked tokens it can support per vault group. This affects the protocol's staking operations and prevents the full utilization of available staking capacity.

Tools Used

Manual

Recommendations

Ensure that the deposit function accounts for cases where the maxDeposits on Chainlink is decreased. The protocol should update the totalDepositRoom for each vault group accordingly to prevent outdated values from interfering with deposits and efficient staking operations.

Updates

Lead Judging Commences

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

Support

FAQs

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