DEV Community

Devid Udyanskaya
Devid Udyanskaya

Posted on

SpookySwap Swap Bounds for V3 Integrators

Set a fresh quote, an explicit minimum output and a short deadline before submitting a V3 swap. Those bounds decide whether a pending trade executes at an acceptable price or reverts after the pool changes.

What does the minimum output protect?

The router checks the swap’s actual output against amountOutMinimum at execution. In a concentrated-liquidity pool, that output depends on active liquidity across the price path, the pool fee and trades that execute first; a quote is only a snapshot of pool state. SpookySwap on Fantom gives you a chain-specific deployment to verify when you build this flow: read the router and QuoterV2 addresses from the current contract registry, keyed by chain ID.

For an exact-input swap, derive the floor from the quoted output in integer token units: minOut = floor(quoteOut × (10,000 − toleranceBps) / 10,000). If a quote is 2,500 USDC and tolerance is 50 basis points, the floor is 2,487.5 USDC, or 2,487,500,000 units at six decimals. If execution would deliver less, the router reverts the whole swap.

How should you obtain and age a quote?

Use the V3 QuoterV2 for route simulation, not a spot-price read or an off-chain reserve approximation. A quote traverses the chosen pools and fee tiers, accounting for price movement and liquidity changes across ticks; its output and gas estimate help compare candidate routes. Treat it as a simulation, not an oracle or an execution guarantee.

Run the quote against a recent block, then build the transaction promptly. For reliability, associate the result with chain ID, token addresses, fee tiers, input amount and block number; refresh if the wallet takes long enough for pool state to move. A stale quote can still simulate successfully while producing an output that fails your minimum.

Which bounds and checks belong in the transaction?

For exact input, pass the input amount, amountOutMinimum, route or pool parameters, recipient and deadline to the router method matching the deployed ABI. A tolerance around 25–100 bps is a common starting range for liquid pairs; volatile or thin pools may need more, but widening it increases the loss the user accepts. Let the user choose that trade-off and show both quoted output and minimum output.

  • Deadline: commonly 60–180 seconds from submission, adjusted for wallet delay and the chain’s block cadence.
  • Recipient: bind it to the intended wallet or contract; don’t accept an untrusted route response.
  • Input amount: use raw units from token decimals, and confirm the balance and allowance cover it.
  • Pool path: validate each token and fee tier against the deployed factory and router.

Do not silently increase tolerance to “fix” a revert. Check whether the quote aged, the route lacks active liquidity, the transaction deadline passed or the token’s transfer behavior is unsupported. Fee-on-transfer tokens are a particular edge case: SpookySwap’s published integration notes say its routers do not support them, so a conventional quote and minimum-output check cannot make that route safe.

What is the integration sequence?

SpookySwap’s published Fantom registry lists a V3 core factory, QuoterV2 and SwapRouter02. Resolve those contracts from the official registry for the selected chain rather than copying a deployment address into shared code; the ABI and supported methods must match that deployment.

  1. Read the wallet’s chain ID and reject a mismatch with the selected deployment.
  2. Validate token addresses and decimals, then check balance and allowance.
  3. Request a QuoterV2 simulation for the intended amount, path and fee tiers.
  4. Calculate minimum output in raw units from the user’s tolerance.
  5. Simulate the exact router call and recipient, then submit with a short deadline.
  6. On confirmation, decode the receipt and report actual output rather than the quote.

I would pin deployments by chain ID, keep tolerances explicit, and refresh the quote immediately before wallet approval. That preserves a clear failure signal when state moves instead of disguising it with a permissive bound.

Top comments (0)