Skip to content

feat: add overflow-safe score and probability arithmetic - #1432

Merged
greatest0fallt1me merged 1 commit into
Predictify-org:masterfrom
SupremeCarMart100:feat/overflow-safe-score-probability-arithmetic
Aug 30, 2026
Merged

feat: add overflow-safe score and probability arithmetic#1432
greatest0fallt1me merged 1 commit into
Predictify-org:masterfrom
SupremeCarMart100:feat/overflow-safe-score-probability-arithmetic

Conversation

@SupremeCarMart100

Copy link
Copy Markdown
Contributor

Summary

Replaces raw integer arithmetic in score and probability calculation paths with checked or saturating
operations, eliminating the risk of silent wrapping or panics when adversarial or extreme stake values
are passed through the payout pipeline.

Adds a new ArithmeticUtils struct as a single source of truth for checked math primitives used across the
contract.

Changes

New: ArithmeticUtils in utils.rs

Four #[inline] primitives that return Err(Error::Overflow) (code 672) instead of panicking or silently
wrapping:

  • checked_add — rejects negative operands, errors on overflow
  • checked_mul — same contract
  • checked_mul_div(value, numerator, denominator) — guards intermediate product and division by zero
  • checked_accumulate — drop-in replacement for += in loops over user-controlled values

Fixed call sites

┌────────────┬───────────────────────────────┬────────────────────────────────────────────────────────┐
│ File │ Function │ Change │
├────────────┼───────────────────────────────┼────────────────────────────────────────────────────────┤
│ bets.rs │ calculate_implied_probability │ outcome * 100 → checked_mul_div, safe fallback 0 │
├────────────┼───────────────────────────────┼────────────────────────────────────────────────────────┤
│ bets.rs │ calculate_payout_multiplier │ total * 100 → checked_mul_div, safe fallback 0 │
├────────────┼─────────────────────────────────┼──────────────────────────────────────────────────────┤
│ queries.rs │ calculate_payout │ stake * total_staked, share * 2 → checked_mul_div, │
│ │ │ propagates Error::Overflow │
├────────────┼─────────────────────────────────┼──────────────────────────────────────────────────────┤
│ queries.rs │ calculate_outcome_pool │ pool += stake → checked_accumulate, propagates │
│ │ │ Error::Overflow │
├────────────┼─────────────────────────────────┼──────────────────────────────────────────────────────┤
│ queries.rs │ calculate_implied_probabilities │ pool * 100, pool1 + pool2 → checked ops, safe │
│ │ │ fallback 50 │
├────────────┼─────────────────────────────────┼──────────────────────────────────────────────────────┤
│ voting.rs │ calculate_user_payout │ winning_total += → checked_accumulate, propagates │
│ │ │ Error::Overflow │
├────────────┼─────────────────────────────────┼──────────────────────────────────────────────────────┤
│ markets.rs │ calculate_payout │ already used checked_mul but mapped to InvalidInput; │
│ │ │ upgraded to Error::Overflow for precise diagnostics │
├────────────┼─────────────────────────────────┼──────────────────────────────────────────────────────┤
│ markets.rs │ calculate_winning_stats │ winning_total += → saturating_add │
├────────────┼─────────────────────────────────┼──────────────────────────────────────────────────────┤
│ utils.rs │ calculate_percentage │ raw * → saturating_mul; added zero-denominator guard │
├────────────┼─────────────────────────────────┼──────────────────────────────────────────────────────┤
│ utils.rs │ weighted_average │ raw * / += → saturating_mul / saturating_add │
├────────────┼─────────────────────────────────┼──────────────────────────────────────────────────────┤
│ utils.rs │ simple_interest │ chained raw * → saturating_mul chain │
└────────────┴─────────────────────────────────┴──────────────────────────────────────────────────────┘

Tests

New file: src/arithmetic_overflow_tests.rs — 36 tests across three modules:

  • arithmetic_utils_tests — checked_add, checked_mul, checked_mul_div, checked_accumulate: normal,
    i128::MAX boundary, overflow, negative-input rejection, zero-denominator, idempotency
  • numeric_utils_overflow_tests — saturation behavior for calculate_percentage, weighted_average,
    simple_interest
  • market_utils_payout_tests — calculate_payout: normal, zero stake, zero winning total, overflow on
    stake×fee and share×pool, proportional split, idempotency

test result: ok. 36 passed; 0 failed
Full suite: 703 passed (up from 701); 147 pre-existing failures unchanged

Compatibility

  • All public function signatures are unchanged
  • Error::Overflow (code 672) already existed in the enum — no new error codes added
  • Callers that previously received a panic or silent wrap will now receive Error::Overflow; callers of
    infallible helpers (calculate_percentage, etc.) see saturating results rather than wrapping

Security & failure modes

  • Overflow is now explicit and diagnosable via the Error::Overflow code rather than silent data
    corruption
  • All fixed functions are pure — no mutable state — so retries and concurrent calls cannot produce
    inconsistent results
  • Negative financial inputs are rejected at the ArithmeticUtils boundary, enforcing the contract
    invariant that stakes and totals are always non-negative

closes #1410

Implement ArithmeticUtils with checked primitives (checked_add,
checked_mul, checked_mul_div, checked_accumulate) that return
Error::Overflow (code 672) instead of panicking or silently wrapping.

Fixed call sites:
- bets.rs: calculate_implied_probability, calculate_payout_multiplier
  — raw outcome * 100 replaced with checked_mul_div (fallback 0)
- queries.rs: calculate_payout — raw stake * total replaced with
  checked_mul_div; calculate_outcome_pool pool += stake replaced with
  checked_accumulate; calculate_implied_probabilities pool * 100 and
  pool1 + pool2 replaced with checked ops (fallback 50)
- voting.rs: calculate_user_payout winning_total accumulation replaced
  with checked_accumulate (propagates Error::Overflow)
- markets.rs: calculate_payout upgraded from InvalidInput to
  Error::Overflow; calculate_winning_stats uses saturating_add
- utils.rs: NumericUtils::calculate_percentage, weighted_average,
  simple_interest use saturating ops with zero-denominator guard

Added arithmetic_overflow_tests.rs with 36 tests covering:
normal operation, i128::MAX boundary, overflow detection, negative-
input rejection, zero-denominator, and idempotency/replay safety.

Test results: 36/36 new tests pass; 703 total passing (up from 701);
pre-existing 147 failures are unrelated SDK-compat issues.
@drips-wave

drips-wave Bot commented Aug 30, 2026

Copy link
Copy Markdown

@SupremeCarMart100 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@greatest0fallt1me
greatest0fallt1me merged commit ef36c79 into Predictify-org:master Aug 30, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Quality-2][Medium] Add overflow-safe score and probability arithmetic

2 participants