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

447 lines (446 loc) 17.6 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 { execSync } from "child_process"; import { handleError } from "../common.js"; import * as fs from "fs"; // Helper function to execute cargo stylus commands with directory handling function executeCargoStylusCommand(command_1, cwd_1) { return __awaiter(this, arguments, void 0, function* (command, cwd, args = []) { try { const fullCommand = `cargo stylus ${command} ${args.join(" ")}`; const options = { encoding: "utf8", }; if (cwd) { // Check if directory exists if (!fs.existsSync(cwd)) { fs.mkdirSync(cwd, { recursive: true }); } options.cwd = cwd; } const output = execSync(fullCommand, options); return output; } catch (error) { if (error.stdout) { return error.stdout; } throw new Error(`Failed to execute cargo stylus command: ${error.message}`); } }); } export function registerStylusTools(server) { // 1. Create a new Stylus project server.tool("createStylusProject", "Create a new Cargo Stylus project", { projectName: z.string().describe("Project name"), path: z .string() .optional() .describe("Path to create the project (optional)"), }, (_a) => __awaiter(this, [_a], void 0, function* ({ projectName, path: projectPath }) { try { // For 'new' command, we need to be in the parent directory where we want to create the project const output = yield executeCargoStylusCommand(`new ${projectName}`, projectPath); return { content: [ { type: "text", text: `Project created successfully:\n\n${output}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error creating Stylus project: ${handleError(error)}`, }, ], }; } })); // 2. Initialize a Stylus project server.tool("initStylusProject", "Initialize a Stylus project in the current directory", { path: z .string() .optional() .describe("Path to initialize the project (optional)"), }, (_a) => __awaiter(this, [_a], void 0, function* ({ path: projectPath }) { try { // For 'init' command, we need to be in the directory where we want to initialize the project const output = yield executeCargoStylusCommand("init", projectPath); return { content: [ { type: "text", text: `Project initialized successfully:\n\n${output}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error initializing Stylus project: ${handleError(error)}`, }, ], }; } })); // 3. Export a Solidity ABI server.tool("exportStylusAbi", "Export a Solidity ABI for a Stylus contract", { path: z .string() .optional() .describe("Path to the Stylus project (optional)"), }, (_a) => __awaiter(this, [_a], void 0, function* ({ path: projectPath }) { try { // We need to be in the project directory const output = yield executeCargoStylusCommand("export-abi", projectPath); return { content: [ { type: "text", text: `ABI exported successfully:\n\n${output}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error exporting Stylus ABI: ${handleError(error)}`, }, ], }; } })); // 4. Check a Stylus contract server.tool("checkStylusContract", "Check if a Stylus contract is valid for deployment", { path: z .string() .optional() .describe("Path to the Stylus project (optional)"), }, (_a) => __awaiter(this, [_a], void 0, function* ({ path: projectPath }) { try { // We need to be in the project directory const output = yield executeCargoStylusCommand("check", projectPath); return { content: [ { type: "text", text: `Contract check results:\n\n${output}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error checking Stylus contract: ${handleError(error)}`, }, ], }; } })); // 5. Deploy a Stylus contract server.tool("deployStylusContract", "Deploy a Stylus contract to the Arbitrum network. Uses STYLUS_PRIVATE_KEY, STYLUS_PRIVATE_KEY_PATH, or STYLUS_KEYSTORE_PATH from environment variables for authentication.", { endpoint: z.string().describe("RPC endpoint URL"), path: z .string() .optional() .describe("Path to the Stylus project (optional)"), estimateGas: z .boolean() .optional() .describe("Only estimate gas instead of deploying (optional)"), }, (_a) => __awaiter(this, [_a], void 0, function* ({ endpoint, path: projectPath, estimateGas }) { try { const args = [`--endpoint=${endpoint}`]; // Get authentication method from environment variables const privateKey = process.env.STYLUS_PRIVATE_KEY; const privateKeyPath = process.env.STYLUS_PRIVATE_KEY_PATH; const keystorePath = process.env.STYLUS_KEYSTORE_PATH; // At least one authentication method must be provided if (privateKey) { args.push(`--private-key=${privateKey}`); } else if (privateKeyPath) { args.push(`--private-key-path=${privateKeyPath}`); } else if (keystorePath) { args.push(`--keystore-path=${keystorePath}`); } else { throw new Error("Authentication required: Set one of STYLUS_PRIVATE_KEY, STYLUS_PRIVATE_KEY_PATH, or STYLUS_KEYSTORE_PATH environment variables"); } if (estimateGas) { args.push("--estimate-gas"); } // We need to be in the project directory const output = yield executeCargoStylusCommand("deploy", projectPath, args); return { content: [ { type: "text", text: `${estimateGas ? "Gas estimation" : "Deployment"} results:\n\n${output}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error ${estimateGas ? "estimating gas" : "deploying"} Stylus contract: ${handleError(error)}`, }, ], }; } })); // 6. Verify a Stylus contract deployment server.tool("verifyStylusContract", "Verify the deployment of a Stylus contract", { deploymentTx: z.string().describe("Deployment transaction hash"), endpoint: z.string().optional().describe("RPC endpoint URL (optional)"), path: z .string() .optional() .describe("Path to the Stylus project (optional)"), }, (_a) => __awaiter(this, [_a], void 0, function* ({ deploymentTx, endpoint, path: projectPath }) { try { const args = [`--deployment-tx=${deploymentTx}`]; if (endpoint) { args.push(`--endpoint=${endpoint}`); } // We need to be in the project directory const output = yield executeCargoStylusCommand("verify", projectPath, args); return { content: [ { type: "text", text: `Verification results:\n\n${output}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error verifying Stylus contract: ${handleError(error)}`, }, ], }; } })); // 7. Activate a deployed Stylus contract server.tool("activateStylusContract", "Activate an already deployed Stylus contract. Uses STYLUS_PRIVATE_KEY, STYLUS_PRIVATE_KEY_PATH, or STYLUS_KEYSTORE_PATH from environment variables for authentication.", { contractAddress: z.string().describe("Deployed contract address"), endpoint: z.string().describe("RPC endpoint URL"), path: z .string() .optional() .describe("Path to the Stylus project (optional)"), }, (_a) => __awaiter(this, [_a], void 0, function* ({ contractAddress, endpoint, path: projectPath }) { try { const args = [`--address=${contractAddress}`, `--endpoint=${endpoint}`]; // Get authentication method from environment variables const privateKey = process.env.STYLUS_PRIVATE_KEY; const privateKeyPath = process.env.STYLUS_PRIVATE_KEY_PATH; const keystorePath = process.env.STYLUS_KEYSTORE_PATH; // At least one authentication method must be provided if (privateKey) { args.push(`--private-key=${privateKey}`); } else if (privateKeyPath) { args.push(`--private-key-path=${privateKeyPath}`); } else if (keystorePath) { args.push(`--keystore-path=${keystorePath}`); } else { throw new Error("Authentication required: Set one of STYLUS_PRIVATE_KEY, STYLUS_PRIVATE_KEY_PATH, or STYLUS_KEYSTORE_PATH environment variables"); } // We need to be in the project directory const output = yield executeCargoStylusCommand("activate", projectPath, args); return { content: [ { type: "text", text: `Activation results:\n\n${output}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error activating Stylus contract: ${handleError(error)}`, }, ], }; } })); // 8. Cache a Stylus contract server.tool("cacheStylusContract", "Cache a contract using the Stylus CacheManager", { subcommand: z .enum(["bid", "status", "suggest-bid", "help"]) .describe("Cache subcommand to execute"), contractAddress: z.string().describe("Contract address to cache"), endpoint: z.string().describe("RPC endpoint URL"), path: z .string() .optional() .describe("Path to the Stylus project (optional)"), }, (_a) => __awaiter(this, [_a], void 0, function* ({ subcommand, contractAddress, endpoint, path: projectPath }) { try { const args = [`--address=${contractAddress}`, `--endpoint=${endpoint}`]; // We need to be in the project directory const output = yield executeCargoStylusCommand(`cache ${subcommand}`, projectPath, args); return { content: [ { type: "text", text: `Cache ${subcommand} results:\n\n${output}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error caching Stylus contract: ${handleError(error)}`, }, ], }; } })); // 9. Generate C code bindings server.tool("generateStylusBindings", "Generate C code bindings for a Stylus contract", { input: z.string().describe("Input file or contract ABI"), outDir: z .string() .describe("Output directory for the generated bindings"), path: z .string() .optional() .describe("Path to the Stylus project (optional)"), }, (_a) => __awaiter(this, [_a], void 0, function* ({ input, outDir, path: projectPath }) { try { // We need to be in the project directory const output = yield executeCargoStylusCommand(`cgen --input ${input} --out_dir ${outDir}`, projectPath); return { content: [ { type: "text", text: `C bindings generation results:\n\n${output}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error generating C bindings: ${handleError(error)}`, }, ], }; } })); // 10. Replay a transaction in GDB server.tool("replayStylusTransaction", "Replay a Stylus transaction in GDB debugger", { txHash: z.string().describe("Transaction hash to replay"), endpoint: z.string().optional().describe("RPC endpoint URL (optional)"), path: z .string() .optional() .describe("Path to the Stylus project (optional)"), }, (_a) => __awaiter(this, [_a], void 0, function* ({ txHash, endpoint, path: projectPath }) { try { const args = [`--tx=${txHash}`]; if (endpoint) { args.push(`--endpoint=${endpoint}`); } // We need to be in the project directory const output = yield executeCargoStylusCommand("replay", projectPath, args); return { content: [ { type: "text", text: `Transaction replay results:\n\n${output}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error replaying transaction: ${handleError(error)}`, }, ], }; } })); // 11. Trace a transaction server.tool("traceStylusTransaction", "Trace a Stylus transaction", { txHash: z.string().describe("Transaction hash to trace"), endpoint: z.string().optional().describe("RPC endpoint URL (optional)"), path: z .string() .optional() .describe("Path to the Stylus project (optional)"), }, (_a) => __awaiter(this, [_a], void 0, function* ({ txHash, endpoint, path: projectPath }) { try { const args = [`--tx=${txHash}`]; if (endpoint) { args.push(`--endpoint=${endpoint}`); } // We need to be in the project directory const output = yield executeCargoStylusCommand("trace", projectPath, args); return { content: [ { type: "text", text: `Transaction trace results:\n\n${output}`, }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error tracing transaction: ${handleError(error)}`, }, ], }; } })); }