UNPKG

arbitrum-mcp-tools

Version:

A comprehensive collection of Model Context Protocol (MCP) tools for interacting with the Arbitrum blockchain. Enables AI assistants like Claude, Cursor, and Windsurf to perform blockchain operations including account analysis, contract interaction, cross

174 lines (173 loc) 8.29 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { z } from "zod"; import { formatEther } from "ethers"; import { handleError, formatTokenBalance } from "../common.js"; import { alchemy } from "../../index.js"; import { AssetTransfersCategory } from "alchemy-sdk"; export function registerBatchOperationsTools(server) { // 1. Bulk Balances server.tool("getBatchBalances", "Get balances for multiple addresses in single call", { addresses: z.array(z.string()).describe("Array of addresses to check"), tokenAddress: z .string() .optional() .describe("ERC-20 token address (optional)"), }, (_a) => __awaiter(this, [_a], void 0, function* ({ addresses, tokenAddress }) { try { const balances = yield Promise.all(addresses.map((address) => __awaiter(this, void 0, void 0, function* () { if (tokenAddress) { // Fetch balance for a specific ERC-20 token const tokenBalancesResponse = yield alchemy.core.getTokenBalances(address, [tokenAddress]); const specificTokenBalance = tokenBalancesResponse.tokenBalances.find((tb) => tb.contractAddress.toLowerCase() === tokenAddress.toLowerCase()); const metadata = yield alchemy.core.getTokenMetadata(tokenAddress); const name = metadata.name || "Unknown Token"; const symbol = metadata.symbol || "???"; if (specificTokenBalance && specificTokenBalance.tokenBalance) { const formattedBalance = formatTokenBalance(BigInt(specificTokenBalance.tokenBalance), metadata.decimals || 18); return `${address}: ${formattedBalance} ${symbol} (${name})`; } else { return `${address}: 0 ${symbol} (${name}) (or no balance found for this token)`; } } // Fetch native ETH balance const balance = yield alchemy.core.getBalance(address); return `${address}: ${formatEther(balance.toString())} ETH`; }))); return { content: [ { type: "text", text: `Balances:\n${balances.join("\n")}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error: ${handleError(error)}`, }, ], }; } })); // 2. Multi-Address Analysis server.tool("multiAddressAnalysis", "Compare token holdings and transactions across multiple addresses", { addresses: z.array(z.string()).describe("Array of addresses to analyze"), includeNfts: z.boolean().optional().describe("Include NFTs in analysis"), includeTransactions: z .boolean() .optional() .describe("Include transactions in analysis"), }, (_a) => __awaiter(this, [_a], void 0, function* ({ addresses, includeNfts, includeTransactions }) { try { // Get balances for all addresses const balancePromises = addresses.map((address) => __awaiter(this, void 0, void 0, function* () { const ethBalance = yield alchemy.core.getBalance(address); const tokenBalances = yield alchemy.core.getTokenBalances(address); // Format the balances const formattedEthBalance = formatEther(ethBalance.toString()); const tokens = yield Promise.all(tokenBalances.tokenBalances.map((token) => __awaiter(this, void 0, void 0, function* () { const metadata = yield alchemy.core.getTokenMetadata(token.contractAddress); return { name: metadata.name || "Unknown", symbol: metadata.symbol || "???", balance: token.tokenBalance || "0", }; }))); return { address, ethBalance: formattedEthBalance, tokens, }; })); // Add NFT data if requested const nftPromises = includeNfts ? addresses.map((address) => __awaiter(this, void 0, void 0, function* () { const nfts = yield alchemy.nft.getNftsForOwner(address, { pageSize: 50, }); return { address, nftCount: nfts.totalCount, collections: nfts.ownedNfts.reduce((acc, nft) => { const collectionName = nft.contract.name || "Unknown Collection"; acc[collectionName] = (acc[collectionName] || 0) + 1; return acc; }, {}), }; })) : []; // Add transaction data if requested const txPromises = includeTransactions ? addresses.map((address) => __awaiter(this, void 0, void 0, function* () { const transactions = yield alchemy.core.getAssetTransfers({ fromBlock: "0x0", fromAddress: address, category: [ AssetTransfersCategory.EXTERNAL, AssetTransfersCategory.ERC20, AssetTransfersCategory.ERC721, AssetTransfersCategory.ERC1155, ], }); return { address, txCount: transactions.transfers.length, }; })) : []; // Wait for all promises to resolve const [balances, nfts, txs] = yield Promise.all([ Promise.all(balancePromises), Promise.all(nftPromises), Promise.all(txPromises), ]); // Format the output let output = "Multi-address analysis:\n\n"; balances.forEach((balance) => { output += `Address: ${balance.address}\n`; output += `ETH Balance: ${balance.ethBalance} ETH\n`; output += `Token Count: ${balance.tokens.length}\n`; if (includeNfts) { const addressNfts = nfts.find((n) => n.address === balance.address); if (addressNfts) { output += `NFT Count: ${addressNfts.nftCount}\n`; } } if (includeTransactions) { const addressTxs = txs.find((t) => t.address === balance.address); if (addressTxs) { output += `Transaction Count: ${addressTxs.txCount}\n`; } } output += "---\n"; }); return { content: [{ type: "text", text: output }], }; } catch (error) { return { content: [ { type: "text", text: `Analysis failed: ${handleError(error)}`, }, ], }; } })); }