#41952 [SC-Insight] Reduce storage costs by eliminating stakedTimes in StakeV2::startUnstake

Submitted on Mar 19th 2025 at 15:17:42 UTC by @Ragnarok for Audit Comp | Yeet

  • Report ID: #41952

  • Report Type: Smart Contract

  • Report severity: Insight

  • Target: https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/StakeV2.sol

  • Impacts:

Description

Description

In the StakeV2::startUnstake function, stakedTimes[msg.sender] is used to limit the number of times a user can initiate an unstake. However, instead of maintaining a separate counter, we can use vestings[msg.sender].length to enforce this restriction.

By doing so, we eliminate an unnecessary SSTORE operation (used to update stakedTimes[msg.sender]), which can help reduce gas costs.

StakeV2::startUnstake function:

function startUnstake(uint256 unStakeAmount) external {
    require(unStakeAmount > 0, "Amount must be greater than 0");
=>  require(stakedTimes[msg.sender] < STAKING_LIMIT, "Amount must be less then the STAKING_LIMIT constant"); 
    ...
    vestings[msg.sender].push(Vesting(unStakeAmount, start, end));
=>  stakedTimes[msg.sender]++;
    emit VestingStarted(msg.sender, unStakeAmount, vestings[msg.sender].length - 1);
}

Recommendation

Modify the StakeV2::startUnstake function:

function startUnstake(uint256 unStakeAmount) external {
    require(unStakeAmount > 0, "Amount must be greater than 0");
=>  require(vestings[msg.sender].length < STAKING_LIMIT, "Amount must be less then the STAKING_LIMIT constant"); 
    ...
    vestings[msg.sender].push(Vesting(unStakeAmount, start, end));
=>  // Delete this line
    emit VestingStarted(msg.sender, unStakeAmount, vestings[msg.sender].length - 1);
}

Modify the StakeV2::_unstake function:

function _unstake(uint256 index) private {
    Vesting memory vesting = vestings[msg.sender][index];
    (uint256 unlockedAmount, uint256 lockedAmount) = calculateVesting(vesting);
    require(unlockedAmount != 0, "No unlocked amount");

    _remove(msg.sender, index);
    if (lockedAmount > 0) {
        emit RageQuit(msg.sender, unlockedAmount, lockedAmount, index);
    } else {
        emit Unstake(msg.sender, unlockedAmount, index);
    }
=>  stakedTimes[msg.sender]--; // Delete this line
}

Proof of Concept

Proof of Concept

Modify the StakeV2::startUnstake function:

function startUnstake(uint256 unStakeAmount) external {
    require(unStakeAmount > 0, "Amount must be greater than 0");
=>  require(vestings[msg.sender].length < STAKING_LIMIT, "Amount must be less then the STAKING_LIMIT constant"); 
    ...
    vestings[msg.sender].push(Vesting(unStakeAmount, start, end));
=>  // Delete this line
    emit VestingStarted(msg.sender, unStakeAmount, vestings[msg.sender].length - 1);
}

Modify the StakeV2::_unstake function:

function _unstake(uint256 index) private {
    Vesting memory vesting = vestings[msg.sender][index];
    (uint256 unlockedAmount, uint256 lockedAmount) = calculateVesting(vesting);
    require(unlockedAmount != 0, "No unlocked amount");

    _remove(msg.sender, index);
    if (lockedAmount > 0) {
        emit RageQuit(msg.sender, unlockedAmount, lockedAmount, index);
    } else {
        emit Unstake(msg.sender, unlockedAmount, index);
    }
=>  stakedTimes[msg.sender]--; // Delete this line
}

Was this helpful?