68870 sc insight reward calculation intermediate multiplication overflow

Submitted on Mar 11th 2026 at 18:15:40 UTC by @ZenHunter for Audit Comp | Folks Finance: Staking Contracts

  • Report ID: #68870

  • Report Type: Smart Contract

  • Report severity: Insight

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

  • Impacts:

Description

Brief/Intro

In Staking._stake, the reward is computed by multiplying three terms — amount, aprBps, and stakingDurationSeconds — before dividing by the denominator. In Solidity 0.8.x, this multiplication reverts on overflow. For sufficiently large inputs the intermediate product exceeds type(uint256).max, causing every stake() call for that input combination to revert unexpectedly and permanently denying the staking service to affected users. Math.mulDiv (which uses 512-bit intermediate arithmetic and never overflows) is already imported via OpenZeppelin (import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"), so this is a one-line change with no new dependencies.

Vulnerability Details

The affected computation is at Staking.sol#L267–L268:

// src/Staking.sol#L267-L268
uint256 rewardBpsDenominator = 1e4 * 365 days;
uint256 reward = (amount * stakingPeriod.aprBps * stakingPeriod.stakingDurationSeconds) / rewardBpsDenominator;

The expression evaluates left-to-right:

  1. amount * stakingPeriod.aprBps — first intermediate product

  2. (result) * stakingPeriod.stakingDurationSeconds — second intermediate product

  3. / rewardBpsDenominator — division applied only after both multiplications

In Solidity 0.8.x, step 2 reverts with a panic if the product exceeds type(uint256).max. The division that would reduce the value never executes.

As APR or duration increase, the threshold decreases and becomes reachable at lower amounts. The fix — Math.mulDiv — is already imported in the contract and performs the equivalent (a * b) / c calculation without intermediate overflow.

Impact Details

Impact category: Security Best Practices

References

  • Affected lines: Staking.sol#L267–L268

  • OpenZeppelin Math.mulDiv: already imported at Staking.sol#L13

Recommendation

Replace the chained multiplication with Math.mulDiv, which computes floor(a × b / denominator) using 512-bit intermediate arithmetic and never overflows:

Math.mulDiv is already imported via OpenZeppelin (import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"), so this is a one-line change with no new dependencies.

Proof of Concept

File: report/poc/test/RewardOverflow.t.sol

Command:

Output:

Was this helpful?