Decrease a liquidity position

In this example, we first fetch all liquidity positions of an account, then select one of it to decrease (or withdraw) some liquidity.

The full example code of this section can be found here.

Important note: after decreasing liquidity, the tokens are still in the pool contract. Another separate collect operation is needed to collect your decreased token (and fees) from the pool. Check this example Collect a liquidity position.

1. Fetch liquidity positions

 1const chain:BaseChain = initialChainTable[ChainId.BSC]
 2const rpc = 'https://bsc-dataseed2.defibit.io/'
 3console.log('rpc: ', rpc)
 4const web3 = new Web3(new Web3.providers.HttpProvider(rpc))
 5const account =  web3.eth.accounts.privateKeyToAccount(privateKey)
 6console.log('address: ', account.address)
 7
 8const liquidityManagerAddress = '0x93C22Fbeff4448F2fb6e432579b0638838Ff9581'
 9const liquidityManagerContract = getLiquidityManagerContract(liquidityManagerAddress, web3)
10
11console.log('liquidity manager address: ', liquidityManagerAddress)
12
13const testAAddress = '0xCFD8A067e1fa03474e79Be646c5f6b6A27847399'
14const testBAddress = '0xAD1F11FBB288Cd13819cCB9397E59FAAB4Cdc16F'
15
16const testA = await fetchToken(testAAddress, chain, web3)
17const testB = await fetchToken(testBAddress, chain, web3)
18const fee = 2000 // 2000 means 0.2%
19
20const liquidities = await fetchLiquiditiesOfAccount(
21    chain,
22    web3,
23    liquidityManagerContract,
24    account.address,
25    [testA]
26)
27console.log('liquidity len: ', liquidities.length)
28console.log('liquidity: ', liquidities)

the code above is nearly the same as Fetch liquidity positions, you can view more detailed explains though this link

1. select one of your liquidities to decrease (or withdraw)

1const liquidity0 = liquidities[0]
2
3const decRate = 0.1
4const originLiquidity = new BigNumber(liquidity0.liquidity)
5const decLiquidity = originLiquidity.times(decRate)

of course one can only withdraw his/her liquidity, cannot withdraw others.

here, decLiquidity is value of liquidity to withdraw, and it is type of BigNumber

3. pre-compute undecimal amount of tokenX and tokenY after withdraw (optional)

the market may change very fast, our frontend app should allow user to select a slippery value before withdraw.

setting slippery value can be viewed on many apps such as uniswapv3, pancake

of course you can skip this step and fill minAmountX and minAmountY as ‘0’ if you donot care

1const {amountX, amountY} = getWithdrawLiquidityValue(
2    liquidity0,
3    liquidity0.state,
4    decLiquidity
5)
6// here the slippery is 1.5%
7const minAmountX = amountX.times(0.985).toFixed(0)
8const minAmountY = amountY.times(0.985).toFixed(0)

here, getWithdrawLiquidityValue is the function provide by our sdk to pre compute undecimal_amount of tokenX and tokenY withdrawed from liquidity.

 1/**
 2 * @param liquidity: Liquidity, the liquidity object describe the liquidity you want to withdraw
 3 * @param state: State, the state queried from the pool, can be obtained by liquidity.state
 4 * @param withdrawLiquidity: BigNumber, value of liquidity you want to withdraw, could not larger than liquidity.liquidity
 5 * @return amountX: BigNumber, estimated undecimal amount of tokenX acquired after withdraw
 6 * @return amountY: BigNumber, estimated undecimal amount of tokenY acquired after withdraw
 7 * @return amountXDecimal: number, estimated decimal amount of tokenX acquired after withdraw
 8 * @return amountYDecimal: number, estimated decimal amount of tokenY acquired after withdraw
 9 */
10 getWithdrawLiquidityValue(liquidity, state, withdrawLiquidity)
  1. get calling of decreaseLiquidity (or we say withdraw)

 1const gasPrice = '5000000000'
 2
 3const {decLiquidityCalling, options} = getDecLiquidityCall(
 4    liquidityManagerContract,
 5    account.address,
 6    chain,
 7    {
 8        tokenId: liquidity0.tokenId,
 9        liquidDelta: decLiquidity.toFixed(0),
10        minAmountX,
11        minAmountY
12    } as DecLiquidityParam,
13    gasPrice
14)

the function getDecLiquidityCall(…) has following params

1/**
2 * @param liquidityManagerContract: web3.eth.Contract, the liquidity manager contract obj
3 * @param accountAddress: string, string of owner's address
4 * @param chain: BaseChain, the obj describing chain we are using
5 * @param gasPrice: string| number, gas price
6 */
7 getDecLiquidityCall(liquidityManagerContract, accountAddress, chain, params, gasPrice)

5. estimate gas (optional)

of course you can skip this step if you don’t want to limit gas

1const gasLimit = await decLiquidityCalling.estimateGas(options)
2console.log('gas limit: ', gasLimit)

6. send transaction!

for metamask or other explorer’s wallet provider, you can easily write

1await decLiquidityCalling.send({...options, gas: Number(gasLimit)})

otherwise, you could use following code

 1// sign transaction
 2const signedTx = await web3.eth.accounts.signTransaction(
 3    {
 4        ...options,
 5        to: liquidityManagerAddress,
 6        data: decLiquidityCalling.encodeABI(),
 7        gas: new BigNumber(Number(gasLimit) * 1.1).toFixed(0, 2),
 8    },
 9    privateKey
10)
11// send transaction
12const tx = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);
13console.log('tx: ', tx);

after sending transaction, we will successfully decrease the liquidity (if no revert occurred)