protocols/uniswap-v3-integration
Uniswap V3 Integration Guide
ethereumguide🏛️ Officialconfidence highhealth 100%
v1.0.0·Updated 3/20/2026
Core Contract Addresses (Ethereum Mainnet)
- SwapRouter02:
0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45 - NonfungiblePositionManager:
0xC36442b4a4522E871399CD717aBDD847Ab11FE88 - Quoter V2:
0x61fFE014bA17989E743c5F6cB21bF9697530B21e - Factory:
0x1F98431c8aD98523631AE4a59f267346ea31F984
Install SDK
npm install @uniswap/v3-sdk @uniswap/sdk-core
npm install @uniswap/smart-order-router # auto routing
Execute a Swap (minimal path)
import { ethers } from 'ethers'
const SWAP_ROUTER = '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45'
const swapRouterABI = [
'function exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params) external payable returns (uint256 amountOut)'
]
async function swapExactInput(signer, tokenIn, tokenOut, amountIn, minOut) {
// 1. Approve
const token = new ethers.Contract(tokenIn, ['function approve(address,uint256)'], signer)
await token.approve(SWAP_ROUTER, amountIn)
// 2. Swap
const router = new ethers.Contract(SWAP_ROUTER, swapRouterABI, signer)
return router.exactInputSingle({
tokenIn, tokenOut,
fee: 3000, // 0.3% — most commonly used fee tier
recipient: await signer.getAddress(),
amountIn,
amountOutMinimum: minOut,
sqrtPriceLimitX96: 0
})
}
Fee Tiers
| Fee | Use Case |
|---|---|
| 100 (0.01%) | Stablecoin pairs (USDC/USDT) |
| 500 (0.05%) | Correlated assets (ETH/stETH) |
| 3000 (0.3%) | Standard pairs (ETH/USDC) |
| 10000 (1%) | Highly volatile low-cap tokens |
Get a Price Quote
const quoterABI = ['function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96) params) external returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)']
const quoter = new ethers.Contract('0x61fFE014bA17989E743c5F6cB21bF9697530B21e', quoterABI, provider)
const [amountOut] = await quoter.quoteExactInputSingle.staticCall({
tokenIn: WETH, tokenOut: USDC, amountIn: ethers.parseEther('1'), fee: 3000, sqrtPriceLimitX96: 0
})
Using the Smart Order Router (optimal path auto-routing)
import { AlphaRouter, SwapType } from '@uniswap/smart-order-router'
import { CurrencyAmount, TradeType, Percent } from '@uniswap/sdk-core'
const router = new AlphaRouter({ chainId: 1, provider })
const route = await router.route(
CurrencyAmount.fromRawAmount(WETH_TOKEN, amountIn.toString()),
USDC_TOKEN,
TradeType.EXACT_INPUT,
{ slippageTolerance: new Percent(50, 10_000), deadline: Math.floor(Date.now()/1000) + 180, type: SwapType.SWAP_ROUTER_02, recipient }
)
// route.methodParameters.calldata — send directly
Adding Liquidity (Position Manager)
const NPM_ABI = ['function mint((address token0, address token1, uint24 fee, int24 tickLower, int24 tickUpper, uint256 amount0Desired, uint256 amount1Desired, uint256 amount0Min, uint256 amount1Min, address recipient, uint256 deadline)) external returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)']
// tick calculation: price → tick = Math.floor(Math.log(price) / Math.log(1.0001))
Common Pitfalls
- Setting
amountOutMinimumto 0 exposes you to sandwich attacks — set it to at least 95% of the expected output - Use
staticCallfor the Quoter — do not send it as a real transaction - Always set a
deadlineto prevent transactions from waiting indefinitely in the mempool
Uniswap V3 Integration Guide
Core Contract Addresses (Ethereum Mainnet)
- SwapRouter02:
0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45 - NonfungiblePositionManager:
0xC36442b4a4522E871399CD717aBDD847Ab11FE88 - Quoter V2:
0x61fFE014bA17989E743c5F6cB21bF9697530B21e - Factory:
0x1F98431c8aD98523631AE4a59f267346ea31F984
Install SDK
npm install @uniswap/v3-sdk @uniswap/sdk-core
npm install @uniswap/smart-order-router # auto routing
Execute a Swap (minimal path)
import { ethers } from 'ethers'
const SWAP_ROUTER = '0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45'
const swapRouterABI = [
'function exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params) external payable returns (uint256 amountOut)'
]
async function swapExactInput(signer, tokenIn, tokenOut, amountIn, minOut) {
// 1. Approve
const token = new ethers.Contract(tokenIn, ['function approve(address,uint256)'], signer)
await token.approve(SWAP_ROUTER, amountIn)
// 2. Swap
const router = new ethers.Contract(SWAP_ROUTER, swapRouterABI, signer)
return router.exactInputSingle({
tokenIn, tokenOut,
fee: 3000, // 0.3% — most commonly used fee tier
recipient: await signer.getAddress(),
amountIn,
amountOutMinimum: minOut,
sqrtPriceLimitX96: 0
})
}
Fee Tiers
| Fee | Use Case |
|---|---|
| 100 (0.01%) | Stablecoin pairs (USDC/USDT) |
| 500 (0.05%) | Correlated assets (ETH/stETH) |
| 3000 (0.3%) | Standard pairs (ETH/USDC) |
| 10000 (1%) | Highly volatile low-cap tokens |
Get a Price Quote
const quoterABI = ['function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96) params) external returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)']
const quoter = new ethers.Contract('0x61fFE014bA17989E743c5F6cB21bF9697530B21e', quoterABI, provider)
const [amountOut] = await quoter.quoteExactInputSingle.staticCall({
tokenIn: WETH, tokenOut: USDC, amountIn: ethers.parseEther('1'), fee: 3000, sqrtPriceLimitX96: 0
})
Using the Smart Order Router (optimal path auto-routing)
import { AlphaRouter, SwapType } from '@uniswap/smart-order-router'
import { CurrencyAmount, TradeType, Percent } from '@uniswap/sdk-core'
const router = new AlphaRouter({ chainId: 1, provider })
const route = await router.route(
CurrencyAmount.fromRawAmount(WETH_TOKEN, amountIn.toString()),
USDC_TOKEN,
TradeType.EXACT_INPUT,
{ slippageTolerance: new Percent(50, 10_000), deadline: Math.floor(Date.now()/1000) + 180, type: SwapType.SWAP_ROUTER_02, recipient }
)
// route.methodParameters.calldata — send directly
Adding Liquidity (Position Manager)
const NPM_ABI = ['function mint((address token0, address token1, uint24 fee, int24 tickLower, int24 tickUpper, uint256 amount0Desired, uint256 amount1Desired, uint256 amount0Min, uint256 amount1Min, address recipient, uint256 deadline)) external returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)']
// tick calculation: price → tick = Math.floor(Math.log(price) / Math.log(1.0001))
Common Pitfalls
- Setting
amountOutMinimumto 0 exposes you to sandwich attacks — set it to at least 95% of the expected output - Use
staticCallfor the Quoter — do not send it as a real transaction - Always set a
deadlineto prevent transactions from waiting indefinitely in the mempool - WETH address:
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2