sweepUnclaimedBonus() reserves the entire unpaid bonus snapshot whenever any principal remains unclaimed. Individual bonus claims, however, pay floor-rounded shares. The difference is global rounding residue that no position can receive. A holder can leave a zero-share position unclaimed and indefinitely prevent recoveryAddress from receiving that residue.
Each SURVIVED/EXPIRED claim pays floor(score[i] × B / S) and increments claimedBonus by that amount. The sweep instead reserves the aggregate unpaid snapshot:
For the remaining positions U, the true outstanding bonus liability is:
sum(floor(score[i] × B / S), i in U).
The contract reserves B - claimedBonus instead. The difference is the fixed global residue:
B - sum(floor(score[i] × B / S), all positions).
That residue is not payable to any staker, yet it remains reserved until every principal is claimed.
Likelihood
The factory permits ordinary ERC-20s without a decimals restriction.
Equal-score positions and a bonus not divisible by their count deterministically create residue.
Any holder can maintain the lock simply by not claiming; there is no deadline.
Impact
A third party's unallocatable bonus cannot reach recoveryAddress while a zero-share principal remains.
The holdout cannot capture the residue; it receives only its principal.
The same accounting is used by both SURVIVED and EXPIRED.
No attachment or private fixture is required. Configure one valid pool with minStake = 1, a standard two-decimal ERC-20, and a test account holding exactly 201 raw units. The account funds 101 equal one-unit stakes and a 100-unit bonus. All stakes occur at the same timestamp before the risk window opens, so their k=2 scores are equal.
The final two assertions prove the 100-unit balance was never an outstanding entitlement: it becomes sweepable solely because the unrelated one-unit principal was claimed. Replacing SURVIVED with the EXPIRED resolution path produces the same lock because both paths use the same share and sweep accounting.
Permit a zero-bonus position to be settled permissionlessly once a genuine claim has made the outcome immutable. The position's principal is sent to its owner; a nonzero bonus entitlement remains protected and cannot be forced. This avoids adding a staker list or attempting to infer rounding residue from aggregate accounting.
The claimsStarted guard preserves the moderator's pre-claim re-flag window. The nonzero-entitlement guard preserves every real bonus claim. Once the last zero-share holdout is settled, totalEligibleStake becomes zero and the existing sweep transfers only the already-unallocatable residue to recoveryAddress.
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.