ISwapRouter is the interface your contract (or off-chain script) calls to execute token swaps through Uniswap V3 pools. It lives at:
@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.solThe canonical deployment is the SwapRouter contract. On most chains the address is 0xE592427A0AEce92De3Edee1F18E0157C05861564.
Note: Uniswap has since shipped SwapRouter02 (combines V2 + V3 routing) and the UniversalRouter (V2, V3, Permit2, NFT purchases). For new integrations, evaluate those first — they offer better gas efficiency and approval flows via Permit2. This guide covers the original SwapRouter, which remains widely deployed and is the simplest to learn from.
Installation#
|
|
In Foundry add the remapping:
@uniswap/v3-periphery/=lib/v3-periphery/
@uniswap/v3-core/=lib/v3-core/Interface Overview#
ISwapRouter exposes four functions:
| Function | Use Case |
|---|---|
exactInputSingle |
Swap a fixed amount of one token for as much as possible of another (single pool). |
exactInput |
Same idea but across a multi-hop path (two or more pools). |
exactOutputSingle |
Receive an exact amount of the output token, spending as little as possible (single pool). |
exactOutput |
Exact-output across a multi-hop path. |
All four accept a struct of parameters and return the resulting amount.
Fee Tiers#
Uniswap V3 pools are deployed per fee tier. You pass the fee value to every swap call, so understand these before writing any swap code.
| Fee | Basis Points | Typical Use |
|---|---|---|
| 100 | 0.01 % | Stable-to-stable pairs (USDC/USDT). |
| 500 | 0.05 % | Correlated pairs (WETH/stETH) or high-volume majors. |
| 3000 | 0.3 % | Most standard pairs. |
| 10000 | 1 % | Exotic or low-liquidity tokens. |
If you’re unsure which tier has the deepest liquidity for your pair, query the Quoter (see below) across all tiers and pick the one that returns the best output.
Getting a Quote#
Before executing a swap you need a quote to derive a safe amountOutMinimum (slippage protection). Use the QuoterV2 contract off-chain:
|
|
Note:
QuoterV2simulates the swap viastaticcall— it reverts internally and decodes the revert data to return the quote. Call it off-chain (viaeth_call) to avoid gas costs. Never call it on-chain in a transaction.
A 0.5 % tolerance is a reasonable starting point for major pairs. Volatile or low-liquidity pairs may need 1–3 %. The right value depends on how long your transaction might sit in the mempool — longer waits need wider tolerances.
Exact-Input Single Swap#
The simplest case: swap a known amount of token A for token B through one pool.
|
|
Parameter Breakdown#
| Parameter | Notes |
|---|---|
tokenIn / tokenOut |
ERC-20 addresses. Order matters — it determines swap direction. |
fee |
Identifies which pool to use. See the fee tier table above. |
recipient |
Where the output tokens are sent. |
deadline |
Unix timestamp after which the tx reverts. For atomic contract-to-contract calls block.timestamp is fine (it’s always “now”). For user-submitted transactions, pass the deadline in as a parameter — otherwise a validator can hold the tx and execute it at an arbitrarily later time. |
amountIn |
Exact number of input tokens (in the token’s smallest unit). |
amountOutMinimum |
Slippage guard. Never set to 0 in production — this makes you vulnerable to sandwich attacks. Derive it from a Quoter call minus your slippage tolerance. |
sqrtPriceLimitX96 |
Cap how far the pool price can move during the swap. Encoded as a Q64.96 fixed-point square root price. 0 means no limit. A non-zero value causes the swap to stop early (partial fill) if the price reaches the limit — useful for large swaps where you want to cap price impact. |
Exact-Output Single Swap#
When you need exactly N of the output token and are willing to spend up to a maximum of the input token. The setup is the same as exact-input — only the struct and the refund logic differ:
|
|
Important: Always refund the difference. You transferred
amountInMaximumbut the router may have spent less. Without the refund, the surplus is stuck in the contract.
Multi-Hop Swaps#
For tokens that don’t share a direct pool (or where routing through an intermediary gives a better price), use exactInput or exactOutput with an encoded path.
A path is a tightly packed sequence of (token, fee, token, fee, token, …). You must use abi.encodePacked — standard abi.encode pads each element to 32 bytes and produces an invalid path that will revert.
|
|
For
exactOutputthe path is reversed — it starts with the output token and ends with the input token.
Wrapping / Unwrapping ETH#
The SwapRouter does not accept raw ETH. Wrap it first:
|
|
To unwrap after receiving WETH as output, set recipient to address(this), then withdraw and forward:
|
|
Your contract needs a
receive() external payable {}function to accept ETH from the WETH withdraw call.
Testing with Foundry#
Fork mainnet and run a real swap in a Foundry test:
|
|
Run with:
|
|
Common Pitfalls#
Warning — fund loss risk: The first two items can result in permanent loss of funds, not just reverted transactions.
- Setting
amountOutMinimumto 0 in production — makes you vulnerable to sandwich attacks. A MEV bot can manipulate the pool price before your swap and extract the difference. Always derive this value from a fresh Quoter call. - Exact-output refund — forgetting to return unspent tokens to the caller after an
exactOutputswap leaves funds permanently stuck in the contract. - Forgetting
approve— the router pulls tokens viatransferFrom. Without an approval the swap reverts with a generic ERC-20 error. - Wrong fee tier — if no pool exists for that fee, the swap reverts. Use the Quoter to verify the pool exists and has liquidity.
- Stale
deadline— if the deadline is in the past the tx reverts. For on-chain integrations useblock.timestamp; for user txs pass the deadline as a parameter with 5–20 minutes of buffer. - Using raw
approveinstead ofSafeERC20— tokens like USDT require the allowance to be zeroed before setting a new value. Rawapprovecalls will revert on these tokens. Use OpenZeppelin’sSafeERC20library.
Deployed Addresses#
See the official Uniswap V3 deployment list for full, copy-pasteable addresses across all supported chains.