Skip to content

Commit 8591ac2

Browse files
committed
fix: critical bug fixes and add message signing capabilities
## Critical Bug Fixes - Fix parameter order mismatch in approve_token_spending tool - Corrected approveERC20 function call arguments - This bug prevented token approvals from working - Fix Buffer compatibility issue in wallet.ts for Bun runtime - Replaced Node.js Buffer with platform-agnostic Array conversion - Ensures compatibility across Bun and Node.js environments ## New Features - Add message signing service functions (wallet.ts) - signMessage(): Sign arbitrary messages for authentication - signTypedData(): Sign EIP-712 structured data - Add sign_message MCP tool - Enables SIWE authentication flows - Supports off-chain signature verification - Add sign_typed_data MCP tool - Enables gasless transactions via meta-transactions - Supports EIP-2612 permit signatures - Enables relay service integration ## Documentation - Update README with message signing capabilities section - Add new tools to API reference (24 tools total, up from 22) - Document SIWE, permit, and meta-transaction support These changes enable AI agents to: - Authenticate using Sign-In With Ethereum (SIWE) - Create meta-transactions for gasless transfers - Generate permit signatures for off-chain approvals - Sign any EIP-712 structured data for dApp integrations
1 parent 83d6d4c commit 8591ac2

3 files changed

Lines changed: 253 additions & 74 deletions

File tree

README.md

Lines changed: 93 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,14 @@ All services are exposed through a consistent interface of MCP tools, resources,
5656
### Token services
5757

5858
- **ERC20 Tokens**
59+
5960
- Get token metadata (name, symbol, decimals, supply)
6061
- Check token balances
6162
- Transfer tokens between addresses
6263
- Approve spending allowances
6364

6465
- **NFTs (ERC721)**
66+
6567
- Get collection and token metadata
6668
- Verify token ownership
6769
- Transfer NFTs between addresses
@@ -89,6 +91,14 @@ All services are exposed through a consistent interface of MCP tools, resources,
8991
- **Transaction status** and receipt information
9092
- **Error handling** with descriptive messages
9193

94+
### Message Signing Capabilities
95+
96+
- **Personal Message Signing** - Sign arbitrary messages for authentication and verification
97+
- **EIP-712 Typed Data Signing** - Sign structured data for gasless transactions and meta-transactions
98+
- **SIWE Support** - Enable Sign-In With Ethereum authentication flows
99+
- **Permit Signatures** - Create off-chain approvals for gasless token operations
100+
- **Meta-Transaction Support** - Sign transaction data for relay services and gasless transfers
101+
92102
### AI-Guided Workflows (Prompts)
93103

94104
- **Transaction preparation** - Guidance for planning and executing transfers
@@ -102,6 +112,7 @@ All services are exposed through a consistent interface of MCP tools, resources,
102112
## 🌐 Supported Networks
103113

104114
### Mainnets
115+
105116
- Ethereum (ETH)
106117
- Optimism (OP)
107118
- Arbitrum (ARB)
@@ -135,6 +146,7 @@ All services are exposed through a consistent interface of MCP tools, resources,
135146
- Lumia
136147

137148
### Testnets
149+
138150
- Sepolia
139151
- Optimism Sepolia
140152
- Arbitrum Sepolia
@@ -204,18 +216,23 @@ export EVM_ACCOUNT_INDEX="0" # Optional: Account index for HD wallet derivation
204216
```
205217

206218
The mnemonic option supports hierarchical deterministic (HD) wallet derivation:
219+
207220
- Uses BIP-39 standard mnemonic phrases (12 or 24 words)
208221
- Supports BIP-44 derivation path: `m/44'/60'/0'/0/{accountIndex}`
209222
- `EVM_ACCOUNT_INDEX` allows you to derive different accounts from the same mnemonic
210223
- Default account index is 0 (first account)
211224

212225
**Wallet is used for:**
226+
213227
- Transferring native tokens (`transfer_native` tool)
214228
- Transferring ERC20 tokens (`transfer_erc20` tool)
215229
- Approving token spending (`approve_token_spending` tool)
216230
- Writing to smart contracts (`write_contract` tool)
231+
- Signing messages for authentication (`sign_message` tool)
232+
- Signing structured data for gasless transactions (`sign_typed_data` tool)
233+
234+
⚠️ **Security**:
217235

