#
tokens: 25861/50000 19/19 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .env.example
├── .eslintrc
│   └── index.js
├── .gitignore
├── .npmignore
├── .npmrc
├── jest.config.js
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── required-methods.md
├── src
│   ├── handlers
│   │   ├── utils.ts
│   │   ├── wallet.ts
│   │   └── wallet.types.ts
│   ├── index.ts
│   ├── tools.ts
│   └── types.ts
├── tests
│   └── handlers
│       ├── network.test.ts
│       ├── provider.test.ts
│       └── wallet.test.ts
└── tsconfig.json
```

# Files

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

```
1 | node_modules/
2 | build/
3 | .env
4 | .DS_Store
5 | *.log
6 | 
```

--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------

```
1 | @dcspark:registry=https://npm.pkg.github.com
```

--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------

```
1 | .eslintrc
2 | tests
3 | .gitignore
4 | .git
5 | .DS_Store
6 | .vscode
7 | .env
```

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
1 | # Optional private key for wallet operations
2 | PRIVATE_KEY=your_private_key_here
3 | 
```

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

```markdown
  1 | # MCP Crypto Wallet EVM
  2 | 
  3 | This repository contains a Model Context Protocol (MCP) server that provides Claude with access to Ethereum and EVM-compatible blockchain operations via ethers.js v5. The server enables Claude to perform operations like creating wallets, checking balances, sending transactions, and interacting with smart contracts on EVM-compatible blockchains.
  4 | 
  5 | <a href="https://glama.ai/mcp/servers/@dcSpark/mcp-cryptowallet-evm">
  6 |   <img width="380" height="200" src="https://glama.ai/mcp/servers/@dcSpark/mcp-cryptowallet-evm/badge" alt="Crypto Wallet EVM MCP server" />
  7 | </a>
  8 | 
  9 | ## Overview
 10 | 
 11 | The MCP server exposes the following tools to Claude:
 12 | 
 13 | ### Wallet Creation and Management
 14 | - `wallet_create_random`: Create a new wallet with a random private key
 15 | - `wallet_from_private_key`: Create a wallet from a private key
 16 | - `wallet_from_mnemonic`: Create a wallet from a mnemonic phrase
 17 | - `wallet_from_encrypted_json`: Create a wallet by decrypting an encrypted JSON wallet
 18 | - `wallet_encrypt`: Encrypt a wallet with a password
 19 | 
 20 | ### Wallet Properties
 21 | - `wallet_get_address`: Get the wallet address
 22 | - `wallet_get_public_key`: Get the wallet public key
 23 | - `wallet_get_private_key`: Get the wallet private key (with appropriate security warnings)
 24 | - `wallet_get_mnemonic`: Get the wallet mnemonic phrase (if available)
 25 | 
 26 | ### Blockchain Methods
 27 | - `wallet_get_balance`: Get the balance of the wallet
 28 | - `wallet_get_chain_id`: Get the chain ID the wallet is connected to
 29 | - `wallet_get_gas_price`: Get the current gas price
 30 | - `wallet_get_transaction_count`: Get the number of transactions sent from this account (nonce)
 31 | - `wallet_call`: Call a contract method without sending a transaction
 32 | 
 33 | ### Transaction Methods
 34 | - `wallet_send_transaction`: Send a transaction
 35 | - `wallet_sign_transaction`: Sign a transaction without sending it
 36 | - `wallet_populate_transaction`: Populate a transaction with missing fields
 37 | 
 38 | ### Signing Methods
 39 | - `wallet_sign_message`: Sign a message
 40 | - `wallet_sign_typed_data`: Sign typed data (EIP-712)
 41 | - `wallet_verify_message`: Verify a signed message
 42 | - `wallet_verify_typed_data`: Verify signed typed data
 43 | 
 44 | ### Provider Methods
 45 | - `provider_get_block`: Get a block by number or hash
 46 | - `provider_get_transaction`: Get a transaction by hash
 47 | - `provider_get_transaction_receipt`: Get a transaction receipt
 48 | - `provider_get_code`: Get the code at an address
 49 | - `provider_get_storage_at`: Get the storage at a position for an address
 50 | - `provider_estimate_gas`: Estimate the gas required for a transaction
 51 | - `provider_get_logs`: Get logs that match a filter
 52 | - `provider_get_ens_resolver`: Get the ENS resolver for a name
 53 | - `provider_lookup_address`: Lookup the ENS name for an address
 54 | - `provider_resolve_name`: Resolve an ENS name to an address
 55 | 
 56 | ### Network Methods
 57 | - `network_get_network`: Get the current network information
 58 | - `network_get_block_number`: Get the current block number
 59 | - `network_get_fee_data`: Get the current fee data (base fee, max priority fee, etc.)
 60 | 
 61 | ## Prerequisites
 62 | 
 63 | - Node.js (v16 or higher)
 64 | - Claude Desktop application
 65 | 
 66 | ## Installation
 67 | 
 68 | ### Option 1: Using npx (Recommended)
 69 | 
 70 | You can run the MCP server directly without installation using npx:
 71 | 
 72 | ```bash
 73 | npx @mcp-dockmaster/mcp-cryptowallet-evm
 74 | ```
 75 | 
 76 | This will download and execute the server directly from npm.
 77 | 
 78 | ### Option 2: Manual Installation
 79 | 
 80 | 1. Clone this repository:
 81 |    ```bash
 82 |    git clone https://github.com/dcSpark/mcp-cryptowallet-evm.git
 83 |    cd mcp-cryptowallet-evm
 84 |    ```
 85 | 
 86 | 2. Install dependencies:
 87 |    ```bash
 88 |    npm ci
 89 |    ```
 90 | 
 91 | 3. Build the project:
 92 |    ```bash
 93 |    npm run build
 94 |    ```
 95 | 
 96 | ## Configuration
 97 | 
 98 | ### Environment Variables
 99 | 
100 | The MCP server supports the following environment variables:
101 | 
102 | - `PRIVATE_KEY`: Optional private key to use for wallet operations when no wallet is explicitly provided
103 | 
104 | ### Configure Claude Desktop
105 | 
106 | To configure Claude Desktop to use this MCP server:
107 | 
108 | 1. Open Claude Desktop
109 | 2. Navigate to the Claude Desktop configuration file:
110 |    - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
111 |    - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
112 |    - Linux: `~/.config/Claude/claude_desktop_config.json`
113 | 
114 | 3. Add the MCP server configuration:
115 | 
116 | ```json
117 | {
118 |   "mcpServers": {
119 |     "mcp-cryptowallet-evm": {
120 |       "command": "npx",
121 |       "args": [
122 |         "@mcp-dockmaster/mcp-cryptowallet-evm"
123 |       ]
124 |     }
125 |   }
126 | }
127 | ```
128 | 
129 | Alternatively, if you installed the package locally:
130 | 
131 | ```json
132 | {
133 |   "mcpServers": {
134 |     "mcp-cryptowallet-evm": {
135 |       "command": "node",
136 |       "args": [
137 |         "/path/to/your/mcp-cryptowallet-evm/build/index.js"
138 |       ]
139 |     }
140 |   }
141 | }
142 | ```
143 | 
144 | ### Running Locally
145 | 
146 | ```bash
147 | node build/index.js
148 | ```
149 | 
150 | ## Usage
151 | 
152 | Once configured, restart Claude Desktop. Claude will now have access to the Ethereum and EVM-compatible blockchain tools. You can ask Claude to:
153 | 
154 | 1. Create a new wallet:
155 |    ```
156 |    Can you create a new Ethereum wallet for me?
157 |    ```
158 | 
159 | 2. Check a wallet balance:
160 |    ```
161 |    What's the balance of the Ethereum wallet address 0x742d35Cc6634C0532925a3b844Bc454e4438f44e?
162 |    ```
163 | 
164 | 3. Send a transaction:
165 |    ```
166 |    Can you help me send 0.1 ETH to 0x742d35Cc6634C0532925a3b844Bc454e4438f44e?
167 |    ```
168 | 
169 | Claude will use the MCP server to interact with the Ethereum blockchain directly.
170 | 
171 | ## Development
172 | 
173 | ### Adding New Tools
174 | 
175 | To add new tools to the MCP server:
176 | 
177 | 1. Define the tool in `src/tools.ts`
178 | 2. Create a handler function in the appropriate handler file
179 | 3. Add the handler to the `handlers` object in `src/tools.ts`
180 | 
181 | ### Building
182 | 
183 | ```bash
184 | npm run build
185 | ```
186 | 
187 | ## License
188 | 
189 | MIT
```

--------------------------------------------------------------------------------
/.eslintrc/index.js:
--------------------------------------------------------------------------------

```javascript
 1 | module.exports = {
 2 |   root: true,
 3 |   parser: '@typescript-eslint/parser',
 4 |   plugins: ['@typescript-eslint'],
 5 |   extends: [
 6 |     'eslint:recommended',
 7 |     'plugin:@typescript-eslint/recommended'
 8 |   ],
 9 |   rules: {
10 |     '@typescript-eslint/explicit-module-boundary-types': 'off'
11 |   }
12 | }
13 | 
```

--------------------------------------------------------------------------------
/src/handlers/wallet.types.ts:
--------------------------------------------------------------------------------

```typescript
 1 | export type fromPrivateKeyHandlerInput = {
 2 |   privateKey: string;
 3 |   provider?: string;
 4 | };
 5 | 
 6 | export type createMnemonicPhraseHandlerInput = {
 7 |   locale?: string;
 8 |   length?: 12 | 15 | 18 | 21 | 24;
 9 | };
10 | 
11 | export type fromMnemonicHandlerInput = {
12 |   mnemonic: string;
13 |   provider?: string;
14 |   path?: string;
15 |   locale?: string;
16 | };
17 | 
```

--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------

```javascript
 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */
 2 | export default {
 3 |   preset: 'ts-jest',
 4 |   testEnvironment: 'node',
 5 |   extensionsToTreatAsEsm: ['.ts'],
 6 |   moduleNameMapper: {
 7 |     '^(\\.{1,2}/.*)\\.js$': '$1',
 8 |   },
 9 |   transform: {
10 |     '^.+\\.tsx?$': [
11 |       'ts-jest',
12 |       {
13 |         useESM: true,
14 |       },
15 |     ],
16 |   },
17 | };
18 | 
```

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

```typescript
 1 | export type ToolResultSchema = {
 2 |   content: ToolResultContent[];
 3 |   isError?: boolean;
 4 | }
 5 | 
 6 | export type ToolResultContent = {
 7 |   type: "text";
 8 |   text: string;
 9 | } | {
10 |   type: "image";
11 |   data: string; // base64 encoded image data
12 |   mimeType: string;
13 | } | {
14 |   type: "resource";
15 |   resource: {
16 |     url: string;
17 |     mimeType: string;
18 |     text: string;
19 |   }
20 | }
21 | 
```

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

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2020",
 4 |     "module": "NodeNext",
 5 |     "moduleResolution": "NodeNext",
 6 |     "esModuleInterop": true,
 7 |     "outDir": "build",
 8 |     "strict": true,
 9 |     "declaration": true,
