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';
//Client pre-checks:
// - Router contract has spender allowance for the input HTS tokens
//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 slot0 and liquidity data from the pool
const [slot0, poolLiquidity] = await Promise.all([
poolContract.slot0(),
poolContract.liquidity()
]);
//Construct the pool using the latest data
const pool = new Pool(
token0, token1,
feeTier, slot0.sqrtPriceX96.toString(),
poolLiquidity.toString(), Number(slot0.tick)
);
//Get current position data for the given NFT token serial number
const lp = await nftManagerContract.positions(tokenSN);
const feeTier = Number(lp.fee);
const tickLower = Number(lp.tickLower);
const tickUpper = Number(lp.tickUpper);
//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);
//Get amount0 in token's smallest unit from user's input (input0)
const amount0 = BigNumber(input0).times(Math.pow(10, token0Decimals)).toFixed(0);
//Construct a position using the SDK
// - use fromAmount0() if amount0 needs to be exact
// - use fromAmount1() if amount1 needs to be exact
// - use fromAmounts() if amount0 and amount1 do not need to be exact
const position = Position.fromAmount0({
pool: pool,
tickUpper: tickUpper,
tickLower: tickLower,
amount0: amount0,
useFullPrecision: true
});
//Get the mint amounts based on what the router will give us
const amount0Mint = position.mintAmounts.amount0.toString();
const amount1Mint = position.mintAmounts.amount1.toString();
//Calculate the minimum amounts factoring in the price slippage % and range
const priceSlippagePercent = new Percent(1, 100); //1% price slippage
const minAmounts = position.mintAmountsWithSlippage(priceSlippagePercent);
const amount0Min = minAmounts.amount0.toString();
const amount1Min = minAmounts.amount1.toString();
//IncreaseLiquidityParams struct
const params = {
tokenSN: tokenSN,
fee: feeTier, //500, 1500, 3000 or 10000
tickLower: tickLower, //lower tick of the range
tickUpper: tickUpper, //upper tick of the range
amount0Desired: amount0Mint, //in smallest unit
amount1Desired: amount1Mint, //in smallest unit
amount0Min: amount0Min, //in smallest unit
amount1Min: amount1Min, //in smallest unit
deadline: deadline, //Unix seconds
};
//Construct encoded data for each function
const increaseLiquidityEncoded = nftManagerInterfaces.encodeFunctionData('increaseLiquidity', [params]);
const refundEthEncoded = nftManagerInterfaces.encodeFunctionData('refundETH');
//Build encoded data for multicall
const encodedData = abiInterfaces.encodeFunctionData('multicall',
[[mintEncoded, refundEthEncoded]]);
const encodedDataAsUint8Array = hexToUint8Array(encodedData.substring(2));
//Give spender allowance for both tokens to the NFT Manager contract if needed.
//To avoid having to ask for allowance each time, request max allowance.
//If the token is HBAR, no spender allowance is required.
//Use Hedera's REST API to get current allowances for an account.
await yourGrantSpenderAllowanceFunc(..);
//Execute the contract call
const response = await new ContractExecuteTransaction()
.setPayableAmount(inputHbar) //mint fee + HBAR token amount if used
.setContractId(nftManagerContractId)
.setGas(gasGwei)
.setFunctionParameters(encodedDataAsUint8Array)
.execute(client);
//Fetch the result
const record = await response.getRecord(client);
const result = record.contractFunctionResult!;
const results = abiInterfaces.decodeFunctionResult('multicall', result.bytes)[0];
const mintResult = abiInterfaces.decodeFunctionResult('increaseLiquidity', results[0]);
//Retrieve the newly minted liquidity and amounts for informative purposes
const liquidity = BigNumber(mintResult.liquidity);
const amount0 = BigNumber(mintResult.amount0);
const amount1 = BigNumber(mintResult.amount1);