diff --git a/intent_settlement/src/lib.rs b/intent_settlement/src/lib.rs index 82aa704..fe19e82 100644 --- a/intent_settlement/src/lib.rs +++ b/intent_settlement/src/lib.rs @@ -29,6 +29,17 @@ const PROTOCOL_FEE_BPS: i128 = 5; // 0.05% /// closes. const BID_WINDOW: u64 = 120; // 2 minutes +// Upper sanity bound for src_amount and min_dst_amount. +// +// Largest realistic token amounts use 18-decimal ETH units. +// 1e12 tokens × 1e18 units/token = 1e30, well within i128 range (~1.7e38), +// but downstream arithmetic (fee = amount * 5 / 10_000) multiplies first and +// then divides. To guarantee `amount * PROTOCOL_FEE_BPS` never overflows i128, +// the bound is i128::MAX / PROTOCOL_FEE_BPS ≈ 3.4e37. We choose a round, +// economically implausible threshold: 10^30 (one trillion 18-decimal tokens). +// That is a comfortable safety margin while rejecting only fat-fingered inputs. +pub const MAX_AMOUNT: i128 = 1_000_000_000_000_000_000_000_000_000_000i128; // 10^30 + // Soroban archives ledger entries that go too long without being touched. // Persistent Intent/Solver records get their TTL bumped on every write so // they don't need to be manually restored before later calls can read them. @@ -784,6 +795,10 @@ impl IntentSettlement { panic_with_error!(&env, Error::ZeroAmount); } + if src_amount > MAX_AMOUNT || min_dst_amount > MAX_AMOUNT { + panic_with_error!(&env, Error::AmountTooLarge); + } + if Self::is_dst_allowlist_enabled(env.clone()) && !Self::is_dst_token_allowed(env.clone(), dst_token.clone()) {