The unstake function in the StakingRewardsV2 contract is designed to enforce a cooldown period before users can unstake their tokens. The cooldown period starts from the last time a user interacted with the staking contract, including staking additional tokens. However, a user can bypass this cooldown period by frequently staking a very small amount of tokens (e.g., 1 wei), effectively resetting the cooldown timer without significant cost.
Impact
This behavior undermines the purpose of the cooldown period, which is to prevent immediate unstaking after staking and to ensure a minimum commitment period. Users can unstake their tokens without adhering to the intended cooldown, potentially leading to instability in the staking mechanism and unfair advantages.
and bypass the below too
it's possible to "snapshot" the distribution by staking right before the threshold is met to be a part of the distribution. This incentivizes poor short term behavior and is the antithesis to staking. This is currently corrected by a staking cooldown to disincentivize snapshot or flash staking
Example Scenario:
Initial Stake: A user stakes 100 KWENTA tokens.
Cooldown Period: The cooldown period of two weeks starts.
Frequent Minimal Staking: The user sets up an automated script to stake 1 wei of KWENTA after cooldown period ends.
Cooldown Reset: Each time the user stakes 1 wei, the cooldown period resets to two weeks from the latest interaction.
Bypassing Cooldown: By continuously staking 1 wei, the user ensures that the cooldown period never completes, effectively bypassing the intended delay before unstaking.
Code Snippet
function unstake(uint256 _amount)
public
whenNotPaused
updateReward(msg.sender)
afterCooldown(msg.sender)
{
if (_amount == 0) revert AmountZero();
uint256 nonEscrowedBalance = nonEscrowedBalanceOf(msg.sender);
if (_amount > nonEscrowedBalance) revert InsufficientBalance(nonEscrowedBalance);
// update state
_addTotalSupplyCheckpoint(totalSupply() - _amount);
_addBalancesCheckpoint(msg.sender, balanceOf(msg.sender) - _amount);
// emit unstake event and index msg.sender
emit Unstaked(msg.sender, _amount);
// transfer token from this contract to the caller
kwenta.transfer(msg.sender, _amount);
}
Implement a minimum staking amount requirement to reset the cooldown period. This ensures that only significant staking interactions can reset the cooldown timer.
AresAudits
Medium
Cooldown Period Bypass via Minimal Staking
Summary
Vulnerability Detail
The
unstake
function in theStakingRewardsV2
contract is designed to enforce acooldown
period before users canunstake
their tokens. Thecooldown
period starts from the last time a user interacted with the staking contract, including staking additional tokens. However, a user can bypass thiscooldown
period by frequently staking a very small amount of tokens (e.g., 1 wei), effectively resetting the cooldown timer without significant cost.Impact
This behavior undermines the purpose of the cooldown period, which is to prevent immediate unstaking after staking and to ensure a minimum commitment period. Users can unstake their tokens without adhering to the intended cooldown, potentially leading to instability in the staking mechanism and unfair advantages.
and bypass the below too
Example Scenario:
Code Snippet
https://github.com/sherlock-audit/2024-07-kwenta-staking-contracts/blob/0527fb7425206a3338c23177416436c6286cedf9/token/contracts/StakingRewardsV2.sol#L252C4-L270C18
Tool used
Manual Review
Recommendation
Implement a minimum staking amount requirement to reset the cooldown period. This ensures that only significant staking interactions can reset the cooldown timer.