Liquid Staking

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

Timestamp Manipulation Risk in the Vault Contract's Claim Period Logic

GitHub
https://github.com/Cyfrin/2024-09-stakelink/blob/main/contracts/linkStaking/base/Vault.sol

Summary

The Vault contract uses block.timestamp to check whether the claim period is active. This introduces a potential vulnerability since miners can manipulate block.timestamp within a range of approximately 15 minutes (900 seconds), potentially leading to undesired outcomes. The contract should use block.number for more reliable time-based calculations, minimizing the risk of manipulation by miners.

Vulnerability Details


In the original implementation, block.timestamp is used to determine whether the claim period is active

if (unbondingPeriodEndsAt == 0 || block.timestamp < unbondingPeriodEndsAt) return false;
return block.timestamp <= stakeController.getClaimPeriodEndsAt(address(this));

Miners can manipulate block.timestamp within a small range, which may allow them to gain unfair advantages, such as making the claim period appear shorter or longer than intended.

Impact


The impact of this vulnerability is that miners may manipulate the outcome of time-based events, such as:
- Extending or shortening the claim period by exploiting block.timestamp.
- Potentially locking funds in the contract longer than expected.
- Disrupting the normal functioning of the vault, affecting participants interacting with the staking contract.

Tools Used

Manual Review

Recommendations


To prevent block.timestamp manipulation, use block.number for time-sensitive logic, combined with an estimate of block times. Below is the recommended change:

function claimPeriodActive() external view returns (bool) {
uint256 unbondingPeriodEndsAt = stakeController.getUnbondingEndsAt(address(this));
if (unbondingPeriodEndsAt == 0) return false;
uint256 unbondingEndsAtBlock = blockNumberAtTimestamp(unbondingPeriodEndsAt);
uint256 claimPeriodEndsAtBlock = blockNumberAtTimestamp(stakeController.getClaimPeriodEndsAt(address(this)));
if (block.number < unbondingEndsAtBlock) return false;
return block.number <= claimPeriodEndsAtBlock;
}
function blockNumberAtTimestamp(uint256 timestamp) internal view returns (uint256) {
uint256 blocksSince = (timestamp - block.timestamp) / 12; // Assuming 12-second block time
return block.number + blocksSince;
}


This approach mitigates the vulnerability by using the more stable block.number for time comparisons and reduces the risk of miners manipulating the contract’s logic.

Updates

Lead Judging Commences

inallhonesty Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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