fix(bid): enforce exact bid amount precision and overflow bounds (QE-… - #2556
Open
dumbdev wants to merge 1 commit into
Open
fix(bid): enforce exact bid amount precision and overflow bounds (QE-…#2556dumbdev wants to merge 1 commit into
dumbdev wants to merge 1 commit into
Conversation
…2026-08) Centralise bid amount validation - sign, the i128::MAX / 10_000 overflow ceiling, the documented "bid amount <= invoice amount" rule, and the "expected_return >= bid_amount" return floor - in a documented, pure, tested module (bid_amount.rs) and route the bid-submission and auction-selection paths (contract.rs::place_bid, bid.rs::verify_bid_match) through it. Rejected amounts fail before any state change. checked_bid_profit pins the auction ranking key that compare_bids computes with a lossy saturating_sub: for any pair accepted by validate_bid the exact subtraction is non-negative and cannot overflow i128, so ranking is deterministic; adversarial pairs outside that contract surface as ArithmeticOverflow instead of a clamped, attacker-chosen rank. checked_bid_amount_sum and checked_bid_fee_amount do the same for per-investor exposure aggregation and downstream bps fee math. One explicit, documented tightening: bids financing more than the invoice face value, or offering a return below principal (previously accepted then ranked with a saturating_sub-clamped profit of 0) are now rejected with the existing QuickLendXError::InvalidAmount. No new error codes, no stored state touched, no migration. Tests: 20 focused cases in test_bid_amount_precision.rs covering zero, min, max, near-overflow, fractional, and conversion-boundary values, inclusive boundaries, a 5,000-iteration deterministic random sweep against an independent oracle, overflow-vs-clamp, and purity/repeatability. cargo test --lib: 37 passed. cargo build (dev + release WASM profile with overflow-checks) and cargo clippy --lib: clean. Closes #QE-2026-08 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
@dumbdev 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! 🚀 |
Contributor
Author
|
Pls this is a wide repo problem |
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.
closes #2442
Centralise bid amount validation - sign, the i128::MAX / 10_000 overflow ceiling, the documented "bid amount <= invoice amount" rule, and the "expected_return >= bid_amount" return floor - in a documented, pure, tested module (bid_amount.rs) and route the bid-submission and auction-selection paths (contract.rs::place_bid, bid.rs::verify_bid_match) through it. Rejected amounts fail before any state change.
checked_bid_profit pins the auction ranking key that compare_bids computes with a lossy saturating_sub: for any pair accepted by validate_bid the exact subtraction is non-negative and cannot overflow i128, so ranking is deterministic; adversarial pairs outside that contract surface as ArithmeticOverflow instead of a clamped, attacker-chosen rank. checked_bid_amount_sum and checked_bid_fee_amount do the same for per-investor exposure aggregation and downstream bps fee math.
One explicit, documented tightening: bids financing more than the invoice face value, or offering a return below principal (previously accepted then ranked with a saturating_sub-clamped profit of 0) are now rejected with the existing QuickLendXError::InvalidAmount. No new error codes, no stored state touched, no migration.
Tests: 20 focused cases in test_bid_amount_precision.rs covering zero, min, max, near-overflow, fractional, and conversion-boundary values, inclusive boundaries, a 5,000-iteration deterministic random sweep against an independent oracle, overflow-vs-clamp, and purity/repeatability. cargo test --lib: 37 passed. cargo build (dev + release WASM profile with overflow-checks) and cargo clippy --lib: clean.
CI note: the failing
buildcheck is a pre-existing repo-wide failure, not introduced by this PRThe Soroban Smart Contracts CI / build job fails at a single step — Run Cargo tests (
cargo test --verbose) — while compiling two legacy integration-test files that this PR does not touch:quicklendx-contracts/tests/bid_acceptance_replay.rsno method named get_bid / get_bids_for_invoice / accept_bid_and_fund / get_escrow_details … found for QuickLendXContractClientquicklendx-contracts/tests/invoice_lifecycle_e2e.rsno method named get_invoice / process_partial_payment / settle_invoice / get_platform_metrics … found for QuickLendXContractClientThese files expect the full contract ABI, but the crate is currently a minimal skeleton (
lib.rsexposes onlyerrors+invoice_amount, and nowbid_amount). The CI workflow itself acknowledges this state:# TODO: restore the original 95% gate after the legacy test suite is repaired.This failure predates the PR and exists on
main:buildcheckmainHEAD (d9ca8323)71593c46— merged PR #2551 (invoice_amount), the precedent this PR mirrorsmaincommit (4bc72012,c096a080,96f4e922, …)b2334f8b)Every CI step this change can affect passes:
cargo check --lib)#![no_std]-clean)Local validation of this change:
Repairing the legacy
tests/suite or gating that CI step is out of scope for this issue ("no unrelated refactors, disabled CI") and belongs in a separate infrastructure PR.