Skip to content

fix: prevent unsigned underflow in FluidV1 swap routing functions#558

Open
sunce86 wants to merge 1 commit intopropeller-heads:mainfrom
sunce86:fix/fluid-v1-swap-routing-underflow
Open

fix: prevent unsigned underflow in FluidV1 swap routing functions#558
sunce86 wants to merge 1 commit intopropeller-heads:mainfrom
sunce86:fix/fluid-v1-swap-routing-underflow

Conversation

@sunce86
Copy link
Copy Markdown

@sunce86 sunce86 commented Mar 18, 2026

Summary

swap_routing_in() and swap_routing_out() use a subtraction that can produce a negative result when the debt pool offers better pricing than the collateral pool. The Go reference implementation (KyberSwap pool_simulator.go:273-307) uses *big.Int (signed), and the caller checks a.Sign() <= 0 to route the entire trade through the debt pool.

The Rust port uses U256 (unsigned), so the subtraction wraps around to a large positive number instead of going negative. The existing caller checks a == U256::ZERO || a == U256::MAX but a wrapped value is neither — it falls through to the wrong branch and routes the trade through the collateral pool.

Fix

Replace - with saturating_sub() in both functions. When the result would be negative, this produces 0 instead of wrapping. The existing caller already handles a == U256::ZERO as "route through debt pool", so no caller changes are needed.

Test plan

  • Two new regression tests for negative routing in both functions
  • All 21 existing FluidV1 tests pass unchanged

swap_routing_in() and swap_routing_out() compute a subtraction that
can produce a negative result when the debt pool offers better pricing.
The Go reference (KyberSwap big.Int) handles this natively since big.Int
is signed, and the caller checks a.Sign() <= 0 to route through debt.

The Rust port used unsigned U256 subtraction which wraps on underflow,
producing a large positive value that bypasses the debt routing path
and incorrectly sends the swap through the collateral pool.

Fix: use saturating_sub so underflow produces 0 instead of wrapping.
The existing caller already treats a == 0 as "route through debt pool",
so no caller changes are needed.

Reference: KyberNetwork/kyberswap-dex-lib pool_simulator.go:273-307
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

1 participant