Skip to main content
Helpful?

Swap and Add Liquidity Atomically

When adding liquidity to a Uniswap v3 pool, you must provide two assets in a particular ratio. In many cases, your contract or the user's wallet hold a different ratio of those two assets. In order to deposit 100% of your assets, you must first swap your assets to the optimal ratio and then add liquidity. However, the swap may shift the balance of the pool and thus change the optimal ratio!

This guide will teach you how to execute this swap-and-add operation in a single atomic transaction. First, you will use the Auto Router to fetch calldata to swap to the optimal ratio and add liquidity. Then you will submit the transaction to the on-chain router contract SwapRouter02.sol.

Initializing the Alpha Router

First, import and initialize an instance of the Alpha Router. If you are using a different network, be sure to specify the right chainId parameter.

npm install @uniswap/smart-order-router
import { AlphaRouter } from '@uniswap/smart-order-router'
const router = new AlphaRouter({ chainId: 1, provider: web3Provider })

Fetching calldata from routeToRatio

Now call the routeToRatio method on the Auto Router. This function will return all the calldata you need to submit an atomic swap-and-add transaction.

Parameters

NameRequirementDescription
token0BalancerequiredThe initial balance of token0 that you wish to swap-and-add, where token0 is the token0 in your target liquidity pool.
token1BalancerequiredThe initial balance of token1 that you wish to swap-and-add, where token1 is the token1 in your target liquidity pool.
positionrequiredA Position object that contains the details of the position for which to add liquidity. The position liquidity can be set to 1, since liquidity is still unknown and will be set inside the call to routeToRatio.
swapAndAddConfigrequiredA swapAndAddConfig sets configurations for the routeToRatio algorithm. ratioErrorTolerance determines the margin of error the resulting ratio can have from the optimal ratio. maxIterations determines the maximum times the algorithm will iterate to find a ratio within error tolerance. If maxIterations is exceeded, an error is returned.
swapAndAddOptionsoptionalIf swapAndAddOptions is included, routeToRatio will return the calldata for executing the atomic swap-and-add. These options contain swapConfig and addLiquidityOptions. swapConfig configures to set a recipient of leftover dust from swap, slippageTolerance, deadline, inputTokenPermit and outputTokenPermit. addLiquidityOptions must contain a tokenId to add to an existing position, or recipient to mint a new one. It also includes a slippage tolerance and deadline for adding liquidity.
routingConfigoptionalOptional advanced config for tuning the performance of the routing algorithm. View the AlphaRouterConfig object for these optional configuration parameters.

token0Balance [required]

  • The initial balance of token0 that you wish to swap-and-add, where token0 is the token0 in your target liquidity pool.

token1Balance [required]

  • The initial balance of token1 that you wish to swap-and-add, where token1 is the token1 in your target liquidity pool.

position [required]

  • A position object that contains the details of the position for which to add liquidity. The position liquidity can be set to 1, since liquidity is still unknown and will be set inside the call to routeToRatio.

swapAndAddConfig [required]

  • A swapAndAddConfig sets configurations for the routeToRatio algorithm. ratioErrorTolerance determines the margin of error the resulting ratio can have from the optimal ratio. maxIterations determines the maximum times the algorithm will iterate to find a ratio within error tolerance. If max iterations is exceeded, an error is returned.

swapAndAddOptions [optional]

  • If swapAndAddOptions is included, routeToRatio will return the calldata for executing the atomic swap-and-add. These options contain swapConfig and addLiquidityOptions. swapConfig configures to set a recipient of leftover dust from swap, slippageTolerance, deadline, inputTokenPermit and outputTokenPermit. addLiquidityOptions must contain a tokenId to add to an existing position, or recipient to mint a new one. It also includes a slippage tolerance and deadline for adding liquidity.

routingConfig [optional]

  • Optional config for tuning the performance of the routing algorithm. View the AlphaRouterConfig object for these optional configuration parameters.

Here is a complete example that initializes these parameters and then calls routeToRatio

const USDC = new Token(
ChainId.MAINNET,
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
6,
'USDC',
'USD//C'
);

const WETH = new Token(
1,
'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
18,
'WETH',
'Wrapped Ether'
);

const token0Balance = CurrencyAmount.fromRawAmount(USDC, '5000000000')
const token1Balance = CurrencyAmount.fromRawAmount(WETH, '0')
const pool = new Pool(
USDC,
WETH,
3000,
'1283723400872544054280619964098219',
'8390320113764730804' ,
'193868'
);


const routeToRatioResponse = await router.routeToRatio(
token0Balance,
token1Balance,
position: new Position({
pool,
tickLower: -60,
tickUpper: 60,
liquidity: 1,
}),
swapAndAddConfig: {
ratioErrorTolerance: new Fraction(1, 100),
maxIterations: 6,
},
swapAndAddOptions: {
swapConfig: {
recipient: <myAddress>,
slippage: new Percent(5, 100),
deadline: 100
},
addLiquidityOptions: {
tokenId: 10,
}
}
);

Submitting a Transaction

The routeToRatio function returns a SwapToRatioResponse object. If a route was found successfully, this object will have two fields:

  • status will be set to success
  • result will contain the SwapToRatioRoute object

The SwapToRatioRoute object will have the properties listed out in the type below:

type SwapToRatioRoute = {
quote: CurrencyAmount
quoteGasAdjusted: CurrencyAmount
optimalRatio: Fraction
postSwapTargetPool: Pool
estimatedGasUsed: BigNumber
estimatedGasUsedQuoteToken: CurrencyAmount
estimatedGasUsedUSD: CurrencyAmount
gasPriceWei: BigNumber
trade: Trade<Currency, Currency, TradeType>
route: RouteWithValidQuote[]
blockNumber: BigNumber
methodParameters?: MethodParameters
}

Use the quoted gas price defined as gasPriceWei in the above SwapToRatioRoute object above and generated call data as inputs for the transaction, as done below:

import { SwapToRatioStatus } from "@uniswap/smart-order-router";

const V3_SWAP_ROUTER_ADDRESS = "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45";
const MY_ADDRESS = "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B";

if (routeToRatioResponse.status == SwapToRatioStatus.SUCCESS) {
const route = routeToRatioResponse.result
const transaction = {
data: route.methodParameters.calldata,
to: V3_SWAP_ROUTER_ADDRESS,
value: BigNumber.from(route.methodParameters.value),
from: MY_ADDRESS,
gasPrice: BigNumber.from(route.gasPriceWei),
};
}

await web3Provider.sendTransaction(transaction);
Helpful?