#
tokens: 26282/50000 39/39 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .gitignore
├── config.example.json
├── jest.config.mjs
├── package-lock.json
├── package.json
├── README.md
├── README.npm.md
├── scripts
│   └── publish.cjs
├── sources
│   ├── BUSD_TRANSFER_MCP.mp4
│   └── example.ts
├── src
│   ├── addressConfig.ts
│   ├── cli
│   │   ├── help.ts
│   │   ├── init.ts
│   │   └── version.ts
│   ├── config.ts
│   ├── functions
│   │   ├── fetchBalanceTool.ts
│   │   ├── memeTokenDetails.ts
│   │   ├── pancakeAddLiquidityTool.ts
│   │   ├── pancakeRemoveLiquidityTool.ts
│   │   ├── pancakeSwapPosition.ts
│   │   └── pancakeSwapTool.ts
│   ├── index.ts
│   ├── init.ts
│   ├── lib
│   │   └── bep20Abi.ts
│   ├── main.ts
│   ├── PrivateAES.ts
│   ├── responseUtils.ts
│   ├── test
│   │   └── privateAES.test.ts
│   ├── tools
│   │   ├── buyMemeToken.ts
│   │   ├── getWalletInfo.ts
│   │   ├── goplusSecurityCheck.ts
│   │   ├── pancakeAddLiquidity.ts
│   │   ├── pancakeMyPosition.ts
│   │   ├── pancakeRemovePosition.ts
│   │   ├── pancakeSwap.ts
│   │   ├── queryMemeTokenDetails.ts
│   │   ├── sellMemeToken.ts
│   │   ├── transferBEP20Token.ts
│   │   └── transferNativeToken.ts
│   ├── types
│   │   └── types.ts
│   └── util.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# database
/prisma/db.sqlite
/prisma/db.sqlite-journal
db.sqlite

# next.js
/.next/
/out/
next-env.d.ts

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
.env
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo

# idea files
.idea
yarn.lock

.env*
dist/
```

--------------------------------------------------------------------------------
/README.npm.md:
--------------------------------------------------------------------------------

```markdown


---

## 📦 BNBChain MCP – Binance Smart Chain Tool Server (MCP + CLI Ready)

> A plug-and-play MCP tool server to **send BNB**, **transfer BEP-20 tokens**, **deploy tokens**, and **interact with smart contracts** on the **Binance Smart Chain (BSC)** — built for **Claude Desktop**, **AI agents**, and **developers.**

---

### ⚙️ Core Capabilities

- 🔐 Secure token & native transfers via CLI or MCP
- 🧱 Interact with smart contracts (ABI/function-based)
- 🔄 PancakeSwap integration for swaps & liquidity
- ⚙️ Create meme tokens & deploy BEP-20 smart contracts
- 🧠 Native Claude Desktop integration via MCP
- 🔧 CLI-ready, MCP-compliant, developer-friendly
- 🔑 Password-protected private keys

---

## 🛠 Installation & Setup

### 1. Install

```bash
npm install -g bnbchain-mcp
```

### 2. Run the CLI Setup Wizard

```bash
bnbchain-mcp --init
```

You’ll be prompted to enter:

- ✅ **BSC Wallet Private Key** *(required)* 
- ✅ **Wallet Password** *(required, must be 6 characters)*
- ✅ **Custom RPC URL** *(optional, defaults to:* `https://bsc-dataseed.binance.org` *)

---

## 🧠 Claude Desktop Integration

After CLI setup, the tool can **auto-configure itself into Claude Desktop**.

📍 File modified:

```
~/Library/Application Support/Claude/claude_desktop_config.json
```

Claude will detect and run this MCP server with your selected tools.

---

## 🔨 Supported MCP Tools

| Tool Name             | Description                              |
|----------------------|------------------------------------------|
| `transferNativeToken` | Send BNB to a wallet                     |
| `transferBEP20Token`  | Transfer BEP-20 token via symbol/address |
| `pancakeSwap`         | Swap tokens via PancakeSwap              |
| `createFourMeme`      | Create meme token on Four.Meme           |
| `createBEP20Token`    | Deploy a BEP-20 contract                 |
| `getBalance`          | Get token + native balance               |
| `callContractFunction`| Custom contract calls via ABI            |
| `getWalletInfo`       | Get wallet info for an address           |
| `securityCheck`       | Check token security of BSC tokens       |
| `pancakeAddLiquidity` | Add liquidity to PancakeSwap             |
| `pancakeMyPosition`   | View your PancakeSwap positions          |
| `pancakeRemovePosition`| Remove liquidity from PancakeSwap        |
| `sellMemeToken`        | Sell meme token on Four.Meme             |
| ...and more coming soon 🔧 |

---

## 🧪 Development Workflow

### Compile TypeScript:
```bash
npm run build
```

### Start MCP Server:
```bash
npm start
# or
node build/index.js
```

### Re-configure:
```bash
bnbchain-mcp --init
```

---

## 📘 Model Context Protocol (MCP)

This project is built on **Model Context Protocol** – a standard to help agents and models interact with structured tool APIs.

**MCP Benefits**:
- ✅ Structured input/output
- ✅ Claude + OpenAI compatible
- ✅ Secure + serverless-ready

---

## ✅ Roadmap

- [x] CLI Configuration Wizard
- [x] Claude Desktop Integration
- [x] Token Deploy + Transfer
- [ ] Token charting tools (DEXTools, Gecko)
- [ ] Telegram auto-trading agent
- [ ] AI assistant with BSC on-chain brain

---

## 🤝 Contributing

Feel free to fork, PR, or raise issues.
We're building **tool-first, AI-ready infrastructure** for the next wave of Web3 agents. Join us!

---

## 🛡️ License

MIT — Use freely, contribute openly.

---

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown


---

## 📦 BNBChain MCP – Binance Smart Chain Tool Server (MCP + CLI Ready)

> A comprehensive blockchain tool server for BNB, BEP-20 tokens, smart contract deployment and interaction built on BNB Smart Chain (BSC) and compatible with other EVM networks.

---

## Technology Stack

- **Blockchain**: BNB Smart Chain (BSC)  
- **Web3 Libraries**: Viem 2.23.11, PancakeSwap SDK 5.8.8  
- **CLI/Backend**: TypeScript, Node.js (ESM)  
- **Protocol**: Model Context Protocol (MCP) SDK 1.4.0  
- **Security**: AES encryption with bcrypt for private key protection
- **Token Security**: GoPlus SDK for security checks
- **Data Provider**: Moralis SDK 2.27.2 for blockchain data

---

## Supported Networks

- **BNB Smart Chain Mainnet** (Chain ID: 56)  
  - RPC: https://bsc-dataseed.binance.org (default)
  - Custom RPC supported via environment configuration

---

## Contract Addresses

| Contract Type | Address | Description |
|--------------|---------|-------------|
| Four.Meme Try Buy | 0xF251F83e40a78868FcfA3FA4599Dad6494E46034 | Four.Meme token purchase contract |
| Four.Meme Buy/Sell AMAP | 0x5c952063c7fc8610FFDB798152D69F0B9550762b | Four.Meme auto-market-adjusted pricing |
| Four.Meme Create Token | 0x5c952063c7fc8610FFDB798152D69F0B9550762b | Four.Meme token factory |
| PancakeSwap Router V2 | Integrated via SDK | DEX routing and swaps |
| PancakeSwap V3 Pools | Accessed via SDK | Liquidity pools management |

---

## Features

- **Low-cost BNB & BEP-20 transfers** - Optimized for BSC's low gas fees
- **PancakeSwap V2/V3 integration** - Automated swaps, liquidity management, and position tracking
- **Four.Meme platform support** - Create, buy, and sell meme tokens directly
- **Security-first architecture** - AES-256 encrypted private keys with bcrypt password protection
- **Token security analysis** - Built-in GoPlus security checks for token verification
- **Gas-efficient operations** - Smart routing for optimal gas usage on BSC
- **AI-ready MCP protocol** - Seamless integration with Claude Desktop and AI agents
- **Real-time wallet monitoring** - Track balances and positions across multiple tokens

---

## 🛠 Installation & Setup

### 1. Install

```bash
npm install -g bnbchain-mcp
```

### 2. Run the CLI Setup Wizard

```bash
bnbchain-mcp --init
```

You’ll be prompted to enter:

- ✅ **BSC Wallet Private Key** *(required)* 
- ✅ **Wallet Password** *(required, must be 6 characters)*
- ✅ **Custom RPC URL** *(optional, defaults to:* `https://bsc-dataseed.binance.org` *)

---

## 🧠 Claude Desktop Integration

After CLI setup, the tool can **auto-configure itself into Claude Desktop**.

📍 File modified:

```
~/Library/Application Support/Claude/claude_desktop_config.json
```

Claude will detect and run this MCP server with your selected tools.

---

## 🔨 Supported MCP Tools

| Tool Name             | Description                              |
|----------------------|------------------------------------------|
| `transferNativeToken` | Send BNB to a wallet                     |
| `transferBEP20Token`  | Transfer BEP-20 token via symbol/address |
| `pancakeSwap`         | Swap tokens via PancakeSwap              |
| `createFourMeme`      | Create meme token on Four.Meme           |
| `createBEP20Token`    | Deploy a BEP-20 contract                 |
| `getBalance`          | Get token + native balance               |
| `callContractFunction`| Custom contract calls via ABI            |
| `getWalletInfo`       | Get wallet info for an address           |
| `securityCheck`       | Check token security of BSC tokens       |
| `pancakeAddLiquidity` | Add liquidity to PancakeSwap             |
| `pancakeMyPosition`   | View your PancakeSwap positions          |
| `pancakeRemovePosition`| Remove liquidity from PancakeSwap        |
| `sellMemeToken`        | Sell meme token on Four.Meme             |
| ...and more coming soon 🔧 |

---

## 🧪 Development Workflow

### Compile TypeScript:
```bash
npm run build
```

### Start MCP Server:
```bash
npm start
# or
node build/index.js
```

### Re-configure:
```bash
bnbchain-mcp --init
```

---

## 📘 Model Context Protocol (MCP)

This project is built on **Model Context Protocol** – a standard to help agents and models interact with structured tool APIs.

**MCP Benefits**:
- ✅ Structured input/output
- ✅ Claude + OpenAI compatible
- ✅ Secure + serverless-ready

---

## ✅ Roadmap

- [x] CLI Configuration Wizard
- [x] Claude Desktop Integration
- [x] Token Deploy + Transfer
- [ ] Token charting tools (DEXTools, Gecko)
- [ ] Telegram auto-trading agent
- [ ] AI assistant with BSC on-chain brain

---

