
Uniswap V3 Swap
Execute token swaps through Uniswap V3 concentrated liquidity pools on EVM chains.
Overview
Execute token swaps through Uniswap V3 concentrated liquidity pools on EVM chains.
Facts
0x2626664c2603336E57B271c5C0b26F421741e4810x68b3465833fb72A70ecDF485E0e4C7bD8665Fc450x68b3465833fb72A70ecDF485E0e4C7bD8665Fc450x3bFA4769FB09eefC5a80d6E87c3B9C650f7Ae48E0x3d4e44Eb1374240CE5F1B136041212E6B3B8Df870x61fFE014bA17989E743c5F6cB21bF9697530B21e0xEd1f6473345F45b75833fd55D5ADbEECeadd4fC90x33128a8fC17869897dcE68Ed026d694621f6FDfD0x1F98431c8aD98523631AE4a59f267346ea31F9840x0227628f3F023bb0B980b67D528571c95c6DaC1c0x04e45aaf — struct has NO deadline field0xb858183f — struct has NO deadline fieldSwapRouter02:
function exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params) external payable returns (uint256 amountOut);function exactInput((bytes path, address recipient, uint256 amountIn, uint256 amountOutMinimum) params) external payable returns (uint256 amountOut);function exactOutputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountOut, uint256 amountInMaximum, uint160 sqrtPriceLimitX96) params) external payable returns (uint256 amountIn);function exactOutput((bytes path, address recipient, uint256 amountOut, uint256 amountInMaximum) params) external payable returns (uint256 amountIn);
ERC-20 input token:
function approve(address spender, uint256 amount) external returns (bool);function allowance(address owner, address spender) external view returns (uint256);
Preflight Checks
Run all checks via eth_call before submitting transactions.
### 1 · Pool exists
factory.getPool(tokenIn, tokenOut, fee) → zero address means no pool at this fee tier; try another fee tier or abort.
Fee tier selection heuristic:
function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool);
### 2 · Quote & slippage-based amountOutMinimum
quoter_v2.quoteExactInputSingle(params) for single-hop; quoter_v2.quoteExactInput(path, amountIn) for multi-hop.
function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96) params) external returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate);function quoteExactInput(bytes path, uint256 amountIn) external returns (uint256 amountOut, uint160[] sqrtPriceX96AfterList, uint32[] initializedTicksCrossedList, uint256 gasEstimate);
amountOutMinimum = amountOut × (1 − slippage) — 0.5% for stable pairs, 1–2% for volatileinitializedTicksCrossed: each crossing means the swap consumed an entire liquidity range boundary; >10 signals high price impact — consider splitting the swapquoteExactOutputSingle / quoteExactOutput — a revert means insufficient liquidity for that output amount; abortfunction quoteExactOutputSingle((address tokenIn, address tokenOut, uint256 amount, uint24 fee, uint160 sqrtPriceLimitX96) params) external returns (uint256 amountIn, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate);function quoteExactOutput(bytes path, uint256 amountOut) external returns (uint256 amountIn, uint160[] sqrtPriceX96AfterList, uint32[] initializedTicksCrossedList, uint256 gasEstimate);
> ⚠️ QuoterV2 simulates swaps internally and must be called via eth_call. A revert from any quoteExact* call means the pool cannot fill the order — do not submit the swap.
Typical Flows
Single-hop exact-input — up to 2 tx:
allowance(agentAddress, router) on input token; skip approve if allowance >= amountInapprove(router, amountIn) on input tokenexactInputSingle({tokenIn, tokenOut, fee, recipient: agentAddress, amountIn, amountOutMinimum, sqrtPriceLimitX96: 0}) on SwapRouter02 — selector 0x04e45aafMulti-hop exact-input — up to 2 tx:
allowance(agentAddress, router) on input token; skip approve if allowance >= amountInapprove(router, amountIn) on input tokenexactInput({path, recipient: agentAddress, amountIn, amountOutMinimum}) on SwapRouter02 — selector 0xb858183fMulti-hop path encoding:
path = abi.encodePacked(tokenA, uint24(fee1), tokenB, uint24(fee2), tokenC)= address(20B) | fee(3B) | address(20B) | fee(3B) | address(20B) — total 66 bytesdirection: tokenIn (left) → tokenOut (right)
Example — USDC → WETH → LINK via fee 500 then 3000:
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 | 0x0001F4 | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 | 0x000BB8 | 0x514910771AF9Ca656af840dff83E8264EcF986CA
> sqrtPriceLimitX96: 0 disables the price boundary — the swap executes at any price within slippage tolerance set by amountOutMinimum. Use a non-zero value only if a hard price cap is required.
> ⚠️ SwapRouter02 structs have NO deadline field. V1 SwapRouter structs include deadline and use different selectors (0x414bf389 / 0xc04b8d59). Using V1 selectors against SwapRouter02 will revert.
Policy Controls
allowance(agentAddress, router) >= amountIn, skip the approve tx entirely — saves gas and avoids unnecessary transactions.approve on the token contract address; approving the router address will revert.0 before setting a new non-zero value. Pattern: approve(router, 0) → approve(router, amount). Skipping this causes revert.amountOutMinimum correctly.deposit()/withdraw() directlyamountOutMinimum: 0 acceptable for testing. Chain-specific router: Ethereum Sepolia uses 0x3bFA...e48E; 0x94cC...2bc4 is the Base Sepolia router.References
https://developers.uniswap.org/llms.mdx — SwapRouter02 ABI, exactInputSingle/exactInput params, fee tiers, path encoding, pool addresses.https://cdn.jsdelivr.net/npm/@uniswap/swap-router-contracts/artifacts/contracts/SwapRouter02.sol/SwapRouter02.json — SwapRouter02 full ABI (exactInputSingle/exactInput/multicall), function signatures and struct params.