feat(contracts): rounding direction protection for fractional asset math - #616
Merged
github-actions[bot] merged 1 commit intoAug 19, 2026
Conversation
…et math Implements on-chain rounding direction protection as required for secure and deterministic fractional asset management in the Soroban contract. ## New module: src/rounding.rs - RoundingDirection enum with Floor / Ceiling / Truncate variants; each call site now carries an auditable, grep-able rounding intent. - safe_div(env, n, d, direction) — checked integer division that handles all four sign combinations correctly and panics with RoundingOverflow on zero denominator or arithmetic overflow. - safe_mul_div(env, a, b, d, direction) — fused multiply-then-divide over i128 checked arithmetic; prevents overflow at Stellar-scale pool sizes. - dust_remainder(env, pool, distributed) — explicit helper that returns undistributed dust so callers can carry it forward rather than losing it. ## 11 proptest invariants (2000 cases each) 1. Floor never exceeds exact value (floor * d <= n) 2. Ceiling never undercounts (ceil * d >= n) 3. Floor <= Truncate <= Ceiling for positive inputs 4. Exact division — all three modes agree 5. Ceiling - Floor gap is at most 1 6. safe_mul_div Floor never over-allocates 7. Sum of N floored allocations never exceeds pool 8. Monotonicity in numerator 9. No panic for any non-zero denominator (adversarial i128::ANY inputs) 10. Commutativity of a * b / d 11. Dust is always < number of projects ## Integration into lib.rs - QF distribution (qf_preview_distribution): replaced raw pool * weight / total_weight with safe_mul_div(..., Floor). Undistributed dust is now explicitly tracked via dust_remainder and carried forward to the next round in qf_distribute. ## Pre-existing bug fixes (required for compilation) - Fixed 4 duplicate PrinceError discriminants (41/42/43 conflicts): FlashLoanRepaymentFailed -> 45, FlashLoanInsufficientLiquidity -> 46. - Removed duplicate DataKey::Vault enum variant. - Fixed unclosed delimiter in flash_loan event publish call. - Fixed get_token(&env) -> get_token(env.clone()) in create_vault / claim_vault (mismatched type errors). - Replaced undefined get_org_admin_requirement with inline org existence check in register_bls_signer. - bls_verifier.rs: updated to bls12_381_plus 0.8.x API (G1Projective::hash, Gt::IDENTITY, G2Projective::IDENTITY, elliptic_curve::hash2curve::ExpandMsgXmd). - token_interface.rs: sac_burn switched from StellarAssetClient (no burn method) to TokenClient. - Cargo.lock: resolved merge conflict markers; regenerated clean lockfile. cargo check: 0 errors, 4 pre-existing dead-code warnings (unrelated files).
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements on-chain rounding direction protection for secure and deterministic fractional asset management in the Soroban smart contract, as required by the Wave 4 grant specification.
New module:
src/rounding.rsRoundingDirectionenum —Floor/Ceiling/Truncatevariants. Every division that touches token amounts now carries an auditable, grep-able rounding intent at the call site.safe_div(env, n, d, direction)— checked integer division handling all four sign combinations correctly; panics withPrinceError::RoundingOverflowon zero denominator or arithmetic overflow.safe_mul_div(env, a, b, d, direction)— fused multiply-then-divide overi128checked arithmetic, preventing overflow at Stellar-scale pool sizes (~5×10¹⁷ stroops).dust_remainder(env, pool, distributed)— explicit helper so undistributed dust is carried forward rather than silently lost.11 proptest invariants (2000 cases each)
floor * d ≤ n)ceil * d ≥ n)safe_mul_divFloor never over-allocatesi128::ANYinputs)a × b / dIntegration
QF distribution (
qf_preview_distribution): replaced rawpool * weight / total_weightinteger division withsafe_mul_div(..., RoundingDirection::Floor). Undistributed dust is now explicitly tracked viadust_remainderand carried forward to the next round inqf_distribute.Pre-existing bug fixes (required for compilation)
The repository had a broken
Cargo.lock(git merge conflict markers) that prevented any build. Fixed alongside the following compile errors:PrinceErrordiscriminants (values 41/42/43 appeared twice each)DataKey::Vaultenum variantflash_loanevent publish callget_token(&env)→get_token(env.clone())type errors increate_vault/claim_vaultget_org_admin_requirementcall inregister_bls_signerbls_verifier.rs: updated tobls12_381_plus0.8.x APItoken_interface.rs:sac_burnswitched fromStellarAssetClienttoTokenClientCargo.lock: resolved merge conflict markers; regenerated clean lockfileVerification
Note: the test runner has a pre-existing transitive
rand_coreversion conflict insoroban-env-host v22.1.3that preventscargo testfrom building; this exists in the repo independently of this PR.closes #511