## 🤝 Contributing

Feel free to fork, PR, or raise issues.
We're building **tool-first, AI-ready infrastructure** for the next wave of Web3 agents. Join us!

---

## 🛡️ License

MIT — Use freely, contribute openly.

---

```

--------------------------------------------------------------------------------
/src/types/types.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * Fetch tokens from PancakeSwap token list
 * @returns Array of token data
 */

import { Address } from "viem";

export interface TokenInfo {
  address: Address;
  decimals: number;
  symbol?: string;
  name?: string;
}

```

--------------------------------------------------------------------------------
/src/cli/help.ts:
--------------------------------------------------------------------------------

```typescript
// src/cli/help.ts
export function printHelp(): void {
    console.log(`
  📦 BNB Chain MCP CLI
  
  Usage:
    bnbchain-mcp [options]
  
  Options:
    -i, --init      Initialize configuration
    -v, --version   Show CLI version
    -h, --help      Show help info
  
  Examples:
    bnbchain-mcp --init
    bnbchain-mcp --version
    bnbchain-mcp
    `);
}
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "Node",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "noEmitOnError": false,
    "isolatedModules": true

  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

```

--------------------------------------------------------------------------------
/config.example.json:
--------------------------------------------------------------------------------

```json
{
    "mcpServers": {
        "bsc-mcp": {
            "command": "node",
            "args": [
                "/Users/Username/Desktop/bsc-mpc/build/index.js"
            ],
            "env": {
                "BSC_WALLET_PRIVATE_KEY": "BSC_WALLET_PRIVATE_KEY",
                "BSC_RPC_URL": "BSC_RPC_URL"
            },
            "disabled": false,
            "autoApprove": []
        }
    }
}
```

--------------------------------------------------------------------------------
/jest.config.mjs:
--------------------------------------------------------------------------------

```
export default {
  transform: {
    '^.+\\.tsx?$': 'ts-jest',
    '^.+\\.mjs$': 'ts-jest',
  },
  moduleFileExtensions: ['js', 'json', 'ts', 'tsx', 'mjs'],
  testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[tj]s?(x)'],
  moduleNameMapper: {
    '^(\\.{1,2}/.*)\\.js$': '$1',
  },
  extensionsToTreatAsEsm: ['.ts', '.tsx'],
  globals: {
    'ts-jest': {
      useESM: true,
    },
  },
};
```

--------------------------------------------------------------------------------
/src/addressConfig.ts:
--------------------------------------------------------------------------------

```typescript
import { Address } from "viem"

export const AddressConfig: {
    [key: string]: Address
} = {
    "FourMemeTryBuyContract": "0xF251F83e40a78868FcfA3FA4599Dad6494E46034",
    "FourMemeBuyTokenAMAPContract": "0x5c952063c7fc8610FFDB798152D69F0B9550762b",
    "FourMemeSellTokenAMAPContract": "0x5c952063c7fc8610FFDB798152D69F0B9550762b",
    "FourMemeCreateTokenContract": "0x5c952063c7fc8610FFDB798152D69F0B9550762b",
}
```

--------------------------------------------------------------------------------
/src/cli/version.ts:
--------------------------------------------------------------------------------

```typescript
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

let version = '1.0.0'; // fallback

try {
  const pkgPath = path.resolve(__dirname, '../../package.json');
  const pkgJson = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
  version = pkgJson.version || version;
} catch {
}

export { version };

```

--------------------------------------------------------------------------------
/src/cli/init.ts:
--------------------------------------------------------------------------------

```typescript
import { execSync } from 'child_process';
import { fileURLToPath } from 'url';
import path from 'path';

// __dirname workaround for ES Modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

export async function init() {
  const initPath = path.resolve(__dirname, '../init.js');

  try {
    execSync(`node "${initPath}"`, { stdio: 'inherit' });
  } catch (err) {
    console.error('❌ Failed to run init:', err);
    process.exit(1);
  }
}
```

--------------------------------------------------------------------------------
/src/test/privateAES.test.ts:
--------------------------------------------------------------------------------

```typescript
import { encryptPrivateKey, decryptPrivateKey } from "../PrivateAES.js";

describe.only("privateAES", () => {
    
    test("test", async () => {
        const privateKey = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';
        const password = '12345678';
        const encrypted =await encryptPrivateKey(privateKey, password);
        console.log(encrypted);
        const decrypted =await decryptPrivateKey(encrypted, password);
        console.log(decrypted);
        expect(decrypted).toBe(privateKey);
    })
})
```

--------------------------------------------------------------------------------
/src/lib/bep20Abi.ts:
--------------------------------------------------------------------------------

```typescript
export const bep20abi = [
  {
    name: "transfer",
    type: "function",
    stateMutability: "nonpayable",
    inputs: [
      { name: "recipient", type: "address" },
      { name: "amount", type: "uint256" },
    ],
    outputs: [{ type: "bool" }],
  },
  {
    name: "decimals",
    type: "function",
    stateMutability: "view",
    inputs: [],
    outputs: [{ type: "uint8" }],
  },
  {
    name: "symbol",
    type: "function",
    stateMutability: "view",
    inputs: [],
    outputs: [{ type: "string" }],
  },
  {
    name: "name",
    type: "function",
    stateMutability: "view",
    inputs: [],
    outputs: [{ type: "string" }],
  },
] as const;

```

--------------------------------------------------------------------------------
/src/functions/fetchBalanceTool.ts:
--------------------------------------------------------------------------------

```typescript
import { sanitizeData } from "../responseUtils";

const BALANCE_API_URL = "https://app.termix.ai/api/bscBalanceCheck";

type BalanceData = {
  address: string;
  nativeBalance: string;
  tokenBalances: {
    token_address: string;
    symbol: string;
    name: string;
    logo: string;
    decimals: string;
    balance: string;
  }[];
};

export async function getBalance(address: string) {
  const response = await fetch(`${BALANCE_API_URL}?address=${address}`);
  if (!response.ok) {
    throw new Error(`Balance Fetch Error: ${response.statusText}`);
  }
  const result = await response.json() as BalanceData;

  return sanitizeData(result, {
    strictMode: true,
    maxLength: 200,
    allowMarkdown: false
  });

}

```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
// src/index.ts
import { parseArgs } from 'node:util';
import { init } from './cli/init.js';
import { version } from './cli/version.js';
import { printHelp } from './cli/help.js';
import { main } from './main.js';


process.on('uncaughtException', (error: Error) => {
  console.error('❌ Uncaught exception:', error);
});

process.on('unhandledRejection', (error: Error | unknown) => {
  console.error('❌ Unhandled rejection:', error);
});

interface CliOptions {
  init?: boolean;
  help?: boolean;
  version?: boolean;
}

let values: CliOptions;

try {
  const args = parseArgs({
    options: {
      init: { type: 'boolean', short: 'i' },
      help: { type: 'boolean', short: 'h' },
      version: { type: 'boolean', short: 'v' },
    },
  });
  values = args.values as CliOptions;
} catch (err) {
  console.error('❌ Unrecognized argument. For help, run `bnbchain-mcp --help`.');
  process.exit(1);
}

if (values.help) {
  printHelp();
  process.exit(0);
}

if (values.version) {
  console.log(version);
  process.exit(0);
}

if (values.init) {
  await init(); // run init.js logic
} else {
  main().catch((error: Error) => {
    console.error('❌ Fatal error in main():', error);
    process.exit(1);
  });
}

```

--------------------------------------------------------------------------------
/src/tools/getWalletInfo.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { getBalance } from "../functions/fetchBalanceTool.js";
import { getAccount } from "../config.js";

export function registerGetWalletInfo(server: McpServer) {
  server.tool("Get_Wallet_Info", "👛View detailed balance and holdings for any wallet address", {
      address: z.string().optional().describe("When querying the user's own wallet value, it is null"),
    },
    async ({ address }) => {
      try {
        if (address === '' || !address || address === 'null') {
          const account = await getAccount();
          address = account.address
        }
        const balance = await getBalance(address);

        return {
          content: [
            {
              type: "text",
              text: `Native Balance (BNB): ${balance.nativeBalance}\n\nToken Balances:\n${JSON.stringify(balance.tokenBalances)}\n\nWallet Address: ${address}`,
            },
          ],
        };
      } catch (error) {
        const errorMessage =
          error instanceof Error ? error.message : String(error);
        return {
          content: [
            { type: "text", text: `Failed to fetch balance: ${errorMessage}` },
          ],
          isError: true,
        };
      }
    }
  );
}

```

--------------------------------------------------------------------------------
/src/tools/pancakeMyPosition.ts:
--------------------------------------------------------------------------------

```typescript

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { myPosition } from "../functions/pancakeSwapPosition.js";
import { bigIntReplacer } from "../util.js";
import { getAccount } from "../config.js";

export function registerPancakeMyPosition(server: McpServer) {

    server.tool("View_PancakeSwap_Positions", "📊View your active liquidity positions on PancakeSwap", {}, async ({}) => {

            try {
            
                const account = await getAccount();
                const positions = await myPosition(account.address);
                return {
                    content: [
                        {
                            type: "text",
                            text: `get user potitions successfully. ${JSON.stringify(positions, bigIntReplacer)}`
                        },
                    ],
                };
            } catch (error) {
                const errorMessage =
                    error instanceof Error ? error.message : String(error);
                return {
                    content: [
                        {
                            type: "text",
                            text: `get user potitions failed: ${errorMessage}`,
                        },
                    ],
                    isError: true,
                };
            }
        }
    );
}
```

--------------------------------------------------------------------------------
/src/tools/queryMemeTokenDetails.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { memeTokenDetail } from "../functions/memeTokenDetails.js";

export function registerQueryMemeTokenDetails(server: McpServer) {
    server.tool(
        "QueryMemeTokenDetails",
        "Fetches token details for a given meme token using the four.meme API. Default price in USDT.",
        {
            tokenName: z.string().describe("The name of the token to query (e.g., HGUSDT)")
        },
        async ({ tokenName }) => {
            try {
                const data = await memeTokenDetail(tokenName);

                return {
                    content: [
                        {
                            type: "text",
                            text: `Token details for "${tokenName}": ${JSON.stringify(data, null, 2)}`
                        }
                    ]
                };
            } catch (error) {
                const errorMessage = error instanceof Error ? error.message : String(error);
                return {
                    content: [
                        {
                            type: "text",
                            text: `Failed to fetch token details: ${errorMessage}`
                        }
                    ],
                    isError: true
                };
            }
        }
    );
}