10 |     "sourceMap": true,
11 |     "resolveJsonModule": true,
12 |     "allowSyntheticDefaultImports": true,
13 |     "skipLibCheck": true
14 |   },
15 |   "include": ["src/**/*"],
16 |   "exclude": ["node_modules", "build", "tests"]
17 | }
18 | 
```

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

```typescript
 1 | #!/usr/bin/env node
 2 | 
 3 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
 4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 5 | import {
 6 |   CallToolRequestSchema,
 7 |   ErrorCode,
 8 |   ListToolsRequestSchema,
 9 |   McpError,
10 | } from "@modelcontextprotocol/sdk/types.js";
11 | import { handlers, tools } from "./tools.js";
12 | 
13 | const server = new Server({
14 |   name: "mcp-cryptowallet-evm",
15 |   version: "1.0.0",
16 | }, {
17 |   capabilities: {
18 |     tools: {}
19 |   }
20 | });
21 | 
22 | const transport = new StdioServerTransport();
23 | await server.connect(transport);
24 | 
25 | server.setRequestHandler(ListToolsRequestSchema, async () => {
26 |   return { tools };
27 | });
28 | 
29 | server.setRequestHandler(CallToolRequestSchema, async (request) => {
30 |   const handler = handlers[request.params.name];
31 |   if (handler) {
32 |     try {
33 |       const input = request.params.arguments;
34 |       return await handler(input);
35 |     } catch (error) {
36 |       return { toolResult: { error: (error as Error).message }, content: [], isError: true };
37 |     }
38 |   }
39 |   return { toolResult: { error: "Method not found" }, content: [], isError: true };
40 | });
41 | 
```

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

```json
 1 | {
 2 |   "name": "@mcp-dockmaster/mcp-cryptowallet-evm",
 3 |   "version": "1.0.6",
 4 |   "description": "MCP server for EVM crypto wallet operations using ethers.js v5",
 5 |   "main": "build/index.js",
 6 |   "type": "module",
 7 |   "bin": {
 8 |     "mcp-cryptowallet-evm": "./build/index.js"
 9 |   },
10 |   "scripts": {
11 |     "build": "tsc",
12 |     "start": "node build/index.js",
13 |     "test": "node --test ./tests/**/*.test.ts",
14 |     "lint": "eslint src/**/*.ts"
15 |   },
16 |   "repository": {
17 |     "type": "git",
18 |     "url": "git+https://github.com/dcSpark/mcp-cryptowallet-evm.git"
19 |   },
20 |   "keywords": [
21 |     "mcp",
22 |     "ethereum",
23 |     "evm",
24 |     "wallet",
25 |     "ethers"
26 |   ],
27 |   "author": "dcSpark",
28 |   "license": "MIT",
29 |   "bugs": {
30 |     "url": "https://github.com/dcSpark/mcp-cryptowallet-evm/issues"
31 |   },
32 |   "homepage": "https://github.com/dcSpark/mcp-cryptowallet-evm#readme",
33 |   "dependencies": {
34 |     "@modelcontextprotocol/sdk": "^1.6.1",
35 |     "@scure/bip39": "^1.5.4",
36 |     "ethers": "^5.7.2"
37 |   },
38 |   "devDependencies": {
39 |     "@types/jest": "^29.5.0",
40 |     "@types/node": "^18.15.11",
41 |     "@typescript-eslint/eslint-plugin": "^5.57.1",
42 |     "@typescript-eslint/parser": "^5.57.1",
43 |     "eslint": "^8.38.0",
44 |     "jest": "^29.5.0",
45 |     "ts-jest": "^29.1.0",
46 |     "typescript": "^5.0.4"
47 |   }
48 | }
49 | 
```

--------------------------------------------------------------------------------
/tests/handlers/network.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ethers } from 'ethers';
 2 | import {
 3 |   getNetworkHandler,
 4 |   getBlockNumberHandler,
 5 |   getFeeDataHandler
 6 | } from '../../src/handlers/wallet.js';
 7 | 
 8 | // Mock ethers.js functions
 9 | jest.mock('ethers', () => {
10 |   const originalModule = jest.requireActual('ethers');
11 |   
12 |   // Create a mock provider
13 |   const mockProvider = {
14 |     getNetwork: jest.fn().mockResolvedValue({ 
15 |       name: 'homestead', 
16 |       chainId: 1,
17 |       ensAddress: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'
18 |     }),
19 |     getBlockNumber: jest.fn().mockResolvedValue(1000000),
20 |     getFeeData: jest.fn().mockResolvedValue({
21 |       gasPrice: originalModule.utils.parseUnits('50', 'gwei'),
22 |       maxFeePerGas: originalModule.utils.parseUnits('100', 'gwei'),
23 |       maxPriorityFeePerGas: originalModule.utils.parseUnits('2', 'gwei')
24 |     })
25 |   };
26 |   
27 |   return {
28 |     ...originalModule,
29 |     providers: {
30 |       JsonRpcProvider: jest.fn().mockReturnValue(mockProvider)
31 |     },
32 |     getDefaultProvider: jest.fn().mockReturnValue(mockProvider),
33 |     utils: originalModule.utils
34 |   };
35 | });
36 | 
37 | describe('Network Methods Handlers', () => {
38 |   test('getNetworkHandler should return network information', async () => {
39 |     const result = await getNetworkHandler({ 
40 |       provider: 'https://mainnet.infura.io/v3/your-api-key'
41 |     });
42 |     
43 |     expect(result.isError).toBe(false);
44 |     expect(result.toolResult).toHaveProperty('network');
45 |     expect(result.toolResult.network).toHaveProperty('name');
46 |     expect(result.toolResult.network).toHaveProperty('chainId');
47 |     expect(result.toolResult.network).toHaveProperty('ensAddress');
48 |     expect(result.toolResult.network.name).toBe('homestead');
49 |     expect(result.toolResult.network.chainId).toBe(1);
50 |   });
51 |   
52 |   test('getBlockNumberHandler should return the current block number', async () => {
53 |     const result = await getBlockNumberHandler({ 
54 |       provider: 'https://mainnet.infura.io/v3/your-api-key'
55 |     });
56 |     
57 |     expect(result.isError).toBe(false);
58 |     expect(result.toolResult).toHaveProperty('blockNumber');
59 |     expect(result.toolResult.blockNumber).toBe(1000000);
60 |   });
61 |   
62 |   test('getFeeDataHandler should return fee data', async () => {
63 |     const result = await getFeeDataHandler({ 
64 |       provider: 'https://mainnet.infura.io/v3/your-api-key'
65 |     });
66 |     
67 |     expect(result.isError).toBe(false);
68 |     expect(result.toolResult).toHaveProperty('feeData');
69 |     expect(result.toolResult.feeData).toHaveProperty('gasPrice');
70 |     expect(result.toolResult.feeData).toHaveProperty('maxFeePerGas');
71 |     expect(result.toolResult.feeData).toHaveProperty('maxPriorityFeePerGas');
72 |   });
73 | });
74 | 
```

--------------------------------------------------------------------------------
/required-methods.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Required Methods for EVM Crypto Wallet MCP Server
 2 | 
 3 | Based on the ethers.js v5 documentation and the example MCP servers, the following methods should be implemented for our crypto wallet MCP server:
 4 | 
 5 | ## Wallet Creation and Management
 6 | - `wallet_create_random` - Create a new wallet with a random private key
 7 | - `wallet_from_private_key` - Create a wallet from a private key
 8 | - `wallet_from_mnemonic` - Create a wallet from a mnemonic phrase
 9 | - `wallet_from_encrypted_json` - Create a wallet by decrypting an encrypted JSON wallet
10 | - `wallet_encrypt` - Encrypt a wallet with a password
11 | 
12 | ## Wallet Properties
13 | - `wallet_get_address` - Get the wallet address
14 | - `wallet_get_public_key` - Get the wallet public key
15 | - `wallet_get_private_key` - Get the wallet private key (with appropriate security warnings)
16 | - `wallet_get_mnemonic` - Get the wallet mnemonic phrase (if available)
17 | 
18 | ## Blockchain Methods
19 | - `wallet_get_balance` - Get the balance of the wallet
20 | - `wallet_get_chain_id` - Get the chain ID the wallet is connected to
21 | - `wallet_get_gas_price` - Get the current gas price
22 | - `wallet_get_transaction_count` - Get the number of transactions sent from this account (nonce)
23 | - `wallet_call` - Call a contract method without sending a transaction
24 | 
25 | ## Transaction Methods
26 | - `wallet_send_transaction` - Send a transaction
27 | - `wallet_sign_transaction` - Sign a transaction without sending it
28 | - `wallet_populate_transaction` - Populate a transaction with missing fields
29 | 
30 | ## Signing Methods
31 | - `wallet_sign_message` - Sign a message
32 | - `wallet_sign_typed_data` - Sign typed data (EIP-712)
33 | - `wallet_verify_message` - Verify a signed message
34 | - `wallet_verify_typed_data` - Verify signed typed data
35 | 
36 | ## Provider Methods
37 | - `provider_get_block` - Get a block by number or hash
38 | - `provider_get_transaction` - Get a transaction by hash
39 | - `provider_get_transaction_receipt` - Get a transaction receipt
40 | - `provider_get_code` - Get the code at an address
41 | - `provider_get_storage_at` - Get the storage at a position for an address
42 | - `provider_estimate_gas` - Estimate the gas required for a transaction
43 | - `provider_get_logs` - Get logs that match a filter
44 | - `provider_get_ens_resolver` - Get the ENS resolver for a name
45 | - `provider_lookup_address` - Lookup the ENS name for an address
46 | - `provider_resolve_name` - Resolve an ENS name to an address
47 | 
48 | ## Network Methods
49 | - `network_get_network` - Get the current network information
50 | - `network_get_block_number` - Get the current block number
51 | - `network_get_fee_data` - Get the current fee data (base fee, max priority fee, etc.)
52 | 
53 | These methods cover the core functionality needed for a comprehensive EVM crypto wallet implementation using ethers.js v5.
54 | 
```

--------------------------------------------------------------------------------
/src/handlers/utils.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { ethers } from "ethers";
  2 | import { ToolResultSchema } from "../types.js";
  3 | 
  4 | 
  5 | let provider: ethers.providers.Provider | undefined;
  6 | try {
  7 |   provider = process.env.PROVIDER_URL ? 
  8 |     ethers.providers.getDefaultProvider(process.env.PROVIDER_URL) : 
  9 |     ethers.providers.getDefaultProvider('https://eth.llamarpc.com');
 10 | } catch (error) {
 11 |   console.error("Error initializing provider:", error);
 12 | }
 13 | 
 14 | /**
 15 |  * Creates a success response with the given message
 16 |  * @param message Optional message to include in the response
 17 |  * @returns A ToolResultSchema with the message
 18 |  */
 19 | export const createSuccessResponse = (message?: string): ToolResultSchema => {
 20 |   return {
 21 |     content: [
 22 |       {
 23 |         type: "text",
 24 |         text: message || "Operation completed successfully"
 25 |       }
 26 |     ],
 27 |     isError: false,
 28 |   };
 29 | };
 30 | 
 31 | /**
 32 |  * Creates an error response with the given message
 33 |  * @param message The error message
 34 |  * @returns A ToolResultSchema with the error message
 35 |  */
 36 | export const createErrorResponse = (message: string): ToolResultSchema => {
 37 |   return {
 38 |     content: [
 39 |       {
 40 |         type: "text",
 41 |         text: message
 42 |       }
 43 |     ],
 44 |     isError: true
 45 |   };
 46 | };
 47 | 
 48 | /**
 49 |  * Gets the provider setup in memory
 50 |  * @returns An ethers.js provider
 51 |  */
 52 | export const getProvider = (): ethers.providers.Provider => {
 53 |   if (!provider) {
 54 |     throw new Error(`Invalid provider URL: ${process.env.PROVIDER_URL}`);
 55 |   }
 56 |   return provider
 57 | };
 58 | 
 59 | export const setProvider = (providerURL: string) => {
 60 |   try {
 61 |     provider = ethers.providers.getDefaultProvider(providerURL);
 62 |   } catch (error) {
 63 |     throw new Error(`Invalid provider URL: ${providerURL}`);
 64 |   }
 65 | };
 66 | 
 67 | /**
 68 |  * Gets a wallet from a private key, mnemonic, or JSON wallet
 69 |  * @param walletData The wallet data (private key, mnemonic, or JSON)
 70 |  * @param password Optional password for encrypted JSON wallets
 71 |  * @param provider Optional provider to connect the wallet to
 72 |  * @returns An ethers.js wallet
 73 |  */
 74 | export const getWallet = async (
 75 |   walletData?: string, 
 76 |   password?: string,
 77 | ): Promise<ethers.Wallet> => {
 78 |   const provider = getProvider()
 79 |   // If walletData is not provided, check for PRIVATE_KEY environment variable
 80 |   if (!walletData && process.env.PRIVATE_KEY) {
 81 |     const wallet = new ethers.Wallet(process.env.PRIVATE_KEY);
 82 |     return provider ? wallet.connect(provider) : wallet;
 83 |   }
 84 |   
 85 |   // If no walletData and no environment variable, throw an error
 86 |   if (!walletData) {
 87 |     throw new Error("Wallet data is required or set PRIVATE_KEY environment variable");
 88 |   }
 89 |   
 90 |   try {
 91 |     // Try to parse as JSON first
 92 |     if (walletData.startsWith("{")) {
 93 |       if (!password) {
 94 |         throw new Error("Password is required for encrypted JSON wallets");
 95 |       }
 96 |       
 97 |       const wallet = await ethers.Wallet.fromEncryptedJson(walletData, password);
 98 |       return provider ? wallet.connect(provider) : wallet;
 99 |     }
100 |     
101 |     // Check if it's a mnemonic (12, 15, 18, 21, or 24 words)
102 |     const words = walletData.trim().split(/\s+/);
103 |     if ([12, 15, 18, 21, 24].includes(words.length)) {
104 |       const wallet = ethers.Wallet.fromMnemonic(walletData);
105 |       return provider ? wallet.connect(provider) : wallet;
106 |     }
107 |     
108 |     // Assume it's a private key
109 |     const wallet = new ethers.Wallet(walletData);
110 |     return provider ? wallet.connect(provider) : wallet;
111 |   } catch (error) {
112 |     throw new Error(`Invalid wallet data: ${(error as Error).message}`);
113 |   }
114 | };
115 | 
```

