Summary
prediction_market/src/lib.rs claim computes each winner's payout as:
let payout = (entry.net * total_pool) / winning_side;
Each payout uses independent integer division. The sum of all payouts is Σ floor(net_i * P / W) ≤ P, with the deficit P - Σ payout_i (rounding dust) never distributed and never accounted for. It accumulates in the contract's XLM balance permanently.
Impact
- Stranded funds: the dust is not added to
AccumulatedFees, not refunded, and not tracked anywhere. It is permanently unreachable.
- Invariant violation:
contract_balance == AccumulatedFees + Σ unclaimed_payouts + Σ refundable_gross is broken by the dust.
- Compounding: every resolved market that has multiple winners leaves dust behind.
- Audit confusion: the discrepancy grows over time and is invisible to on-chain accounting.
Why it's hard
- Distributing the remainder requires knowing the full set of winners at claim time, but claims are per-user and asynchronous.
- Adding remainder to fees requires a post-claim reconciliation pass with no trigger.
- Pre-computing exact payouts at resolve time requires iterating all winners (unbounded gas risk).
Fix
- Implement a settlement model: either (a) compute and store exact per-winner payouts at
resolve_market time with a bounded winner iteration, or (b) sweep the remainder to AccumulatedFees via a finalization function callable only after all claims, or (c) round up the last claimer's payout (requires on-chain tracking of claim order).
Summary
prediction_market/src/lib.rsclaimcomputes each winner's payout as:Each payout uses independent integer division. The sum of all payouts is
Σ floor(net_i * P / W) ≤ P, with the deficitP - Σ payout_i(rounding dust) never distributed and never accounted for. It accumulates in the contract's XLM balance permanently.Impact
AccumulatedFees, not refunded, and not tracked anywhere. It is permanently unreachable.contract_balance == AccumulatedFees + Σ unclaimed_payouts + Σ refundable_grossis broken by the dust.Why it's hard
Fix
resolve_markettime with a bounded winner iteration, or (b) sweep the remainder toAccumulatedFeesvia a finalization function callable only after all claims, or (c) round up the last claimer's payout (requires on-chain tracking of claim order).