v4-sdk-integration
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseUniswap v4 SDK Integration
Uniswap v4 SDK 集成
App-layer SDK for swaps, quotes, and liquidity. For Solidity hook contracts, use theskill. For Trading API or v3-centric swaps, use theuniswap-hooksskill.swap-integration
用于兑换、报价、流动性操作的应用层SDK。如果要开发Solidity hook合约,请使用技能。如果需要对接Trading API或以v3为核心的兑换功能,请使用uniswap-hooks技能。swap-integration
When to Use
适用场景
- Token swap UI (single-hop or multi-hop)
- Quote/price display before executing a trade
- Liquidity position management (add/remove/collect)
- Pool state reads (price, tick, liquidity)
- 代币兑换UI(单跳或多跳)
- 交易执行前的报价/价格展示
- 流动性头寸管理(添加/移除/提取收益)
- 池状态读取(价格、tick、流动性)
Packages
安装依赖包
bash
npm i @uniswap/v4-sdk @uniswap/sdk-core @uniswap/universal-router-sdkbash
npm i @uniswap/v4-sdk @uniswap/sdk-core @uniswap/universal-router-sdkv4 vs v3 Decision Table
v4 与 v3 对比决策表
| Aspect | v3 | v4 |
|---|---|---|
| Swap execution | SwapRouter directly | Universal Router required (V4Planner) |
| Pool architecture | One contract per pool | Singleton PoolManager |
| Pool state reads | Direct pool contract | StateView contract |
| Native ETH | Wrap to WETH | Native support ( |
| Position NFTs | NonfungiblePositionManager | PositionManager + multicall |
| Fee collection | Explicit | Automatic on position modification |
| Position discovery | Onchain enumeration | Offchain event indexing |
| Token approvals | Direct approve | Permit2 required |
| Contract addresses | Same across chains | Different per chain — verify from deployments |
| 对比项 | v3 | v4 |
|---|---|---|
| 兑换执行 | 直接调用SwapRouter | 需使用Universal Router(搭配V4Planner) |
| 池架构 | 每个池对应独立合约 | 单例PoolManager |
| 池状态读取 | 直接调用池合约 | 通过StateView合约 |
| 原生ETH | 需要包装为WETH | 原生支持( |
| 头寸NFT | NonfungiblePositionManager | PositionManager + 批量调用(multicall) |
| 手续费收取 | 显式调用 | 修改头寸时自动收取 |
| 头寸查询 | 链上枚举 | 链下事件索引 |
| 代币授权 | 直接授权 | 必须使用Permit2 |
| 合约地址 | 跨链统一 | 各链不同——需从部署页面核实 |
Core Contracts (Per Chain)
核心合约(按链区分)
Look up addresses at https://docs.uniswap.org/contracts/v4/deployments — they differ per chain.
| Contract | Purpose |
|---|---|
| PoolManager | Singleton pool state |
| Universal Router | Swap execution entry point |
| Quoter | Offchain quote simulation (callStatic) |
| StateView | Pool state reads (getSlot0, getLiquidity) |
| PositionManager | LP position lifecycle |
| Permit2 | Token approval layer (same across chains: |
可在 https://docs.uniswap.org/contracts/v4/deployments 查询各链对应的合约地址,不同链地址存在差异。
| 合约 | 用途 |
|---|---|
| PoolManager | 单例池状态存储 |
| Universal Router | 兑换执行入口 |
| Quoter | 链下报价模拟(callStatic调用) |
| StateView | 池状态读取(getSlot0、getLiquidity) |
| PositionManager | 流动性提供者头寸全生命周期管理 |
| Permit2 | 代币授权层(全链统一地址: |
Swap Pattern (Universal Router)
兑换实现模式(Universal Router)
All swaps use: V4Planner -> RoutePlanner -> Universal Router .
execute()Single-hop (exact input):
typescript
import { Actions, V4Planner } from '@uniswap/v4-sdk';
import { CommandType, RoutePlanner } from '@uniswap/universal-router-sdk';
const v4Planner = new V4Planner();
v4Planner.addAction(Actions.SWAP_EXACT_IN_SINGLE, [swapConfig]);
v4Planner.addAction(Actions.SETTLE_ALL, [inputCurrency, amountIn]);
v4Planner.addAction(Actions.TAKE_ALL, [outputCurrency, amountOutMinimum]);
const routePlanner = new RoutePlanner();
routePlanner.addCommand(CommandType.V4_SWAP, [v4Planner.actions, v4Planner.params]);
const deadline = Math.floor(Date.now() / 1000) + 3600;
// Note: universalRouter.execute() is pseudocode for the viem call pattern.
// With viem, use: walletClient.writeContract({ address: UNIVERSAL_ROUTER_ADDRESS, abi: universalRouterAbi, functionName: 'execute', args: [routePlanner.commands, [v4Planner.finalize()], deadline], ...txOptions })
await universalRouter.execute(routePlanner.commands, [v4Planner.finalize()], deadline, txOptions);Multi-hop (exact input):
typescript
import { Actions, V4Planner, encodeMultihopExactInPath } from '@uniswap/v4-sdk';
import { CommandType, RoutePlanner } from '@uniswap/universal-router-sdk';
const v4Planner = new V4Planner();
// Build multi-hop path: tokenA -> tokenB -> tokenC
const path = encodeMultihopExactInPath([poolKeyAB, poolKeyBC], tokenA);
v4Planner.addAction(Actions.SWAP_EXACT_IN, [{ path, amountIn, amountOutMinimum }]);
// SETTLE_ALL uses first pool's input currency; TAKE_ALL uses last pool's output currency
v4Planner.addAction(Actions.SETTLE_ALL, [tokenA, amountIn]);
v4Planner.addAction(Actions.TAKE_ALL, [tokenC, amountOutMinimum]);
const routePlanner = new RoutePlanner();
routePlanner.addCommand(CommandType.V4_SWAP, [v4Planner.actions, v4Planner.params]);
const deadline = Math.floor(Date.now() / 1000) + 3600;
// Note: universalRouter.execute() is pseudocode for the viem call pattern.
// With viem, use: walletClient.writeContract({ address: UNIVERSAL_ROUTER_ADDRESS, abi: universalRouterAbi, functionName: 'execute', args: [routePlanner.commands, [v4Planner.finalize()], deadline], ...txOptions })
await universalRouter.execute(routePlanner.commands, [v4Planner.finalize()], deadline, txOptions);SwapConfig ():
SwapExactInSingletypescript
const swapConfig = {
poolKey: { currency0, currency1, fee, tickSpacing, hooks },
zeroForOne,
amountIn,
amountOutMinimum,
hookData: '0x00',
};所有兑换都遵循:V4Planner -> RoutePlanner -> Universal Router 的流程。
execute()单跳(固定输入金额):
typescript
import { Actions, V4Planner } from '@uniswap/v4-sdk';
import { CommandType, RoutePlanner } from '@uniswap/universal-router-sdk';
const v4Planner = new V4Planner();
v4Planner.addAction(Actions.SWAP_EXACT_IN_SINGLE, [swapConfig]);
v4Planner.addAction(Actions.SETTLE_ALL, [inputCurrency, amountIn]);
v4Planner.addAction(Actions.TAKE_ALL, [outputCurrency, amountOutMinimum]);
const routePlanner = new RoutePlanner();
routePlanner.addCommand(CommandType.V4_SWAP, [v4Planner.actions, v4Planner.params]);
const deadline = Math.floor(Date.now() / 1000) + 3600;
// Note: universalRouter.execute() is pseudocode for the viem call pattern.
// With viem, use: walletClient.writeContract({ address: UNIVERSAL_ROUTER_ADDRESS, abi: universalRouterAbi, functionName: 'execute', args: [routePlanner.commands, [v4Planner.finalize()], deadline], ...txOptions })
await universalRouter.execute(routePlanner.commands, [v4Planner.finalize()], deadline, txOptions);多跳(固定输入金额):
typescript
import { Actions, V4Planner, encodeMultihopExactInPath } from '@uniswap/v4-sdk';
import { CommandType, RoutePlanner } from '@uniswap/universal-router-sdk';
const v4Planner = new V4Planner();
// Build multi-hop path: tokenA -> tokenB -> tokenC
const path = encodeMultihopExactInPath([poolKeyAB, poolKeyBC], tokenA);
v4Planner.addAction(Actions.SWAP_EXACT_IN, [{ path, amountIn, amountOutMinimum }]);
// SETTLE_ALL uses first pool's input currency; TAKE_ALL uses last pool's output currency
v4Planner.addAction(Actions.SETTLE_ALL, [tokenA, amountIn]);
v4Planner.addAction(Actions.TAKE_ALL, [tokenC, amountOutMinimum]);
const routePlanner = new RoutePlanner();
routePlanner.addCommand(CommandType.V4_SWAP, [v4Planner.actions, v4Planner.params]);
const deadline = Math.floor(Date.now() / 1000) + 3600;
// Note: universalRouter.execute() is pseudocode for the viem call pattern.
// With viem, use: walletClient.writeContract({ address: UNIVERSAL_ROUTER_ADDRESS, abi: universalRouterAbi, functionName: 'execute', args: [routePlanner.commands, [v4Planner.finalize()], deadline], ...txOptions })
await universalRouter.execute(routePlanner.commands, [v4Planner.finalize()], deadline, txOptions);SwapConfig ():
SwapExactInSingletypescript
const swapConfig = {
poolKey: { currency0, currency1, fee, tickSpacing, hooks },
zeroForOne,
amountIn,
amountOutMinimum,
hookData: '0x00',
};Quoting Pattern
报价实现模式
Use the Quoter contract with — this simulates the swap offchain without executing it
or spending gas.
callStatictypescript
const quote = await quoterContract.callStatic.quoteExactInputSingle({
poolKey,
zeroForOne,
exactAmount: amountIn,
hookData: '0x00',
});Four available methods:
- — single-hop, exact input amount
quoteExactInputSingle - — multi-hop, exact input amount
quoteExactInput - — single-hop, exact output amount
quoteExactOutputSingle - — multi-hop, exact output amount
quoteExactOutput
使用Quoter合约搭配调用——这会在链下模拟兑换操作,不会实际执行也不会消耗Gas。
callStatictypescript
const quote = await quoterContract.callStatic.quoteExactInputSingle({
poolKey,
zeroForOne,
exactAmount: amountIn,
hookData: '0x00',
});四个可用方法:
- — 单跳、固定输入金额
quoteExactInputSingle - — 多跳、固定输入金额
quoteExactInput - — 单跳、固定输出金额
quoteExactOutputSingle - — 多跳、固定输出金额
quoteExactOutput
Pool State Reads (StateView)
池状态读取(StateView)
typescript
import { Pool } from '@uniswap/v4-sdk';
const poolId = Pool.getPoolId(currency0, currency1, fee, tickSpacing, hooks);
const [slot0, liquidity] = await Promise.all([
stateViewContract.getSlot0(poolId),
stateViewContract.getLiquidity(poolId),
]);
// slot0 → { sqrtPriceX96, tick, protocolFee, lpFee }typescript
import { Pool } from '@uniswap/v4-sdk';
const poolId = Pool.getPoolId(currency0, currency1, fee, tickSpacing, hooks);
const [slot0, liquidity] = await Promise.all([
stateViewContract.getSlot0(poolId),
stateViewContract.getLiquidity(poolId),
]);
// slot0 → { sqrtPriceX96, tick, protocolFee, lpFee }ERC20 Approval Flow (Permit2)
ERC20 授权流程(Permit2)
ERC20 swaps require two approvals — token -> Permit2, then Permit2 -> Universal Router:
typescript
// Step 1: Approve Permit2 on the token contract
await erc20Contract.approve(PERMIT2_ADDRESS, MaxUint256);
// Step 2: Approve Universal Router on Permit2
await permit2Contract.approve(tokenAddress, UNIVERSAL_ROUTER_ADDRESS, MAX_UINT160, deadline);Native ETH swaps bypass both approvals — pass in the transaction options instead.
valueERC20代币兑换需要两步授权——代币合约授权给Permit2,再将Permit2授权给Universal Router:
typescript
// Step 1: Approve Permit2 on the token contract
await erc20Contract.approve(PERMIT2_ADDRESS, MaxUint256);
// Step 2: Approve Universal Router on Permit2
await permit2Contract.approve(tokenAddress, UNIVERSAL_ROUTER_ADDRESS, MAX_UINT160, deadline);原生ETH兑换不需要这两步授权——直接在交易参数中传入即可。
valuePosition Management (PositionManager)
头寸管理(PositionManager)
All operations use :
PositionManager.multicall()| Operation | SDK Method |
|---|---|
| Add liquidity | |
| Remove liquidity | |
| Collect fees | |
| Create position | |
typescript
const { calldata, value } = V4PositionManager.addCallParameters(position, {
slippageTolerance: new Percent(50, 10_000),
deadline: deadline.toString(),
tokenId: tokenId.toString(),
useNative: token0.isNative ? Ether.onChain(chainId) : undefined,
batchPermit,
hookData: '0x',
});
await walletClient.writeContract({
address: POSITION_MANAGER_ADDRESS,
functionName: 'multicall',
args: [[calldata]],
value: BigInt(value),
});所有操作都使用:
PositionManager.multicall()| 操作 | SDK 方法 |
|---|---|
| 添加流动性 | |
| 移除流动性 | |
| 提取手续费 | |
| 创建头寸 | |
typescript
const { calldata, value } = V4PositionManager.addCallParameters(position, {
slippageTolerance: new Percent(50, 10_000),
deadline: deadline.toString(),
tokenId: tokenId.toString(),
useNative: token0.isNative ? Ether.onChain(chainId) : undefined,
batchPermit,
hookData: '0x',
});
await walletClient.writeContract({
address: POSITION_MANAGER_ADDRESS,
functionName: 'multicall',
args: [[calldata]],
value: BigInt(value),
});Strict Rules
严格规则
- NEVER call PoolManager directly for swaps — ALWAYS route through Universal Router.
- NEVER assume contract addresses are the same across chains — look up from the deployments page.
- NEVER call Quoter onchain (gas expensive) — ALWAYS use for offchain simulation.
callStatic - NEVER skip Permit2 for ERC20 swaps — direct to Universal Router will not work.
approve - ALWAYS set a deadline on swaps and LP operations.
- ALWAYS handle native ETH with , not WETH, in v4 pool contexts.
Ether.onChain(chainId) - ALWAYS use to compute pool identifiers — do not construct manually.
Pool.getPoolId()
- 绝对不要直接调用PoolManager执行兑换——必须通过Universal Router路由。
- 绝对不要假设合约地址跨链统一——请从部署页面查询对应链的地址。
- 绝对不要在链上调用Quoter(Gas成本极高)——必须使用进行链下模拟。
callStatic - 绝对不要在ERC20兑换中跳过Permit2——直接授权给Universal Router无法正常工作。
- 必须为兑换和流动性操作设置截止时间。
- 在v4池场景下必须使用处理原生ETH,不要使用WETH。
Ether.onChain(chainId) - 必须使用计算池ID——不要手动构造。
Pool.getPoolId()
Links
相关链接
- SDK overview: https://docs.uniswap.org/sdk/v4/overview
- Swap guides: https://docs.uniswap.org/sdk/v4/guides/swaps/quoting
- Liquidity guide: https://docs.uniswap.org/sdk/v4/guides/liquidity/add-remove-liquidity
- Pool data: https://docs.uniswap.org/sdk/v4/guides/advanced/pool-data
- Deployments: https://docs.uniswap.org/contracts/v4/deployments
- npm: https://www.npmjs.com/package/@uniswap/v4-sdk
- SDK 概览: https://docs.uniswap.org/sdk/v4/overview
- 兑换指南: https://docs.uniswap.org/sdk/v4/guides/swaps/quoting
- 流动性指南: https://docs.uniswap.org/sdk/v4/guides/liquidity/add-remove-liquidity
- 池数据: https://docs.uniswap.org/sdk/v4/guides/advanced/pool-data
- 部署信息: https://docs.uniswap.org/contracts/v4/deployments
- npm 包地址: https://www.npmjs.com/package/@uniswap/v4-sdk
Related Skills
相关技能
- — Trading API and v3-centric swap integration (not direct v4 SDK)
swap-integration - — Solidity hook contract generation (not app-layer SDK)
uniswap-hooks
- ——对接Trading API和以v3为核心的兑换集成(非直接使用v4 SDK)
swap-integration - ——Solidity Hook合约生成(非应用层SDK)
uniswap-hooks