UNPKG

uniderp-mcp

Version:

> A plug-and-play MCP tool server to **send ETH**, **transfer ERC-20 tokens**, **deploy tokens**, and **interact with smart contracts** on the **UNICHAIN** — built for **Claude Desktop**, **AI agents**, and **developers.**

218 lines (217 loc) 10 kB
"use strict"; // import { // parseAbi, // type Address, // Hex, // PrivateKeyAccount, // } from 'viem'; // import { // Pool, // Position, // nearestUsableTick, // FeeAmount, // encodeSqrtRatioX96 // } from '@pancakeswap/v3-sdk'; // import { ChainId, Currency, CurrencyAmount, Percent, Token } from '@pancakeswap/sdk'; // import dotenv from 'dotenv'; // import { publicClient, walletClient } from '../config.js'; // import { TICK_SPACINGS, TickMath } from "@pancakeswap/v3-sdk"; // import { FACTORY_ADDRESSES, NFT_POSITION_MANAGER_ADDRESSES } from '@pancakeswap/v3-sdk'; // const POSITION_MANAGER_ADDRESS = NFT_POSITION_MANAGER_ADDRESSES[ChainId.BSC]; // const FACTORY_ADDRESS = FACTORY_ADDRESSES[ChainId.BSC]; // dotenv.config(); // // Contract ABI definitions // 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 ERC20_ABI = parseAbi([ // 'function allowance(address owner, address spender) external view returns (uint256)', // 'function approve(address spender, uint256 amount) external returns (bool)', // 'function balanceOf(address account) external view returns (uint256)' // ]); // const POSITION_MANAGER_ABI = [ // { "inputs": [{ "components": [{ "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": "uint256", "name": "amount0Desired", "type": "uint256" }, { "internalType": "uint256", "name": "amount1Desired", "type": "uint256" }, { "internalType": "uint256", "name": "amount0Min", "type": "uint256" }, { "internalType": "uint256", "name": "amount1Min", "type": "uint256" }, { "internalType": "address", "name": "recipient", "type": "address" }, { "internalType": "uint256", "name": "deadline", "type": "uint256" }], "internalType": "struct INonfungiblePositionManager.MintParams", "name": "params", "type": "tuple" }], "name": "mint", "outputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }, { "internalType": "uint128", "name": "liquidity", "type": "uint128" }, { "internalType": "uint256", "name": "amount0", "type": "uint256" }, { "internalType": "uint256", "name": "amount1", "type": "uint256" }], "stateMutability": "payable", "type": "function" } // ]; // async function approveTokensIfNeeded( // account: PrivateKeyAccount, // token: Currency, // spender: Address, // amount: string, // ): Promise<void> { // if (!token.isNative) { // const tokenAddress = token.address as Address; // const accountAddress = account.address; // const allowance = await publicClient.readContract({ // address: tokenAddress, // abi: ERC20_ABI, // functionName: 'allowance', // args: [accountAddress, spender] // }); // if (BigInt(allowance.toString()) < BigInt(amount)) { // const hash = await walletClient(account).writeContract({ // account: account, // address: tokenAddress, // abi: ERC20_ABI, // functionName: 'approve', // args: [spender, BigInt(amount)], // }); // await publicClient.waitForTransactionReceipt({ hash }); // } // } // } // function sortTokens( // tokenA: Currency, // tokenB: Currency, // amountA: CurrencyAmount<Currency>, // amountB: CurrencyAmount<Currency>, // ): [Token, Token, CurrencyAmount<Currency>, CurrencyAmount<Currency>] { // let token0 = tokenA.isNative ? tokenA.wrapped : tokenA; // let token1 = tokenB.isNative ? tokenB.wrapped : tokenB; // if (token0.sortsBefore(token1)) { // return [token0, token1, amountA, amountB]; // } else { // return [token1, token0, amountB, amountA]; // } // } // async function checkBalance( // account: PrivateKeyAccount, // token: Currency, // amount: CurrencyAmount<Currency> // ) { // const accountAddress = account.address; // if (token.isNative) { // const balance = await publicClient.getBalance({ address: accountAddress }); // const balanceAmount = CurrencyAmount.fromRawAmount(token, balance.toString()); // if (balanceAmount.lessThan(amount)) { // throw new Error(`Insufficient balance of ${token.symbol}`); // } // return // } // const balance = await publicClient.readContract({ // address: token.address as Address, // abi: ERC20_ABI, // functionName: 'balanceOf', // args: [accountAddress] // }); // const balanceAmount = CurrencyAmount.fromRawAmount(token, balance.toString()); // if (balanceAmount.lessThan(amount)) { // throw new Error(`Insufficient balance of ${token.symbol}`); // } // } // /** // * Add V3 liquidity // * @param tokenA first token // * @param tokenB second token // * @param fee fee tier // * @param amountA amount of tokenA // * @param amountB amount of tokenB // * @param account account // * @param slippageTolerance slippage tolerance // * @param deadline transaction deadline // * @param priceLower lower price bound percentage (default: 80% of current price) // * @param priceUpper upper price bound percentage (default: 120% of current price) // * @returns transaction receipt // */ // export async function addLiquidityV3( // tokenA: Currency, // tokenB: Currency, // fee: FeeAmount, // amountA: CurrencyAmount<Currency>, // amountB: CurrencyAmount<Currency>, // account: PrivateKeyAccount, // slippageTolerance: Percent = new Percent('50', '10000'), // default 0.5% // deadline: number = Math.floor(Date.now() / 1000) + 20 * 60, // default 20 minutes // priceLower: number = 0.8, // priceUpper: number = 1.2 // ): Promise<Hex> { // await Promise.all([ // approveTokensIfNeeded(account, tokenA, POSITION_MANAGER_ADDRESS, amountA.quotient.toString()), // approveTokensIfNeeded(account, tokenB, POSITION_MANAGER_ADDRESS, amountB.quotient.toString()), // ]); // await checkBalance(account, tokenA, amountA) // await checkBalance(account, tokenB, amountB) // const [token0, token1, amount0, amount1] = sortTokens(tokenA, tokenB, amountA, amountB); // const poolAddress = await publicClient.readContract({ // address: FACTORY_ADDRESS, // abi: FACTORY_ABI, // functionName: 'getPool', // args: [ // token0.address as Address, // token1.address as Address, // fee // ] // }) as Address; // if (!poolAddress || poolAddress === '0x0000000000000000000000000000000000000000') { // throw new Error(`Pool for ${tokenA.symbol}/${tokenB.symbol} not found`); // } // const [liquidity, slot0] = await Promise.all([ // publicClient.readContract({ // address: poolAddress, // abi: POOL_ABI, // functionName: 'liquidity' // }), // publicClient.readContract({ // address: poolAddress, // abi: POOL_ABI, // functionName: 'slot0' // }) // ]); // const pool = new Pool( // token0, // token1, // fee, // (slot0 as any)[0].toString(), // sqrtPriceX96 // liquidity.toString(), // (slot0 as any)[1] // tick // ); // // Retrieve tickSpacing from the SDK constants // const tickSpacing = TICK_SPACINGS[fee]; // fee should correspond to a valid // // Convert prices to square root ratio and then to ticks // const priceLowerRatio = encodeSqrtRatioX96(priceLower * 1e18, 1e18); // const priceUpperRatio = encodeSqrtRatioX96(priceUpper * 1e18, 1e18); // const lowerPriceTick = TickMath.getTickAtSqrtRatio(priceLowerRatio); // const upperPriceTick = TickMath.getTickAtSqrtRatio(priceUpperRatio); // // Round ticks to the nearest valid tick // const tickLower = nearestUsableTick(lowerPriceTick, tickSpacing); // const tickUpper = nearestUsableTick(upperPriceTick, tickSpacing); // const position = Position.fromAmounts({ // pool, // tickLower, // tickUpper, // amount0: amount0.quotient.toString(), // amount1: amount1.quotient.toString(), // useFullPrecision: true // }); // const { amount0: amount0Min, amount1: amount1Min } = position.mintAmountsWithSlippage(slippageTolerance); // const value = tokenA.isNative // ? amountA.quotient.toString() // : tokenB.isNative // ? amountB.quotient.toString() // : '0'; // const mintParams = { // token0: token0.address as Address, // token1: token1.address as Address, // fee, // tickLower, // tickUpper, // amount0Desired: BigInt(amount0.quotient.toString()), // amount1Desired: BigInt(amount1.quotient.toString()), // amount0Min: BigInt(amount0Min.toString()), // amount1Min: BigInt(amount1Min.toString()), // recipient: account.address, // deadline: BigInt(deadline) // }; // const hash = await walletClient(account).writeContract({ // address: POSITION_MANAGER_ADDRESS, // abi: POSITION_MANAGER_ABI, // functionName: 'mint', // args: [mintParams], // value: BigInt(value), // account: account // }); // return hash; // }