Removing Liquidity
This guide shows how to remove liquidity from a V2.2 pool using the SDKs, and Viem. In this example, we will be removing liquidity from a LBPair of USDC/USDC.e/1bps
1. Required imports and constants for this guide
Imports
import { ChainId, Token } from '@traderjoe-xyz/sdk-core'
import { PairV2, LB_ROUTER_V22_ADDRESS, jsonAbis, } from '@traderjoe-xyz/sdk-v2'
import { getContract, createPublicClient, createWalletClient, http, BaseError, ContractFunctionRevertedError } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { avalanche } from 'viem/chains'
import { config } from 'dotenv';
Constants: chain and wallet
config();
const privateKey = process.env.PRIVATE_KEY;
const { LBRouterV22ABI, LBPairV21ABI } = jsonAbis
const CHAIN_ID = ChainId.AVALANCHE
const router = LB_ROUTER_V22_ADDRESS[CHAIN_ID]
const account = privateKeyToAccount(`0x${privateKey}`)
Note that in your project, you most likely will not hardcode the private key at any time. You would be using libraries like web3react or wagmi to connect to a wallet, sign messages, interact with contracts, and get the values for PROVIDER
, SIGNER
and ACCOUNT
Constants: tokens and LBPair bin step
const USDC = new Token(
CHAIN_ID,
'0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E',
6,
'USDC',
'USD Coin'
)
const USDC_E = new Token(
CHAIN_ID,
'0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664',
6,
'USDC.e',
'USD Coin bridged'
)
const BIN_STEP = "1"
2. Create Viem clients
const publicClient = createPublicClient({
chain: avalanche,
transport: http()
})
const walletClient = createWalletClient({
account,
chain: avalanche,
transport: http()
})
3. Getting data
LBPair and active bin
const pair = new PairV2(USDC, USDC_E)
const binStep = Number(BIN_STEP)
const pairVersion = 'v22'
const lbPair = await pair.fetchLBPair(binStep, pairVersion, publicClient, CHAIN_ID)
if (lbPair.LBPair == "0x0000000000000000000000000000000000000000") {
console.log("No LB pair found with given parameters")
return
}
const lbPairData = await PairV2.getLBPairReservesAndId(lbPair.LBPair, pairVersion, publicClient)
const activeBinId = lbPairData.activeId.toNumber()
Liquidity positions
Use the below code to fetch your positions directly from chain. You need all the binId
s where you have liquidity and the amount of liquidity
in each bin.
const pairContract = getContract({ address: lbPair.LBPair, abi: LBPairV21ABI })
const range = 200 // should be enough in most cases
const addressArray = Array.from({ length: 2 * range + 1 }).fill(account.address);
const binsArray = [];
for (let i = activeBinId - range; i <= activeBinId + range; i++) {
binsArray.push(i);
}
const allBins: Bigint[] = await publicClient.readContract({
address: pairContract.address,
abi: pairContract.abi,
functionName: 'balanceOfBatch',
args: [addressArray, binsArray]
})
const userOwnedBins = binsArray.filter((bin, index) => allBins[index] != 0n);
const nonZeroAmounts = allBins.filter(amount => amount !== 0n);
Please take note, that we aren't fetching ERC20 token amounts underlying for this example. This results in using parameters amountXmin and amountYmin equal to zero. Calculating them would require additionally:
Fetch tokenSupply of all owned bins
Fetch reserves of all owned bins
Calculate reserves owned by user (ownedReserves * liquidityOwned / totalSupply)
Applying desired slippage to above.
This process will become easier, when public API is opened.
4. Grant LBRouter access to your LBTokens
const approved = await publicClient.readContract({
address: pairContract.address,
abi: pairContract.abi,
functionName: 'isApprovedForAll',
args: [account.address, router]
})
if (!approved) {
const { request } = await publicClient.simulateContract({
address: pairContract.address,
abi: pairContract.abi,
functionName: 'approveForAll',
args: [router, true],
account
})
const hashApproval = await walletClient.writeContract(request)
console.log(`Approving transaction sent with hash ${hashApproval}`)
}
5. Set removeLiquidity parameters
// set transaction deadline
const currentTimeInSec = Math.floor((new Date().getTime()) / 1000)
// set array of remove liquidity parameters
const removeLiquidityInput = {
tokenX: USDC_E.address,
tokenY: USDC.address,
binStep: Number(BIN_STEP),
amountXmin: 0,
amountYmin: 0,
ids: userOwnedBins,
amounts: nonZeroAmounts,
to: account.address,
deadline: currentTimeInSec + 3600
}
Note that removeLiquidityParams
will look different for the removeLiquidityAVAX
method. Please refer to this link for specific details.
6. Execute removeLiquidity contract call
const { request } = await publicClient.simulateContract({
address: router,
abi: LBRouterV22ABI,
functionName: "removeLiquidity",
args: [
removeLiquidityInput.tokenX,
removeLiquidityInput.tokenY,
removeLiquidityInput.binStep,
removeLiquidityInput.amountXmin, //zero in this example
removeLiquidityInput.amountYmin, //zero in this example
removeLiquidityInput.ids,
removeLiquidityInput.amounts,
removeLiquidityInput.to,
removeLiquidityInput.deadline],
account
})
const removalHash = await walletClient.writeContract(request)
console.log(`Transaction sent with hash ${removalHash}`)
Last updated