Cobo Agentic Wallet
Uniswap V3 Swap

Uniswap V3 Swap

Execute token swaps through Uniswap V3 concentrated liquidity pools on EVM chains.

CategoryTradingDeFi
ChainsBASE_ETHETHMATICSETH
C
Cobo· Author
138 views·126 uses

Overview

Execute token swaps through Uniswap V3 concentrated liquidity pools on EVM chains.

Facts

swap_router_02 (BASE_ETH): 0x2626664c2603336E57B271c5C0b26F421741e481
swap_router_02 (ETH): 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45
swap_router_02 (MATIC): 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45
swap_router_02 (SETH): 0x3bFA4769FB09eefC5a80d6E87c3B9C650f7Ae48E
fee tiers: 100 (0.01%) / 500 (0.05%) / 3000 (0.3%) / 10000 (1%)
quoter_v2 (BASE_ETH): 0x3d4e44Eb1374240CE5F1B136041212E6B3B8Df87
quoter_v2 (ETH / MATIC): 0x61fFE014bA17989E743c5F6cB21bF9697530B21e
quoter_v2 (SETH): 0xEd1f6473345F45b75833fd55D5ADbEECeadd4fC9
factory (BASE_ETH): 0x33128a8fC17869897dcE68Ed026d694621f6FDfD
factory (ETH / MATIC): 0x1F98431c8aD98523631AE4a59f267346ea31F984
factory (SETH): 0x0227628f3F023bb0B980b67D528571c95c6DaC1c
selector exactInputSingle: 0x04e45aaf — struct has NO deadline field
selector exactInput: 0xb858183f — struct has NO deadline field
target_in: swap_router_02 + input token contract

SwapRouter02:

solidity
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:

solidity
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:

Stable pairs: try 100 → 500
Major token pairs: try 500 → 3000
Long-tail pairs: try 3000 → 10000
solidity
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.

solidity
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 volatile
initializedTicksCrossed: each crossing means the swap consumed an entire liquidity range boundary; >10 signals high price impact — consider splitting the swap
Exact-output reachability: call quoteExactOutputSingle / quoteExactOutput — a revert means insufficient liquidity for that output amount; abort
solidity
function 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:

1.Check allowance(agentAddress, router) on input token; skip approve if allowance >= amountIn
2.approve(router, amountIn) on input token
3.exactInputSingle({tokenIn, tokenOut, fee, recipient: agentAddress, amountIn, amountOutMinimum, sqrtPriceLimitX96: 0}) on SwapRouter02 — selector 0x04e45aaf

Multi-hop exact-input — up to 2 tx:

1.Check allowance(agentAddress, router) on input token; skip approve if allowance >= amountIn
2.approve(router, amountIn) on input token
3.exactInput({path, recipient: agentAddress, amountIn, amountOutMinimum}) on SwapRouter02 — selector 0xb858183f

Multi-hop path encoding:

text
path = abi.encodePacked(tokenA, uint24(fee1), tokenB, uint24(fee2), tokenC)
= address(20B) | fee(3B) | address(20B) | fee(3B) | address(20B) — total 66 bytes
direction: tokenIn (left) → tokenOut (right)

Example — USDC → WETH → LINK via fee 500 then 3000:

text
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

Check allowance before approve: If allowance(agentAddress, router) >= amountIn, skip the approve tx entirely — saves gas and avoids unnecessary transactions.
Approve target is the input token, not SwapRouter02: Call approve on the token contract address; approving the router address will revert.
USDT allowance reset (ETH mainnet): USDT requires setting allowance to 0 before setting a new non-zero value. Pattern: approve(router, 0)approve(router, amount). Skipping this causes revert.
Approve must confirm before swap: Unconfirmed approve causes swap revert.
Slippage is agent's responsibility: Policy validates address + USD value only; set amountOutMinimum correctly.
Large swap price impact: Split large amounts into smaller transactions.
Policy denial: Parse reason (per-tx or cumulative cap exceeded); retry with compliant amount.
Not applicable for: native ETH → WETH wrapping or WETH → ETH unwrapping — use WETH9 deposit()/withdraw() directly
Testnet: Identical flow to mainnet with no real value; low liquidity on Sepolia — prefer WETH/USDC pair. Many pools thin or empty; amountOutMinimum: 0 acceptable for testing. Chain-specific router: Ethereum Sepolia uses 0x3bFA...e48E; 0x94cC...2bc4 is the Base Sepolia router.
Partial reference: use web search for unlisted tokens, contracts, parameters, or up-to-date addresses

References

Docs: https://developers.uniswap.org/llms.mdx — SwapRouter02 ABI, exactInputSingle/exactInput params, fee tiers, path encoding, pool addresses.
ABI: 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.