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