--------------------------------------------------------------------------------
/tests/handlers/provider.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { ethers } from 'ethers';
  2 | import {
  3 |   getBlockHandler,
  4 |   getTransactionHandler,
  5 |   getTransactionReceiptHandler,
  6 |   getCodeHandler,
  7 |   getStorageAtHandler,
  8 |   estimateGasHandler,
  9 |   getLogsHandler,
 10 |   getEnsResolverHandler,
 11 |   lookupAddressHandler,
 12 |   resolveNameHandler
 13 | } from '../../src/handlers/wallet.js';
 14 | 
 15 | // Mock ethers.js functions
 16 | jest.mock('ethers', () => {
 17 |   const originalModule = jest.requireActual('ethers');
 18 |   
 19 |   // Create a mock provider
 20 |   const mockProvider = {
 21 |     getBlock: jest.fn().mockResolvedValue({ 
 22 |       hash: '0xblock', 
 23 |       number: 1000000, 
 24 |       timestamp: Date.now() / 1000,
 25 |       transactions: ['0xtx1', '0xtx2']
 26 |     }),
 27 |     getTransaction: jest.fn().mockResolvedValue({ 
 28 |       hash: '0xtx', 
 29 |       from: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', 
 30 |       to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' 
 31 |     }),
 32 |     getTransactionReceipt: jest.fn().mockResolvedValue({ 
 33 |       status: 1, 
 34 |       blockNumber: 1000000,
 35 |       gasUsed: originalModule.BigNumber.from(21000)
 36 |     }),
 37 |     getCode: jest.fn().mockResolvedValue('0x'),
 38 |     getStorageAt: jest.fn().mockResolvedValue('0x0000000000000000000000000000000000000000000000000000000000000000'),
 39 |     estimateGas: jest.fn().mockResolvedValue(originalModule.BigNumber.from(21000)),
 40 |     getLogs: jest.fn().mockResolvedValue([{
 41 |       blockNumber: 1000000,
 42 |       blockHash: '0xblock',
 43 |       transactionIndex: 0,
 44 |       removed: false,
 45 |       address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
 46 |       data: '0x',
 47 |       topics: ['0xtopic1', '0xtopic2']
 48 |     }]),
 49 |     getResolver: jest.fn().mockResolvedValue({
 50 |       address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
 51 |       name: 'test.eth'
 52 |     }),
 53 |     lookupAddress: jest.fn().mockResolvedValue('test.eth'),
 54 |     resolveName: jest.fn().mockResolvedValue('0x742d35Cc6634C0532925a3b844Bc454e4438f44e')
 55 |   };
 56 |   
 57 |   return {
 58 |     ...originalModule,
 59 |     providers: {
 60 |       JsonRpcProvider: jest.fn().mockReturnValue(mockProvider)
 61 |     },
 62 |     getDefaultProvider: jest.fn().mockReturnValue(mockProvider),
 63 |     utils: originalModule.utils
 64 |   };
 65 | });
 66 | 
 67 | describe('Provider Methods Handlers', () => {
 68 |   test('getBlockHandler should return a block', async () => {
 69 |     const result = await getBlockHandler({ 
 70 |       provider: 'https://mainnet.infura.io/v3/your-api-key',
 71 |       blockHashOrBlockTag: 'latest',
 72 |       includeTransactions: true
 73 |     });
 74 |     
 75 |     expect(result.isError).toBe(false);
 76 |     expect(result.toolResult).toHaveProperty('block');
 77 |     expect(result.toolResult.block).toHaveProperty('hash');
 78 |     expect(result.toolResult.block).toHaveProperty('number');
 79 |     expect(result.toolResult.block).toHaveProperty('timestamp');
 80 |     expect(result.toolResult.block).toHaveProperty('transactions');
 81 |   });
 82 |   
 83 |   test('getTransactionHandler should return a transaction', async () => {
 84 |     const result = await getTransactionHandler({ 
 85 |       provider: 'https://mainnet.infura.io/v3/your-api-key',
 86 |       transactionHash: '0xtx'
 87 |     });
 88 |     
 89 |     expect(result.isError).toBe(false);
 90 |     expect(result.toolResult).toHaveProperty('transaction');
 91 |     expect(result.toolResult.transaction).toHaveProperty('hash');
 92 |     expect(result.toolResult.transaction).toHaveProperty('from');
 93 |     expect(result.toolResult.transaction).toHaveProperty('to');
 94 |   });
 95 |   
 96 |   test('getTransactionReceiptHandler should return a transaction receipt', async () => {
 97 |     const result = await getTransactionReceiptHandler({ 
 98 |       provider: 'https://mainnet.infura.io/v3/your-api-key',
 99 |       transactionHash: '0xtx'
100 |     });
101 |     
102 |     expect(result.isError).toBe(false);
103 |     expect(result.toolResult).toHaveProperty('receipt');
104 |     expect(result.toolResult.receipt).toHaveProperty('status');
105 |     expect(result.toolResult.receipt).toHaveProperty('blockNumber');
106 |     expect(result.toolResult.receipt).toHaveProperty('gasUsed');
107 |   });
108 |   
109 |   test('getCodeHandler should return code at an address', async () => {
110 |     const result = await getCodeHandler({ 
111 |       provider: 'https://mainnet.infura.io/v3/your-api-key',
112 |       address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'
113 |     });
114 |     
115 |     expect(result.isError).toBe(false);
116 |     expect(result.toolResult).toHaveProperty('code');
117 |     expect(result.toolResult.code).toBe('0x');
118 |   });
119 |   
120 |   test('getStorageAtHandler should return storage at a position', async () => {
121 |     const result = await getStorageAtHandler({ 
122 |       provider: 'https://mainnet.infura.io/v3/your-api-key',
123 |       address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
124 |       position: '0x0'
125 |     });
126 |     
127 |     expect(result.isError).toBe(false);
128 |     expect(result.toolResult).toHaveProperty('storage');
129 |     expect(result.toolResult.storage).toBe('0x0000000000000000000000000000000000000000000000000000000000000000');
130 |   });
131 |   
132 |   test('estimateGasHandler should estimate gas for a transaction', async () => {
133 |     const result = await estimateGasHandler({ 
134 |       provider: 'https://mainnet.infura.io/v3/your-api-key',
135 |       transaction: {
136 |         to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
137 |         data: '0x'
138 |       }
139 |     });
140 |     
141 |     expect(result.isError).toBe(false);
142 |     expect(result.toolResult).toHaveProperty('gasEstimate');
143 |     expect(result.toolResult.gasEstimate).toBe('21000');
144 |   });
145 |   
146 |   test('getLogsHandler should return logs matching a filter', async () => {
147 |     const result = await getLogsHandler({ 
148 |       provider: 'https://mainnet.infura.io/v3/your-api-key',
149 |       filter: {
150 |         address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
151 |         fromBlock: 'latest',
152 |         toBlock: 'latest'
153 |       }
154 |     });
155 |     
156 |     expect(result.isError).toBe(false);
157 |     expect(result.toolResult).toHaveProperty('logs');
158 |     expect(Array.isArray(result.toolResult.logs)).toBe(true);
159 |     expect(result.toolResult.logs.length).toBe(1);
160 |     expect(result.toolResult.logs[0]).toHaveProperty('blockNumber');
161 |     expect(result.toolResult.logs[0]).toHaveProperty('topics');
162 |   });
163 |   
164 |   test('getEnsResolverHandler should return an ENS resolver', async () => {
165 |     const result = await getEnsResolverHandler({ 
166 |       provider: 'https://mainnet.infura.io/v3/your-api-key',
167 |       name: 'test.eth'
168 |     });
169 |     
170 |     expect(result.isError).toBe(false);
171 |     expect(result.toolResult).toHaveProperty('resolver');
172 |     expect(result.toolResult.resolver).toHaveProperty('address');
173 |     expect(result.toolResult.resolver).toHaveProperty('name');
174 |   });
175 |   
176 |   test('lookupAddressHandler should lookup an ENS name for an address', async () => {
177 |     const result = await lookupAddressHandler({ 
178 |       provider: 'https://mainnet.infura.io/v3/your-api-key',
179 |       address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'
180 |     });
181 |     
182 |     expect(result.isError).toBe(false);
183 |     expect(result.toolResult).toHaveProperty('name');
184 |     expect(result.toolResult.name).toBe('test.eth');
185 |   });
186 |   
187 |   test('resolveNameHandler should resolve an ENS name to an address', async () => {
188 |     const result = await resolveNameHandler({ 
189 |       provider: 'https://mainnet.infura.io/v3/your-api-key',
190 |       name: 'test.eth'
191 |     });
192 |     
193 |     expect(result.isError).toBe(false);
194 |     expect(result.toolResult).toHaveProperty('address');
195 |     expect(result.toolResult.address).toBe('0x742d35Cc6634C0532925a3b844Bc454e4438f44e');
196 |   });
197 | });
198 | 
```

--------------------------------------------------------------------------------
/tests/handlers/wallet.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { ethers } from 'ethers';
  2 | import {
  3 |   createWalletHandler,
  4 |   fromPrivateKeyHandler,
  5 |   fromMnemonicHandler,
  6 |   fromEncryptedJsonHandler,
  7 |   encryptWalletHandler,
  8 |   getAddressHandler,
  9 |   getPublicKeyHandler,
 10 |   getPrivateKeyHandler,
 11 |   getMnemonicHandler,
 12 |   getBalanceHandler,
 13 |   getChainIdHandler,
 14 |   getGasPriceHandler,
 15 |   getTransactionCountHandler,
 16 |   callHandler,
 17 |   signMessageHandler,
 18 |   verifyMessageHandler
 19 | } from '../../src/handlers/wallet.js';
 20 | 
 21 | // Mock ethers.js functions
 22 | jest.mock('ethers', () => {
 23 |   const originalModule = jest.requireActual('ethers');
 24 |   
 25 |   // Create a mock wallet
 26 |   const mockWallet = {
 27 |     address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
 28 |     privateKey: '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
 29 |     publicKey: '0x04a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6',
 30 |     mnemonic: {
 31 |       phrase: 'test test test test test test test test test test test junk',
 32 |       path: "m/44'/60'/0'/0/0",
 33 |       locale: 'en'
 34 |     },
 35 |     connect: jest.fn().mockImplementation((provider) => {
 36 |       mockWallet.provider = provider;
 37 |       return mockWallet;
 38 |     }),
 39 |     getBalance: jest.fn().mockResolvedValue(originalModule.utils.parseEther('1.0')),
 40 |     getChainId: jest.fn().mockResolvedValue(1),
 41 |     getGasPrice: jest.fn().mockResolvedValue(originalModule.utils.parseUnits('50', 'gwei')),
 42 |     getTransactionCount: jest.fn().mockResolvedValue(5),
 43 |     call: jest.fn().mockResolvedValue('0x0000000000000000000000000000000000000000000000000000000000000001'),
 44 |     signMessage: jest.fn().mockResolvedValue('0xsignature'),
 45 |     encrypt: jest.fn().mockResolvedValue(JSON.stringify({ version: 3, id: 'test', address: '742d35cc6634c0532925a3b844bc454e4438f44e' })),
 46 |     provider: null
 47 |   };
 48 |   
 49 |   // Create a mock provider
 50 |   const mockProvider = {
 51 |     getBalance: jest.fn().mockResolvedValue(originalModule.utils.parseEther('1.0')),
 52 |     getBlock: jest.fn().mockResolvedValue({ 
 53 |       hash: '0xblock', 
 54 |       number: 1000000, 
 55 |       timestamp: Date.now() / 1000 
 56 |     }),
 57 |     getTransaction: jest.fn().mockResolvedValue({ 
 58 |       hash: '0xtx', 
 59 |       from: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', 
 60 |       to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e' 
 61 |     }),
 62 |     getTransactionReceipt: jest.fn().mockResolvedValue({ 
 63 |       status: 1, 
 64 |       blockNumber: 1000000 
 65 |     }),
 66 |     getCode: jest.fn().mockResolvedValue('0x'),
 67 |     getStorageAt: jest.fn().mockResolvedValue('0x0000000000000000000000000000000000000000000000000000000000000000'),
 68 |     estimateGas: jest.fn().mockResolvedValue(ethers.BigNumber.from(21000)),
 69 |     getLogs: jest.fn().mockResolvedValue([]),
 70 |     getResolver: jest.fn().mockResolvedValue(null),
 71 |     lookupAddress: jest.fn().mockResolvedValue(null),
 72 |     resolveName: jest.fn().mockResolvedValue(null),
 73 |     getNetwork: jest.fn().mockResolvedValue({ name: 'homestead', chainId: 1 }),
 74 |     getBlockNumber: jest.fn().mockResolvedValue(1000000),
 75 |     getFeeData: jest.fn().mockResolvedValue({
 76 |       gasPrice: ethers.utils.parseUnits('50', 'gwei'),
 77 |       maxFeePerGas: ethers.utils.parseUnits('100', 'gwei'),
 78 |       maxPriorityFeePerGas: ethers.utils.parseUnits('2', 'gwei')
 79 |     })
 80 |   };
 81 |   
 82 |   return {
 83 |     ...originalModule,
 84 |     Wallet: {
 85 |       createRandom: jest.fn().mockReturnValue(mockWallet),
 86 |       fromMnemonic: jest.fn().mockReturnValue(mockWallet),
 87 |       fromEncryptedJson: jest.fn().mockResolvedValue(mockWallet)
 88 |     },
 89 |     providers: {
 90 |       JsonRpcProvider: jest.fn().mockReturnValue(mockProvider)
 91 |     },
 92 |     getDefaultProvider: jest.fn().mockReturnValue(mockProvider),
 93 |     utils: originalModule.utils
 94 |   };
 95 | });
 96 | 
 97 | describe('Wallet Creation and Management Handlers', () => {
 98 |   test('createWalletHandler should create a random wallet', async () => {
 99 |     const result = await createWalletHandler({});
100 |     
101 |     expect(result.isError).toBe(false);
102 |     expect(result.toolResult).toHaveProperty('address');
103 |     expect(result.toolResult).toHaveProperty('privateKey');
104 |     expect(result.toolResult).toHaveProperty('publicKey');
105 |     expect(result.toolResult).toHaveProperty('mnemonic');
106 |   });
107 |   
108 |   test('createWalletHandler should encrypt wallet if password is provided', async () => {
109 |     const result = await createWalletHandler({ password: 'test123' });
110 |     
111 |     expect(result.isError).toBe(false);
112 |     expect(result.toolResult).toHaveProperty('encryptedWallet');
113 |   });
114 |   
115 |   test('fromPrivateKeyHandler should create a wallet from a private key', async () => {
116 |     const result = await fromPrivateKeyHandler({ 
117 |       privateKey: '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' 
118 |     });
119 |     
120 |     expect(result.isError).toBe(false);
121 |     expect(result.toolResult).toHaveProperty('address');
122 |     expect(result.toolResult).toHaveProperty('privateKey');
123 |     expect(result.toolResult).toHaveProperty('publicKey');
124 |   });
125 |   
126 |   test('fromMnemonicHandler should create a wallet from a mnemonic', async () => {
127 |     const result = await fromMnemonicHandler({ 
128 |       mnemonic: 'test test test test test test test test test test test junk' 
129 |     });
130 |     
131 |     expect(result.isError).toBe(false);
132 |     expect(result.toolResult).toHaveProperty('address');
133 |     expect(result.toolResult).toHaveProperty('privateKey');
134 |     expect(result.toolResult).toHaveProperty('publicKey');
135 |     expect(result.toolResult).toHaveProperty('mnemonic');
136 |   });
137 |   
138 |   test('fromEncryptedJsonHandler should create a wallet from encrypted JSON', async () => {
139 |     const result = await fromEncryptedJsonHandler({ 
140 |       json: JSON.stringify({ version: 3, id: 'test', address: '742d35cc6634c0532925a3b844bc454e4438f44e' }),
141 |       password: 'test123'
142 |     });
143 |     
144 |     expect(result.isError).toBe(false);
145 |     expect(result.toolResult).toHaveProperty('address');
146 |     expect(result.toolResult).toHaveProperty('privateKey');
147 |     expect(result.toolResult).toHaveProperty('publicKey');
148 |   });
149 |   
150 |   test('encryptWalletHandler should encrypt a wallet', async () => {
151 |     const result = await encryptWalletHandler({ 
152 |       wallet: '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
153 |       password: 'test123'
154 |     });
155 |     
156 |     expect(result.isError).toBe(false);
157 |     expect(result.toolResult).toHaveProperty('encryptedWallet');
158 |   });
159 | });
160 | 
161 | describe('Wallet Properties Handlers', () => {
162 |   test('getAddressHandler should return the wallet address', async () => {
163 |     const result = await getAddressHandler({ 
164 |       wallet: '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' 
165 |     });
166 |     
167 |     expect(result.isError).toBe(false);
168 |     expect(result.toolResult).toHaveProperty('address');
169 |     expect(result.toolResult.address).toBe('0x742d35Cc6634C0532925a3b844Bc454e4438f44e');
170 |   });
171 |   
172 |   test('getPublicKeyHandler should return the wallet public key', async () => {
173 |     const result = await getPublicKeyHandler({ 
174 |       wallet: '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' 
175 |     });
176 |     
177 |     expect(result.isError).toBe(false);
178 |     expect(result.toolResult).toHaveProperty('publicKey');
179 |   });
180 |   
181 |   test('getPrivateKeyHandler should return the wallet private key', async () => {
182 |     const result = await getPrivateKeyHandler({ 
183 |       wallet: '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' 
184 |     });
185 |     
186 |     expect(result.isError).toBe(false);
187 |     expect(result.toolResult).toHaveProperty('privateKey');
188 |     expect(result.toolResult.privateKey).toBe('0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef');
189 |   });
190 |   
191 |   test('getMnemonicHandler should return the wallet mnemonic', async () => {
192 |     const result = await getMnemonicHandler({ 
193 |       wallet: 'test test test test test test test test test test test junk' 
194 |     });
195 |     
196 |     expect(result.isError).toBe(false);
197 |     expect(result.toolResult).toHaveProperty('mnemonic');
198 |     expect(result.toolResult.mnemonic).toBe('test test test test test test test test test test test junk');
199 |   });
200 | });
201 | 
202 | describe('Blockchain Methods Handlers', () => {
203 |   test('getBalanceHandler should return the wallet balance', async () => {
204 |     const result = await getBalanceHandler({ 
205 |       wallet: '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
206 |       provider: 'https://mainnet.infura.io/v3/your-api-key'
207 |     });
208 |     
209 |     expect(result.isError).toBe(false);
210 |     expect(result.toolResult).toHaveProperty('balance');
211 |     expect(result.toolResult).toHaveProperty('balanceInEth');
212 |     expect(result.toolResult.balanceInEth).toBe('1.0');
213 |   });
214 |   
215 |   test('getChainIdHandler should return the chain ID', async () => {
216 |     const result = await getChainIdHandler({ 
217 |       wallet: '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
218 |       provider: 'https://mainnet.infura.io/v3/your-api-key'
219 |     });
220 |     
221 |     expect(result.isError).toBe(false);
222 |     expect(result.toolResult).toHaveProperty('chainId');
223 |     expect(result.toolResult.chainId).toBe(1);
224 |   });
225 |   
226 |   test('getGasPriceHandler should return the gas price', async () => {
227 |     const result = await getGasPriceHandler({ 
228 |       wallet: '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
229 |       provider: 'https://mainnet.infura.io/v3/your-api-key'
230 |     });
231 |     
232 |     expect(result.isError).toBe(false);
233 |     expect(result.toolResult).toHaveProperty('gasPrice');
234 |     expect(result.toolResult).toHaveProperty('gasPriceInGwei');
235 |     expect(result.toolResult.gasPriceInGwei).toBe('50.0');
236 |   });
237 |   
238 |   test('getTransactionCountHandler should return the transaction count', async () => {
239 |     const result = await getTransactionCountHandler({ 
240 |       wallet: '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
241 |       provider: 'https://mainnet.infura.io/v3/your-api-key'
242 |     });
243 |     
244 |     expect(result.isError).toBe(false);
245 |     expect(result.toolResult).toHaveProperty('transactionCount');
246 |     expect(result.toolResult.transactionCount).toBe(5);
247 |   });
248 |   
249 |   test('callHandler should call a contract method', async () => {
250 |     const result = await callHandler({ 
251 |       wallet: '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
252 |       provider: 'https://mainnet.infura.io/v3/your-api-key',
253 |       transaction: {
254 |         to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
255 |         data: '0x70a08231000000000000000000000000742d35cc6634c0532925a3b844bc454e4438f44e'
256 |       }
257 |     });
258 |     
259 |     expect(result.isError).toBe(false);
260 |     expect(result.toolResult).toHaveProperty('result');
261 |   });
262 | });
263 | 
264 | describe('Signing Methods Handlers', () => {
265 |   test('signMessageHandler should sign a message', async () => {
266 |     const result = await signMessageHandler({ 
267 |       wallet: '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
268 |       message: 'Hello, world!'
269 |     });
270 |     
271 |     expect(result.isError).toBe(false);
272 |     expect(result.toolResult).toHaveProperty('signature');
273 |     expect(result.toolResult).toHaveProperty('message');
274 |     expect(result.toolResult.signature).toBe('0xsignature');
275 |     expect(result.toolResult.message).toBe('Hello, world!');
276 |   });
277 |   
278 |   test('verifyMessageHandler should verify a signed message', async () => {
279 |     const result = await verifyMessageHandler({ 
280 |       message: 'Hello, world!',
281 |       signature: '0xsignature',
282 |       address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e'
283 |     });
284 |     
285 |     expect(result.isError).toBe(false);
286 |     expect(result.toolResult).toHaveProperty('isValid');
287 |     expect(result.toolResult).toHaveProperty('recoveredAddress');
288 |   });
289 | });
290 | 
```

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

