68849 sc insight elapsed computed twice in withdraw code optimization

Submitted on Mar 11th 2026 at 16:09:21 UTC by @ZenHunter for Audit Comp | Folks Finance: Staking Contracts

  • Report ID: #68849

  • Report Type: Smart Contract

  • Report severity: Insight

  • Target: https://github.com/Folks-Finance/folks-staking-contracts/blob/main/src/Staking.sol

Description

Brief/Intro

In Staking._withdraw, the expression block.timestamp - userStake.unlockTime is evaluated twice on consecutive lines when computing accruedAmount and accruedReward. This is a redundant computation: the same arithmetic result is produced both times from the same immutable inputs within a single function call. While this is not a security vulnerability, it is an avoidable gas inefficiency and a minor readability issue that can be eliminated with a one-line local variable.

Vulnerability Details

The _withdraw internal function computes the elapsed time since unlock on two back-to-back lines:

// src/Staking.sol#L317-L320
uint256 accruedAmount =
    _getAccrued(userStake.amount, userStake.unlockDuration, block.timestamp - userStake.unlockTime);
uint256 accruedReward =
    _getAccrued(userStake.reward, userStake.unlockDuration, block.timestamp - userStake.unlockTime);

Both calls pass the identical sub-expression block.timestamp - userStake.unlockTime as the third argument. Within the EVM, each evaluation of this expression re-reads userStake.unlockTime and performs the subtraction.

Impact Details

This finding carries no direct security impact. No funds are at risk, and no invariant is violated. The consequence in the worst case (no compiler optimisation) is a marginal gas overhead on every withdraw call — one redundant subtraction instruction and one redundant read of userStake.unlockTime. In practice the overhead is negligible, but the fix also improves code clarity for future maintainers and auditors.

Impact category: Code Optimizations and Enhancements

References

  • Affected lines: https://github.com/Folks-Finance/folks-staking-contracts/blob/main/src/Staking.sol#L317–L320

Recommendation

Cache the sub-expression in a named local variable before the two _getAccrued calls:

Proof of Concept

Command:

Output:

Both variants produce identical results (assertEq passes), and the optimized path saves 174 gas per withdraw call.

Was this helpful?