Skip to content

Latest commit

 

History

History
69 lines (50 loc) · 5.94 KB

File metadata and controls

69 lines (50 loc) · 5.94 KB

Closes #725

TL;DR

Adds the panic-free sat_* family to credence_math so UX/aggregation paths can never revert a transaction on an internal i128 * bps overflow. The new helpers clamp to i128::MIN / i128::MAX on overflow and silently return 0 when the denominator is zero. Drive-by also fixes six pre-existing compile defects in credence_errors that the new math→errors dep edge surfaced (see ### Drive-by: credence_errors section for the full list and the two wire-code renumberings).

What's new

  • pub const PERCENT_DENOMINATOR: i128 = 100; (paired with the existing BPS_DENOMINATOR = 10_000).
  • Panic-free sat_* family (24/hour ops, dashboards, off-chain scoring):
    • sat_mul_div_i128(a, b, denom, mode) -> i128#[inline], # Examples doctest that locks in saturation + denom == 0 → 0.
    • sat_mul_bps, sat_bps (alias), sat_mul_bps_u64, sat_bps_u64 (alias), sat_bps_round_up, sat_split_bps.
    • sat_percent, sat_mul_percent (alias), sat_percent_u64, sat_mul_percent_u64 (alias), sat_percent_round_up, sat_split_percent.
  • Panicking percentage siblings (new in this PR; mirror the existing bps family):
    • percent(amount, percentage, mul_msg, _div_msg) -> i128
    • percent_u64(amount, percentage, mul_msg) -> u64
    • percent_round_up(amount, percentage, msg) -> i128
    • split_percent(amount, percentage, mul_msg, div_msg, sub_msg) -> (i128, i128)

Behavioural contract of sat_mul_div_i128

input mul_div_i128 (existing) sat_mul_div_i128 (new)
denom == 0 panics with msg returns 0
final overflow panics with msg clamps to i128::MIN/MAX
otherwise returns a * b / denom rounded same

Inline tests lock the contract: sat_mul_div_returns_zero_on_zero_denom (covers the denom == 0 → 0 branch), sat_helper_saturates_to_both_bounds (exercises the mag >= max_neg branch with i128::MIN, not just i128::MAX), sat_helper_u64_saturates, sat_helper_round_up_matches_panicking_family_for_in_range_values, sat_split_bps_and_percent, percent_signature_remains_four_args.

Migration notes

  • percent's trailing _div_msg parameter and split_percent's div_msg parameter are intentionally retained as no-op forward-compat placeholders so existing call sites keep compiling. Do not drop them.
  • The new sat_* helpers do not have a default rounding mode — callers must pass an explicit Rounding::Down / Up / Nearest to sat_mul_div_i128.

Drive-by: credence_errors repair

The new math→errors dep edge surfaced six pre-existing compile defects in credence_errors that had been silently sitting on main because no other crate reached for ContractError over that path. Surgical fixes:

  1. Removed duplicate SignatureExpired = 109 declaration (Authorization block). Every match arm and category() points at SignatureExpired = 222 (Bond block), so 222 is the surviving wire-stable value.
  2. Renumbered InvariantViolation from a duplicate 218 to the lowest free Bond code (230). No callsite can have been relying on the duplicate-discriminant state. Wire-bearing; explicitly noted here for downstream indexers/alerts.
  3. Renumbered EmergencyDrainNotPermitted from a duplicate 113 to the free Auth code (114). AdminSuspended retains its original 113. Wire-bearing.
  4. Consolidated a split category() Bond arm so BatchTooLarge / EmptyBatch coverage is one arm.
  5. Added missing arms in description() for BatchTooLarge / EmptyBatch and missing arms in is_recoverable() for BatchTooLarge / EmptyBatch / UnsupportedDecimals / UnauthorizedToken / EmergencyDrainNotPermitted.
  6. Removed references to the undeclared variant ContractError::DuplicateIdempotencyKey from description() and is_recoverable(); a TODO(#follow-up) breadcrumb is left at the enum anchor so future contributors re-add the three arms (category, description, is_recoverable) when the variant is formally declared with a wire-stable code.

The post-repair rustdoc on InvariantViolation = 230 and EmergencyDrainNotPermitted = 114 honestly call out the wire-code repair rather than restating the stale "Wire-stable: do not renumber" directive.

Verification run locally

  • cargo build -p credence_math — clean.
  • cargo test -p credence_math — 29 unit tests + 5 doc-tests pass.
  • cargo clippy -p credence_math --all-targets -- -D warnings — clean.
  • cargo build -p credence_math --target wasm32-unknown-unknown --release — clean.

Out of scope (surfaced as follow-ups — Refs, not Closes)

  • cargo clippy --workspace --all-targets -- -D warnings still fails because of pre-existing issues in:
    • contracts/credence_errors/src/test_errors.rs (E0004 missing UnauthorizedToken / EmergencyDrainNotPermitted / SlippageExceeded; unreachable_patterns on ContractError::DelegationNotExpired).
    • contracts/credence_bond/ — pre-existing syntax-level state in early_exit_penalty.rs and rolling_bond.rs. These predate this branch and need a separate follow-up.
  • pub use-style alias polish: sat_bps, sat_bps_u64, sat_mul_percent, sat_mul_percent_u64 are currently pub fn forwarders; pub use … as …; would be zero-cost. Cosmetic.

Acceptance criteria

  • Matches the summary above (sat_mul_bps, sat_percent, and companions, plus the necessary credence_math deps and docs). ✅
  • No regression in the existing test suite (29 unit + 5 doctests pass). ✅
  • Documented where it is observable (docs/decimal-handling.md — new ## Percentage Chains and ## Saturating Helpers (sat_*) sections, plus a ### Migration notes sub-section). ✅
  • Lint, type-check, and tests all pass locally for the affected crate. ✅
  • PR description references this issue with Closes #725. ✅