sherlock-audit / 2024-07-kwenta-staking-contracts-judging

4 stars 4 forks source link

AresAudits - Cooldown Period Bypass via Minimal Staking #168

Closed sherlock-admin2 closed 3 months ago

sherlock-admin2 commented 3 months ago

AresAudits

Medium

Cooldown Period Bypass via Minimal Staking

Summary

Vulnerability Detail

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:

  1. Initial Stake: A user stakes 100 KWENTA tokens.
  2. Cooldown Period: The cooldown period of two weeks starts.
  3. Frequent Minimal Staking: The user sets up an automated script to stake 1 wei of KWENTA after cooldown period ends.
  4. Cooldown Reset: Each time the user stakes 1 wei, the cooldown period resets to two weeks from the latest interaction.
  5. 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);
}

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.