web3-tools-mcp 1.5.0 → 2.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.
Files changed (109) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/README.md +346 -164
  3. package/dist/anvil.d.ts +1 -1
  4. package/dist/anvil.d.ts.map +1 -1
  5. package/dist/anvil.js +11 -9
  6. package/dist/anvil.js.map +1 -1
  7. package/dist/chain-meta.d.ts.map +1 -1
  8. package/dist/chain-meta.js +7 -3
  9. package/dist/chain-meta.js.map +1 -1
  10. package/dist/chains.d.ts +960 -0
  11. package/dist/chains.d.ts.map +1 -0
  12. package/dist/chains.js +108 -0
  13. package/dist/chains.js.map +1 -0
  14. package/dist/client.d.ts +10 -1717
  15. package/dist/client.d.ts.map +1 -1
  16. package/dist/client.js +33 -216
  17. package/dist/client.js.map +1 -1
  18. package/dist/http-server.d.ts +18 -0
  19. package/dist/http-server.d.ts.map +1 -0
  20. package/dist/http-server.js +89 -0
  21. package/dist/http-server.js.map +1 -0
  22. package/dist/index.js +77 -32
  23. package/dist/index.js.map +1 -1
  24. package/dist/kv-storage.d.ts +23 -0
  25. package/dist/kv-storage.d.ts.map +1 -0
  26. package/dist/kv-storage.js +69 -0
  27. package/dist/kv-storage.js.map +1 -0
  28. package/dist/oauth.d.ts +31 -0
  29. package/dist/oauth.d.ts.map +1 -0
  30. package/dist/oauth.js +227 -0
  31. package/dist/oauth.js.map +1 -0
  32. package/dist/preview.d.ts.map +1 -1
  33. package/dist/preview.js +14 -4
  34. package/dist/preview.js.map +1 -1
  35. package/dist/tools/advanced.d.ts +4 -4
  36. package/dist/tools/advanced.d.ts.map +1 -1
  37. package/dist/tools/advanced.js +228 -247
  38. package/dist/tools/advanced.js.map +1 -1
  39. package/dist/tools/balance.d.ts +1 -1
  40. package/dist/tools/balance.d.ts.map +1 -1
  41. package/dist/tools/balance.js +97 -103
  42. package/dist/tools/balance.js.map +1 -1
  43. package/dist/tools/contract-info.d.ts +3 -3
  44. package/dist/tools/contract-info.d.ts.map +1 -1
  45. package/dist/tools/contract-info.js +150 -161
  46. package/dist/tools/contract-info.js.map +1 -1
  47. package/dist/tools/contract.d.ts +4 -4
  48. package/dist/tools/contract.d.ts.map +1 -1
  49. package/dist/tools/contract.js +91 -110
  50. package/dist/tools/contract.js.map +1 -1
  51. package/dist/tools/ens.d.ts +6 -6
  52. package/dist/tools/ens.d.ts.map +1 -1
  53. package/dist/tools/ens.js +239 -297
  54. package/dist/tools/ens.js.map +1 -1
  55. package/dist/tools/gas.d.ts +4 -4
  56. package/dist/tools/gas.d.ts.map +1 -1
  57. package/dist/tools/gas.js +90 -138
  58. package/dist/tools/gas.js.map +1 -1
  59. package/dist/tools/index.d.ts +1 -1
  60. package/dist/tools/index.d.ts.map +1 -1
  61. package/dist/tools/logs.d.ts +1 -1
  62. package/dist/tools/logs.d.ts.map +1 -1
  63. package/dist/tools/logs.js +36 -37
  64. package/dist/tools/logs.js.map +1 -1
  65. package/dist/tools/signatures.d.ts.map +1 -1
  66. package/dist/tools/signatures.js +67 -89
  67. package/dist/tools/signatures.js.map +1 -1
  68. package/dist/tools/transactions.d.ts +48 -4
  69. package/dist/tools/transactions.d.ts.map +1 -1
  70. package/dist/tools/transactions.js +228 -44
  71. package/dist/tools/transactions.js.map +1 -1
  72. package/dist/types.d.ts +2 -1
  73. package/dist/types.d.ts.map +1 -1
  74. package/dist/utils.d.ts +3 -8
  75. package/dist/utils.d.ts.map +1 -1
  76. package/dist/utils.js +36 -98
  77. package/dist/utils.js.map +1 -1
  78. package/dist/wallet-client.d.ts +11 -1
  79. package/dist/wallet-client.d.ts.map +1 -1
  80. package/dist/wallet-client.js +27 -9
  81. package/dist/wallet-client.js.map +1 -1
  82. package/dist/walletconnect.d.ts +52 -0
  83. package/dist/walletconnect.d.ts.map +1 -0
  84. package/dist/walletconnect.js +221 -0
  85. package/dist/walletconnect.js.map +1 -0
  86. package/package.json +21 -9
  87. package/src/anvil.ts +15 -18
  88. package/src/chain-meta.ts +8 -4
  89. package/src/chains.ts +145 -0
  90. package/src/client.ts +39 -252
  91. package/src/http-server.ts +117 -0
  92. package/src/index.ts +98 -49
  93. package/src/kv-storage.ts +83 -0
  94. package/src/oauth.ts +270 -0
  95. package/src/preview.ts +15 -14
  96. package/src/tools/advanced.ts +195 -213
  97. package/src/tools/balance.ts +103 -108
  98. package/src/tools/contract-info.ts +163 -184
  99. package/src/tools/contract.ts +123 -144
  100. package/src/tools/ens.ts +290 -345
  101. package/src/tools/gas.ts +122 -170
  102. package/src/tools/index.ts +2 -2
  103. package/src/tools/logs.ts +45 -43
  104. package/src/tools/signatures.ts +68 -86
  105. package/src/tools/transactions.ts +283 -50
  106. package/src/types.ts +2 -15
  107. package/src/utils.ts +56 -127
  108. package/src/wallet-client.ts +31 -9
  109. package/src/walletconnect.ts +269 -0