```

--------------------------------------------------------------------------------
/src/functions/memeTokenDetails.ts:
--------------------------------------------------------------------------------

```typescript
export const memeTokenDetail = async (tokenName: string) => {
    // Fetch both BNB price and token details in parallel
    const [bnbPriceRes, tokenRes] = await Promise.all([
        fetch("https://api.binance.com/api/v3/ticker/price?symbol=BNBUSDT"),
        fetch(`https://www.four.meme/meme-api/v1/private/token/query?tokenName=${encodeURIComponent(tokenName)}`, {
            method: "GET",
            headers: {
                "Content-Type": "application/json",
                Accept: "application/json"
            }
        })
    ]);

    // Validate both responses
    if (!bnbPriceRes.ok) throw new Error(`Failed to fetch BNB price: ${bnbPriceRes.status}`);
    if (!tokenRes.ok) throw new Error(`Failed to fetch token details: ${tokenRes.status}`);

    const [bnbPriceJson, tokenJson] = await Promise.all([bnbPriceRes.json(), tokenRes.json()]);

    const BNB_TO_USDT = parseFloat(bnbPriceJson.price);
    const token = tokenJson.data?.[0];

    if (!token || !token.tokenPrice?.price) {
        return {
            content: [{ type: "text", text: "Token not found or price unavailable." }],
            isError: true
        };
    }

    const bnbPrice = parseFloat(token.tokenPrice.price);
    const priceInUsdt = bnbPrice * BNB_TO_USDT;

    const data = {
        ...token,
        tokenPrice: {
            ...token.tokenPrice,
            priceInUsdt: priceInUsdt.toFixed(10)
        }
    };

    return data;
};

```

--------------------------------------------------------------------------------
/src/tools/transferNativeToken.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { parseEther } from "viem";
import { getAccount, walletClient } from "../config.js";
import { buildTxUrl, checkTransactionHash } from "../util.js";

export function registerTransferNativeToken(server: McpServer) {
  server.tool("Send_BNB", "💎Transfer native token (BNB), Before execution, check the wallet information first", {
      recipientAddress: z.string(),
      amount: z.string(),
    },
    async ({ recipientAddress, amount }) => {
      let txHash = undefined;
      try {

        const account = await getAccount();
        txHash = await walletClient(account).sendTransaction({
          to: recipientAddress as `0x${string}`,
          value: parseEther(amount),
        });

        const txUrl = await checkTransactionHash(txHash)
        
        return {
          content: [
            {
              type: "text",
              text: `Transaction sent successfully. ${txUrl}`,
              url: txUrl,
            },
          ],
        };
      } catch (error) {
        const errorMessage =
          error instanceof Error ? error.message : String(error);
        const txUrl = buildTxUrl(txHash);
        return {
          content: [
            {
              type: "text",
              text: `transaction failed: ${errorMessage}`,
              url: txUrl,
            },
          ],
          isError: true,
        };
      }
    }
  );
}

```

--------------------------------------------------------------------------------
/src/tools/pancakeSwap.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { pancakeSwap } from "../functions/pancakeSwapTool.js";
import { getAccount, publicClient } from "../config.js";
import { buildTxUrl, checkTransactionHash } from "../util.js";

export function registerPancakeSwap(server: McpServer) {
  server.tool("PancakeSwap_Token_Exchange", "💱Exchange tokens on BNBChain using PancakeSwap DEX", {
      inputToken: z.string(),
      outputToken: z.string(),
      amount: z.string(),
    },
    async ({ inputToken, outputToken, amount }) => {
      let txHash = undefined;
      try {
        const account = await getAccount();
        txHash = await pancakeSwap({
          account,
          inputToken,
          outputToken,
          amount,
        });
        const txUrl = await checkTransactionHash(txHash)
        
        return {
          content: [
            {
              type: "text",
              text: `PancakeSwap transaction sent successfully. ${txUrl}`,
              url: txUrl,
            },
          ],
        };
      } catch (error) {
        const errorMessage =
          error instanceof Error ? error.message : String(error);
        const txUrl = buildTxUrl(txHash);
        return {
          content: [
            {
              type: "text",
              text: `transaction failed: ${errorMessage}`,
              url: txUrl,
            },
          ],
          isError: true,
        };
      }
    }
  );
}

```

--------------------------------------------------------------------------------
/src/tools/pancakeRemovePosition.ts:
--------------------------------------------------------------------------------

```typescript

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { removeLiquidityV3 } from "../functions/pancakeRemoveLiquidityTool.js";
import { getAccount } from "../config.js";
import { buildTxUrl, checkTransactionHash } from "../util.js";

export function registerPancakeRemovePosition(server: McpServer) {

    server.tool("Remove_PancakeSwap_Liquidity", "🔄Withdraw your liquidity from PancakeSwap pools", {
            positionId: z.string(),
            percent: z.number().max(100).min(1),
        },
        async ({ positionId, percent }) => {

            let txHash = undefined;
            try {

                const account = await getAccount();
                txHash = await removeLiquidityV3(account, BigInt(positionId), percent);
                const txUrl = await checkTransactionHash(txHash)
                
                return {
                    content: [
                        {
                            type: "text",
                            text: `remove liquidity position on panceke successfully. ${txUrl}`,
                            url: txUrl,
                        },
                    ],
                };
            } catch (error) {
                const errorMessage =
                  error instanceof Error ? error.message : String(error);
                const txUrl = buildTxUrl(txHash);
                return {
                  content: [
                    {
                      type: "text",
                      text: `transaction failed: ${errorMessage}`,
                      url: txUrl,
                    },
                  ],
                  isError: true,
                };
            }
        }
    );
}
```

--------------------------------------------------------------------------------
/src/tools/goplusSecurityCheck.ts:
--------------------------------------------------------------------------------

```typescript
// @ts-ignore
import { GoPlus, ErrorCode } from "@goplus/sdk-node";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { ChainId } from "@pancakeswap/sdk";
import { z } from "zod";
import { sanitizeData } from "../responseUtils";

export function registerGoplusSecurityCheck(server: McpServer) {
  server.tool("Token_Security_Check", "🔒Analyze BNBChain tokens for potential security risks powered by GoPlus", {
      tokenAddress: z.string(),
    },
    async ({ tokenAddress }) => {
      try {
        const chainId = ChainId.BSC.toString(); // BSC chain ID
        const addresses = [tokenAddress];

        // Call GoPlus API to check token security
        let res = await (GoPlus as any).tokenSecurity(chainId, addresses, 30);

        if (res.code !== (ErrorCode as any).SUCCESS) {
          return {
            content: [
              {
                type: "text",
                text: `Security check failed: ${res.message}`,
              },
            ],
            isError: true,
          };
        }
        

        const securityData = res.result[tokenAddress] || {};

        const sanitizedData = sanitizeData(securityData, {
          strictMode: true,
          maxLength: 200,
          allowMarkdown: false
        });
        return {
          content: [
            {
              type: "text",
              text: `Security check successful for ${tokenAddress}: ${JSON.stringify(
                sanitizedData,
                null,
                2
              )}`,
            },
          ],
        };
      } catch (error) {
        const errorMessage =
          error instanceof Error ? error.message : String(error);
        return {
          content: [
            {
              type: "text",
              text: `Security check failed: ${errorMessage}`,
            },
          ],
          isError: true,
        };
      }
    }
  );
}

```

--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import dotenv from "dotenv";
dotenv.config();

// Import tool registrations
import { registerTransferNativeToken } from "./tools/transferNativeToken.js";
import { registerTransferBEP20Token } from "./tools/transferBEP20Token.js";
import { registerPancakeSwap } from "./tools/pancakeSwap.js";
import { registerGetWalletInfo } from "./tools/getWalletInfo.js";
import { registerBuyMemeToken } from "./tools/buyMemeToken.js";
import { registerSellMemeToken } from "./tools/sellMemeToken.js";
import { registerPancakeAddLiquidity } from "./tools/pancakeAddLiquidity.js";
import { registerPancakeMyPosition } from "./tools/pancakeMyPosition.js";
import { registerPancakeRemovePosition } from "./tools/pancakeRemovePosition.js";
import { registerGoplusSecurityCheck } from "./tools/goplusSecurityCheck.js";
import { registerQueryMemeTokenDetails } from "./tools/queryMemeTokenDetails.js";

// Main server entry
export async function main() {
    const server = new McpServer({
        name: "bsc-mcp",
        version: "1.0.0"
    });

    // Register all tools
    registerTransferNativeToken(server);
    registerTransferBEP20Token(server);
    registerPancakeSwap(server);
    registerGetWalletInfo(server);
    registerBuyMemeToken(server);
    registerSellMemeToken(server);
    registerPancakeAddLiquidity(server);
    registerPancakeMyPosition(server);
    registerPancakeRemovePosition(server);
    registerGoplusSecurityCheck(server);
    registerQueryMemeTokenDetails(server);

    const transport = new StdioServerTransport();

    transport.onmessage = (message /** @type {JSONRPCMessage} */) => {
        console.log("📩 Received message:", JSON.stringify(message, null, 2));
    };

    transport.onerror = (error) => {
        console.error("🚨 Transport error:", error);
    };

    await server.connect(transport);
}

```

--------------------------------------------------------------------------------
/src/tools/transferBEP20Token.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { parseUnits, getContract, Address, publicActions } from "viem"; 
import { bep20abi } from "../lib/bep20Abi.js";
import { getAccount, walletClient } from "../config.js";
import { buildTxUrl, checkTransactionHash } from "../util.js";

