Normal behavior:
The k=2 bonus formula computes amount × (T − entryTime)² per deposit. The protocol maintains two per-user accumulators (userSumStakeTime, userSumStakeTimeSq) and two global accumulators (sumStakeTime, sumStakeTimeSq) in uint256. These are computed in unchecked-width multiplications before any accumulation.
The issue:
Several intermediate multiplications in the scoring path compute amount × timestamp × timestamp directly, which can overflow uint256 for tokens with sufficiently large supplies. Solidity 0.8.26 uses checked arithmetic by default — an overflow reverts the transaction. If overflow is triggered at claim time (inside _clampUserSums or _bonusShare), the revert permanently locks the affected staker's funds with no recovery path.
Overflow sites:
Overflow threshold analysis:
With current timestamps t ≈ 1.75 × 10⁹ (seconds since epoch):
While this ceiling is astronomically high for any realistic ERC20, the factory allowlist (setStakeTokenAllowed) is the only guard against non-standard tokens. Governance error (admitting a high-supply token) or a proxy-token upgrade post-allowlisting could admit a token where stakers overflow at claim time with no recourse.
Critical difference between overflow sites:
stake() overflow → transaction reverts, staker receives their tokens back. Self-healing.
_markRiskWindowStart() overflow → risk-window-open transaction reverts, but no permanent lock.
_clampUserSums() / _bonusShare() overflow → claim-time revert. The staker's eligibleStake is non-zero, hasClaimed is false, but every claimSurvived / claimExpired call reverts. Funds are permanently locked since there is no admin recovery path.
Likelihood:
A standard ERC20 with realistic supply (≤ 10³⁰ raw units) is safe — the existing test testBonusShareDoesNotOverflowAtHighMagnitudes confirms correctness at 1e34 raw units
A governance error admits a token with supply > 10³⁸ raw units (e.g., a meme token with 18 decimals and 10²⁰ "whole" token supply)
A proxy-upgrade to a rebasing/high-supply model on an already-allowlisted token
Impact:
Stakers with eligibleStake > 0 on a resolved SURVIVED/EXPIRED pool cannot call claimSurvived() / claimExpired() — every call reverts at _clampUserSums or _bonusShare
Funds are permanently locked; no admin can force-transfer them out since there is no recovery entrypoint
The existing test suite already documents the safe ceiling:
Demonstration of the claim-time lock (conceptual — requires a mock token with extreme supply):
Option A — Tighten the allowlist with an explicit per-token supply cap:
Option B — Add a per-pool stake cap in stake():
Option C — Use Math.mulDiv for the squared intermediates:
Restructure _clampUserSums and _bonusShare to defer the full multiply-then-divide to Math.mulDiv (512-bit intermediates), eliminating the overflow at claim time. The _bonusShare final step already uses mulDiv; the intermediate accumulator multiplications need the same treatment.
Option A is the lightest-touch fix and prevents the problematic token from entering the system entirely. Option C provides defense-in-depth but requires restructuring the accumulator math.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.