```typescript
  1 | import {
  2 |   createWalletHandler,
  3 |   fromPrivateKeyHandler,
  4 |   fromMnemonicHandler,
  5 |   fromEncryptedJsonHandler,
  6 |   encryptWalletHandler,
  7 |   getAddressHandler,
  8 |   getPublicKeyHandler,
  9 |   getPrivateKeyHandler,
 10 |   getBalanceHandler,
 11 |   getChainIdHandler,
 12 |   getGasPriceHandler,
 13 |   getTransactionCountHandler,
 14 |   callHandler,
 15 |   sendTransactionHandler,
 16 |   signTransactionHandler,
 17 |   populateTransactionHandler,
 18 |   signMessageHandler,
 19 |   signTypedDataHandler,
 20 |   verifyMessageHandler,
 21 |   verifyTypedDataHandler,
 22 |   getBlockHandler,
 23 |   getTransactionHandler,
 24 |   getTransactionReceiptHandler,
 25 |   getCodeHandler,
 26 |   getStorageAtHandler,
 27 |   estimateGasHandler,
 28 |   getLogsHandler,
 29 |   getEnsResolverHandler,
 30 |   lookupAddressHandler,
 31 |   resolveNameHandler,
 32 |   getNetworkHandler,
 33 |   getBlockNumberHandler,
 34 |   getFeeDataHandler,
 35 |   createMnemonicPhraseHandler,
 36 |   setProviderHandler
 37 | } from "./handlers/wallet.js";
 38 | 
 39 | export const tools = [
 40 |   {
 41 |     name: "wallet_provider_set",
 42 |     description: "Set the provider URL. By default, the provider URL is set to the ETH mainnet or the URL set in the PROVIDER_URL environment variable.",
 43 |     inputSchema: {
 44 |       type: "object",
 45 |       properties: {
 46 |         providerURL: { type: "string", description: "The provider RPC URL" }
 47 |       },
 48 |       required: ["providerURL"]
 49 |     }
 50 |   },
 51 |   // Wallet Creation and Management
 52 |   {
 53 |     name: "wallet_create_random",
 54 |     description: "Create a new wallet with a random private key",
 55 |     inputSchema: {
 56 |       type: "object",
 57 |       properties: {
 58 |         password: { type: "string", description: "Optional password to encrypt the wallet" },
 59 |         path: { type: "string", description: "Optional HD path" },
 60 |         locale: { type: "string", description: "Optional locale for the wordlist" }
 61 |       },
 62 |       required: []
 63 |     }
 64 |   },
 65 |   {
 66 |     name: "wallet_from_private_key",
 67 |     description: "Create a wallet from a private key",
 68 |     inputSchema: {
 69 |       type: "object",
 70 |       properties: {
 71 |         privateKey: { type: "string", description: "The private key" }
 72 |       },
 73 |       required: ["privateKey"]
 74 |     }
 75 |   },
 76 |   {
 77 |     name: "wallet_create_mnemonic_phrase",
 78 |     description: "Create a mnemonic phrase",
 79 |     inputSchema: {
 80 |       type: "object",
 81 |       properties: {
 82 |         length: { type: "number", description: "The length of the mnemonic phrase", enum: [12, 15, 18, 21, 24] },
 83 |         locale: { type: "string", description: "Optional locale for the wordlist" }
 84 |       },
 85 |       required: ["length"]
 86 |     }
 87 |   },
 88 |   {
 89 |     name: "wallet_from_mnemonic",
 90 |     description: "Create a wallet from a mnemonic phrase",
 91 |     inputSchema: {
 92 |       type: "object",
 93 |       properties: {
 94 |         mnemonic: { type: "string", description: "The mnemonic phrase" },
 95 |         path: { type: "string", description: "Optional HD path" },
 96 |         locale: { type: "string", description: "Optional locale for the wordlist" }
 97 |       },
 98 |       required: ["mnemonic"]
 99 |     }
100 |   },
101 |   {
102 |     name: "wallet_from_encrypted_json",
103 |     description: "Create a wallet by decrypting an encrypted JSON wallet",
104 |     inputSchema: {
105 |       type: "object",
106 |       properties: {
107 |         json: { type: "string", description: "The encrypted JSON wallet" },
108 |         password: { type: "string", description: "The password to decrypt the wallet" }
109 |       },
110 |       required: ["json", "password"]
111 |     }
112 |   },
113 |   {
114 |     name: "wallet_encrypt",
115 |     description: "Encrypt a wallet with a password",
116 |     inputSchema: {
117 |       type: "object",
118 |       properties: {
119 |         wallet: { type: "string", description: "The wallet to encrypt (private key, mnemonic, or JSON)" },
120 |         password: { type: "string", description: "The password to encrypt the wallet" },
121 |         options: { 
122 |           type: "object", 
123 |           description: "Optional encryption options",
124 |           properties: {
125 |             scrypt: {
126 |               type: "object",
127 |               properties: {
128 |                 N: { type: "number" },
129 |                 r: { type: "number" },
130 |                 p: { type: "number" }
131 |               }
132 |             }
133 |           }
134 |         }
135 |       },
136 |       required: ["wallet", "password"]
137 |     }
138 |   },
139 | 
140 |   // Wallet Properties
141 |   {
142 |     name: "wallet_get_address",
143 |     description: "Get the wallet address",
144 |     inputSchema: {
145 |       type: "object",
146 |       properties: {
147 |         wallet: { type: "string", description: "The wallet (private key, mnemonic, or JSON). If not provided, uses PRIVATE_KEY environment variable if set." }
148 |       },
149 |       required: []
150 |     }
151 |   },
152 |   {
153 |     name: "wallet_get_public_key",
154 |     description: "Get the wallet public key",
155 |     inputSchema: {
156 |       type: "object",
157 |       properties: {
158 |         wallet: { type: "string", description: "The wallet (private key, mnemonic, or JSON). If not provided, uses PRIVATE_KEY environment variable if set." }
159 |       },
160 |       required: []
161 |     }
162 |   },
163 |   {
164 |     name: "wallet_get_private_key",
165 |     description: "Get the wallet private key (with appropriate security warnings)",
166 |     inputSchema: {
167 |       type: "object",
168 |       properties: {
169 |         wallet: { type: "string", description: "The wallet (private key, mnemonic, or JSON). If not provided, uses PRIVATE_KEY environment variable if set." },
170 |         password: { type: "string", description: "The password to decrypt the wallet if it's encrypted" }
171 |       },
172 |       required: []
173 |     }
174 |   },
175 |   // Blockchain Methods
176 |   {
177 |     name: "wallet_get_balance",
178 |     description: "Get the balance of the wallet",
179 |     inputSchema: {
180 |       type: "object",
181 |       properties: {
182 |         wallet: { type: "string", description: "The wallet (private key, mnemonic, or JSON). If not provided, uses PRIVATE_KEY environment variable if set." },
183 |         blockTag: { type: "string", description: "Optional block tag (latest, pending, etc.)" }
184 |       },
185 |       required: []
186 |     }
187 |   },
188 |   {
189 |     name: "wallet_get_chain_id",
190 |     description: "Get the chain ID the wallet is connected to",
191 |     inputSchema: {
192 |       type: "object",
193 |       properties: {
194 |         wallet: { type: "string", description: "The wallet (private key, mnemonic, or JSON). If not provided, uses PRIVATE_KEY environment variable if set." }
195 |       },
196 |       required: []
197 |     }
198 |   },
199 |   {
200 |     name: "wallet_get_gas_price",
201 |     description: "Get the current gas price",
202 |     inputSchema: {
203 |       type: "object",
204 |       properties: {
205 |         wallet: { type: "string", description: "The wallet (private key, mnemonic, or JSON). If not provided, uses PRIVATE_KEY environment variable if set." }
206 |       },
207 |       required: []
208 |     }
209 |   },
210 |   {
211 |     name: "wallet_get_transaction_count",
212 |     description: "Get the number of transactions sent from this account (nonce)",
213 |     inputSchema: {
214 |       type: "object",
215 |       properties: {
216 |         wallet: { type: "string", description: "The wallet (private key, mnemonic, or JSON). If not provided, uses PRIVATE_KEY environment variable if set." },
217 |         blockTag: { type: "string", description: "Optional block tag (latest, pending, etc.)" }
218 |       },
219 |       required: []
220 |     }
221 |   },
222 |   {
223 |     name: "wallet_call",
224 |     description: "Call a contract method without sending a transaction",
225 |     inputSchema: {
226 |       type: "object",
227 |       properties: {
228 |         wallet: { type: "string", description: "The wallet (private key, mnemonic, or JSON). If not provided, uses PRIVATE_KEY environment variable if set." },
229 |         transaction: { 
230 |           type: "object", 
231 |           description: "The transaction to call",
232 |           properties: {
233 |             to: { type: "string" },
234 |             from: { type: "string" },
235 |             data: { type: "string" },
236 |             value: { type: "string" },
237 |             gasLimit: { type: "string" },
238 |             gasPrice: { type: "string" }
239 |           },
240 |           required: ["to"]
241 |         },
242 |         blockTag: { type: "string", description: "Optional block tag (latest, pending, etc.)" }
243 |       },
244 |       required: ["transaction"]
245 |     }
246 |   },
247 | 
248 |   // Transaction Methods
249 |   {
250 |     name: "wallet_send_transaction",
251 |     description: "Send a transaction",
252 |     inputSchema: {
253 |       type: "object",
254 |       properties: {
255 |         wallet: { type: "string", description: "The wallet (private key, mnemonic, or JSON). If not provided, uses PRIVATE_KEY environment variable if set." },
256 |         transaction: { 
257 |           type: "object", 
258 |           description: "The transaction to send",
259 |           properties: {
260 |             to: { type: "string" },
261 |             from: { type: "string" },
262 |             data: { type: "string" },
263 |             value: { type: "string" },
264 |             gasLimit: { type: "string" },
265 |             gasPrice: { type: "string" },
266 |             nonce: { type: "number" },
267 |             type: { type: "number" },
268 |             maxFeePerGas: { type: "string" },
269 |             maxPriorityFeePerGas: { type: "string" }
270 |           },
271 |           required: ["to"]
272 |         }
273 |       },
274 |       required: ["transaction"]
275 |     }
276 |   },
277 |   {
278 |     name: "wallet_sign_transaction",
279 |     description: "Sign a transaction without sending it",
280 |     inputSchema: {
281 |       type: "object",
282 |       properties: {
283 |         wallet: { type: "string", description: "The wallet (private key, mnemonic, or JSON). If not provided, uses PRIVATE_KEY environment variable if set." },
284 |         transaction: { 
285 |           type: "object", 
286 |           description: "The transaction to sign",
287 |           properties: {
288 |             to: { type: "string" },
289 |             from: { type: "string" },
290 |             data: { type: "string" },
291 |             value: { type: "string" },
292 |             gasLimit: { type: "string" },
293 |             gasPrice: { type: "string" },
294 |             nonce: { type: "number" },
295 |             type: { type: "number" },
296 |             maxFeePerGas: { type: "string" },
297 |             maxPriorityFeePerGas: { type: "string" }
298 |           },
299 |           required: ["to"]
300 |         }
301 |       },
302 |       required: ["transaction"]
303 |     }
304 |   },
305 |   {
306 |     name: "wallet_populate_transaction",
307 |     description: "Populate a transaction with missing fields",
308 |     inputSchema: {
309 |       type: "object",
310 |       properties: {
311 |         wallet: { type: "string", description: "The wallet (private key, mnemonic, or JSON). If not provided, uses PRIVATE_KEY environment variable if set." },
312 |         transaction: { 
313 |           type: "object", 
314 |           description: "The transaction to populate",
315 |           properties: {
316 |             to: { type: "string" },
317 |             from: { type: "string" },
318 |             data: { type: "string" },
319 |             value: { type: "string" },
320 |             gasLimit: { type: "string" },
321 |             gasPrice: { type: "string" },
322 |             nonce: { type: "number" },
323 |             type: { type: "number" },
324 |             maxFeePerGas: { type: "string" },
325 |             maxPriorityFeePerGas: { type: "string" }
326 |           },
327 |           required: ["to"]
328 |         }
329 |       },
330 |       required: ["transaction"]
331 |     }
332 |   },
333 | 
334 |   // Signing Methods
335 |   {
336 |     name: "wallet_sign_message",
337 |     description: "Sign a message",
338 |     inputSchema: {
339 |       type: "object",
340 |       properties: {
341 |         wallet: { type: "string", description: "The wallet (private key, mnemonic, or JSON). If not provided, uses PRIVATE_KEY environment variable if set." },
342 |         message: { type: "string", description: "The message to sign" }
343 |       },
344 |       required: ["message"]
345 |     }
346 |   },
347 |   {
348 |     name: "wallet_sign_typed_data",
349 |     description: "Sign typed data (EIP-712)",
350 |     inputSchema: {
351 |       type: "object",
352 |       properties: {
353 |         wallet: { type: "string", description: "The wallet (private key, mnemonic, or JSON). If not provided, uses PRIVATE_KEY environment variable if set." },
354 |         domain: { type: "object", description: "The domain data" },
355 |         types: { type: "object", description: "The type definitions" },
356 |         value: { type: "object", description: "The value to sign" }
357 |       },
358 |       required: ["domain", "types", "value"]
359 |     }
360 |   },
361 |   {
362 |     name: "wallet_verify_message",
363 |     description: "Verify a signed message",
364 |     inputSchema: {
365 |       type: "object",
366 |       properties: {
367 |         message: { type: "string", description: "The original message" },
368 |         signature: { type: "string", description: "The signature to verify" },
369 |         address: { type: "string", description: "The address that supposedly signed the message" }
370 |       },
371 |       required: ["message", "signature", "address"]
372 |     }
373 |   },
374 |   {
375 |     name: "wallet_verify_typed_data",
376 |     description: "Verify signed typed data",
377 |     inputSchema: {
378 |       type: "object",
379 |       properties: {
380 |         domain: { type: "object", description: "The domain data" },
381 |         types: { type: "object", description: "The type definitions" },
382 |         value: { type: "object", description: "The value that was signed" },
383 |         signature: { type: "string", description: "The signature to verify" },
384 |         address: { type: "string", description: "The address that supposedly signed the data" }
385 |       },
386 |       required: ["domain", "types", "value", "signature", "address"]
387 |     }
388 |   },
389 | 
390 |   // Provider Methods
391 |   {
392 |     name: "provider_get_block",
393 |     description: "Get a block by number or hash",
394 |     inputSchema: {
395 |       type: "object",
396 |       properties: {
397 |         blockHashOrBlockTag: { type: "string", description: "Block hash or block tag (latest, pending, etc.)" },
398 |         includeTransactions: { type: "boolean", description: "Whether to include full transactions or just hashes" }
399 |       },
400 |       required: ["blockHashOrBlockTag"]
401 |     }
402 |   },
403 |   {
404 |     name: "provider_get_transaction",
405 |     description: "Get a transaction by hash",
406 |     inputSchema: {
407 |       type: "object",
408 |       properties: {
409 |         transactionHash: { type: "string", description: "The transaction hash" }
410 |       },
411 |       required: ["transactionHash"]
412 |     }
413 |   },
414 |   {
415 |     name: "provider_get_transaction_receipt",
416 |     description: "Get a transaction receipt",
417 |     inputSchema: {
418 |       type: "object",
419 |       properties: {
420 |         transactionHash: { type: "string", description: "The transaction hash" }
421 |       },
422 |       required: ["transactionHash"]
423 |     }
424 |   },
425 |   {
426 |     name: "provider_get_code",
427 |     description: "Get the code at an address",
428 |     inputSchema: {
429 |       type: "object",
430 |       properties: {
431 |         address: { type: "string", description: "The address to get code from" },
432 |         blockTag: { type: "string", description: "Optional block tag (latest, pending, etc.)" }
433 |       },
434 |       required: ["address"]
435 |     }
436 |   },
437 |   {
438 |     name: "provider_get_storage_at",
439 |     description: "Get the storage at a position for an address",
440 |     inputSchema: {
441 |       type: "object",
442 |       properties: {
443 |         address: { type: "string", description: "The address to get storage from" },
444 |         position: { type: "string", description: "The storage position" },
445 |         blockTag: { type: "string", description: "Optional block tag (latest, pending, etc.)" }
446 |       },
447 |       required: ["address", "position"]
448 |     }
449 |   },
450 |   {
451 |     name: "provider_estimate_gas",
452 |     description: "Estimate the gas required for a transaction",
453 |     inputSchema: {
454 |       type: "object",
455 |       properties: {
456 |         transaction: { 
457 |           type: "object", 
458 |           description: "The transaction to estimate gas for",
459 |           properties: {
460 |             to: { type: "string" },
461 |             from: { type: "string" },
462 |             data: { type: "string" },
463 |             value: { type: "string" }
464 |           }
465 |         }
466 |       },
467 |       required: ["transaction"]
468 |     }
469 |   },
470 |   {
471 |     name: "provider_get_logs",
472 |     description: "Get logs that match a filter",
473 |     inputSchema: {
474 |       type: "object",
475 |       properties: {
476 |         filter: { 
477 |           type: "object", 
478 |           description: "The filter to apply",
479 |           properties: {
480 |             address: { type: "string" },
481 |             topics: { type: "array", items: { type: "string" } },
482 |             fromBlock: { type: "string" },
483 |             toBlock: { type: "string" }
484 |           }
485 |         }
486 |       },
487 |       required: ["filter"]
488 |     }
489 |   },
490 |   {
491 |     name: "provider_get_ens_resolver",
492 |     description: "Get the ENS resolver for a name",
493 |     inputSchema: {
494 |       type: "object",
495 |       properties: {
496 |         name: { type: "string", description: "The ENS name" }
497 |       },
498 |       required: ["name"]
499 |     }
500 |   },
501 |   {
502 |     name: "provider_lookup_address",
503 |     description: "Lookup the ENS name for an address",
504 |     inputSchema: {
505 |       type: "object",
506 |       properties: {
507 |         address: { type: "string", description: "The address to lookup" }
508 |       },
509 |       required: ["address"]
510 |     }
511 |   },
512 |   {
513 |     name: "provider_resolve_name",
514 |     description: "Resolve an ENS name to an address",
515 |     inputSchema: {
516 |       type: "object",
517 |       properties: {
518 |         name: { type: "string", description: "The ENS name to resolve" }
519 |       },
520 |       required: ["name"]
521 |     }
522 |   },
523 | 
524 |   // Network Methods
525 |   {
526 |     name: "network_get_network",
527 |     description: "Get the current network information",
528 |     inputSchema: {
529 |       type: "object",
530 |       properties: {},
531 |       required: []
532 |     }
533 |   },
534 |   {
535 |     name: "network_get_block_number",
536 |     description: "Get the current block number",
537 |     inputSchema: {
538 |       type: "object",
539 |       properties: {},
540 |       required: []
541 |     }
542 |   },
543 |   {
544 |     name: "network_get_fee_data",
545 |     description: "Get the current fee data (base fee, max priority fee, etc.)",
546 |     inputSchema: {
547 |       type: "object",
548 |       properties: {},
549 |       required: []
550 |     }
551 |   }
552 | ];
553 | 
554 | type HandlerDictionary = Record<string, (input: any) => any>;
555 | 
556 | export const handlers: HandlerDictionary = {
557 |   // Provider Methods
558 |   "wallet_provider_set": setProviderHandler,
559 |   // Wallet Creation and Management
560 |   "wallet_create_random": createWalletHandler,
561 |   "wallet_from_private_key": fromPrivateKeyHandler,
562 |   "wallet_from_mnemonic": fromMnemonicHandler,
563 |   "wallet_from_encrypted_json": fromEncryptedJsonHandler,
564 |   "wallet_encrypt": encryptWalletHandler,
565 | 
566 |   // Wallet Properties
567 |   "wallet_get_address": getAddressHandler,
568 |   "wallet_get_public_key": getPublicKeyHandler,
569 |   "wallet_get_private_key": getPrivateKeyHandler,
570 | 
571 |   // Blockchain Methods
572 |   "wallet_get_balance": getBalanceHandler,
573 |   "wallet_get_chain_id": getChainIdHandler,
574 |   "wallet_get_gas_price": getGasPriceHandler,
575 |   "wallet_get_transaction_count": getTransactionCountHandler,
576 |   "wallet_call": callHandler,
577 | 
578 |   // Transaction Methods
579 |   "wallet_send_transaction": sendTransactionHandler,
580 |   "wallet_sign_transaction": signTransactionHandler,
581 |   "wallet_populate_transaction": populateTransactionHandler,
582 | 
583 |   // Signing Methods
584 |   "wallet_sign_message": signMessageHandler,
585 |   "wallet_sign_typed_data": signTypedDataHandler,
586 |   "wallet_verify_message": verifyMessageHandler,
587 |   "wallet_verify_typed_data": verifyTypedDataHandler,
588 | 
589 |   // Provider Methods
590 |   "provider_get_block": getBlockHandler,
591 |   "provider_get_transaction": getTransactionHandler,
592 |   "provider_get_transaction_receipt": getTransactionReceiptHandler,
593 |   "provider_get_code": getCodeHandler,
594 |   "provider_get_storage_at": getStorageAtHandler,
595 |   "provider_estimate_gas": estimateGasHandler,
596 |   "provider_get_logs": getLogsHandler,
597 |   "provider_get_ens_resolver": getEnsResolverHandler,
598 |   "provider_lookup_address": lookupAddressHandler,
599 |   "provider_resolve_name": resolveNameHandler,
600 | 
601 |   // Network Methods
602 |   "network_get_network": getNetworkHandler,
603 |   "network_get_block_number": getBlockNumberHandler,
604 |   "network_get_fee_data": getFeeDataHandler,
605 | 
606 |   // Mnemonic Methods
607 |   "wallet_create_mnemonic_phrase": createMnemonicPhraseHandler
608 | };
609 | 
```