export function registerTransferBEP20Token(server: McpServer) {
  server.tool("Send_BEP20_Token", "📤Send any BEP-20 token to another wallet (requires wallet check first)", {
      recipientAddress: z.string(),
      amount: z.string(),
      address: z.string(),
    },
    async ({ recipientAddress, amount, address }) => {
      let txHash = undefined;
      try {
        // Get token details including address and decimals

        const account = await getAccount();
        const client = walletClient(account).extend(publicActions)

        const contract = getContract({
          address: address as Address,
          abi: bep20abi,
          client,
        });

        const decimals = await contract.read.decimals();
        // Parse the amount based on token decimals
        const parsedAmount = parseUnits(amount, decimals);

        txHash = await contract.write.transfer([
          `0x${recipientAddress.replace("0x", "")}`,
          parsedAmount,
        ], {
          gas: BigInt(100000),
        });
        const txUrl = await checkTransactionHash(txHash)
        

        return {
          content: [
            {
              type: "text",
              text: `BEP-20 token transfer sent successfully. ${txUrl}`,
              url: txUrl,
            },
          ],
        };
      } catch (error) {
        const errorMessage =
          error instanceof Error ? error.message : String(error);
        const txUrl = buildTxUrl(txHash);
        return {
          content: [
            {
              type: "text",
              text: `transaction failed: ${errorMessage}`,
              url: txUrl,
            },
          ],
          isError: true,
        };
      }
    }
  );
}

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "bnbchain-mcp",
  "version": "1.0.12",
  "main": "index.js",
  "type": "module",
  "bin": {
    "bnbchain-mcp": "build/index.js"
  },
  "scripts": {
    "test": "node test/testServer.js",
    "test:privateAES": "npx jest src/test/privateAES.test.ts -t \"test\"",
    "start": "node build/index.js",
    "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
    "init": "node ./build/init.js",
    "init:build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\" && node ./build/init.js",
    "publish:auto": "node scripts/publish.cjs"
  },
  "files": [
    "build"
  ],
  "keywords": [
    "binance-smart-chain",
    "bsc",
    "blockchain",
    "web3",
    "cryptocurrency",
    "defi",
    "model-context-protocol",
    "mcp",
    "claude-integration",
    "ai-agents",
    "token-transfers",
    "smart-contracts",
    "pancakeswap",
    "bep20",
    "swap",
    "liquidity",
    "wallet-management",
    "blockchain-tools",
    "crypto-cli",
    "defi-tools",
    "meme-tokens",
    "token-deployment",
    "blockchain-development",
    "web3-infrastructure",
    "crypto-automation",
    "token-security",
    "moralis-integration",
    "bnb-chain"
  ],
  "author": "",
  "license": "MIT",
  "description": "",
  "devDependencies": {
    "@types/bcrypt": "^5.0.2",
    "@types/jest": "^29.5.14",
    "@types/figlet": "^1.7.0",
    "@types/fs-extra": "^11.0.4",
    "@types/node": "^22.10.0",
    "@types/prompts": "^2.4.9",
    "jest": "^29.7.0",
    "ts-jest": "^29.3.0",
    "typescript": "^5.8.2"
  },
  "dependencies": {
    "@goplus/sdk-node": "^1.0.12",
    "@modelcontextprotocol/sdk": "^1.4.0",
    "@pancakeswap/sdk": "^5.8.8",
    "@pancakeswap/smart-router": "6.1.6",
    "@pancakeswap/tokens": "^0.6.24",
    "@pancakeswap/v3-sdk": "^3.9.0",
    "bcrypt": "^5.1.1",
    "chalk": "^5.4.1",
    "dotenv": "^16.4.7",
    "figlet": "^1.8.0",
    "fs-extra": "^11.3.0",
    "graphql-request": "^7.1.2",
    "moralis": "^2.27.2",
    "ora": "^8.2.0",
    "prompts": "^2.4.2",
    "ts-node-dev": "^2.0.0",
    "viem": "^2.23.11"
  }
}

```

--------------------------------------------------------------------------------
/src/tools/pancakeAddLiquidity.ts:
--------------------------------------------------------------------------------

```typescript

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import {
    parseUnits,
} from "viem";
import { addLiquidityV3 } from "../functions/pancakeAddLiquidityTool.js";
import { CurrencyAmount, } from "@pancakeswap/sdk";
import {
    FeeAmount
} from '@pancakeswap/v3-sdk';
import { getToken } from "../functions/pancakeSwapTool.js";
import { getAccount, } from "../config.js";
import { buildTxUrl, checkTransactionHash } from "../util.js";

export function registerPancakeAddLiquidity(server: McpServer) {

    server.tool("Add_PancakeSwap_Liquidity", "💧Provide liquidity to PancakeSwap trading pairs", {
            token0: z.string(),
            token1: z.string(),
            token0Amount: z.string(),
            token1Amount: z.string(),
        },
        async ({ token0, token1, token0Amount, token1Amount }) => {

            let txHash = undefined;
            try {
            
                // Define tokens
                const tokenA = await getToken(token0);
                const tokenB = await getToken(token1);
            
                // Amounts to add
                const amountTokenA = CurrencyAmount.fromRawAmount(tokenA, parseUnits(token0Amount, tokenA.decimals).toString());
                const amountTokenB = CurrencyAmount.fromRawAmount(tokenB, parseUnits(token1Amount, tokenB.decimals).toString());
            
                const account = await getAccount();

                txHash = await addLiquidityV3(
                    tokenA,
                    tokenB,
                    FeeAmount.MEDIUM, // 0.3%
                    amountTokenA,
                    amountTokenB,
                    account,
                );

                const txUrl = await checkTransactionHash(txHash)
                return {
                    content: [
                        {
                            type: "text",
                            text: `add liquidity to pancake successfully. ${txUrl}`,
                            url: txUrl,
                        },
                    ],
                };
            } catch (error) {
                const errorMessage =
                    error instanceof Error ? error.message : String(error);
                const txUrl = buildTxUrl(txHash);
                return {
                    content: [
                        {
                            type: "text",
                            text: `transaction failed: ${errorMessage}`,
                            url: txUrl,
                        },
                    ],
                    isError: true,
                };
            }
        }
    );
}
```

--------------------------------------------------------------------------------
/scripts/publish.cjs:
--------------------------------------------------------------------------------

```
#!/usr/bin/env node

const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');

// Logging helpers
const log = (msg) => console.log(`\n🟡 ${msg}`);
const success = (msg) => console.log(`✅ ${msg}`);
const error = (msg) => {
    console.error(`❌ ${msg}`);
    process.exit(1);
};

// Paths
const pkgPath = path.resolve('package.json');
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
const branch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
const args = process.argv.slice(2);

// Run shell command
const run = (cmd) => {
    log(`Running: ${cmd}`);
    execSync(cmd, { stdio: 'inherit' });
};

// Auto-increment patch version
const bumpPatchVersion = (version) => {
    const [major, minor, patch] = version.split('.').map(Number);
    return `${major}.${minor}.${patch + 1}`;
};

// Swap README for NPM
const swapReadmeForNpm = () => {
    const npmReadme = path.resolve('README.npm.md');
    const targetReadme = path.resolve('README.md');

    if (fs.existsSync(npmReadme)) {
        fs.copyFileSync(targetReadme, path.resolve('README.github.md'));
        fs.copyFileSync(npmReadme, targetReadme);
        success('Swapped README.npm.md → README.md for NPM publish');
    } else {
        log('No README.npm.md found. Using default README.md');
    }
};

// Restore original README
const restoreReadme = () => {
    const ghReadme = path.resolve('README.github.md');
    const targetReadme = path.resolve('README.md');

    if (fs.existsSync(ghReadme)) {
        fs.copyFileSync(ghReadme, targetReadme);
        fs.unlinkSync(ghReadme);
        success('Restored original GitHub README.md');
    }
};

// Main flow
const main = () => {
    console.log(`📍 Current Git Branch: ${branch}`);

    if (!fs.existsSync('build/index.js')) {
        error('Build not found. Run `npm run build` first.');
    }

    // Check for version override
    const versionFlagIndex = args.indexOf('--version');
    let newVersion = '';

    if (versionFlagIndex !== -1 && args[versionFlagIndex + 1]) {
        newVersion = args[versionFlagIndex + 1];
        success(`Custom version passed via CLI: ${newVersion}`);
    } else {
        newVersion = bumpPatchVersion(pkg.version);
        success(`Auto-incremented patch version: ${pkg.version} → ${newVersion}`);
    }

    pkg.version = newVersion;
    fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));

    swapReadmeForNpm();

    run('npm run build');

    const tag = branch === 'develop' ? '--tag alpha' : '';
    run(`npm publish ${tag}`);

    restoreReadme();

    success(`Published ${pkg.name}@${newVersion} to NPM (${tag || 'latest'})`);
};

main();

```

--------------------------------------------------------------------------------
/src/PrivateAES.ts:
--------------------------------------------------------------------------------

```typescript
import * as crypto from 'crypto';
import * as bcrypt from 'bcrypt';

const algorithm = 'aes-256-gcm';

/**
 * Encrypts an EVM private key using bcrypt and user password
 * @param privateKey The EVM private key to encrypt
 * @param password User-provided password
 * @returns Encrypted string (Base64 encoded)
 */
export async function encryptPrivateKey(privateKey: string, password: string): Promise<string> {
  // bcrypt work factor - higher is more secure but slower
  const saltRounds = 12;
  
  // Generate bcrypt hash as key material
  const hashedPassword = await bcrypt.hash(password, saltRounds);
  
  // Extract bcrypt salt - it's in the first 29 characters of the hash
  const bcryptSalt = hashedPassword.substring(0, 29);
  
  // Generate symmetric encryption key using SHA-256 from bcrypt hash
  const key = crypto.createHash('sha256').update(hashedPassword).digest();
  
  // Generate random initialization vector
  const iv = crypto.randomBytes(12);
  
  // Create cipher object
  const cipher = crypto.createCipheriv(algorithm, key, iv);
  
  // Encrypt private key
  let encrypted = cipher.update(privateKey, 'utf8', 'base64');
  encrypted += cipher.final('base64');
  
  // Get authentication tag
  const authTag = cipher.getAuthTag();
  
  // Concatenate all necessary components and encode as Base64 string
  const result = Buffer.concat([
    Buffer.from(bcryptSalt), // Store bcrypt salt
    iv,
    authTag,
    Buffer.from(encrypted, 'base64')
  ]);
  
  return result.toString('base64');
}

/**
 * Decrypts an EVM private key using bcrypt and user password
 * @param encryptedData Encrypted private key data (Base64 encoded)
 * @param password User-provided password
 * @returns Decrypted private key or null (if password is incorrect)
 */
export async function decryptPrivateKey(encryptedData: string, password: string): Promise<string | null> {
  try {
    // Decode Base64
    const data = Buffer.from(encryptedData, 'base64');
    
    // Extract components
    const bcryptSalt = data.subarray(0, 29).toString();
    const iv = data.subarray(29, 41);
    const authTag = data.subarray(41, 57);
    const encrypted = data.subarray(57).toString('base64');
    
    // Use bcrypt to check password
    // Reconstruct salt and password to create the same hash as during encryption
    const hashedPassword = await bcrypt.hash(password, bcryptSalt);
    
    // Generate key using the same method from the hash
    const key = crypto.createHash('sha256').update(hashedPassword).digest();
    
    // Create decipher
    const decipher = crypto.createDecipheriv(algorithm, key, iv);
    decipher.setAuthTag(authTag);
    
    // Decrypt
    let decrypted = decipher.update(encrypted, 'base64', 'utf8');
    decrypted += decipher.final('utf8');
    
    return decrypted;
  } catch (error) {
    // Decryption failed (incorrect password or data corruption)
    return null;
  }
}

