Function name: decreaseLiquidity⛽ Recommended gas: 300,000 gwei (~ $0.026 USD)
Struct Parameter Name
Description
uint256 tokenSN
The serial number of the token for which liquidity is being increased
uint128 liquidity
Liquidity amount to remove
uint256 amount0Min
The minimum amount for the first token in its smallest unit
uint256 amount1Min
The minimum amount for the second token in its smallest unit
uint256 deadline
Deadline in Unix seconds
Copy
Ask AI
struct DecreaseLiquidityParams { uint256 tokenSN; uint128 liquidity; uint256 amount0Min; uint256 amount1Min; uint256 deadline;}/// @notice Decreases the amount of liquidity in a position and accounts it to the position/// @param params tokenSN The serial number of the token for which liquidity is being decreased,/// amount The amount by which liquidity will be decreased,/// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,/// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,/// deadline The time by which the transaction must be included to effect the change/// @return amount0 The amount of token0 accounted to the position's tokens owed/// @return amount1 The amount of token1 accounted to the position's tokens owedfunction decreaseLiquidity(DecreaseLiquidityParams calldata params) external payable returns (uint256 amount0, uint256 amount1);
The following code demonstrates how to remove all liquidity from an existing position, collect the swap fees, and return the deposit amounts.
When removing liquidity from a pool that involves HBAR, include unwrapWHBAR in your call to convert the Wrapped HBAR (WHBAR) output token back into the native HBAR cryptocurrency.
Call the collect function after removing the liquidity to withdraw the amounts to the recipient address. It will also collect any swap fees earned in the position.
To burn the NFT after completely exiting the position, include burn in the multi-call. See Burning the NFT.
import * as ethers from 'ethers'; //V6import { Pool, Position, nearestUsableTick, priceToClosestTick } from '@uniswap/v3-sdk';import { Fraction, Percent, Token, Price } from '@uniswap/sdk-core';import { ContractExecuteTransaction, .. } from '@hashgraph/sdk';//Set one of Hedera's JSON RPC Relay as the providerconst provider = new ethers.JsonRpcProvider(hederaJsonRelayUrl, '', { batchMaxCount: 1, //workaround for V6});//Load the ABI data for UniswapV3Poolconst poolInterfaces = new ethers.Interface(poolAbi);//Load the ABI data for NonfungiblePositionManagerconst nftManagerInterfaces = new ethers.Interface(nftManagerAbi);//Construct the pool contractconst poolContract = new ethers.Contract(poolEvmAddress, poolInterfaces.fragments, provider);//Construct the NFT Manager contractconst nftManagerContract = new ethers.Contract(nftManagerEvmAddress, nftManagerInterfaces.fragments, provider);//Get current position data for the given NFT token serial numberconst lp = await nftManagerContract.positions(tokenSN);const token0Address = lp.token0;const token1Address = lp.token1;const feeTier = Number(lp.fee);const tickLower = Number(lp.tickLower);const tickUpper = Number(lp.tickUpper);const liquidity = lp.liquidity.toString();//Get current slot0 and liquidity data from the poolconst [slot0, poolLiquidity] = await Promise.all([ poolContract.slot0(), poolContract.liquidity()]);//Construct the tokens//For Hedera chain id, see https://chainlist.org/?testnets=true&search=Hederaconst token0 = new Token(hederaChainId, token0Address, token0Decimals);const token1 = new Token(hederaChainId, token1Address, token1Decimals);//Construct the pool using the latest dataconst pool = new Pool( token0, token1, feeTier, slot0.sqrtPriceX96.toString(), poolLiquidity.toString(), Number(slot0.tick));//Construct a position from liquidity and rangeconst position = new Position({ pool: pool, tickUpper: tickUpper, tickLower: tickLower, liquidity: liquidity});//Calculate the maximum amounts factoring in the price slippage % and rangeconst priceSlippagePercent = new Percent(1, 100); //1% price slippageconst burnAmounts = position.burnAmountsWithSlippage(priceSlippagePercent);const amount0Min = burnAmounts.amount0.toString();const amount1Min = burnAmounts.amount1.toString();//DecreaseLiquidityParams structconst params = { tokenSN: tokenSN, liquidity: liquidity, //liquidity amount to remove amount0Min: amount0Min, //in smallest unit amount1Min: amount1Min, //in smallest unit deadline: deadline, //Unix seconds};//get max possible value for amount0Max and amount1Maxconst MAX_UINT128 = new BigNumber(2).pow(128).minus(1).toFixed(0);//CollectParams structconst collectParams = { tokenSN: tokenSN, recipient: recipientAddress, //0x.. amount0Max: MAX_UINT128, //collect max fees and amount amount1Max: MAX_UINT128, //collect max fees and amount};//Construct encoded data for each function//The unwrapWHBAR is needed when collecting the HBAR swap fees//Optionally include 'collect' here to collect fees.//Optionally include 'burn' to burn the NFT if all liquidity is removed.const decreaseEncoded = nftManagerInterfaces.encodeFunctionData('decreaseLiquidity', [params]); const collectEncoded = nftManagerInterfaces.encodeFunctionData('collect', [collectParams]); //The unwrapWHBAR is only needed when removing liquidity that includes HBARconst unwrapWHBAREncoded = nftManagerInterfaces.encodeFunctionData('unwrapWHBAR', [0, recipientAddress]);//Build encoded data for the multicallconst encodedData = nftManagerInterfaces.encodeFunctionData('multicall', [[decreaseEncoded, collectEncoded, unwrapWHBAREncoded]]); const encodedDataAsUint8Array = hexToUint8Array(encodedData.substring(2));//Execute the contract callconst response = await new ContractExecuteTransaction() .setContractId(nftManagerContractId) .setGas(gasGwei) .setFunctionParameters(encodedDataAsUint8Array) .execute(client);//Fetch the resultconst record = await response.getRecord(client); const result = record.contractFunctionResult!;const results = nftManagerInterfaces.decodeFunctionResult('multicall', result.bytes)[0];const collectResult = nftManagerInterfaces.decodeFunctionResult('collect', results[1]);//Retrieve the amounts removed for informative purposesconst removedAmount0 = BigNumber(collectResult.amount0);const removedAmount1 = BigNumber(collectResult.amount1);
After completely exiting a position, you may burn the NFT position if it is no longer needed. The following code demonstrates how to set a spender allowance for the NFT and include the burn function in the multicall.
A Note allowance for the NFT Manager contract must be approved by the token holder to enable the contract to retrieve the NFT to burn it.
//Updated code to include burning of the NFT token//Construct encoded data for each function//The unwrapWHBAR is needed when collecting the HBAR swap fees//Optionally include 'collect' here to collect fees.//Optionally include 'burn' to burn the NFT if all liquidity is removed.const decreaseEncoded = nftManagerInterfaces.encodeFunctionData('decreaseLiquidity', [params]); const collectEncoded = nftManagerInterfaces.encodeFunctionData('collect', [collectParams]); const burnEncoded = nftManagerInterfaces.encodeFunctionData('burn', [tokenSN]);//The unwrapWHBAR is only needed when removing liquidity that includes HBARconst unwrapWHBAREncoded = nftManagerInterfaces.encodeFunctionData('unwrapWHBAR', [0, recipientAddress]);//Build encoded data for the multicallconst encodedData = nftManagerInterfaces.encodeFunctionData('multicall', [[decreaseEncoded, collectEncoded, unwrapWHBAREncoded, burnEncoded]]); const encodedDataAsUint8Array = hexToUint8Array(encodedData.substring(2));//Give NFT spender allowance to NFT Manager contractconst nftId = new NftId(lpTokenId, tokenSN);const approveResult = await new AccountAllowanceApproveTransaction() .approveTokenNftAllowance(nftId, ownerId, nftManagerContractId) .execute(client);const allowanceReceipt = await approveResult.getReceipt(client);console.log(`NFT allowance status: ${allowanceReceipt.status}`);//Execute the contract callconst response = await new ContractExecuteTransaction() .setContractId(nftManagerContractId) .setGas(gasGwei) .setFunctionParameters(encodedDataAsUint8Array) .execute(client);