@mcp-dockmaster/mcp-cryptowallet-evm 1.0.0

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