Problem Statement / Feature Objective
The protocol charges a small fee on each settlement transaction, computed as a percentage of the converted amount. The current fee calculation uses integer division in i128 fixed-point arithmetic that truncates the remainder without any rounding policy, creating a systematic extraction vector. An attacker can submit a very large number of micro-settlements—each for the minimum dust amount—causing the protocol to lose the truncated remainder on every single transaction. Over 1 million such transactions, the cumulative extracted value could reach several dollars in equivalent settlement tokens. The objective is to implement a fair rounding policy (banker's rounding or round-half-up) for protocol fee computation.
Technical Invariants & Bounds
Fee formula: fee = amount * fee_rate_bps / 10000, where fee_rate_bps is in basis points (range [1, 1000], i.e. 0.01% to 10%). Both amount and fee are i128 with 7-decimal precision. The truncation error per transaction is at most (10000 - 1) / 10000 * 1e-7 = ~0.0001 of the smallest unit. With 1 million transactions, max extractable = 1e6 * 1e-7 = 0.1 units of the settlement token. However, if the attacker uses the minimum possible non-zero amount (1 in fixed-point = 1e-7 tokens), the fee truncation rounds to 0 every time, extracting 100% of the intended fee. The fix: implement round_half_up: fee = ((amount * fee_rate_bps) + 5000) / 10000. This adds exactly 1 addition and 1 division per transaction, costing ~2,000 extra instructions. The fee collector must receive the correct fee; verify with property-based testing that for all amounts in [1, MAX_SETTLEMENT] and fee_rates in [1, 1000], the computed fee satisfies: fee * 10000 >= amount * fee_rate_bps (no over-collection) AND fee * 10000 <= amount * fee_rate_bps + 9999 (maximum 1 rounding error).
Codebase Navigation Guide
Protocol fee calculation: contracts/settlement/src/fees.rs — likely compute_fee() function at line 34. Fee rate constants: contracts/settlement/src/constants.rs — FEE_RATE_BPS. Fee collection logic: contracts/settlement/src/lib.rs — collect_fee() around line 230. The token transfer for fee is done via Soroban token client in contracts/settlement/src/token_utils.rs. Tests in contracts/settlement/src/test.rs may have fee tests at line 400+.
Implementation Blueprint
Step 1: In contracts/settlement/src/fees.rs, modify compute_fee(amount: i128, rate_bps: u32) -> i128 to return ((amount * rate_bps as i128) + 5000) / 10000 (round-half-up). Step 2: Add a comment referencing the rounding policy. Step 3: Add property-based test in test.rs using a loop that enumerates edge cases: amount = 1 (minimum), amount = 10000000 (1 token), amount = MAX_SETTLEMENT, rate_bps = 1, 50, 100, 500, 1000. Assert that the invariant fee * 10000 >= amount * rate_bps (no overcharge) holds. Step 4: Also add a test that verifies total fees collected across N micro-settlements matches expected cumulative fee within 1-unit tolerance. Step 5: Run cargo test --package settlement to confirm.
Problem Statement / Feature Objective
The protocol charges a small fee on each settlement transaction, computed as a percentage of the converted amount. The current fee calculation uses integer division in i128 fixed-point arithmetic that truncates the remainder without any rounding policy, creating a systematic extraction vector. An attacker can submit a very large number of micro-settlements—each for the minimum dust amount—causing the protocol to lose the truncated remainder on every single transaction. Over 1 million such transactions, the cumulative extracted value could reach several dollars in equivalent settlement tokens. The objective is to implement a fair rounding policy (banker's rounding or round-half-up) for protocol fee computation.
Technical Invariants & Bounds
Fee formula: fee = amount * fee_rate_bps / 10000, where fee_rate_bps is in basis points (range [1, 1000], i.e. 0.01% to 10%). Both amount and fee are i128 with 7-decimal precision. The truncation error per transaction is at most (10000 - 1) / 10000 * 1e-7 = ~0.0001 of the smallest unit. With 1 million transactions, max extractable = 1e6 * 1e-7 = 0.1 units of the settlement token. However, if the attacker uses the minimum possible non-zero amount (1 in fixed-point = 1e-7 tokens), the fee truncation rounds to 0 every time, extracting 100% of the intended fee. The fix: implement round_half_up: fee = ((amount * fee_rate_bps) + 5000) / 10000. This adds exactly 1 addition and 1 division per transaction, costing ~2,000 extra instructions. The fee collector must receive the correct fee; verify with property-based testing that for all amounts in [1, MAX_SETTLEMENT] and fee_rates in [1, 1000], the computed fee satisfies: fee * 10000 >= amount * fee_rate_bps (no over-collection) AND fee * 10000 <= amount * fee_rate_bps + 9999 (maximum 1 rounding error).
Codebase Navigation Guide
Protocol fee calculation: contracts/settlement/src/fees.rs — likely compute_fee() function at line 34. Fee rate constants: contracts/settlement/src/constants.rs — FEE_RATE_BPS. Fee collection logic: contracts/settlement/src/lib.rs — collect_fee() around line 230. The token transfer for fee is done via Soroban token client in contracts/settlement/src/token_utils.rs. Tests in contracts/settlement/src/test.rs may have fee tests at line 400+.
Implementation Blueprint
Step 1: In contracts/settlement/src/fees.rs, modify compute_fee(amount: i128, rate_bps: u32) -> i128 to return ((amount * rate_bps as i128) + 5000) / 10000 (round-half-up). Step 2: Add a comment referencing the rounding policy. Step 3: Add property-based test in test.rs using a loop that enumerates edge cases: amount = 1 (minimum), amount = 10000000 (1 token), amount = MAX_SETTLEMENT, rate_bps = 1, 50, 100, 500, 1000. Assert that the invariant fee * 10000 >= amount * rate_bps (no overcharge) holds. Step 4: Also add a test that verifies total fees collected across N micro-settlements matches expected cumulative fee within 1-unit tolerance. Step 5: Run cargo test --package settlement to confirm.