In a Sauce program, swap withConstant product AMM (x*y=k) with permissionless pair creation. Deployed across 10 chains with identical interfaces.@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
- Constant-product pairs, one per token pair, with a 0.3% fee. Simple and everywhere, but with more price impact than a V3 pool for the same trade.
- The pair fills in full or reverts, so its Router, with an exact approval and
amountOutMin, strands nothing. A Sauce program trades the pair itself with@sauce/router’suniswapV2, with no Router and no approval.
Pitfalls for a program
amountOutMinis the only bound: there is no price limit.- Through the Router, approve it for exactly
amountIn, and give adeadline(ctx.timestamp()is this block).uniswapV2needs neither: it pushes the input to the pair. - A fee-on-transfer token needs the
...SupportingFeeOnTransferTokensvariants, which the registry ABI does not declare. - The pair must exist:
Factory.getPairreturns0where it does not.
In a Sauce program
Sell the Pot’s USDC for WETH at the pair:uniswapV2 prices the output from the pair’s reserves with its 0.3% fee (3000 ppm), pushes
amountIn to the pair, pays recipient and reverts below minOut. The pair takes the whole input
or the program reverts, so no input is left to return.
What it offers
- swap: Swap an exact amount of input tokens for as many output tokens as possible, routed through a path of pairs
- addLiquidity: Deposit two tokens into a pair to mint LP tokens proportional to pool share
- removeLiquidity: Burn LP tokens to withdraw both underlying tokens from a pair
Notes
- Path array defines the swap route:
[tokenIn, tokenOut]for direct swaps,[tokenIn, WETH, tokenOut]for multi-hop through WETH - 0.3% fee per hop, taken from input amount before swap
- Both tokens must be ERC20-approved to the Router before addLiquidity/swap
- LP tokens must be ERC20-approved to the Router before removeLiquidity
- For tokens with transfer taxes (fee-on-transfer), use
swapExactTokensForTokensSupportingFeeOnTransferTokensinstead (not in SDK but available on-chain) - Prefer V3 for better capital efficiency, but V2 for maximum compatibility and simplicity
- On Arc (5042) the router’s
WETH()is a stub that always reverts withUnsupportedProtocolError(), so the ETH paths (swapExactETHForTokens,addLiquidityETH, …) revert. Trade USDC’s ERC-200x3600000000000000000000000000000000000000; Arc’s native coin is USDC and it has no canonical wrapped native - From a Pot program,
@sauce/routertrades a Uniswap V2 pair directly, with no router and no approval:uniswapV2(pair, tokenIn, tokenOut, amountIn, 3000, minOut, recipient)reads the pair’sgetReserves, prices the output in the program with the pair’s fee in parts per million, transfers the input to the pair and callspair.swapwith emptydata, returning the amount out. The direction is the sort order oftokenInandtokenOut(token0is the lower address), so there is no argument to get wrong. The swap consumes its whole input or reverts; there is no refund path.tryUniswapV2takes the same arguments and returns0, moving nothing, when the pair cannot fill the whole input at the floor. TheuniswapV2Swapaction is the same call
Its names in a program
On Ethereum,UniswapV2 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.
UniswapV2FactoryABI
import UniswapV2FactoryABI from "./protocols/uniswap-v2/UniswapV2FactoryABI.json"; Speaks for: factory.
getPair(address tokenA, address tokenB) view returns (address pair): the address is aUniswapV2.PaircreatePair(address tokenA, address tokenB) returns (address pair)
UniswapV2PairABI
import UniswapV2PairABI from "./protocols/uniswap-v2/UniswapV2PairABI.json";
getReserves() view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast)token0() view returns (address): the address is a tokentoken1() view returns (address): the address is a tokenswap(uint256 amount0Out, uint256 amount1Out, address to, bytes data)
UniswapV2RouterABI
import UniswapV2RouterABI from "./protocols/uniswap-v2/UniswapV2RouterABI.json"; Speaks for: router.
swapExactTokensForTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline) returns (uint256[] amounts)swapTokensForExactTokens(uint256 amountOut, uint256 amountInMax, address[] path, address to, uint256 deadline) returns (uint256[] amounts)addLiquidity(address tokenA, address tokenB, uint256 amountADesired, uint256 amountBDesired, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline) returns (uint256 amountA, uint256 amountB, uint256 liquidity)removeLiquidity(address tokenA, address tokenB, uint256 liquidity, uint256 amountAMin, uint256 amountBMin, address to, uint256 deadline) returns (uint256 amountA, uint256 amountB)getAmountsOut(uint256 amountIn, address[] path) view returns (uint256[] amounts)
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.
