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.
View preliminary resultsAppeals are being carefully reviewed by our judges.
The contest is complete and the rewards are being distributed.