```

--------------------------------------------------------------------------------
/src/tools/sellMemeToken.ts:
--------------------------------------------------------------------------------

```typescript

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import {
    parseUnits,
    type Hex,
} from "viem";
import { getAccount, publicClient, walletClient } from "../config.js";
import { AddressConfig } from "../addressConfig.js";
import { buildTxUrl, checkTransactionHash } from "../util.js";

const tokenAbi = [
    { "inputs": [{ "internalType": "address", "name": "owner", "type": "address" }, { "internalType": "address", "name": "spender", "type": "address" }], "name": "allowance", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" },
    { "inputs": [{ "internalType": "address", "name": "spender", "type": "address" }, { "internalType": "uint256", "name": "amount", "type": "uint256" }], "name": "approve", "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], "stateMutability": "nonpayable", "type": "function" },

]

export function registerSellMemeToken(server: McpServer) {

    server.tool("Sell_Meme_Token", "💰Sell meme tokens for other currencies", {
            token: z.string(),
            tokenValue: z.string(),
        },
        async ({ token, tokenValue }) => {

            let txHash = undefined;
            try {

                const account = await getAccount();
                const allowanceAmount = await publicClient.readContract({
                    address: token as Hex,
                    abi: tokenAbi,
                    functionName: 'allowance',
                    args: [account.address, AddressConfig.FourMemeSellTokenAMAPContract],
                }) as bigint;
                if (allowanceAmount < parseUnits(tokenValue, 18)) {

                    const hash = await walletClient(account).writeContract({
                        account,
                        address: token as Hex,
                        abi: tokenAbi,
                        functionName: 'approve',
                        args: [AddressConfig.FourMemeSellTokenAMAPContract, parseUnits(tokenValue, 18)],
                    });

                    await publicClient.waitForTransactionReceipt({
                        hash: hash,
                        retryCount: 300,
                        retryDelay: 100,
                    });
                }


                txHash = await walletClient(account).writeContract({
                    account,
                    address: AddressConfig.FourMemeSellTokenAMAPContract,
                    abi: [{
                        "inputs": [
                            {
                                "internalType": "address",
                                "name": "token",
                                "type": "address"
                            },
                            {
                                "internalType": "uint256",
                                "name": "amount",
                                "type": "uint256"
                            }
                        ],
                        "name": "sellToken",
                        "outputs": [],
                        "stateMutability": "nonpayable",
                        "type": "function"
                    }],
                    functionName: 'sellToken',
                    args: [token as Hex, parseUnits(tokenValue, 18)],
                });

                const txUrl = await checkTransactionHash(txHash)
        
                return {
                    content: [
                        {
                            type: "text",
                            text: `sell meme token successfully. ${txUrl}`,
                            url: txUrl,
                        },
                    ],
                };
            } catch (error) {
                const errorMessage =
                  error instanceof Error ? error.message : String(error);
                const txUrl = buildTxUrl(txHash);
                return {
                  content: [
                    {
                      type: "text",
                      text: `transaction failed: ${errorMessage}`,
                      url: txUrl,
                    },
                  ],
                  isError: true,
                };
            }
        }
    );
}
```

--------------------------------------------------------------------------------
/src/config.ts:
--------------------------------------------------------------------------------

```typescript
import { Hex, http, publicActions, createWalletClient, createPublicClient, PrivateKeyAccount } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { bsc } from "viem/chains";
import { getPassword, } from "./util.js";
import { decryptPrivateKey, } from "./PrivateAES.js";

export const rpcUrl = process.env.BSC_RPC_URL || "https://bsc-dataseed.binance.org";

class ObfuscatedSecureBuffer {
    private buffers: Uint8Array[] = [];
    private indexMap: number[] = [];
    private salt: Uint8Array = new Uint8Array(32);
    private length: number = 0;
    private isActive = false;

    constructor() {
    }
    updata(data: Uint8Array | string) {
        let originalData: Uint8Array;
        if (typeof data === 'string') {
            const hexString = data.startsWith('0x') ? data.slice(2) : data;
            originalData = new Uint8Array(hexString.length / 2);
            for (let i = 0; i < hexString.length; i += 2) {
                originalData[i / 2] = parseInt(hexString.substr(i, 2), 16);
            }
        } else {
            originalData = new Uint8Array(data);
        }

        this.length = originalData.length;

        this.salt = new Uint8Array(32);
        crypto.getRandomValues(this.salt);

        const bufferCount = 3;
        this.buffers = [];

        for (let i = 0; i < bufferCount; i++) {
            const buffer = new Uint8Array(this.length);
            crypto.getRandomValues(buffer);
            this.buffers.push(buffer);
        }

        this.indexMap = Array.from({ length: this.length }, (_, i) => i)
            .sort(() => 0.5 - Math.random());

        for (let i = 0; i < this.length; i++) {
            const targetIndex = this.indexMap[i];
            const bufferIndex = i % bufferCount;

            const saltByte = this.salt[i % this.salt.length];
            const obfuscatedByte = originalData[i] ^ saltByte ^ (i & 0xff);

            this.buffers[bufferIndex][targetIndex] = obfuscatedByte;
        }
        this.isActive = true;
    }

    getData(): Uint8Array {
        const result = new Uint8Array(this.length);
        const bufferCount = this.buffers.length;

        for (let i = 0; i < this.length; i++) {
            const targetIndex = this.indexMap[i];
            const bufferIndex = i % bufferCount;

            const saltByte = this.salt[i % this.salt.length];
            const obfuscatedByte = this.buffers[bufferIndex][targetIndex];
            result[i] = obfuscatedByte ^ saltByte ^ (i & 0xff);
        }

        return result;
    }

    getHexString(): string {
        const data = this.getData();
        return '0x' + Array.from(data)
            .map(b => b.toString(16).padStart(2, '0'))
            .join('');
    }

    zeroize(): void {
        for (const buffer of this.buffers) {
            buffer.fill(0);
            crypto.getRandomValues(buffer);
            buffer.fill(0xff);
            buffer.fill(0);
        }

        this.salt.fill(0);
        this.indexMap.fill(0);

        this.buffers = [];
        this.indexMap = [];

        this.isActive = false;
    }

    active(): boolean {
        return this.isActive;
    }
}


let obfuscatedPrivateKey = new ObfuscatedSecureBuffer();;
export const getAccount = async () => {
    const BSC_WALLET_PRIVATE_KEY = process.env.BSC_WALLET_PRIVATE_KEY as Hex
    if (!BSC_WALLET_PRIVATE_KEY) {
        throw new Error("BSC_WALLET_PRIVATE_KEY is not defined");
    }
    if (obfuscatedPrivateKey.active()) {
        const pk = obfuscatedPrivateKey.getHexString();
        return privateKeyToAccount(
            pk as Hex
        );
    }

    const { agreed, value: password } = await getPassword()
    if (!password) {
        throw new Error("You did not enter a password.");
    }

    const pk = await decryptPrivateKey(BSC_WALLET_PRIVATE_KEY, password);

    if (agreed) {

        obfuscatedPrivateKey.updata(pk as string);
        setTimeout(() => {
            obfuscatedPrivateKey.zeroize();
        }, 1000 * 60 * 60);
        return privateKeyToAccount(
            pk as Hex
        );
    } else {

        return privateKeyToAccount(
            pk as Hex
        );
    }

};

export const getClient = async () => {
    const account = await getAccount()
    const client = createWalletClient({
        account,
        chain: bsc,
        transport: http(rpcUrl),
    }).extend(publicActions);

    return client;
};

export const publicClient = createPublicClient({
    chain: bsc,
    transport: http(rpcUrl),
});

export const walletClient = (account: PrivateKeyAccount) => createWalletClient({
    chain: bsc,
    transport: http(rpcUrl),
    account: account,
});
```

--------------------------------------------------------------------------------
/src/responseUtils.ts:
--------------------------------------------------------------------------------

```typescript

/**
 * Clean the string, remove or escape characters and patterns that may cause prompt injection
 * 
 * @param input The input string to be cleaned
 * @param options Cleaning options
 * @returns Cleaned safe string
 */
export function sanitizeString(
    input: string, 
    options: {
      maxLength?: number,         // Maximum allowed length
      strictMode?: boolean,       // Strict mode (more aggressive filtering)
      allowMarkdown?: boolean,    // Whether to allow markdown syntax
      escapeQuotes?: boolean      // Whether to escape quotes instead of removing
    } = {}
  ): string {
    // Set default values
    const {
      maxLength = 500,
      strictMode = true,
      allowMarkdown = false,
      escapeQuotes = true
    } = options;
    
    if (!input || typeof input !== 'string') {
      return '';
    }
    
    let sanitized = input;
    
    // 1. Remove possible code blocks and formatted text
    if (!allowMarkdown) {
      // Remove code blocks
      sanitized = sanitized.replace(/```[\s\S]*?```/g, "[Code block removed]");
      // Remove inline code
      sanitized = sanitized.replace(/`[^`]*`/g, "[Code removed]");
    }
    
    // 2. Handle possible closing symbols and instruction patterns
    
    // Handle HTML/XML tags
    sanitized = sanitized.replace(/<[^>]*>/g, "");
    
    // Handle various bracket pairs
    sanitized = sanitized.replace(/\{[\s\S]*?\}/g, "[Content filtered]"); // Curly brackets
    sanitized = sanitized.replace(/\[[\s\S]*?\]/g, "[Content filtered]"); // Square brackets
    sanitized = sanitized.replace(/\([\s\S]*?\)/g, "[Content filtered]"); // Parentheses
    
    // 3. Handle potential instruction keywords
    const aiKeywords = [
      "system", "user", "assistant", "model", "prompt", "instruction", 
      "context", "token", "function", "completion", "response", "davinci", 
      "claude", "gpt", "llm", "api", "openai", "anthropic"
    ];
    
    const keywordPattern = new RegExp(`\\b(${aiKeywords.join('|')})\\b`, 'gi');
    sanitized = sanitized.replace(keywordPattern, (match) => `_${match}_`);
    
    // 4. Handle quotes (escape or remove)
    if (escapeQuotes) {
      // Escape quotes
      sanitized = sanitized.replace(/"/g, '\\"').replace(/'/g, "\\'");
    } else {
      // Remove quotes
      sanitized = sanitized.replace(/["']/g, "");
    }
    
    // 5. Additional processing in strict mode
    if (strictMode) {
      // Remove all possible control characters and special characters
      sanitized = sanitized.replace(/[\u0000-\u001F\u007F-\u009F\u2000-\u200F\u2028-\u202F]/g, "");
      
      // Handle possible injection separators and special patterns
      sanitized = sanitized.replace(/\.\.\./g, "…"); // Ellipsis
      sanitized = sanitized.replace(/\-\-\-+/g, "—"); // Em dash
      sanitized = sanitized.replace(/={2,}/g, "==");  // Equal sign sequence
      
      // Handle URL and link patterns
      sanitized = sanitized.replace(/(https?:\/\/[^\s]+)/g, "[Link removed]");
    }
    
    // 6. Handle possible JSON structure markers
    sanitized = sanitized
      .replace(/(\s*"\w+"\s*:)/g, "【Property】:")  // JSON property name
      .replace(/(\[\s*\]|\{\s*\})/g, "【Empty】"); // Empty array or object
    
    // 7. Limit length
    if (sanitized.length > maxLength) {
      sanitized = sanitized.substring(0, maxLength) + "...";
    }
    
    return sanitized;
  }
  
  /**
   * Recursively process the object, applying the sanitizeString function to all string values
   * 
   * @param data The data object to be processed
   * @param options Options to be passed to sanitizeString
   * @param maxDepth Maximum recursion depth
   * @param currentDepth Current recursion depth
   * @returns Processed safe data object
   */
  export function sanitizeData(
    data: any,
    options = {},
    maxDepth = 5,
    currentDepth = 0
  ): any {
    // Handle recursion depth limit
    if (currentDepth >= maxDepth) {
      return typeof data === 'object' && data !== null 
        ? "[Nested object simplified]" 
        : data;
    }
    
    // Basic types return directly
    if (data === null || data === undefined) {
      return data;
    }
    
    // Handle string
    if (typeof data === 'string') {
      return sanitizeString(data, options);
    }
    
    // Handle numbers and booleans
    if (typeof data === 'number' || typeof data === 'boolean') {
      return data;
    }
    
    // Handle array
    if (Array.isArray(data)) {
      return data.map(item => sanitizeData(item, options, maxDepth, currentDepth + 1));
    }
    
    // Handle object
    if (typeof data === 'object') {
      const result: Record<string, any> = {};
      for (const key in data) {
        if (Object.prototype.hasOwnProperty.call(data, key)) {
          // Also clean the property name
          const safeKey = key.replace(/[<>{}\[\]]/g, "");
          result[safeKey] = sanitizeData(data[key], options, maxDepth, currentDepth + 1);
        }
      }
      return result;
    }
    
    // Other types
    return String(data);
  }
```

--------------------------------------------------------------------------------
/src/functions/pancakeSwapTool.ts:
--------------------------------------------------------------------------------

```typescript
import {
  Hash,
  getContract,
  Hex,
  parseUnits,
  isAddress,
  PrivateKeyAccount,
  Address,
} from "viem";
import { erc20Abi, hexToBigInt, maxUint256 } from "viem";
import {
  ChainId,
  CurrencyAmount,
  Native,
  Percent,
  Token,
  TradeType,
} from "@pancakeswap/sdk";
import {
  SMART_ROUTER_ADDRESSES,
  SmartRouter,
  SmartRouterTrade,
  SwapRouter,
} from "@pancakeswap/smart-router";
import { GraphQLClient } from "graphql-request";
import { publicClient, walletClient } from "../config.js";
import { bep20abi } from "../lib/bep20Abi.js";


export const getToken = async (
  token: string,
) => {
  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: any) => 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: any) => item.address.toLowerCase() === address)
    if (!tokenInfo) {
      
      const contract = getContract({
        address: address as Address,
        abi: bep20abi,
        client: publicClient,
      });

      decimal = await contract.read.decimals();
      symbol = await contract.read.symbol();
    } else {
        
      decimal = tokenInfo.decimals
      symbol = tokenInfo.symbol
    }
  }
  
  return new Token(
    ChainId.BSC,
    address as Hex,
    decimal,
    symbol,
  )


}

export const pancakeSwap = async ({
  account,
  inputToken,
  outputToken,
  amount,
}: {
  // privateKey: string;
  amount: string;
  inputToken: string;
  outputToken: string;
  account: PrivateKeyAccount;
}): Promise<Hash> => {

  const chainId = ChainId.BSC


  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(account),
        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[ChainId.BSC]
    const allowance = await TokenContract.read.allowance([account.address, smartRouterAddress]) as bigint
    if (allowance < parseAmountIn) {
      const approveResult = await TokenContract.write.approve([smartRouterAddress, parseAmountIn])

      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,
    
  }) as SmartRouterTrade<TradeType>


  const { value, calldata } = SwapRouter.swapCallParameters(trade, {
    recipient: account.address,
    slippageTolerance: new Percent(500, 10000),
  })

  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: bigint, margin = 1000n): bigint => {
    return (value * (10000n + margin)) / 10000n;
  };

  const txHash = await walletClient(account).sendTransaction({
    account: account,
    chainId,
    // @ts-ignore
    to: SMART_ROUTER_ADDRESSES[chainId],
    data: calldata,
    value: hexToBigInt(value),
    gas: calculateGasMargin(gasEstimate),
  });
  return txHash;
};









```

--------------------------------------------------------------------------------
/src/init.ts:
--------------------------------------------------------------------------------

```typescript
import prompts, { PromptObject } from 'prompts';
import figlet from 'figlet';
import chalk from 'chalk';
import path from 'path';
import fs from 'fs-extra';
import os from 'os';
import { fileURLToPath } from 'url';
import { encryptPrivateKey } from './PrivateAES.js';

import dotenv from "dotenv";
import { Hex } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
dotenv.config();

// Binance Gold Color
const yellow = chalk.hex('#F0B90B');

// ESModule __dirname workaround
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// Cancel handler
const onCancel = () => {
    console.log(chalk.red('\n❌ Configuration cancelled by user (Ctrl+C or ESC). Exiting...'));
    process.exit(0);
};

// Show Banner
const showBanner = () => {
    const banner = figlet.textSync('BNB Chain MCP ', { font: 'Big' });
    console.log(yellow(banner));
    console.log(yellow('🚀 Welcome to the BNB Chain MCP Configurator\n'));
};

// User Input Types
interface UserInputs {
    walletPassword: string;
    privateKey: string;
    rpcUrl?: string;
}
function validatePassword(password: string) {
    // At least 8 characters
    if (password.trim() === '') return 'Wallet Password is required!';
    if (password.length < 8 || password.length > 128) return 'Wallet Password must be between 8 and 128 characters!';
    
    // Check if it contains at least one lowercase letter
    if (!/[a-z]/.test(password)) return 'Wallet Password must contain at least one lowercase letter!';
    
    // Check if it contains at least one uppercase letter
    if (!/[A-Z]/.test(password)) return 'Wallet Password must contain at least one uppercase letter!';
    
    // Check if it contains at least one number
    if (!/[0-9]/.test(password)) return 'Wallet Password must contain at least one number!';
    
    // Check if it contains at least one special character
    if (!/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)) return 'Wallet Password must contain at least one special character! (!@#$%^&*()_+-=[]{};\':\\"|,.<>\/?)';
    
    return true;
}
// Ask for credentials
const getInputs = async (): Promise<UserInputs> => {
    const questions: PromptObject[] = [
        {
            type: 'password',
            name: 'walletPassword',
            message: '🔐 Enter your Wallet Password (The password must be between 8 and 128 characters):',
            validate: (val: string) => {
                return validatePassword(val);
            },
        },
        {
            type: 'password',
            name: 'privateKey',
            message: '🔑 Enter your BNB Chain Wallet Private Key:',
            validate: (val: string) =>
                val.trim() === '' ? 'Private key is required!' : true,
        },
        {
            type: 'text',
            name: 'rpcUrl',
            message: '🌐 Enter your BNB Chain RPC URL (optional):',
        },
    ];

    return await prompts(questions, { onCancel }) as UserInputs;
};

// Generate .env file
const generateEnvFile = async (privateKey: string, address: string, rpcUrl?: string, ): Promise<void> => {
    const envContent = `
BSC_WALLET_PRIVATE_KEY=${privateKey}
BSC_WALLET_ADDRESS=${address}
BSC_RPC_URL=${rpcUrl || ''}
`.trim();

    await fs.writeFile('.env', envContent);
    console.log(yellow('✅ .env file generated.'));
};

// Generate config object
const generateConfig = async (privateKey: string, address: string, rpcUrl?: string, ): Promise<any> => {
    const indexPath = path.resolve(__dirname, '..', 'build', 'index.js'); // one level up from cli/

    return {
        'bsc-mcp': {
            command: 'node',
            args: [indexPath],
            env: {
                BSC_WALLET_PRIVATE_KEY: privateKey,
                BSC_WALLET_ADDRESS: address,
                BSC_RPC_URL: rpcUrl || '',
            },
            disabled: false,
            autoApprove: []
        }
    };
};

// Configure Claude Desktop
const configureClaude = async (config: object): Promise<boolean> => {
    const userHome = os.homedir();
    let claudePath;
    const platform = os.platform();
    if (platform == "darwin") {
        claudePath = path.join(userHome, 'Library/Application Support/Claude/claude_desktop_config.json');
    } else if (platform == "win32") {
        claudePath = path.join(userHome, 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json');
    } else {
        console.log(chalk.red('❌ Unsupported platform.'));
        return false;
    }
    
    if (!fs.existsSync(claudePath)) {
        console.log(chalk.yellow('⚠️ Claude config file not found. Creating a new one with default configuration.'));
        // Create a default configuration object
        const defaultConfig = {
            mcpServers: {}
        };
        // Write the default configuration to the file
        await fs.writeJSON(claudePath, defaultConfig, { spaces: 2 });
    }

    
    const jsonData = fs.readFileSync(claudePath, 'utf8');
    const data = JSON.parse(jsonData);
    
    data.mcpServers = {
        ...data.mcpServers,
        ...config,
    };
    
    await fs.writeJSON(claudePath, data, { spaces: 2 });
    console.log(yellow('✅ BNB Chain MCP configured for Claude Desktop. Please RESTART your Claude to enjoy it 🎉'));
    return true;
};

// Save fallback config file
const saveFallbackConfig = async (config: object): Promise<void> => {
    await fs.writeJSON('config.json', config, { spaces: 2 });
    console.log(yellow('📁 Saved config.json in root project folder.'));
};

// Main logic
const init = async () => {
    showBanner();

    const { privateKey, rpcUrl, walletPassword } = await getInputs();
    const _0xPrivateKey = privateKey.startsWith('0x') ? privateKey : `0x${privateKey}`
    const account = privateKeyToAccount(
        _0xPrivateKey as Hex
    );

    const privateKeyEncrypt = await encryptPrivateKey(_0xPrivateKey, walletPassword);

    await generateEnvFile(privateKeyEncrypt, account.address, rpcUrl);

    const config = await generateConfig(privateKeyEncrypt, account.address, rpcUrl);

    const { setupClaude } = await prompts({
        type: 'confirm',
        name: 'setupClaude',
        message: '🧠 Do you want to configure in Claude Desktop?',
        initial: true
    }, { onCancel });

    if (setupClaude) {
        const success = await configureClaude(config);
        if (!success) {
            await saveFallbackConfig(config);
        }
    } else {
        await saveFallbackConfig(config);
    }
};

init(); 
```

--------------------------------------------------------------------------------
/src/functions/pancakeSwapPosition.ts:
--------------------------------------------------------------------------------

```typescript

import dotenv from 'dotenv';
dotenv.config();

import { TickMath, SqrtPriceMath } from '@pancakeswap/v3-sdk';
import {
    Address,
    PrivateKeyAccount,
    formatUnits,
    parseAbi,
} from "viem";
import { publicClient, } from '../config.js';



const POSITION_MANAGER_ADDRESS = '0x46A15B0b27311cedF172AB29E4f4766fbE7F4364' as Address;
const FACTORY_ADDRESS = '0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865' as Address;

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 decimals() external view returns (uint256)',
    'function symbol() external view returns (string)',
    'function name() external view returns (string)',
]);

const masterChefV3ABI = parseAbi([
    'function balanceOf(address account) external view returns (uint256)',
    'function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256)',
    'function positions(uint256) external view returns (uint96 nonce, address operator, address token0, address token1, uint24 fee, int24 tickLower, int24 tickUpper, uint128 liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, uint128 tokensOwed0, uint128 tokensOwed1)',
]);

export const myPosition = async (accountAddress: Address) => {


    const balance = await publicClient.readContract({
        abi: masterChefV3ABI,
        address: POSITION_MANAGER_ADDRESS,
        functionName: 'balanceOf',
        args: [accountAddress],
    })


    if (Number(balance) === 0) {
        return;
    }

    const nftCalls = []
    for (let i = 0; i < Number(balance); i++) {
        const nftCall = {
            abi: masterChefV3ABI,
            address: POSITION_MANAGER_ADDRESS,
            functionName: 'tokenOfOwnerByIndex',
            args: [accountAddress, BigInt(i)],
        }
        nftCalls.push(nftCall)
    }


    const nftIds = await publicClient.multicall<BigInt[]>({
        contracts: nftCalls,
        allowFailure: false,
    })


    const positionCalls = nftIds.map((nftId) => {
        return {
            abi: masterChefV3ABI,
            address: POSITION_MANAGER_ADDRESS,
            functionName: 'positions',
            args: [nftId],
        }
    })

    const positions = await publicClient.multicall<any[]>({
        contracts: positionCalls,
        allowFailure: false,
    }) as any[]

    const getTokenInfo = async (token: Address) => {
        const infoCalls = [
            {
                address: token,
                abi: ERC20_ABI,
                functionName: 'symbol',
                args: [],
            },
            {
                address: token,
                abi: ERC20_ABI,
                functionName: 'name',
                args: [],
            },
            {
                address: token,
                abi: ERC20_ABI,
                functionName: 'decimals',
                args: [],
            },
        ]

        const tokenInfo = await publicClient.multicall<any[]>({
            contracts: infoCalls,
            allowFailure: false,
        }) as any[]
        return {
            token,
            symbol: tokenInfo[0] as string,
            name: tokenInfo[1] as string,
            decimals: Number(tokenInfo[2]),
        }
    }

    const poolTokenInfos = await Promise.all(positions.map(async (position) => {
        const tokenInfos = await Promise.all([
            getTokenInfo(position[2] as Address),
            getTokenInfo(position[3] as Address),
        ]);
        return {
            token0: tokenInfos[0],
            token1: tokenInfos[1],
        }
    })) as any[]

    const poolCalls = []
    for (const position of positions) {
        const poolCall = {
            address: FACTORY_ADDRESS,
            abi: FACTORY_ABI,
            functionName: 'getPool',
            args: [
                position[2] as Address,
                position[3] as Address,
                BigInt(position[4])
            ]
        }
        poolCalls.push(poolCall);
    }

    const pools = await publicClient.multicall<any[]>({
        contracts: poolCalls,
        allowFailure: false,
    }) as any[]

    const slot0Calls = []
    for (const pool of pools) {
        const slot0Call = {
            address: pool as Address,
            abi: POOL_ABI,
            functionName: 'slot0',
        }
        slot0Calls.push(slot0Call);
    }
    const slot0s = await publicClient.multicall<any[]>({
        contracts: slot0Calls,
        allowFailure: false,
    }) as any[]

    const positionInfos = []
    for (let i = 0; i < pools.length; i++) {
        const positionInfo = {
            tickCurrent: slot0s[i][1],
            tickLower: positions[i][5],
            tickUpper: positions[i][6],
            liquidity: positions[i][7],
            ...poolTokenInfos[i],
            feeTier: positions[i][4],
            positionId: nftIds[i],
        }
        const sqrtPriceX96 = TickMath.getSqrtRatioAtTick(positionInfo.tickCurrent);
        const sqrtPriceAX96 = TickMath.getSqrtRatioAtTick(positionInfo.tickLower);
        const sqrtPriceBX96 = TickMath.getSqrtRatioAtTick(positionInfo.tickUpper);
        const liquidity = BigInt(positionInfo.liquidity);
        let amount0;
        let amount1;
        if (positionInfo.tickCurrent < positionInfo.tickLower) {
            amount0 = SqrtPriceMath.getAmount0Delta(sqrtPriceAX96, sqrtPriceBX96, liquidity,
                false);
            amount1 = BigInt(0);
        } else if (positionInfo.tickCurrent > positionInfo.tickUpper) {
            amount0 = BigInt(0);
            amount1 = SqrtPriceMath.getAmount1Delta(sqrtPriceAX96, sqrtPriceBX96, liquidity,
                false);
        } else {
            amount0 = SqrtPriceMath.getAmount0Delta(sqrtPriceX96, sqrtPriceBX96, liquidity,
                false);
            amount1 = SqrtPriceMath.getAmount1Delta(sqrtPriceAX96, sqrtPriceX96, liquidity,
                false);
        }
        positionInfos.push({
            ...positionInfo,
            amount0,
            amount1,
            amount0Format: formatUnits(amount0, Number(positionInfo.token0.decimals)),
            amount1Format: formatUnits(amount1, Number(positionInfo.token1.decimals)),
        });

    }

    return positionInfos;
}



```

--------------------------------------------------------------------------------
/src/tools/buyMemeToken.ts:
--------------------------------------------------------------------------------

```typescript

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import {
    parseUnits,
    type Hex,
} from "viem";
import { getAccount, publicClient, walletClient } from "../config.js";
import { AddressConfig } from "../addressConfig.js";
import { buildTxUrl, checkTransactionHash } from "../util.js";


export function registerBuyMemeToken(server: McpServer) {

    server.tool("Buy_Meme_Token", "🚀Purchase meme tokens on BNBChain", {
            token: z.string(),
            tokenValue: z.string().default("0"),
            bnbValue: z.string().default("0"),
        },
        async ({ token, tokenValue, bnbValue }) => {

            let txHash = undefined;
            try {
                

                const account = await getAccount();
                
                const [,,estimatedAmount,,,amountMsgValue,,] = await publicClient.readContract({
                        address: AddressConfig.FourMemeTryBuyContract,
                        abi: [
                            {
                                "inputs": [
                                    {
                                        "internalType": "address",
                                        "name": "token",
                                        "type": "address"
                                    },
                                    {
                                        "internalType": "uint256",
                                        "name": "amount",
                                        "type": "uint256"
                                    },
                                    {
                                        "internalType": "uint256",
                                        "name": "funds",
                                        "type": "uint256"
                                    }
                                ],
                                "name": "tryBuy",
                                "outputs": [
                                    {
                                        "internalType": "address",
                                        "name": "tokenManager",
                                        "type": "address"
                                    },
                                    {
                                        "internalType": "address",
                                        "name": "quote",
                                        "type": "address"
                                    },
                                    {
                                        "internalType": "uint256",
                                        "name": "estimatedAmount",
                                        "type": "uint256"
                                    },
                                    {
                                        "internalType": "uint256",
                                        "name": "estimatedCost",
                                        "type": "uint256"
                                    },
                                    {
                                        "internalType": "uint256",
                                        "name": "estimatedFee",
                                        "type": "uint256"
                                    },
                                    {
                                        "internalType": "uint256",
                                        "name": "amountMsgValue",
                                        "type": "uint256"
                                    },
                                    {
                                        "internalType": "uint256",
                                        "name": "amountApproval",
                                        "type": "uint256"
                                    },
                                    {
                                        "internalType": "uint256",
                                        "name": "amountFunds",
                                        "type": "uint256"
                                    }
                                ],
                                "stateMutability": "view",
                                "type": "function"
                            }],
                        functionName: 'tryBuy',
                        args: [token as Hex, parseUnits(tokenValue, 18), parseUnits(bnbValue, 18)],
                    });

                let outputAmount;
                let inputAmount;
                if (tokenValue == "0") {
                    outputAmount = (BigInt(estimatedAmount) * BigInt(100 - 20)) / 100n
                    inputAmount = amountMsgValue
                } else {
                    outputAmount = estimatedAmount;
                    inputAmount =  (BigInt(amountMsgValue) * BigInt(100 + 5)) / 100n
                }

                txHash = await walletClient(account).writeContract({
                    account,
                    address: AddressConfig.FourMemeBuyTokenAMAPContract,
                    abi: [{
                        "inputs": [
                            {
                                "internalType": "address",
                                "name": "token",
                                "type": "address"
                            },
                            {
                                "internalType": "uint256",
                                "name": "funds",
                                "type": "uint256"
                            },
                            {
                                "internalType": "uint256",
                                "name": "minAmount",
                                "type": "uint256"
                            }
                        ],
                        "name": "buyTokenAMAP",
                        "outputs": [],
                        "stateMutability": "payable",
                        "type": "function"
                    }],
                    functionName: 'buyTokenAMAP',
                    args: [token as Hex, BigInt(inputAmount), outputAmount],
                    value: BigInt(inputAmount),
                });

                const txUrl = await checkTransactionHash(txHash)
                return {
                    content: [
                        {
                            type: "text",
                            text: `buy meme token successfully. ${txUrl}`,
                            url: txUrl,
                        },
                    ],
                };
            } catch (error) {
                const errorMessage =
                  error instanceof Error ? error.message : String(error);
                const txUrl = buildTxUrl(txHash);
                return {
                  content: [
                    {
                      type: "text",
                      text: `transaction failed: ${errorMessage}`,
                      url: txUrl,
                    },
                  ],
                  isError: true,
                };
            }
        }
    );
}
```

--------------------------------------------------------------------------------
/src/functions/pancakeRemoveLiquidityTool.ts:
--------------------------------------------------------------------------------

```typescript
import { ChainId, Native, Percent } from "@pancakeswap/sdk";
import { PositionMath, Multicall } from "@pancakeswap/v3-sdk";
import { 
    Address, 
    Hex, 
    encodeFunctionData, 
    getAddress, 
    zeroAddress, 
    maxUint128, 
    parseAbi,
    PrivateKeyAccount,
    publicActions, 
} from "viem";

import dotenv from 'dotenv';
import { publicClient, walletClient } 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' as Address;
const FACTORY_ADDRESS = '0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865' as Address;

export const removeLiquidityV3 = async (account: PrivateKeyAccount, tokenId: BigInt, percent: number) => {

    const calldatas: Hex[] = []

    const client = walletClient(account).extend(publicActions)

    const positionInfo = await publicClient.readContract({
        address: POSITION_MANAGER_ADDRESS,
        abi: positionManagerABI,
        functionName: 'positions',
        args: [tokenId]
    }) as any[]

    const bnb = Native.onChain(ChainId.BSC)

    const liquidity: bigint = 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] as Address,
                positionInfo[3] as Address,
                positionInfo[4]
            ]
        })

        const slot0 = await client.readContract({
            address: poolAddrs as Address,
            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 as any,
    })
    return tx
}
```

--------------------------------------------------------------------------------
/src/functions/pancakeAddLiquidityTool.ts:
--------------------------------------------------------------------------------

```typescript
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;
}
```

--------------------------------------------------------------------------------
/src/util.ts:
--------------------------------------------------------------------------------

```typescript
import { type Hex } from "viem";
import { exec } from 'child_process';
import fs from 'fs';
import path from 'path';
import os from 'os';

import { decryptPrivateKey, } from "./PrivateAES.js";
import { privateKeyToAccount } from "viem/accounts";
import { publicClient } from "./config.js";

const platform = os.platform();
export function buildTxUrl(txHash: Hex | undefined): string | undefined {
  if (!txHash) {
    return undefined;
  }
  const txUrl = `https://bscscan.com/tx/${txHash}`;
  return txUrl
}
export async function checkTransactionHash(txHash: Hex): Promise<string> {

  const txReceipt = await publicClient.waitForTransactionReceipt({
    hash: txHash,
    retryCount: 300,
    retryDelay: 100,
  });
  const txUrl = `https://bscscan.com/tx/${txHash}`;
  if (txReceipt.status !== "success") {
    throw new Error(`Please check the transaction results on bscscan, ${txUrl}`);
  }
  return txUrl;
}

export function bigIntReplacer(key: string, value: any) {
  return typeof value === 'bigint' ? value.toString() : value;
}

export interface InputBoxOptions {
  title?: string;
  message?: string;
  defaultValue?: string;
  termsText?: string;
}

export interface InputResult {
  value: string | null;
  agreed: boolean;
}

let passwordLock = false

export async function getPassword(isRetry?: boolean, num = 0): Promise<InputResult> {
  if (passwordLock) {
    throw new Error("Password lock is enabled. Try again in 24 hours");
  }
  if (num > 10) {
    passwordLock = true;

    setTimeout(() => {
      passwordLock = false;
    }, 1000 * 60 * 60 * 24);
    throw new Error("You have entered the wrong password too many times.");
  }

  const passwordResp = await showInputBoxWithTerms(isRetry);
  if (!passwordResp.value) {
    throw new Error("You did not enter a password.");
  }
  if (passwordResp.value.length < 8 || passwordResp.value.length > 128) {
    throw new Error("The password must be between 8 and 128 characters.");
  }
  const password = passwordResp.value;

  const BSC_WALLET_PRIVATE_KEY = process.env.BSC_WALLET_PRIVATE_KEY as Hex
  if (!BSC_WALLET_PRIVATE_KEY) {
    throw new Error("BSC_WALLET_PRIVATE_KEY is not defined");
  }
  try {

    const pk = await decryptPrivateKey(BSC_WALLET_PRIVATE_KEY, password)
    const account = privateKeyToAccount(
      pk as Hex
    );
    const address = process.env.BSC_WALLET_ADDRESS
    if (!address) {
      throw new Error("BSC_WALLET_ADDRESS is not defined");
    }

    if (account.address != address) {
      return await getPassword(true, ++num);
    }

  } catch (error) {
    if (error instanceof Error) {
      if (error.message === "Password lock is enabled. Try again in 24 hours") {
        throw error;
      }
      if (error.message === "You have entered the wrong password too many times.") {
        throw error;
      }
    }
    return await getPassword(true, ++num);
  }
  return passwordResp;
}

export function showInputBoxWithTerms(isRetry?: boolean): Promise<InputResult> {

  let message = "Enter your Wallet Password:";
  if (isRetry) {
    message = "Wrong password, please try again:";
  }
  return new Promise((resolve, reject) => {

    switch (platform) {
      case 'darwin':
        // For macOS, we use AppleScript to show a dialog with both input and checkbox
        // The AppleScript is more complex but allows for a better UX
        if (isRetry) {
          message = "❌" + message
        }
        const appleScript = `
        tell application "System Events"
        set userPassword to ""
        set buttonPressed to ""
        
        repeat
            try
                set userInput to display dialog "${message}" default answer "" with hidden answer buttons {"cancel", "confirm"} default button "confirm" with icon note
                set userPassword to text returned of userInput
                set buttonPressed to button returned of userInput
                
                if buttonPressed is "cancel" then
                    exit repeat
                end if
                
                if (length of userPassword >= 8) and (length of userPassword <= 128) then
                    exit repeat
                end if
                
                display dialog "Wallet Password must be between 8 and 128 characters!" buttons {"confirm"} default button "confirm" with icon caution
            on error
                -- Handle any errors (like when user clicks the red close button)
                exit repeat
            end try
        end repeat
        
        if buttonPressed is not "cancel" then
            set agreeToTerms to button returned of (display dialog "🔒 You will stay signed in for the next hour." buttons {"no", "yes"} default button "no" with icon caution)
            return userPassword & "============" & agreeToTerms
        else
            return "canceled"
        end if
    end tell
        `;

        exec(`osascript -e '${appleScript}'`, (error, stdout, stderr) => {
          if (error) {
            // User cancelled
            if (error.code === 1 || error.code === 255) {
              resolve({ value: null, agreed: false });
            } else {
              reject(error);
            }
            return;
          }

          if (stdout.trim() === "canceled") {
            reject(new Error("Please enter the password before using ❕"));
            return;
          }
          const [password, agree] = stdout.trim().split("============");
          resolve({
            value: password,
            agreed: agree === "yes"
          });
        });
        break;

      case 'win32':

        const winCommand = `
        Add-Type -AssemblyName System.Windows.Forms
        Add-Type -AssemblyName System.Drawing
        
        $form = New-Object System.Windows.Forms.Form
        $form.Text = 'wallet password'
        $form.Size = New-Object System.Drawing.Size(450,300)
        $form.StartPosition = 'CenterScreen'
        
        $label = New-Object System.Windows.Forms.Label
        $label.Location = New-Object System.Drawing.Point(10,20)
        $label.Size = New-Object System.Drawing.Size(380,40)
        $label.Text = '${message}'
        $form.Controls.Add($label)
        
        # User input label
        $userLabel = New-Object System.Windows.Forms.Label
        $userLabel.Location = New-Object System.Drawing.Point(10,70)
        $userLabel.Size = New-Object System.Drawing.Size(150,20)
        $userLabel.Text = 'Input Password:'
        $form.Controls.Add($userLabel)
        
        # User input textbox
        $passwordTextBox = New-Object System.Windows.Forms.TextBox
        $passwordTextBox.Location = New-Object System.Drawing.Point(160,70)
        $passwordTextBox.Size = New-Object System.Drawing.Size(250,20)
        $passwordTextBox.PasswordChar = '*' 
        $form.Controls.Add($passwordTextBox)
        
        # Error message label
        $errorLabel = New-Object System.Windows.Forms.Label
        $errorLabel.Location = New-Object System.Drawing.Point(160,95)
        $errorLabel.Size = New-Object System.Drawing.Size(250,20)
        $errorLabel.ForeColor = [System.Drawing.Color]::Red
        $errorLabel.Text = ''
        $form.Controls.Add($errorLabel)
        
        $checkbox = New-Object System.Windows.Forms.CheckBox
        $checkbox.Location = New-Object System.Drawing.Point(10,130)
        $checkbox.Size = New-Object System.Drawing.Size(350,20)
        $checkbox.Text = 'You will stay signed in for the next hour.'
        $form.Controls.Add($checkbox)
        
        $button = New-Object System.Windows.Forms.Button
        $button.Location = New-Object System.Drawing.Point(175,190)
        $button.Size = New-Object System.Drawing.Size(100,30)
        $button.Text = 'Confirm'
        $button.Add_Click({
            # Validate password length
            if ($passwordTextBox.Text.Length -lt 8 -or $passwordTextBox.Text.Length -gt 128) {
                $errorLabel.Text = 'Wallet Password must be between 8 and 128 characters!'
            } else {
                $form.DialogResult = [System.Windows.Forms.DialogResult]::OK
                $form.Close()
            }
        })
        $form.Controls.Add($button)
        
        $form.AcceptButton = $button
        $form.Add_Shown({$form.Activate()})
        [void]$form.ShowDialog()
        
        if ($form.DialogResult -eq [System.Windows.Forms.DialogResult]::OK) {
            $result = @{
              agreed = $checkbox.Checked
              value = $passwordTextBox.Text
            }
        
            $jsonResult = ConvertTo-Json -InputObject $result
            Write-Output $jsonResult
        }
        exit 0
`

        const tempScriptPath = path.join('.', 'terms_form.ps1');
        fs.writeFileSync(tempScriptPath, winCommand);

        exec(`powershell -ExecutionPolicy Bypass -File "${tempScriptPath}"`, (error, stdout, stderr) => {
          fs.unlinkSync(tempScriptPath);

          if (error && error.code !== 1) {
            resolve({
              value: null,
              agreed: false
            });
            return;
          }
          if (!stdout) {
            reject(new Error("Please enter the password before using ❕"));
            return;
          }
          const stdoutJSON = JSON.parse(stdout);
          resolve({
            value: stdoutJSON.value as string,
            agreed: stdoutJSON.agreed as boolean
          });
        });
        break;

      default:
        reject(new Error(`Unsupported platform and command-line input is not available: ${platform}`));
    }
  });
}

```