Skip to main content

Scenario

This page adds liquidity to the USDC/WETH pool on Ethereum twice: to the Uniswap V2 pair from USDC alone, and as a new Uniswap V3 position from USDC and WETH. The ops also work with ABI-identical forks, such as SushiSwap V2. Fee-on-transfer tokens are not supported. The ops call the Uniswap router or position manager with zero minimums, so guards are the only protection:
  • A band guard on the pool price (poolPrice on V2, sqrtPriceX96 on V3) reverts the transaction if the price moved, for example after a sandwich trade.
  • A slippage guard on liquidity reverts it if the deposit mints less than the simulated liquidity minus bps.
Install the SDK with npm install @lifi/composer-sdk @lifi/compose-spec and set LIFI_API_KEY; see Before you start.

Add V2 liquidity from one token

A V2 deposit needs both tokens in the pool’s ratio. The flow splits 2,000 USDC in half, swaps one half to WETH, and deposits both halves with uniswapV2.addLiquidity. Adapted from uniswapV2AddLiquidity.ts.
Save it as uniswap-v2.mts and run node uniswap-v2.mts. Two settings matter:
  • slippage at 350 bps, not 50. Less WETH from the swap mints less LP. A 50 bps liquidity floor would revert a swap fill that the swap’s own 3% tolerance accepts, so the floor covers 300 bps plus the 50 bps band.
  • maxPriceImpactBps: 0. The compiler compares the USD value in with the USD value out and, by default, rejects anything worse than 500 bps. It values the LP token below the tokens deposited, so this deposit fails with price_impact_exceeded (HTTP 422). 0 disables the check; the guards are the protection.
What to check in the result:
  • outputs['add-liquidity.liquidity'] is the UNI-V2 LP token, a terminal output owned by the signer. amount.estimate is the simulated LP amount; amount.minimum (basis: 'guard') is the slippage floor.
  • outputs['add-liquidity.residualA'] and residualB are the USDC and WETH the router did not use, swept to the signer. A zero residual is omitted.
  • poolPrice is a handle, not a resource, so it has no outputs entry.

Mint a V3 position

uniswapV3.mint opens a concentrated-liquidity position for a tick range and mints the position NFT straight to recipient. This example deposits 1,000 USDC and 0.5 WETH across the full range of the 0.05% pool. Adapted from uniswapV3Mint.ts.
Save it as uniswap-v3.mts and run node uniswap-v3.mts. What to know about mint:
  • recipient must not be the execution proxy. The position NFT is not a resource, so sweepTo cannot move it out of the proxy. Bind recipient to the signer or another address.
  • Ticks must be multiples of the pool’s tick spacing. The compiler does not check this; the pool reverts on a misaligned tick, and the compile simulation reports the revert.
  • Token order does not matter. The op sorts tokenA and tokenB into the pool’s token0 and token1. Binding the same token twice fails with validation_error.
  • Residuals depend on the range. A position takes the two tokens in the ratio its range needs at the current price and returns the rest as residualA / residualB.
  • The liquidity floor does not show in outputs. On V3, liquidity is a handle: the slippage guard enforces its floor on chain, but no amount.minimum appears.
The new tokenId is final once the transaction is mined. Read it from the ERC-721 Transfer event the position manager emits to recipient in the execute receipt.

Other liquidity operations

  • uniswapV2.removeLiquidity burns the LP token and returns tokenA and tokenB. The router is called with zero minimums, so put a slippage guard on each output.
  • uniswapV3.increaseLiquidity adds two tokens to an existing position (tokenId). Bind owner to the position’s owner: the op asserts on chain that owner owns tokenId and that the position holds the two input tokens. Guard it like mint, with band on sqrtPriceX96 and slippage on liquidity.
See the op catalog for each op’s ports and config.

Troubleshooting

  • simulation_revert (HTTP 422): a contract check failed in simulation. Each op asserts its contracts belong together: on V2, that router, factory and pair match and the pair has reserves; on V3, that factory, pool and fee match the position manager. Also check that V3 ticks are multiples of the tick spacing and that owner owns tokenId.
  • Reverts at execution: the pool price moved outside the band, or the deposit minted less than the slippage floor. Compile again at the new price.