--------------------------------------------------------------------------------
/src/handlers/wallet.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { ethers, providers } from "ethers";
  2 | import { ToolResultSchema } from "../types.js";
  3 | import { createSuccessResponse, createErrorResponse, getProvider, getWallet, setProvider } from "./utils.js";
  4 | import { fromPrivateKeyHandlerInput, createMnemonicPhraseHandlerInput } from "./wallet.types.js";
  5 | import { generateMnemonic,  } from '@scure/bip39';
  6 | 
  7 | // Provider handlers
  8 | 
  9 | export const setProviderHandler = async (input: any): Promise<ToolResultSchema> => {
 10 |   try {
 11 |     setProvider(input.providerURL);
 12 |     return createSuccessResponse(`Provider set successfully:
 13 |       Provider URL: ${input.providerURL}
 14 |     `);
 15 |   } catch (error) {
 16 |     return createErrorResponse(`Failed to set provider: ${(error as Error).message}`);
 17 |   }
 18 | };
 19 | 
 20 | 
 21 | // Wallet Creation and Management
 22 | export const createWalletHandler = async (input: any): Promise<ToolResultSchema> => {
 23 |   try {
 24 |     const options: any = {};
 25 | 
 26 |     if (input.path) {
 27 |       options.path = input.path;
 28 |     }
 29 | 
 30 |     if (input.locale) {
 31 |       options.locale = input.locale;
 32 |     }
 33 | 
 34 |     const wallet = ethers.Wallet.createRandom(options);
 35 | 
 36 |     let result: any = {
 37 |       address: wallet.address,
 38 |       mnemonic: wallet.mnemonic?.phrase,
 39 |       privateKey: wallet.privateKey,
 40 |       publicKey: wallet.publicKey
 41 |     };
 42 | 
 43 |     if (input.password) {
 44 |       // If a password is provided, encrypt the wallet
 45 |       const encryptedWallet = await wallet.encrypt(input.password);
 46 |       result.encryptedWallet = encryptedWallet;
 47 |     }
 48 | 
 49 |     return createSuccessResponse(`Wallet created successfully:
 50 |       Address: ${wallet.address}
 51 |       Private Key: ${wallet.privateKey}
 52 |       Public Key: ${wallet.publicKey}
 53 |       Mnemonic: ${wallet.mnemonic?.phrase}
 54 |       Encrypted Wallet: ${result.encryptedWallet ? "Yes" : "No"}
 55 |     `);
 56 |   } catch (error) {
 57 |     return createErrorResponse(`Failed to create wallet: ${(error as Error).message}`);
 58 |   }
 59 | };
 60 | 
 61 | export const fromPrivateKeyHandler = async (input: fromPrivateKeyHandlerInput): Promise<ToolResultSchema> => {
 62 |   try {
 63 |     if (!input.privateKey) {
 64 |       return createErrorResponse("Private key is required");
 65 |     }
 66 | 
 67 |     const provider = getProvider()
 68 |     const wallet = new ethers.Wallet(input.privateKey, provider);
 69 | 
 70 |     return createSuccessResponse(
 71 |     `Wallet created from private key successfully:
 72 |       Address: ${wallet.address}
 73 |       Private Key: ${wallet.privateKey}
 74 |       Public Key: ${wallet.publicKey}
 75 |     `);
 76 |   } catch (error) {
 77 |     return createErrorResponse(`Failed to create wallet from private key: ${(error as Error).message}`);
 78 |   }
 79 | };
 80 | 
 81 | export const createMnemonicPhraseHandler = async (input: createMnemonicPhraseHandlerInput): Promise<ToolResultSchema> => {
 82 |   try {
 83 |     const { wordlist } = await import(`@scure/bip39/wordlists/${input.locale || 'english'}`);
 84 |     if (!wordlist) {
 85 |       return createErrorResponse("Invalid locale");
 86 |     }
 87 |     // Convert length to entropy bits (12 words = 128 bits, 15 words = 160 bits, etc)
 88 |     const entropyBits = ((input.length ?? 12) / 3) * 32;
 89 |     const mnemonic = generateMnemonic(wordlist, entropyBits);
 90 | 
 91 |     return createSuccessResponse(
 92 |     `Mnemonic phrase created successfully:
 93 |       Mnemonic: "${mnemonic}"
 94 |     `);
 95 |   } catch (error) {
 96 |     return createErrorResponse(`Failed to create mnemonic phrase: ${(error as Error).message}`);
 97 |   }
 98 | };
 99 | 
