import * as ethers from 'ethers'; //V6
import { 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 provider
const provider = new ethers.JsonRpcProvider(hederaJsonRelayUrl, '', {
batchMaxCount: 1, //workaround for V6
});
//Load the ABI data for UniswapV3Pool
const poolInterfaces = new ethers.Interface(poolAbi);
//Load the ABI data for NonfungiblePositionManager
const nftManagerInterfaces = new ethers.Interface(nftManagerAbi);
//Construct the pool contract
const poolContract = new ethers.Contract(poolEvmAddress,
poolInterfaces.fragments, provider);
//Construct the NFT Manager contract
const nftManagerContract = new ethers.Contract(nftManagerEvmAddress,
nftManagerInterfaces.fragments, provider);
//Get current position data for the given NFT token serial number
const 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 pool
const [slot0, poolLiquidity] = await Promise.all([
poolContract.slot0(),
poolContract.liquidity()
]);
//Construct the tokens
//For Hedera chain id, see https://chainlist.org/?testnets=true&search=Hedera
const token0 = new Token(hederaChainId, token0Address, token0Decimals);
const token1 = new Token(hederaChainId, token1Address, token1Decimals);
//Construct the pool using the latest data
const pool = new Pool(
token0, token1,
feeTier, slot0.sqrtPriceX96.toString(),
poolLiquidity.toString(), Number(slot0.tick)
);
//Construct a position from liquidity and range
const position = new Position({
pool: pool,
tickUpper: tickUpper,
tickLower: tickLower,
liquidity: liquidity
});
//Calculate the maximum amounts factoring in the price slippage % and range
const priceSlippagePercent = new Percent(1, 100); //1% price slippage
const burnAmounts = position.burnAmountsWithSlippage(priceSlippagePercent);
const amount0Min = burnAmounts.amount0.toString();
const amount1Min = burnAmounts.amount1.toString();
//DecreaseLiquidityParams struct
const 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 amount1Max
const MAX_UINT128 = new BigNumber(2).pow(128).minus(1).toFixed(0);
//CollectParams struct
const 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 HBAR
const unwrapWHBAREncoded = nftManagerInterfaces.encodeFunctionData('unwrapWHBAR', [0, recipientAddress]);
//Build encoded data for the multicall
const encodedData = nftManagerInterfaces.encodeFunctionData('multicall',
[[decreaseEncoded, collectEncoded, unwrapWHBAREncoded]]);
const encodedDataAsUint8Array = hexToUint8Array(encodedData.substring(2));
//Execute the contract call
const response = await new ContractExecuteTransaction()
.setContractId(nftManagerContractId)
.setGas(gasGwei)
.setFunctionParameters(encodedDataAsUint8Array)
.execute(client);
//Fetch the result
const 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 purposes
const removedAmount0 = BigNumber(collectResult.amount0);
const removedAmount1 = BigNumber(collectResult.amount1);