UNPKG

bnbchain-mcp

Version:

---

214 lines (213 loc) 8.29 kB
import { ChainId, Native, Percent } from "@pancakeswap/sdk"; import { PositionMath, Multicall } from "@pancakeswap/v3-sdk"; import { encodeFunctionData, getAddress, zeroAddress, maxUint128, parseAbi, } from "viem"; import dotenv from 'dotenv'; import { account, client } from "../config.js"; dotenv.config(); const positionManagerABI = [ { inputs: [ { components: [ { internalType: 'uint256', name: 'tokenId', type: 'uint256' }, { internalType: 'uint128', name: 'liquidity', type: 'uint128' }, { internalType: 'uint256', name: 'amount0Min', type: 'uint256' }, { internalType: 'uint256', name: 'amount1Min', type: 'uint256' }, { internalType: 'uint256', name: 'deadline', type: 'uint256' }, ], internalType: 'struct INonfungiblePositionManager.DecreaseLiquidityParams', name: 'params', type: 'tuple', }, ], name: 'decreaseLiquidity', outputs: [ { internalType: 'uint256', name: 'amount0', type: 'uint256' }, { internalType: 'uint256', name: 'amount1', type: 'uint256' }, ], stateMutability: 'payable', type: 'function', }, { inputs: [ { components: [ { internalType: 'uint256', name: 'tokenId', type: 'uint256' }, { internalType: 'address', name: 'recipient', type: 'address' }, { internalType: 'uint128', name: 'amount0Max', type: 'uint128' }, { internalType: 'uint128', name: 'amount1Max', type: 'uint128' }, ], internalType: 'struct INonfungiblePositionManager.CollectParams', name: 'params', type: 'tuple', }, ], name: 'collect', outputs: [ { internalType: 'uint256', name: 'amount0', type: 'uint256' }, { internalType: 'uint256', name: 'amount1', type: 'uint256' }, ], stateMutability: 'payable', type: 'function', }, { inputs: [{ internalType: 'uint256', name: 'tokenId', type: 'uint256' }], name: 'positions', outputs: [ { internalType: 'uint96', name: 'nonce', type: 'uint96' }, { internalType: 'address', name: 'operator', type: 'address' }, { internalType: 'address', name: 'token0', type: 'address' }, { internalType: 'address', name: 'token1', type: 'address' }, { internalType: 'uint24', name: 'fee', type: 'uint24' }, { internalType: 'int24', name: 'tickLower', type: 'int24' }, { internalType: 'int24', name: 'tickUpper', type: 'int24' }, { internalType: 'uint128', name: 'liquidity', type: 'uint128' }, { internalType: 'uint256', name: 'feeGrowthInside0LastX128', type: 'uint256' }, { internalType: 'uint256', name: 'feeGrowthInside1LastX128', type: 'uint256' }, { internalType: 'uint128', name: 'tokensOwed0', type: 'uint128' }, { internalType: 'uint128', name: 'tokensOwed1', type: 'uint128' }, ], stateMutability: 'view', type: 'function', }, ]; const FACTORY_ABI = parseAbi([ 'function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool)' ]); const POOL_ABI = parseAbi([ 'function liquidity() external view returns (uint128)', 'function slot0() external view returns (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint8 feeProtocol, bool unlocked)' ]); const Payments_ABI = [ { inputs: [ { internalType: 'uint256', name: 'amountMinimum', type: 'uint256', }, { internalType: 'address', name: 'recipient', type: 'address', }, ], name: 'unwrapWETH9', outputs: [], stateMutability: 'payable', type: 'function', }, { inputs: [ { internalType: 'address', name: 'token', type: 'address', }, { internalType: 'uint256', name: 'amountMinimum', type: 'uint256', }, { internalType: 'address', name: 'recipient', type: 'address', }, ], name: 'sweepToken', outputs: [], stateMutability: 'payable', type: 'function', }, ]; const POSITION_MANAGER_ADDRESS = '0x46A15B0b27311cedF172AB29E4f4766fbE7F4364'; const FACTORY_ADDRESS = '0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865'; export const removeLiquidityV3 = async (tokenId, percent) => { const calldatas = []; const positionInfo = await client.readContract({ address: POSITION_MANAGER_ADDRESS, abi: positionManagerABI, functionName: 'positions', args: [tokenId] }); const bnb = Native.onChain(ChainId.BSC); const liquidity = new Percent(percent, 100).multiply(positionInfo[7]).quotient; // remove liquidity calldatas.push(encodeFunctionData({ abi: positionManagerABI, functionName: 'decreaseLiquidity', args: [ { tokenId, liquidity, amount0Min: BigInt(1), amount1Min: BigInt(1), deadline: Math.floor(Date.now() / 1000) + 1200, }, ], })); const involvesETH = getAddress(bnb.wrapped.address) === getAddress(positionInfo[2]) || getAddress(bnb.wrapped.address) === getAddress(positionInfo[3]); calldatas.push(encodeFunctionData({ abi: positionManagerABI, functionName: 'collect', args: [ { tokenId, recipient: involvesETH ? zeroAddress : account.address, amount0Max: maxUint128, amount1Max: maxUint128, }, ], })); if (involvesETH) { const poolAddrs = await client.readContract({ address: FACTORY_ADDRESS, abi: FACTORY_ABI, functionName: 'getPool', args: [ positionInfo[2], positionInfo[3], positionInfo[4] ] }); const slot0 = await client.readContract({ address: poolAddrs, abi: POOL_ABI, functionName: 'slot0', }); const token0Amount = PositionMath.getToken0Amount(slot0[1], positionInfo[5], positionInfo[6], slot0[0], positionInfo[7]); const discountedAmount0 = new Percent(percent, 100).multiply(token0Amount).quotient; const token1Amount = PositionMath.getToken1Amount(slot0[1], positionInfo[5], positionInfo[6], slot0[0], positionInfo[7]); const discountedAmount1 = new Percent(percent, 100).multiply(token1Amount).quotient; const ethAmount = getAddress(bnb.wrapped.address) === getAddress(positionInfo[2]) ? discountedAmount0 : discountedAmount1; const token = getAddress(bnb.wrapped.address) === getAddress(positionInfo[2]) ? positionInfo[3] : positionInfo[2]; const tokenAmount = getAddress(bnb.wrapped.address) === getAddress(positionInfo[2]) ? discountedAmount1 : discountedAmount0; calldatas.push(encodeFunctionData({ abi: Payments_ABI, functionName: 'unwrapWETH9', args: [ethAmount, account.address], })); calldatas.push(encodeFunctionData({ abi: Payments_ABI, functionName: 'sweepToken', args: [token, tokenAmount, account.address], })); } const data = Multicall.encodeMulticall(calldatas); const tx = await client.sendTransaction({ to: POSITION_MANAGER_ADDRESS, data: data, value: BigInt(0), account: account, chain: client.chain, }); return tx; };