bnbchain-mcp
Version:
---
140 lines (139 loc) • 5.4 kB
JavaScript
import { createPublicClient, createWalletClient, http, getContract, parseUnits, isAddress, } from "viem";
import { bsc } from "viem/chains";
import { erc20Abi, hexToBigInt, maxUint256 } from "viem";
import { ChainId, CurrencyAmount, Native, Percent, Token, TradeType, } from "@pancakeswap/sdk";
import { SMART_ROUTER_ADDRESSES, SmartRouter, SwapRouter, } from "@pancakeswap/smart-router";
import { GraphQLClient } from "graphql-request";
import { account } from "../config.js";
export const getToken = async (token) => {
if (token.toUpperCase() === "BNB") {
return Native.onChain(ChainId.BSC);
}
let address = token.toLowerCase();
let decimal;
const url = "https://tokens.pancakeswap.finance/pancakeswap-extended.json";
const resp = await fetch(url);
const data = await resp.json();
let tokens = data.tokens;
let symbol;
if (!isAddress(address)) {
const tokenInfo = tokens.find((item) => item.symbol.toLowerCase() === address);
if (!tokenInfo) {
throw new Error("Token not found");
}
address = tokenInfo.address;
decimal = tokenInfo.decimals;
symbol = tokenInfo.symbol;
}
else {
const tokenInfo = tokens.find((item) => item.address.toLowerCase() === address);
if (!tokenInfo) {
throw new Error("Token not found");
}
decimal = tokenInfo.decimals;
symbol = tokenInfo.symbol;
}
return new Token(ChainId.BSC, address, decimal, symbol);
};
export const pancakeSwap = async ({
// privateKey,
inputToken, outputToken, amount, }) => {
const chainId = 56;
const rpcUrl = process.env.BSC_RPC_URL || "https://bsc-dataseed.binance.org";
const publicClient = createPublicClient({
chain: bsc,
transport: http(rpcUrl),
});
const walletClient = createWalletClient({
chain: bsc,
transport: http(rpcUrl),
account: account,
});
let currencyA = await getToken(inputToken);
let currencyB = await getToken(outputToken);
let amountDecimal = currencyA.decimals;
const parseAmountIn = parseUnits(amount, amountDecimal);
const amountValue = CurrencyAmount.fromRawAmount(currencyA, parseAmountIn);
if (!currencyA.isNative) {
const TokenContract = getContract({
address: currencyA.address,
abi: erc20Abi,
client: {
wallet: walletClient,
public: publicClient,
},
});
if (!TokenContract.write ||
!TokenContract.write.approve ||
!TokenContract.read.allowance) {
throw new Error("Unable to Swap Tokens");
}
amountDecimal = await TokenContract.read.decimals();
const smartRouterAddress = SMART_ROUTER_ADDRESSES[56];
const allowance = await TokenContract.read.allowance([account.address, smartRouterAddress]);
if (allowance < parseAmountIn) {
const approveResult = await TokenContract.write.approve([smartRouterAddress, maxUint256]);
await publicClient.waitForTransactionReceipt({
hash: approveResult,
});
}
}
const quoteProvider = SmartRouter.createQuoteProvider({
onChainProvider: () => publicClient,
});
const v3SubgraphClient = new GraphQLClient('https://api.thegraph.com/subgraphs/name/pancakeswap/exchange-v3-bsc1');
const v2SubgraphClient = new GraphQLClient('https://proxy-worker-api.pancakeswap.com/bsc-exchange1');
const [v2Pools, v3Pools] = await Promise.all([
SmartRouter.getV3CandidatePools({
onChainProvider: () => publicClient,
// @ts-ignore
subgraphProvider: () => v3SubgraphClient,
currencyA: currencyA,
currencyB: currencyB,
subgraphFallback: false,
}),
SmartRouter.getV2CandidatePools({
onChainProvider: () => publicClient,
// @ts-ignore
v2SubgraphProvider: () => v2SubgraphClient,
// @ts-ignore
v3SubgraphProvider: () => v3SubgraphClient,
currencyA: currencyA,
currencyB: currencyB,
}),
]);
const pools = [...v2Pools, ...v3Pools];
const trade = await SmartRouter.getBestTrade(amountValue, currencyB, TradeType.EXACT_INPUT, {
gasPriceWei: () => publicClient.getGasPrice(),
maxHops: 2,
maxSplits: 2,
poolProvider: SmartRouter.createStaticPoolProvider(pools),
quoteProvider,
quoterOptimization: true,
});
const { value, calldata } = SwapRouter.swapCallParameters(trade, {
recipient: account.address,
slippageTolerance: new Percent(1),
});
const tx = {
account: account.address,
// @ts-ignore
to: SMART_ROUTER_ADDRESSES[chainId],
data: calldata,
value: hexToBigInt(value),
};
const gasEstimate = await publicClient.estimateGas(tx);
const calculateGasMargin = (value, margin = 1000n) => {
return (value * (10000n + margin)) / 10000n;
};
const txHash = await walletClient.sendTransaction({
account: account,
chainId,
// @ts-ignore
to: SMART_ROUTER_ADDRESSES[chainId],
data: calldata,
value: hexToBigInt(value),
gas: calculateGasMargin(gasEstimate),
});
return txHash;
};