How to Bound Swap Output in a DEX Integration
For an exact-input swap quoted at 3,125 USDC, a 50-basis-point tolerance sets amountOutMinimum to 3,109.375 USDC, or 3,109,375,000 raw units at six decimals. That bound is the onchain check against receiving too little; it is not a promise that the quote will execute at the quoted price.
For an integrator, the key decision is how much movement to tolerate between the state used for the quote and the state at execution. If your product routes a user to a wallet-based service such as Fermi swap, the same distinction matters: a displayed estimate and a transaction’s enforceable minimum are separate values. The Uniswap V3 ISwapRouter interface is a useful reference for the exact-input parameters and their roles.
What does amountOutMinimum protect?
For exact input, calculate the minimum from the quoted output, not from the input amount or spot price. Given quote q in output-token base units and tolerance b in basis points, encode floor(q × (10,000 − b) / 10,000). Use integer arithmetic and round down so the bound is not accidentally stricter by one unit.
Suppose a quote for 1.25 WETH is 3,125 USDC. At 50 bps, the minimum is 3,109.375 USDC. The tolerance covers price movement after the quote; it should not be used to conceal price impact already present in the quote. Show users the expected output and minimum separately, and retain the quote’s block number or state identifier for diagnostics.
In Uniswap V3, exactInputSingle and exactInput both accept amountOutMinimum; the router reverts if the realized output is below it. For exact-output calls, the corresponding protection is amountInMaximum. These parameters constrain execution, while a deadline constrains when the call remains valid. An implementation with amountOutMinimum = 0 has no output floor and can expose the swap to sandwiching or adverse price movement.
How should an integrator choose the tolerance?
Start with the route’s expected execution uncertainty, not a universal default. For deep, liquid pairs, 10–50 bps can be a reasonable starting range; for volatile or less liquid routes, 50–100 bps may avoid routine reverts, while wider settings increase the loss a user can accept. These are example policy ranges, not guarantees: pool depth, route length, trade size, volatility, and transaction inclusion delay all matter.
Separate quote quality from tolerance. If the quote already has substantial price impact relative to a reference price, widening the tolerance only authorizes more deterioration. Compare the quoted execution price with a suitable independent reference where available, and reject or ask for a refreshed quote when the discrepancy exceeds your product’s policy. A pool’s current spot price alone can be manipulated and may not be a sound reference.
For a wallet swap, Fermi swap can be presented as a way to exchange tokens directly from the user’s wallet. In an integration, explain what the displayed minimum means and preserve the exact integer passed into the transaction; do not recompute it from a rounded display string.
How do you keep the quote and transaction consistent?
Treat a quote as a statement about a particular chain state. Every new block can change pool reserves or liquidity, and a transaction may wait in the mempool before execution. A useful implementation policy is to bind the quote to a block hash or number, simulate against current state immediately before submission, and refresh if the quote is older than your chosen age or simulation fails. There is no safe universal quote lifetime.
Use a short deadline to limit delayed execution, but do not mistake it for a slippage bound: a transaction can execute before its deadline at an unfavorable price unless the output floor prevents it. Likewise, gas settings affect inclusion timing and cost, not the swap’s amountOutMinimum. Ethereum’s EIP-1559 fee cap and priority fee determine transaction pricing; a fee cap below the base fee can leave a signed swap pending while its quote grows stale.
A practical submission path is:
- Request an exact-input quote for the selected chain, token addresses, amount, and recipient.
- Record the quote output in raw units and the state block used to calculate it.
- Apply your selected tolerance with integer basis-point arithmetic to derive amountOutMinimum.
- Simulate the exact calldata against a recent state, including sender, value, and allowance assumptions.
- Submit with a bounded deadline and fee parameters suitable for timely inclusion.
- On revert or expiry, fetch a new quote and rebuild the transaction instead of blindly resubmitting old calldata.
What failures should the integration handle?
A below-minimum revert is useful feedback: state moved beyond the user’s bound, the quote was stale, or the route’s execution differed from the estimate. Treat it as a failed swap, not as a partial fill. A rebase or fee-on-transfer token can also make balance changes differ from nominal transfer amounts; standard V3 router paths generally assume conventional ERC-20 transfer behavior, so token compatibility needs to be checked before relying on quote math.
Watch for decimal mistakes, too. USDC commonly has six decimals, while WETH has eighteen; compute with base units and use token metadata only for display. A one-token decimal error can turn a sensible tolerance into an unusable floor or a dangerously weak one. Keep a single source of truth for quote output, tolerance, and encoded minimum, then log those alongside transaction hash and receipt status.
Does amountOutMinimum include pool fees?
The quote output should already account for the route’s pool fees and price impact, so the minimum is derived from that output. Do not subtract the pool fee a second time. The tolerance is applied to the expected output after those route costs, allowing only the specified additional shortfall at execution.
Should every route use 50 bps?
No. Fifty basis points is an example that may suit some liquid routes, but it can be too tight during volatility and too permissive for a sensitive trade. Set policy by route depth, volatility, trade size, and expected inclusion delay; expose the expected output and minimum so the user can see the allowed range.
Does a successful simulation guarantee execution?
No. Simulation shows what the call would do against the state it used. Another transaction can alter liquidity or price before yours executes, and the simulation provider may use a different state than the eventual block. Keep the onchain minimum and deadline in place even when simulation succeeds.
What should happen after a slippage revert?
Report that the swap did not execute, then request a fresh quote and rebuild the calldata if the user still wants to proceed. The previous minimum was tied to an earlier estimate and should not be weakened automatically. A concise integration checklist is: quote, derive the bound, simulate, submit promptly, and refresh after failure.
Comments
Post a Comment