218-
⚠️ **Security**:
219236
- Never commit your private key or mnemonic to version control
220237
- Use environment variables or a secure key management system
221238
- Store mnemonics securely - they provide access to all derived accounts
@@ -228,11 +245,13 @@ export ETHERSCAN_API_KEY="your-api-key-here"
228245
```
229246

230247
This API key is optional but required for:
248+
231249
- Automatic ABI fetching from block explorers (`get_contract_abi` tool)
232250
- Auto-fetching ABIs when reading contracts (`read_contract` tool with `abiJson` parameter)
233251
- The `fetch_and_analyze_abi` prompt
234252

235253
Get your free API key from:
254+
236255
- [Etherscan](https://etherscan.io/apis) - For Ethereum and compatible chains
237256
- The same key works across all 60+ EVM networks via the Etherscan v2 API
238257

@@ -298,6 +317,7 @@ To connect to the MCP server from Cursor:
298317
3. Scroll down to "MCP Servers" section
299318
4. Click "Add new MCP server"
300319
5. Enter the following details:
320+
301321
- Server name: `evm-mcp-server`
302322
- Type: `command`
303323
- Command: `npx @mcpdotdirect/evm-mcp-server`
@@ -315,18 +335,11 @@ For a more portable configuration that you can share with your team or use acros
315335
"mcpServers": {
316336
"evm-mcp-server": {
317337
"command": "npx",
318-
"args": [
319-
"-y",
320-
"@mcpdotdirect/evm-mcp-server"
321-
]
338+
"args": ["-y", "@mcpdotdirect/evm-mcp-server"]
322339
},
323340
"evm-mcp-http": {
324341
"command": "npx",
325-
"args": [
326-
"-y",
327-
"@mcpdotdirect/evm-mcp-server",
328-
"--http"
329-
]
342+
"args": ["-y", "@mcpdotdirect/evm-mcp-server", "--http"]
330343
}
331344
}
332345
}
@@ -353,11 +366,13 @@ If you're developing a web application and want to connect to the HTTP server wi
353366
```
354367

355368
This connects directly to the HTTP server's SSE endpoint, which is useful for:
369+
356370
- Web applications that need to connect to the MCP server from the browser
357371
- Environments where running local commands isn't ideal
358372
- Sharing a single MCP server instance among multiple users or applications
359373