@@ -1,16 +1,12 @@
1
- import { type AbiFunction, type Address, isAddress, parseAbiItem } from "viem";
2
- import { z } from "zod";
3
- import type { ChainName } from "../types.js";
4
- import { getClientManager, SUPPORTED_CHAINS } from "../client.js";
5
- import { convertArgumentsToTypes, createTool, formatResponse } from "../utils.js";
1
+ import { type AbiFunction, type Address, isAddress, parseAbiItem } from 'viem'
2
+ import { z } from 'zod'
3
+ import { getClientManager, SUPPORTED_CHAINS } from '../client.js'
4
+ import type { ChainName } from '../types.js'
5
+ import { convertArgumentsToTypes, createTool, formatResponse } from '../utils.js'
6
6
 
7
7
  const ContractCallSchema = z.object({
8
- chain: z
9
- .enum(SUPPORTED_CHAINS)
10
- .describe("Blockchain network (mainnet, base, arbitrum, polygon, optimism, celo, localhost)"),
11
- contractAddress: z
12
- .string()
13
- .describe("Contract address to call (must be valid checksummed address)"),
8
+ chain: z.enum(SUPPORTED_CHAINS).describe('Blockchain network (mainnet, base, arbitrum, polygon, optimism, celo, localhost)'),
9
+ contractAddress: z.string().describe('Contract address to call (must be valid checksummed address)'),
14
10
  functionAbi: z
15
11
  .string()
16
12
  .describe(
@@ -19,165 +15,148 @@ const ContractCallSchema = z.object({
19
15
  args: z
20
16
  .array(z.union([z.string(), z.number(), z.boolean(), z.null()]))
21
17
  .optional()
22
- .describe(
23
- "Function arguments in order matching the ABI signature. Automatically type-converted."
24
- ),
25
- blockNumber: z
26
- .string()
27
- .optional()
28
- .describe("Block number for historical queries (defaults to latest)"),
29
- label: z.string().optional().describe("Optional label to identify this call in batch results"),
30
- });
18
+ .describe('Function arguments in order matching the ABI signature. Automatically type-converted.'),
19
+ blockNumber: z.string().optional().describe('Block number for historical queries (defaults to latest)'),
20
+ label: z.string().optional().describe('Optional label to identify this call in batch results')
21
+ })
31
22
 
32
23
  export default {
33
- call_contract_function: createTool(
34
- "Call Contract Functions",
35
- "Execute multiple view/pure contract functions efficiently. BATCH OPTIMIZED - use for reading contract state, token metadata.",
24
+ read_contract: createTool(
25
+ 'Read Contract',
26
+ 'Read state from contracts by calling view/pure functions. Nothing is sent on-chain and no wallet is involved. BATCH OPTIMIZED - use for reading contract state, token metadata.',
36
27
  z.object({
37
28
  calls: z
38
29
  .array(ContractCallSchema)
39
- .describe(
40
- "Array of contract function calls. Automatically batched by chain/block for optimal performance."
41
- ),
30
+ .describe('Array of contract function calls. Automatically batched by chain/block for optimal performance.')
42
31
  }),
43
32
  async (args) => {
44
33
  // Group by chain and blockNumber for efficient multicall
45
- const groupedCalls = new Map<string, typeof args.calls>();
34
+ const groupedCalls = new Map<string, typeof args.calls>()
46
35
  for (const item of args.calls) {
47
- const key = `${item.chain}:${item.blockNumber || "latest"}`;
36
+ const key = `${item.chain}:${item.blockNumber || 'latest'}`
48
37
  if (!groupedCalls.has(key)) {
49
- groupedCalls.set(key, []);
38
+ groupedCalls.set(key, [])
50
39
  }
51
- groupedCalls.get(key)!.push(item);
40
+ groupedCalls.get(key)!.push(item)
52
41
  }
53
42
 
54
43
  const allResults: Array<{
55
- index: number;
56
- label?: string;
57
- contractAddress: string;
58
- functionName: string;
59
- success: boolean;
60
- result?: unknown;
61
- error?: string;
62
- chain: string;
63
- blockNumber: string;
64
- }> = [];
65
-
66
- try {
67
- // Process each group
68
- for (const [key, calls] of groupedCalls) {
69
- const [chain, blockNum] = key.split(":");
70
- const clientManager = getClientManager();
71
- const client = clientManager.getClient(chain as ChainName);
72
- const blockTag = blockNum === "latest" ? "latest" : BigInt(blockNum);
73
-
74
- // Prepare multicall contracts
75
- const multicallContracts = calls.map((call) => {
76
- if (!isAddress(call.contractAddress)) {
77
- throw new Error(`Invalid contract address: ${call.contractAddress}`);
78
- }
79
-
80
- const abiItem = parseAbiItem(call.functionAbi) as AbiFunction;
81
-
82
- if (abiItem.stateMutability !== "view" && abiItem.stateMutability !== "pure") {
83
- throw new Error(`Only view and pure functions can be called: ${call.functionAbi}`);
44
+ index: number
45
+ label?: string
46
+ contractAddress: string
47
+ functionName: string
48
+ success: boolean
49
+ result?: unknown
50
+ error?: string
51
+ chain: string
52
+ blockNumber: string
53
+ }> = []
54
+
55
+ // Process each group
56
+ for (const [key, calls] of groupedCalls) {
57
+ const [chain, blockNum] = key.split(':')
58
+ const clientManager = getClientManager()
59
+ const client = clientManager.getClient(chain as ChainName)
60
+ const blockTag = blockNum === 'latest' ? 'latest' : BigInt(blockNum)
61
+
62
+ // Prepare multicall contracts
63
+ const multicallContracts = calls.map((call) => {
64
+ if (!isAddress(call.contractAddress)) {
65
+ throw new Error(`Invalid contract address: ${call.contractAddress}`)
66
+ }
67
+
68
+ const abiItem = parseAbiItem(call.functionAbi) as AbiFunction
69
+
70
+ if (abiItem.stateMutability !== 'view' && abiItem.stateMutability !== 'pure') {
71
+ throw new Error(`Only view and pure functions can be called: ${call.functionAbi}`)
72
+ }
73
+
74
+ const convertedArgs = convertArgumentsToTypes(call.args || [], abiItem.inputs)
75
+
76
+ return {
77
+ address: call.contractAddress as Address,
78
+ abi: [abiItem],
79
+ functionName: abiItem.name,
80
+ args: convertedArgs
81
+ }
82
+ })
83
+
84
+ // Execute multicall
85
+ // Use deployless mode for localhost/anvil since Multicall3 may not be deployed
86
+ const useDeployless = chain === 'localhost'
87
+ const multicallResults = await client.multicall({
88
+ contracts: multicallContracts,
89
+ blockNumber: blockTag === 'latest' ? undefined : blockTag,
90
+ ...(useDeployless && { deployless: true })
91
+ })
92
+
93
+ // Process results
94
+ multicallResults.forEach((result, idx) => {
95
+ const call = calls[idx]!
96
+ const abiItem = parseAbiItem(call.functionAbi) as AbiFunction
97
+ const originalIndex = args.calls.indexOf(call)
98
+
99
+ if (result.status === 'success') {
100
+ allResults[originalIndex] = {
101
+ index: originalIndex,
102
+ label: call.label,
103
+ contractAddress: call.contractAddress,
104
+ functionName: abiItem.name,
105
+ success: true,
106
+ result: result.result,
107
+ chain: call.chain,
108
+ blockNumber: call.blockNumber || 'latest'
84
109
  }
85
-
86
- const convertedArgs = convertArgumentsToTypes(call.args || [], abiItem.inputs);
87
-
88
- return {
89
- address: call.contractAddress as Address,
90
- abi: [abiItem],
110
+ } else {
111
+ allResults[originalIndex] = {
112
+ index: originalIndex,
113
+ label: call.label,
114
+ contractAddress: call.contractAddress,
91
115
  functionName: abiItem.name,
92
- args: convertedArgs,
93
- };
94
- });
95
-
96
- // Execute multicall
97
- // Use deployless mode for localhost/anvil since Multicall3 may not be deployed
98
- const useDeployless = chain === "localhost";
99
- const multicallResults = await client.multicall({
100
- contracts: multicallContracts,
101
- blockNumber: blockTag === "latest" ? undefined : blockTag,
102
- ...(useDeployless && { deployless: true }),
103
- });
104
-
105
- // Process results
106
- multicallResults.forEach((result, idx) => {
107
- const call = calls[idx]!;
108
- const abiItem = parseAbiItem(call.functionAbi) as AbiFunction;
109
- const originalIndex = args.calls.indexOf(call);
110
-
111
- if (result.status === "success") {
112
- allResults[originalIndex] = {
113
- index: originalIndex,
114
- label: call.label,
115
- contractAddress: call.contractAddress,
116
- functionName: abiItem.name,
117
- success: true,
118
- result: result.result,
119
- chain: call.chain,
120
- blockNumber: call.blockNumber || "latest",
121
- };
122
- } else {
123
- allResults[originalIndex] = {
124
- index: originalIndex,
125
- label: call.label,
126
- contractAddress: call.contractAddress,
127
- functionName: abiItem.name,
128
- success: false,
129
- error: result.error instanceof Error ? result.error.message : String(result.error),
130
- chain: call.chain,
131
- blockNumber: call.blockNumber || "latest",
132
- };
116
+ success: false,
117
+ error: result.error instanceof Error ? result.error.message : String(result.error),
118
+ chain: call.chain,
119
+ blockNumber: call.blockNumber || 'latest'
133
120
  }
134
- });
135
- }
136
-
137
- return formatResponse({
138
- success: true,
139
- totalCalls: args.calls.length,
140
- successfulCalls: allResults.filter((r) => r.success).length,
141
- failedCalls: allResults.filter((r) => !r.success).length,
142
- results: allResults,
143
- });
144
- } catch (error) {
145
- throw new Error(`Contract call failed: ${error}`);
121
+ }
122
+ })
146
123
  }
124
+
125
+ return formatResponse({
126
+ success: true,
127
+ totalCalls: args.calls.length,
128
+ successfulCalls: allResults.filter((r) => r.success).length,
129
+ failedCalls: allResults.filter((r) => !r.success).length,
130
+ results: allResults
131
+ })
147
132
  }
148
133
  ),
149
134
 
150
135
  is_contract: createTool(
151
- "Check Address Type",
152
- "Determine if address is smart contract or EOA. Returns contract status and bytecode length.",
136
+ 'Check Address Type',
137
+ 'Determine if address is smart contract or EOA. Returns contract status and bytecode length.',
153
138
  z.object({
154
- chain: z.enum(SUPPORTED_CHAINS).describe("Blockchain network to query"),
155
- address: z
156
- .string()
157
- .describe("Address to check (returns true if contract, false if EOA/wallet)"),
139
+ chain: z.enum(SUPPORTED_CHAINS).describe('Blockchain network to query'),
140
+ address: z.string().describe('Address to check (returns true if contract, false if EOA/wallet)')
158
141
  }),
159
142
  async (args) => {
160
143
  if (!isAddress(args.address)) {
161
- throw new Error("Invalid address format");
144
+ throw new Error('Invalid address format')
162
145
  }
163
146
 
164
- const clientManager = getClientManager();
165
- const client = clientManager.getClient(args.chain as ChainName);
166
-
167
- try {
168
- const bytecode = await client.getBytecode({ address: args.address as Address });
169
- const isContractFlag = bytecode !== undefined && bytecode !== "0x";
170
-
171
- return formatResponse({
172
- address: args.address,
173
- isContract: isContractFlag,
174
- type: isContractFlag ? "contract" : "EOA",
175
- chain: args.chain,
176
- bytecodeLength: bytecode ? bytecode.length - 2 : 0, // -2 for '0x' prefix
177
- });
178
- } catch (error) {
179
- throw new Error(`Failed to check address type: ${error}`);
180
- }
147
+ const clientManager = getClientManager()
148
+ const client = clientManager.getClient(args.chain as ChainName)
149
+
150
+ const bytecode = await client.getBytecode({ address: args.address as Address })
151
+ const isContractFlag = bytecode !== undefined && bytecode !== '0x'
152
+
153
+ return formatResponse({
154
+ address: args.address,
155
+ isContract: isContractFlag,
156
+ type: isContractFlag ? 'contract' : 'EOA',
157
+ chain: args.chain,
158
+ bytecodeLength: bytecode ? bytecode.length - 2 : 0 // -2 for '0x' prefix
159
+ })
181
160
  }
182
- ),
183
- };
161
+ )
162
+ }