100 | export const fromMnemonicHandler = async (input: any): Promise<ToolResultSchema> => {
101 |   try {
102 |     if (!input.mnemonic) {
103 |       return createErrorResponse("Mnemonic is required");
104 |     }
105 | 
106 |     const options: any = {
107 |       path: input.path,
108 |       wordlist: (input.locale && ethers.wordlists[input.locale]) || ethers.wordlists.en
109 |     };
110 | 
111 |     const provider = getProvider();
112 |     const wallet = ethers.Wallet.fromMnemonic(input.mnemonic, options.path, options.wordlist);
113 | 
114 |     if (provider) wallet.connect(provider);
115 | 
116 |     return createSuccessResponse(
117 |     `Wallet created from mnemonic successfully:
118 |       Address: ${wallet.address}
119 |       Mnemonic: ${input.mnemonic}
120 |       Private Key: ${wallet.privateKey}
121 |       Public Key: ${wallet.publicKey}
122 |     `);
123 |   } catch (error) {
124 |     return createErrorResponse(`Failed to create wallet from mnemonic: ${(error as Error).message}`);
125 |   }
126 | };
127 | 
128 | export const fromEncryptedJsonHandler = async (input: any): Promise<ToolResultSchema> => {
129 |   try {
130 |     if (!input.json) {
131 |       return createErrorResponse("Encrypted JSON is required");
132 |     }
133 | 
134 |     if (!input.password) {
135 |       return createErrorResponse("Password is required");
136 |     }
137 | 
138 |     const wallet = await ethers.Wallet.fromEncryptedJson(input.json, input.password);
139 |     const provider = getProvider()
140 | 
141 |     if (provider) {
142 |       wallet.connect(provider);
143 |     }
144 | 
145 |     return createSuccessResponse(
146 |     `Wallet created from encrypted JSON successfully
147 |       Address: ${wallet.address}
148 |       Private Key: ${wallet.privateKey}
149 |       Public Key: ${wallet.publicKey}
150 |     `);
151 |   } catch (error) {
152 |     return createErrorResponse(`Failed to create wallet from encrypted JSON: ${(error as Error).message}`);
153 |   }
154 | };
155 | 
156 | export const encryptWalletHandler = async (input: any): Promise<ToolResultSchema> => {
157 |   try {
158 |     if (!input.wallet) {
159 |       return createErrorResponse("Wallet is required");
160 |     }
161 | 
162 |     if (!input.password) {
163 |       return createErrorResponse("Password is required");
164 |     }
165 | 
166 |     const wallet = await getWallet(input.wallet);
167 |     const encryptedWallet = await wallet.encrypt(input.password, input.options);
168 | 
169 |     return createSuccessResponse(
170 |     `Wallet encrypted successfully
171 |       Encrypted Wallet: ${encryptedWallet}
172 |     `);
173 |   } catch (error) {
174 |     return createErrorResponse(`Failed to encrypt wallet: ${(error as Error).message}`);
175 |   }
176 | };
177 | 
178 | // Wallet Properties
179 | 
180 | export const getAddressHandler = async (input: any): Promise<ToolResultSchema> => {
181 |   try {
182 |     const wallet = await getWallet(input.wallet);
183 | 
184 |     return createSuccessResponse(
185 |     `Wallet address retrieved successfully:
186 |       Address: ${wallet.address}
187 |     `);
188 |   } catch (error) {
189 |     return createErrorResponse(`Failed to get wallet address: ${(error as Error).message}`);
190 |   }
191 | };
192 | 
193 | export const getPublicKeyHandler = async (input: any): Promise<ToolResultSchema> => {
194 |   try {
195 |     const wallet = await getWallet(input.wallet);
196 | 
197 |     return createSuccessResponse(
198 |     `Wallet public key retrieved successfully:
199 |       Public Key: ${wallet.publicKey}
200 |     `);
201 |   } catch (error) {
202 |     return createErrorResponse(`Failed to get wallet public key: ${(error as Error).message}`);
203 |   }
204 | };
205 | 
206 | export const getPrivateKeyHandler = async (input: any): Promise<ToolResultSchema> => {
207 |   try {
208 |     const wallet = await getWallet(input.wallet, input.password);
209 | 
210 |     return createSuccessResponse(
211 |     `Wallet private key retrieved successfully:
212 |       Private Key: ${wallet.privateKey}
213 |     `);
214 |   } catch (error) {
215 |     return createErrorResponse(`Failed to get wallet private key: ${(error as Error).message}`);
216 |   }
217 | };
218 | // Blockchain Methods
219 | 
220 | export const getBalanceHandler = async (input: any): Promise<ToolResultSchema> => {
221 |   try {
222 |     const wallet = await getWallet(input.wallet, input.password);
223 | 
224 |     const balance = await wallet.getBalance(input.blockTag ?? "latest");
225 | 
226 |     return createSuccessResponse(
227 |       `Wallet balance retrieved successfully
228 |       Balance: ${balance.toString()}
229 |       Balance in ETH: ${ethers.utils.formatEther(balance)}
230 |     `);
231 |   } catch (error) {
232 |     return createErrorResponse(`Failed to get wallet balance: ${(error as Error).message}`);
233 |   }
234 | };
235 | 
236 | export const getChainIdHandler = async (input: any): Promise<ToolResultSchema> => {
237 |   try {
238 |     const wallet = await getWallet(input.wallet, input.password);
239 | 
240 |     if (!wallet.provider) {
241 |       return createErrorResponse("Provider is required to get chain ID, please set the provider URL");
242 |     }
243 | 
244 |     const chainId = await wallet.getChainId();
245 | 
246 |     return createSuccessResponse(
247 |     `Chain ID retrieved successfully
248 |       Chain ID: ${chainId.toString()}
249 |     `);
250 |   } catch (error) {
251 |     return createErrorResponse(`Failed to get chain ID: ${(error as Error).message}`);
252 |   }
253 | };
254 | 
255 | export const getGasPriceHandler = async (input: any): Promise<ToolResultSchema> => {
256 |   try {
257 |     const wallet = await getWallet(input.wallet, input.password);
258 | 
259 |     if (!wallet.provider) {
260 |       return createErrorResponse("Provider is required to get gas price, please set the provider URL");
261 |     }
262 | 
263 |     const gasPrice = await wallet.getGasPrice();
264 | 
265 |     return createSuccessResponse(
266 |     `Gas price retrieved successfully
267 |       Gas price: ${gasPrice.toString()}
268 |       Gas price in Gwei: ${ethers.utils.formatUnits(gasPrice, "gwei")}
269 |     `);
270 |   } catch (error) {
271 |     return createErrorResponse(`Failed to get gas price: ${(error as Error).message}`);
272 |   }
273 | };
274 | 
275 | export const getTransactionCountHandler = async (input: any): Promise<ToolResultSchema> => {
276 |   try {
277 |     const wallet = await getWallet(input.wallet, input.password);
278 | 
279 |     if (!wallet.provider) {
280 |       return createErrorResponse("Provider is required to get transaction count, please set the provider URL");
281 |     }
282 | 
283 |     const transactionCount = await wallet.getTransactionCount(input.blockTag);
284 | 
285 |     return createSuccessResponse(
286 |     `Transaction count retrieved successfully
287 |       Transaction count: ${transactionCount.toString()}
288 |     `);
289 |   } catch (error) {
290 |     return createErrorResponse(`Failed to get transaction count: ${(error as Error).message}`);
291 |   }
292 | };
293 | 
294 | export const callHandler = async (input: any): Promise<ToolResultSchema> => {
295 |   try {
296 |     if (!input.transaction) {
297 |       return createErrorResponse("Transaction is required");
298 |     }
299 | 
300 |     const wallet = await getWallet(input.wallet, input.password);
301 | 
302 |     if (!wallet.provider) {
303 |       return createErrorResponse("Provider is required to call a contract, please set the provider URL");
304 |     }
305 | 
306 |     const result = await wallet.call(input.transaction, input.blockTag);
307 | 
308 |     return createSuccessResponse(
309 |     `Contract call executed successfully
310 |       Result: ${result}
311 |     `);
312 |   } catch (error) {
313 |     return createErrorResponse(`Failed to call contract: ${(error as Error).message}`);
314 |   }
315 | };
316 | 
317 | // Transaction Methods
318 | 
319 | export const sendTransactionHandler = async (input: any): Promise<ToolResultSchema> => {
320 |   try {
321 |     if (!input.transaction) {
322 |       return createErrorResponse("Transaction is required");
323 |     }
324 | 
325 |     const wallet = await getWallet(input.wallet, input.password);
326 |     if (!wallet.provider) {
327 |       return createErrorResponse("Provider is required to send a transaction, please set the provider URL");
328 |     }
329 | 
330 |     const tx = await wallet.sendTransaction(input.transaction);
331 | 
332 |     return createSuccessResponse(
333 |     `Transaction sent successfully
334 |       Hash: ${tx.hash}
335 |       Nonce: ${tx.nonce.toString()}
336 |       Gas limit: ${tx.gasLimit.toString()}
337 |       Gas price: ${tx.gasPrice?.toString()}
338 |       Data: ${tx.data}
339 |     `);
340 |   } catch (error) {
341 |     return createErrorResponse(`Failed to send transaction: ${(error as Error).message}`);
342 |   }
343 | };
344 | 
345 | export const signTransactionHandler = async (input: any): Promise<ToolResultSchema> => {
346 |   try {
347 |     if (!input.transaction) {
348 |       return createErrorResponse("Transaction is required");
349 |     }
350 | 
351 |     const wallet = await getWallet(input.wallet, input.password);
352 | 
353 |     // For signing a transaction, we need to populate it first
354 |     const populatedTx = await wallet.populateTransaction(input.transaction);
355 |     const signedTx = await wallet.signTransaction(populatedTx);
356 | 
357 |     return createSuccessResponse(
358 |     `Transaction signed successfully
359 |       Signed transaction: ${signedTx}
360 |     `);
361 |   } catch (error) {
362 |     return createErrorResponse(`Failed to sign transaction: ${(error as Error).message}`);
363 |   }
364 | };
365 | 
366 | export const populateTransactionHandler = async (input: any): Promise<ToolResultSchema> => {
367 |   try {
368 |     if (!input.transaction) {
369 |       return createErrorResponse("Transaction is required");
370 |     }
371 | 
372 |     const wallet = await getWallet(input.wallet, input.password);
373 | 
374 |     if (!wallet.provider) {
375 |       return createErrorResponse("Provider is required to populate a transaction, please set the provider URL");
376 |     }
377 | 
378 |     const populatedTx = await wallet.populateTransaction(input.transaction);
379 | 
380 |     return createSuccessResponse(
381 |     `Transaction populated successfully
382 |       To: ${populatedTx.to}
383 |       From: ${populatedTx.from}
384 |       Nonce: ${populatedTx.nonce?.toString() ?? "Not specified"}
385 |       Gas limit: ${populatedTx.gasLimit?.toString() ?? "Not specified"}
386 |       Gas price: ${populatedTx.gasPrice?.toString() ?? "Not specified"}
387 |     `);
388 |   } catch (error) {
389 |     return createErrorResponse(`Failed to populate transaction: ${(error as Error).message}`);
390 |   }
391 | };
392 | 
393 | // Signing Methods
394 | 
395 | export const signMessageHandler = async (input: any): Promise<ToolResultSchema> => {
396 |   try {
397 |     if (!input.message) {
398 |       return createErrorResponse("Message is required");
399 |     }
400 | 
401 |     const wallet = await getWallet(input.wallet, input.password);
402 |     const signature = await wallet.signMessage(input.message);
403 | 
404 |     return createSuccessResponse(`Message signed successfully
405 |       Signature: ${signature}
406 |       Message: "${input.message}"
407 |     `);
408 |   } catch (error) {
409 |     return createErrorResponse(`Failed to sign message: ${(error as Error).message}`);
410 |   }
411 | };
412 | 
413 | export const signTypedDataHandler = async (input: any): Promise<ToolResultSchema> => {
414 |   try {
415 |     if (!input.wallet) {
416 |       return createErrorResponse("Wallet is required");
417 |     }
418 | 
419 |     if (!input.domain || !input.types || !input.value) {
420 |       return createErrorResponse("Domain, types, and value are required");
421 |     }
422 | 
423 |     const wallet = await getWallet(input.wallet, input.password);
424 | 
425 |     // Use ethers._signTypedData for EIP-712 signing
426 |     // @ts-ignore - _signTypedData is not in the type definitions but is available
427 |     const signature = await wallet._signTypedData(input.domain, input.types, input.value);
428 | 
429 |     return createSuccessResponse(
430 |     `Typed data signed successfully
431 |       Signature: ${signature}
432 |       Domain: ${input.domain}
433 |       Types: ${input.types}
434 |       Value: ${input.value}
435 |     `);
436 |   } catch (error) {
437 |     return createErrorResponse(`Failed to sign typed data: ${(error as Error).message}`);
438 |   }
439 | };
440 | 
441 | export const verifyMessageHandler = async (input: any): Promise<ToolResultSchema> => {
442 |   try {
443 |     if (!input.message || !input.signature || !input.address) {
444 |       return createErrorResponse("Message, signature, and address are required");
445 |     }
446 | 
447 |     const recoveredAddress = ethers.utils.verifyMessage(input.message, input.signature);
448 |     const isValid = recoveredAddress.toLowerCase() === input.address.toLowerCase();
449 | 
450 |     return createSuccessResponse(
451 |     isValid ? `Signature verified successfully
452 |       Message: "${input.message}"
453 |       Signature: ${input.signature}
454 |       Address: ${input.address}
455 |     ` : `Signature verification failed
456 |       Message: "${input.message}"
457 |       Signature: ${input.signature}
458 |       Address: ${input.address}
459 |     `);
460 |   } catch (error) {
461 |     return createErrorResponse(`Failed to verify message: ${(error as Error).message}`);
462 |   }
463 | };
464 | 
465 | export const verifyTypedDataHandler = async (input: any): Promise<ToolResultSchema> => {
466 |   try {
467 |     if (!input.domain || !input.types || !input.value || !input.signature || !input.address) {
468 |       return createErrorResponse("Domain, types, value, signature, and address are required");
469 |     }
470 | 
471 |     // Use ethers.utils.verifyTypedData for EIP-712 verification
472 |     const recoveredAddress = ethers.utils.verifyTypedData(
473 |       input.domain,
474 |       input.types,
475 |       input.value,
476 |       input.signature
477 |     );
478 | 
479 |     const isValid = recoveredAddress.toLowerCase() === input.address.toLowerCase();
480 | 
481 |     return createSuccessResponse(
482 |     isValid ? `Typed data signature is valid
483 |       Domain: ${input.domain}
484 |       Types: ${input.types}
485 |       Value: ${input.value}
486 |       Signature: ${input.signature}
487 |       Address: ${input.address}
488 |     ` : "Typed data signature is invalid");
489 |   } catch (error) {
490 |     return createErrorResponse(`Failed to verify typed data: ${(error as Error).message}`);
491 |   }
492 | };
493 | 
494 | // Provider Methods
495 | 
496 | export const getBlockHandler = async (input: any): Promise<ToolResultSchema> => {
497 |   try {
498 |     if (!input.blockHashOrBlockTag) {
499 |       return createErrorResponse("Block hash or block tag is required");
500 |     }
501 | 
502 |     const provider = getProvider();
503 |     // In ethers.js v5, getBlock can take includeTransactions as a second parameter
504 |     // but TypeScript definitions might not reflect this
505 |     const block = await (provider as any).getBlock(input.blockHashOrBlockTag, input.includeTransactions);
506 | 
507 |     return createSuccessResponse(
508 |     `Block retrieved successfully
509 |       Block hash: ${block.hash}
510 |       Block number: ${block.number?.toString() ?? "Not specified"}
511 |       Block timestamp: ${block.timestamp?.toString() ?? "Not specified"}
512 |       Block transactions: ${block.transactions?.length ?? "Not specified"}
513 |     `);
514 |   } catch (error) {
515 |     return createErrorResponse(`Failed to get block: ${(error as Error).message}`);
516 |   }
517 | };
518 | 
519 | export const getTransactionHandler = async (input: any): Promise<ToolResultSchema> => {
520 |   try {
521 |     if (!input.transactionHash) {
522 |       return createErrorResponse("Transaction hash is required");
523 |     }
524 | 
525 |     const provider = getProvider();
526 |     const transaction = await provider.getTransaction(input.transactionHash);
527 | 
528 |     return createSuccessResponse(
529 |     `Transaction retrieved successfully
530 |       Transaction hash: ${input.transactionHash}
531 |       Transaction: ${transaction}
532 |     `);
533 |   } catch (error) {
534 |     return createErrorResponse(`Failed to get transaction: ${(error as Error).message}`);
535 |   }
536 | };
537 | 
538 | export const getTransactionReceiptHandler = async (input: any): Promise<ToolResultSchema> => {
539 |   try {
540 |     if (!input.transactionHash) {
541 |       return createErrorResponse("Transaction hash is required");
542 |     }
543 | 
544 |     const provider = getProvider();
545 |     const receipt = await provider.getTransactionReceipt(input.transactionHash);
546 | 
547 |     return createSuccessResponse(
548 |     `Transaction receipt retrieved successfully
549 |       Transaction hash: ${input.transactionHash}
550 |       Transaction receipt: ${receipt}
551 |     `);
552 |   } catch (error) {
553 |     return createErrorResponse(`Failed to get transaction receipt: ${(error as Error).message}`);
554 |   }
555 | };
556 | 
557 | export const getCodeHandler = async (input: any): Promise<ToolResultSchema> => {
558 |   try {
559 |     if (!input.address) {
560 |       return createErrorResponse("Address is required");
561 |     }
562 | 
563 |     const provider = getProvider();
564 |     const code = await provider.getCode(input.address, input.blockTag);
565 | 
566 |     return createSuccessResponse(
567 |     `Code retrieved successfully
568 |       Address: ${input.address}
569 |       Code: ${code}
570 |     `);
571 |   } catch (error) {
572 |     return createErrorResponse(`Failed to get code: ${(error as Error).message}`);
573 |   }
574 | };
575 | 
576 | export const getStorageAtHandler = async (input: any): Promise<ToolResultSchema> => {
577 |   try {
578 |     if (!input.address) {
579 |       return createErrorResponse("Address is required");
580 |     }
581 | 
582 |     if (!input.position) {
583 |       return createErrorResponse("Position is required");
584 |     }
585 | 
586 |     const provider = getProvider();
587 |     const storage = await provider.getStorageAt(input.address, input.position, input.blockTag);
588 | 
589 |     return createSuccessResponse(
590 |     `Storage retrieved successfully
591 |       Address: ${input.address}
592 |       Position: ${input.position}
593 |       Storage: ${storage}
594 |     `);
595 |   } catch (error) {
596 |     return createErrorResponse(`Failed to get storage: ${(error as Error).message}`);
597 |   }
598 | };
599 | 
600 | export const estimateGasHandler = async (input: any): Promise<ToolResultSchema> => {
601 |   try {
602 |     if (!input.transaction) {
603 |       return createErrorResponse("Transaction is required");
604 |     }
605 | 
606 |     const provider = getProvider();
607 |     if (!provider) {
608 |       return createErrorResponse("Provider is required to estimate gas, please set the provider URL");
609 |     }
610 |     const gasEstimate = await provider.estimateGas(input.transaction);
611 | 
612 |     return createSuccessResponse(
613 |     `Gas estimate retrieved successfully
614 |       Gas estimate: ${gasEstimate.toString()}
615 |     `);
616 |   } catch (error) {
617 |     return createErrorResponse(`Failed to estimate gas: ${(error as Error).message}`);
618 |   }
619 | };
620 | 
621 | export const getLogsHandler = async (input: any): Promise<ToolResultSchema> => {
622 |   try {
623 |     if (!input.filter) {
624 |       return createErrorResponse("Filter is required");
625 |     }
626 | 
627 |     const provider = getProvider();
628 |     if (!provider) {
629 |       return createErrorResponse("Provider is required to get logs, please set the provider URL");
630 |     }
631 |     const logs = await provider.getLogs(input.filter);
632 | 
633 |     return createSuccessResponse(
634 |     `Logs retrieved successfully
635 |       Logs: ${logs}
636 |     `);
637 |   } catch (error) {
638 |     return createErrorResponse(`Failed to get logs: ${(error as Error).message}`);
639 |   }
640 | };
641 | 
642 | export const getEnsResolverHandler = async (input: any): Promise<ToolResultSchema> => {
643 |   try {
644 |     if (!input.name) {
645 |       return createErrorResponse("ENS name is required");
646 |     }
647 | 
648 |     const provider = getProvider();
649 |     if (!provider) {
650 |       return createErrorResponse("Provider is required to get ENS resolver, please set the provider URL");
651 |     }
652 |     // In ethers.js v5, getResolver might not be directly on the provider type
653 |     // but it's available in the implementation
654 |     const resolver = await (provider as any).getResolver(input.name);
655 | 
656 |     return createSuccessResponse(
657 |     resolver ? `ENS resolver retrieved successfully
658 |       Address: ${resolver.address}
659 |       Name: ${resolver.name}
660 |     ` : "No resolver found for this ENS name");
661 |   } catch (error) {
662 |     return createErrorResponse(`Failed to get ENS resolver: ${(error as Error).message}`);
663 |   }
664 | };
665 | 
666 | export const lookupAddressHandler = async (input: any): Promise<ToolResultSchema> => {
667 |   try {
668 |     if (!input.address) {
669 |       return createErrorResponse("Address is required");
670 |     }
671 | 
672 |     const provider = getProvider();
673 |     if (!provider) {
674 |       return createErrorResponse("Provider is required to lookup ENS name, please set the provider URL");
675 |     }
676 |     const name = await provider.lookupAddress(input.address);
677 | 
678 |     return createSuccessResponse(
679 |     name ? `ENS name retrieved successfully
680 |       Name: ${name}
681 |     ` : "No ENS name found for this address");
682 |   } catch (error) {
683 |     return createErrorResponse(`Failed to lookup ENS name: ${(error as Error).message}`);
684 |   }
685 | };
686 | 
687 | export const resolveNameHandler = async (input: any): Promise<ToolResultSchema> => {
688 |   try {
689 |     if (!input.name) {
690 |       return createErrorResponse("ENS name is required");
691 |     }
692 | 
693 |     const provider = getProvider();
694 |     if (!provider) {
695 |       return createErrorResponse("Provider is required to resolve ENS name, please set the provider URL");
696 |     }
697 |     const address = await provider.resolveName(input.name);
698 | 
699 |     return createSuccessResponse(
700 |     address ? `ENS name resolved successfully
701 |       Name: ${input.name}
702 |       Address: ${address}
703 |     ` : "Could not resolve this ENS name");
704 |   } catch (error) {
705 |     return createErrorResponse(`Failed to resolve ENS name: ${(error as Error).message}`);
706 |   }
707 | };
708 | 
709 | // Network Methods
710 | 
711 | export const getNetworkHandler = async (input: any): Promise<ToolResultSchema> => {
712 |   try {
713 |     const provider = getProvider();
714 |     if (!provider) {
715 |       return createErrorResponse("Provider is required to get network information, please set the provider URL");
716 |     }
717 |     const network = await provider.getNetwork();
718 | 
719 |     return createSuccessResponse(`Network information retrieved successfully
720 |       Network name: ${network.name}
721 |       Chain ID: ${network.chainId}
722 |       ENS address: ${network.ensAddress}
723 |     `);
724 |   } catch (error) {
725 |     return createErrorResponse(`Failed to get network information: ${(error as Error).message}`);
726 |   }
727 | };
728 | 
729 | export const getBlockNumberHandler = async (input: any): Promise<ToolResultSchema> => {
730 |   try {
731 |     const provider = getProvider();
732 |     if (!provider) {
733 |       return createErrorResponse("Provider is required to get block number, please set the provider URL");
734 |     }
735 |     const blockNumber = await provider.getBlockNumber();
736 | 
737 |     return createSuccessResponse(
738 |     `Block number retrieved successfully
739 |       Block number: ${blockNumber.toString()}
740 |     `);
741 |   } catch (error) {
742 |     return createErrorResponse(`Failed to get block number: ${(error as Error).message}`);
743 |   }
744 | };
745 | 
746 | export const getFeeDataHandler = async (input: any): Promise<ToolResultSchema> => {
747 |   try {
748 |     const provider = getProvider();
749 |     if (!provider) {
750 |       return createErrorResponse("Provider is required to get fee data, please set the provider URL");
751 |     }
752 |     // getFeeData is available in ethers v5.5.0+
753 |     // @ts-ignore - getFeeData might not be in the type definitions depending on the version
754 |     const feeData = await provider.getFeeData();
755 | 
756 |     return createSuccessResponse(`Fee data retrieved successfully
757 |       Gas price: ${feeData.gasPrice?.toString()}
758 |       Max fee per gas: ${feeData.maxFeePerGas?.toString()}
759 |       Max priority fee per gas: ${feeData.maxPriorityFeePerGas?.toString()}
760 |     `);
761 |   } catch (error) {
762 |     return createErrorResponse(`Failed to get fee data: ${(error as Error).message}`);
763 |   }
764 | };
765 | 
```