360374
To use this configuration:
375+
361376
1. Create a `.cursor` directory in your project root if it doesn't exist
362377
2. Save the above JSON as `mcp.json` in the `.cursor` directory
363378
3. Restart Cursor or open your project
@@ -375,14 +390,14 @@ async function main() {
375390
try {
376391
// Get ETH balance for an address using ENS
377392
console.log("Getting ETH balance for vitalik.eth...");
378-
393+
379394
// When using with Cursor, you can simply ask Cursor to:
380395
// "Check the ETH balance of vitalik.eth on mainnet"
381396
// Or "Transfer 0.1 ETH from my wallet to vitalik.eth"
382-
383-
// Cursor will use the MCP server to execute these operations
397+
398+
// Cursor will use the MCP server to execute these operations
384399
// without requiring any additional code from you
385-
400+
386401
// This is the power of the MCP integration - your AI assistant
387402
// can directly interact with blockchain data and operations
388403
} catch (error) {
@@ -425,7 +440,7 @@ const mcp = new McpClient("http://localhost:3000");
425440
const result = await mcp.invokeTool("get-token-balance", {
426441
tokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum
427442
ownerAddress: "vitalik.eth", // ENS name instead of address
428-
network: "ethereum"
443+
network: "ethereum",
429444
});
430445

431446
console.log(result);
@@ -448,7 +463,7 @@ const mcp = new McpClient("http://localhost:3000");
448463

449464
const result = await mcp.invokeTool("resolve-ens", {
450465
ensName: "vitalik.eth",
451-
network: "ethereum"
466+
network: "ethereum",
452467
});
453468

454469
console.log(result);
@@ -464,96 +479,103 @@ console.log(result);
464479

465480
### Tools
466481

467-
The server provides 22 focused MCP tools for agents. **All tools that accept address parameters support both Ethereum addresses and ENS names.**
482+
The server provides 24 focused MCP tools for agents. **All tools that accept address parameters support both Ethereum addresses and ENS names.**
468483

469484
#### Wallet Information
470485

471-
| Tool Name | Description | Key Parameters |
472-
|-----------|-------------|----------------|
473-
| `get_wallet_address` | Get the address of the configured wallet (from EVM_PRIVATE_KEY) | none |
486+
| Tool Name | Description | Key Parameters |
487+
| -------------------- | --------------------------------------------------------------- | -------------- |
488+
| `get_wallet_address` | Get the address of the configured wallet (from EVM_PRIVATE_KEY) | none |
474489

475490
#### Network Information
476491

477-
| Tool Name | Description | Key Parameters |
478-
|-----------|-------------|----------------|
479-
| `get_chain_info` | Get network information | `network` |
480-
| `get_supported_networks` | List all supported EVM networks | none |
481-
| `get_gas_price` | Get current gas prices on a network | `network` |
492+
| Tool Name | Description | Key Parameters |
493+
| ------------------------ | ----------------------------------- | -------------- |
494+
| `get_chain_info` | Get network information | `network` |
495+
| `get_supported_networks` | List all supported EVM networks | none |
496+
| `get_gas_price` | Get current gas prices on a network | `network` |
482497

483498
#### ENS Services
484499

485-
| Tool Name | Description | Key Parameters |
486-
|-----------|-------------|----------------|
487-
| `resolve_ens_name` | Resolve ENS name to address | `ensName`, `network` |
500+
| Tool Name | Description | Key Parameters |
501+
| -------------------- | ---------------------------------- | -------------------- |
502+
| `resolve_ens_name` | Resolve ENS name to address | `ensName`, `network` |
488503
| `lookup_ens_address` | Reverse lookup address to ENS name | `address`, `network` |
489504

490505
#### Block & Transaction Information
491506

492-
| Tool Name | Description | Key Parameters |
493-
|-----------|-------------|----------------|
494-
| `get_block` | Get block data | `blockNumber` or `blockHash`, `network` |
495-
| `get_latest_block` | Get latest block data | `network` |
496-
| `get_transaction` | Get transaction details | `txHash`, `network` |
497-
| `get_transaction_receipt` | Get transaction receipt with logs | `txHash`, `network` |
498-
| `wait_for_transaction` | Wait for transaction confirmation | `txHash`, `confirmations`, `network` |
507+
| Tool Name | Description | Key Parameters |
508+
| ------------------------- | --------------------------------- | --------------------------------------- |
509+
| `get_block` | Get block data | `blockNumber` or `blockHash`, `network` |
510+
| `get_latest_block` | Get latest block data | `network` |
511+
| `get_transaction` | Get transaction details | `txHash`, `network` |
512+
| `get_transaction_receipt` | Get transaction receipt with logs | `txHash`, `network` |
513+
| `wait_for_transaction` | Wait for transaction confirmation | `txHash`, `confirmations`, `network` |
499514

500515
#### Balance & Token Information
501516

502-
| Tool Name | Description | Key Parameters |
503-
|-----------|-------------|----------------|
504-
| `get_balance` | Get native token balance | `address` (address/ENS), `network` |
505-
| `get_token_balance` | Check ERC20 token balance | `tokenAddress` (address/ENS), `ownerAddress` (address/ENS), `network` |
506-
| `get_allowance` | Check token spending allowance | `tokenAddress` (address/ENS), `ownerAddress` (address/ENS), `spenderAddress` (address/ENS), `network` |
517+
| Tool Name | Description | Key Parameters |
518+
| ------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------------- |
519+
| `get_balance` | Get native token balance | `address` (address/ENS), `network` |
520+
| `get_token_balance` | Check ERC20 token balance | `tokenAddress` (address/ENS), `ownerAddress` (address/ENS), `network` |
521+
| `get_allowance` | Check token spending allowance | `tokenAddress` (address/ENS), `ownerAddress` (address/ENS), `spenderAddress` (address/ENS), `network` |
507522

508523
#### Smart Contract Interactions
509524

510-
| Tool Name | Description | Key Parameters |
511-
|-----------|-------------|----------------|
512-
| `get_contract_abi` | Fetch contract ABI from block explorer (60+ networks) | `contractAddress` (address/ENS), `network` |
513-
| `read_contract` | Read smart contract state (auto-fetches ABI if needed) | `contractAddress`, `functionName`, `args[]`, `abiJson` (optional), `network` |
514-
| `write_contract` | Execute state-changing functions (auto-fetches ABI if needed) | `contractAddress`, `functionName`, `args[]`, `value` (optional), `abiJson` (optional), `network` |
525+
| Tool Name | Description | Key Parameters |
526+
| ------------------ | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
527+
| `get_contract_abi` | Fetch contract ABI from block explorer (60+ networks) | `contractAddress` (address/ENS), `network` |
528+
| `read_contract` | Read smart contract state (auto-fetches ABI if needed) | `contractAddress`, `functionName`, `args[]`, `abiJson` (optional), `network` |
529+
| `write_contract` | Execute state-changing functions (auto-fetches ABI if needed) | `contractAddress`, `functionName`, `args[]`, `value` (optional), `abiJson` (optional), `network` |
515530

516531
#### Token Transfers
517532

518-
| Tool Name | Description | Key Parameters |
519-
|-----------|-------------|----------------|
520-
| `transfer_native` | Send native tokens (ETH, etc.) | `to` (address/ENS), `amount`, `network` |
521-
| `transfer_erc20` | Transfer ERC20 tokens | `tokenAddress` (address/ENS), `to` (address/ENS), `amount`, `network` |
522-
| `approve_token_spending` | Approve token allowances | `tokenAddress` (address/ENS), `spenderAddress` (address/ENS), `amount`, `network` |
533+
| Tool Name | Description | Key Parameters |
534+
| ------------------------ | ------------------------------ | --------------------------------------------------------------------------------- |
535+
| `transfer_native` | Send native tokens (ETH, etc.) | `to` (address/ENS), `amount`, `network` |
536+
| `transfer_erc20` | Transfer ERC20 tokens | `tokenAddress` (address/ENS), `to` (address/ENS), `amount`, `network` |
537+
| `approve_token_spending` | Approve token allowances | `tokenAddress` (address/ENS), `spenderAddress` (address/ENS), `amount`, `network` |
523538

524539
#### NFT Services
525540

526-
| Tool Name | Description | Key Parameters |
527-
|-----------|-------------|----------------|
528-
| `get_nft_info` | Get NFT (ERC721) metadata | `tokenAddress` (address/ENS), `tokenId`, `network` |
529-
| `get_erc1155_balance` | Check ERC1155 balance | `tokenAddress` (address/ENS), `tokenId`, `ownerAddress` (address/ENS), `network` |
541+
| Tool Name | Description | Key Parameters |
542+
| --------------------- | ------------------------- | -------------------------------------------------------------------------------- |
543+
| `get_nft_info` | Get NFT (ERC721) metadata | `tokenAddress` (address/ENS), `tokenId`, `network` |
544+
| `get_erc1155_balance` | Check ERC1155 balance | `tokenAddress` (address/ENS), `tokenId`, `ownerAddress` (address/ENS), `network` |
545+
546+
#### Message Signing
547+
548+
| Tool Name | Description | Key Parameters |
549+
| ----------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------- |
550+
| `sign_message` | Sign arbitrary messages for authentication and verification (SIWE, off-chain signatures) | `message` |
551+
| `sign_typed_data` | Sign EIP-712 structured data for gasless transactions, permits, and meta-transactions | `domainJson`, `typesJson`, `primaryType`, `messageJson` |
530552

531553
### Resources
532554

533555
The server exposes blockchain data through the following MCP resource URIs. All resource URIs that accept addresses also support ENS names, which are automatically resolved to addresses.
534556

535557
#### Blockchain Resources
536558

537-
| Resource URI Pattern | Description |
538-
|-----------|-------------|
539-
| `evm://{network}/chain` | Chain information for a specific network |
540-
| `evm://chain` | Ethereum mainnet chain information |
541-
| `evm://{network}/block/{blockNumber}` | Block data by number |
542-
| `evm://{network}/block/latest` | Latest block data |
543-
| `evm://{network}/address/{address}/balance` | Native token balance |
544-
| `evm://{network}/tx/{txHash}` | Transaction details |
545-
| `evm://{network}/tx/{txHash}/receipt` | Transaction receipt with logs |
559+
| Resource URI Pattern | Description |
560+
| ------------------------------------------- | ---------------------------------------- |
561+
| `evm://{network}/chain` | Chain information for a specific network |
562+
| `evm://chain` | Ethereum mainnet chain information |
563+
| `evm://{network}/block/{blockNumber}` | Block data by number |
564+
| `evm://{network}/block/latest` | Latest block data |
565+
| `evm://{network}/address/{address}/balance` | Native token balance |
566+
| `evm://{network}/tx/{txHash}` | Transaction details |
567+
| `evm://{network}/tx/{txHash}/receipt` | Transaction receipt with logs |
546568

547569
#### Token Resources
548570

549-
| Resource URI Pattern | Description |
550-
|-----------|-------------|
551-
| `evm://{network}/token/{tokenAddress}` | ERC20 token information |
552-
| `evm://{network}/token/{tokenAddress}/balanceOf/{address}` | ERC20 token balance |
553-
| `evm://{network}/nft/{tokenAddress}/{tokenId}` | NFT (ERC721) token information |
554-
| `evm://{network}/nft/{tokenAddress}/{tokenId}/isOwnedBy/{address}` | NFT ownership verification |
555-
| `evm://{network}/erc1155/{tokenAddress}/{tokenId}/uri` | ERC1155 token URI |
556-
| `evm://{network}/erc1155/{tokenAddress}/{tokenId}/balanceOf/{address}` | ERC1155 token balance |
571+
| Resource URI Pattern | Description |
572+
| ---------------------------------------------------------------------- | ------------------------------ |
573+
| `evm://{network}/token/{tokenAddress}` | ERC20 token information |
574+
| `evm://{network}/token/{tokenAddress}/balanceOf/{address}` | ERC20 token balance |
575+
| `evm://{network}/nft/{tokenAddress}/{tokenId}` | NFT (ERC721) token information |
576+
| `evm://{network}/nft/{tokenAddress}/{tokenId}/isOwnedBy/{address}` | NFT ownership verification |
577+
| `evm://{network}/erc1155/{tokenAddress}/{tokenId}/uri` | ERC1155 token URI |
578+
| `evm://{network}/erc1155/{tokenAddress}/{tokenId}/balanceOf/{address}` | ERC1155 token balance |
557579

558580
## 🔒 Security Considerations
559581

src/core/services/wallet.ts

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ export const getConfiguredPrivateKey = (): Hex => {
5555
if (!hdKey.privateKey) {
5656
throw new Error("Unable to derive private key from HD account - no private key in HD key");
5757
}
58-
// Convert Uint8Array to hex string
59-
const privateKeyHex = Buffer.from(hdKey.privateKey).toString('hex');
58+
// Convert Uint8Array to hex string (compatible with Bun and Node)
59+
const privateKeyHex = Array.from(hdKey.privateKey)
60+
.map(byte => byte.toString(16).padStart(2, '0'))
61+
.join('');
6062
return `0x${privateKeyHex}` as Hex;
6163
}
6264

@@ -85,3 +87,52 @@ export const getWalletAddressFromKey = (): Address => {
8587
export const getConfiguredWallet = (): { address: Address } => {
8688
return { address: getWalletAddressFromKey() };
8789
};
90+
91+
/**
92+
* Sign an arbitrary message using the configured wallet
93+
* @param message The message to sign (can be a string or hex data)
94+
* @returns The signature as a hex string
95+
*/
96+
export const signMessage = async (message: string): Promise<string> => {
97+
const account = getConfiguredAccount();
98+
99+
// Use the account's signMessage method directly
100+
const signature = await account.signMessage({
101+
message: message
102+
});
103+
104+
return signature;
105+
};
106+
107+
/**
108+
* Sign typed data (EIP-712) using the configured wallet
109+
* @param domain The EIP-712 domain
110+
* @param types The types definition (excluding EIP712Domain)
111+
* @param primaryType The primary type name
112+
* @param message The message data to sign
113+
* @returns The signature as a hex string
114+
*/
115+
export const signTypedData = async (
116+
domain: {
117+
name?: string;
118+
version?: string;
119+
chainId?: number;
120+
verifyingContract?: Address;
121+
salt?: `0x${string}`;
122+
},
123+
types: Record<string, Array<{ name: string; type: string }>>,
124+
primaryType: string,
125+
message: Record<string, any>
126+
): Promise<string> => {
127+
const account = getConfiguredAccount();
128+
129+
// Use the account's signTypedData method
130+
const signature = await account.signTypedData({
131+
domain,
132+
types,
133+
primaryType,
134+
message
135+
});
136+
137+
return signature;
138+
};

0 commit comments

Comments
 (0)