In a Sauce program, swap withConcentrated liquidity AMM allowing LPs to allocate capital within custom price ranges for higher capital efficiency. Supports multiple fee tiers per pair and NFT-based liquidity positions.@sauce/router’s function for the venue (uniswapV2,uniswapV3,uniswapV4,curve,balancerV2, …; Slipstream, PancakeSwap V3 and Algebra pools throughuniswapV3), with a minimum output and, where the pool takes one, a price limit (the@sauce/routerreference; several pools under one limit, the distributed swap).
When to use it
- Concentrated-liquidity pools in fee tiers of 0.01%, 0.05%, 0.3% and 1% (
100,500,3000,10000). - A Sauce program swaps a pool with
@sauce/router’suniswapV3, with a price limit; the Factory finds the pool, the position manager manages liquidity positions.
Pitfalls for a program
- A pool pays out first and then calls back for its input, which a program cannot answer:
uniswapV3calls the Pot’s Router, which pays that callback from the Pot, so do not callpool.swapyourself. - Each fee tier is a separate pool, with its own price and depth.
- The price limit bounds the pool’s price, not the price paid: the fee comes on top
(the
@sauce/routerreference). - Uniswap’s own SwapRouter takes a
deadlinein its params; SwapRouter02 does not.
In a Sauce program
One guarded swap on the 0.05% WETH/USDC pool:uniswapV3 pays recipient and reverts below minOut. The pool stops at the price limit, so
the WETH it did not take goes back to recipient too: amountIn less what the swap spent, not
the Pot’s whole WETH balance.
What it offers
- swap: Swap tokens using exact input (single-hop or multi-hop via encoded path)
- addLiquidity: Mint a new concentrated liquidity position as an NFT within a tick range
- removeLiquidity: Decrease liquidity from an existing NFT position
Notes
- Fee tiers:
100(0.01%, stablecoins),500(0.05%, correlated pairs),3000(0.3%, standard),10000(1%, exotic) - Token order matters:
token0must be the lower address. Sort before calling mint - Tick spacing varies by fee tier: 1 (0.01%), 10 (0.05%), 60 (0.3%), 200 (1%)
- After
decreaseLiquidity, tokens sit in the position untilcollectis called - For multi-hop swaps via
exactInput, thepathis ABI-encoded astokenIn + fee + tokenMiddle + fee + tokenOut - Input token must be ERC20-approved to the SwapRouter for swaps
- Both tokens must be ERC20-approved to the NFPM for minting positions
- Prefer V3 over V2 for major pairs to benefit from concentrated liquidity and lower effective slippage
- From a Pot program,
@sauce/router’suniswapV3(pool, tokenIn, tokenOut, amountIn, sqrtPriceLimitX96, minOut, recipient)swaps one pool through the Pot’s Sauce Router, which answers the pool’s callback from the Pot — no SwapRouter and no approval — checks the pool’s tokens, sendsamountInas positive exact input, and returns the amount out. A path is one call per pool, each spending the last one’s output. TheuniswapV3Swapaction is the same call - On Arc (5042)
WETH9()on SwapRouter02 and the position manager is a stub that always reverts withUnsupportedProtocolError(), sounwrapWETH9,refundETHand native-value swaps revert. Trade USDC’s ERC-200x3600000000000000000000000000000000000000; Arc’s native coin is USDC and it has no canonical wrapped native - SwapRouter02’s params structs drop
deadline(wrap the call in a deadline check if needed).
Its names in a program
On Ethereum,UniswapV3 is a namespace: its contracts need no import.
Deployments
ABI
Where a line says what an address it returns is, a program that calls the function through its namespace calls that contract’s methods on the result directly:<Namespace>.<Contract>.<function>(...).<method>(...). A token’s methods are the ERC-20
functions, and its writes are SafeERC20.
UniswapV3FactoryABI
import UniswapV3FactoryABI from "./protocols/uniswap-v3/UniswapV3FactoryABI.json"; Speaks for: factory.
getPool(address tokenA, address tokenB, uint24 fee) view returns (address pool): the address is aUniswapV3.PoolcreatePool(address tokenA, address tokenB, uint24 fee) returns (address pool)
UniswapV3NonfungiblePositionManagerABI
import UniswapV3NonfungiblePositionManagerABI from "./protocols/uniswap-v3/UniswapV3NonfungiblePositionManagerABI.json"; Speaks for: nonfungiblePositionManager.
mint((address token0, address token1, uint24 fee, int24 tickLower, int24 tickUpper, uint256 amount0Desired, uint256 amount1Desired, uint256 amount0Min, uint256 amount1Min, address recipient, uint256 deadline) params) payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)increaseLiquidity((uint256 tokenId, uint256 amount0Desired, uint256 amount1Desired, uint256 amount0Min, uint256 amount1Min, uint256 deadline) params) payable returns (uint128 liquidity, uint256 amount0, uint256 amount1)decreaseLiquidity((uint256 tokenId, uint128 liquidity, uint256 amount0Min, uint256 amount1Min, uint256 deadline) params) payable returns (uint256 amount0, uint256 amount1)collect((uint256 tokenId, address recipient, uint128 amount0Max, uint128 amount1Max) params) payable returns (uint256 amount0, uint256 amount1)
UniswapV3PoolABI
import UniswapV3PoolABI from "./protocols/uniswap-v3/UniswapV3PoolABI.json";
slot0() view returns (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint8 feeProtocol, bool unlocked)liquidity() view returns (uint128)fee() view returns (uint24)tickSpacing() view returns (int24)token0() view returns (address): the address is a tokentoken1() view returns (address): the address is a token
UniswapV3QuoterV2ABI
import UniswapV3QuoterV2ABI from "./protocols/uniswap-v3/UniswapV3QuoterV2ABI.json"; Speaks for: quoterV2.
quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96) params) returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)
UniswapV3SwapRouter02ABI
import UniswapV3SwapRouter02ABI from "./protocols/uniswap-v3/UniswapV3SwapRouter02ABI.json"; Speaks for: swapRouter02.
exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params) payable returns (uint256 amountOut)exactInput((bytes path, address recipient, uint256 amountIn, uint256 amountOutMinimum) params) payable returns (uint256 amountOut)exactOutputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountOut, uint256 amountInMaximum, uint160 sqrtPriceLimitX96) params) payable returns (uint256 amountIn)exactOutput((bytes path, address recipient, uint256 amountOut, uint256 amountInMaximum) params) payable returns (uint256 amountIn)
UniswapV3SwapRouterABI
import UniswapV3SwapRouterABI from "./protocols/uniswap-v3/UniswapV3SwapRouterABI.json"; Speaks for: swapRouter.
exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 deadline, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96) params) payable returns (uint256 amountOut)exactInput((bytes path, address recipient, uint256 deadline, uint256 amountIn, uint256 amountOutMinimum) params) payable returns (uint256 amountOut)exactOutputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 deadline, uint256 amountOut, uint256 amountInMaximum, uint160 sqrtPriceLimitX96) params) payable returns (uint256 amountIn)exactOutput((bytes path, address recipient, uint256 deadline, uint256 amountOut, uint256 amountInMaximum) params) payable returns (uint256 amountIn)
Next steps
- Protocols: the other protocols, by what they do.
- Protocols and tokens: calling a contract by its namespace, or through its ABI.
- Glossary: the words these pages use, in plain terms.
