Skip to main content
In a Sauce program, swap with @sauce/router’s function for the venue (uniswapV2, uniswapV3, uniswapV4, curve, balancerV2, …; Slipstream, PancakeSwap V3 and Algebra pools through uniswapV3), with a minimum output and, where the pool takes one, a price limit (the @sauce/router reference; several pools under one limit, the distributed swap).
Concentrated 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.

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’s uniswapV3, 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: uniswapV3 calls the Pot’s Router, which pays that callback from the Pot, so do not call pool.swap yourself.
  • 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/router reference).
  • Uniswap’s own SwapRouter takes a deadline in 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: token0 must 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 until collect is called
  • For multi-hop swaps via exactInput, the path is ABI-encoded as tokenIn + 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’s uniswapV3(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, sends amountIn as positive exact input, and returns the amount out. A path is one call per pool, each spending the last one’s output. The uniswapV3Swap action is the same call
  • On Arc (5042) WETH9() on SwapRouter02 and the position manager is a stub that always reverts with UnsupportedProtocolError(), so unwrapWETH9, refundETH and native-value swaps revert. Trade USDC’s ERC-20 0x3600000000000000000000000000000000000000; 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 a UniswapV3.Pool
  • createPool(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 token
  • token1() 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