web3-tools-mcp 1.6.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 (104) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/README.md +328 -234
  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 -218
  17. package/dist/client.js.map +1 -1
  18. package/dist/http-server.d.ts +6 -0
  19. package/dist/http-server.d.ts.map +1 -1
  20. package/dist/http-server.js +55 -40
  21. package/dist/http-server.js.map +1 -1
  22. package/dist/index.js +50 -29
  23. package/dist/index.js.map +1 -1
  24. package/dist/oauth.d.ts +31 -0
  25. package/dist/oauth.d.ts.map +1 -0
  26. package/dist/oauth.js +227 -0
  27. package/dist/oauth.js.map +1 -0
  28. package/dist/preview.d.ts.map +1 -1
  29. package/dist/preview.js +2 -2
  30. package/dist/preview.js.map +1 -1
  31. package/dist/tools/advanced.d.ts +4 -4
  32. package/dist/tools/advanced.d.ts.map +1 -1
  33. package/dist/tools/advanced.js +228 -247
  34. package/dist/tools/advanced.js.map +1 -1
  35. package/dist/tools/balance.d.ts +1 -1
  36. package/dist/tools/balance.d.ts.map +1 -1
  37. package/dist/tools/balance.js +97 -103
  38. package/dist/tools/balance.js.map +1 -1
  39. package/dist/tools/contract-info.d.ts +3 -3
  40. package/dist/tools/contract-info.d.ts.map +1 -1
  41. package/dist/tools/contract-info.js +150 -161
  42. package/dist/tools/contract-info.js.map +1 -1
  43. package/dist/tools/contract.d.ts +4 -4
  44. package/dist/tools/contract.d.ts.map +1 -1
  45. package/dist/tools/contract.js +91 -110
  46. package/dist/tools/contract.js.map +1 -1
  47. package/dist/tools/ens.d.ts +6 -6
  48. package/dist/tools/ens.d.ts.map +1 -1
  49. package/dist/tools/ens.js +239 -297
  50. package/dist/tools/ens.js.map +1 -1
  51. package/dist/tools/gas.d.ts +4 -4
  52. package/dist/tools/gas.d.ts.map +1 -1
  53. package/dist/tools/gas.js +90 -138
  54. package/dist/tools/gas.js.map +1 -1
  55. package/dist/tools/index.d.ts +1 -1
  56. package/dist/tools/index.d.ts.map +1 -1
  57. package/dist/tools/logs.d.ts +1 -1
  58. package/dist/tools/logs.d.ts.map +1 -1
  59. package/dist/tools/logs.js +36 -37
  60. package/dist/tools/logs.js.map +1 -1
  61. package/dist/tools/signatures.d.ts.map +1 -1
  62. package/dist/tools/signatures.js +67 -89
  63. package/dist/tools/signatures.js.map +1 -1
  64. package/dist/tools/transactions.d.ts +35 -5
  65. package/dist/tools/transactions.d.ts.map +1 -1
  66. package/dist/tools/transactions.js +148 -74
  67. package/dist/tools/transactions.js.map +1 -1
  68. package/dist/types.d.ts +1 -1
  69. package/dist/types.d.ts.map +1 -1
  70. package/dist/utils.d.ts +1 -1
  71. package/dist/utils.d.ts.map +1 -1
  72. package/dist/utils.js +35 -98
  73. package/dist/utils.js.map +1 -1
  74. package/dist/wallet-client.d.ts +11 -1
  75. package/dist/wallet-client.d.ts.map +1 -1
  76. package/dist/wallet-client.js +27 -9
  77. package/dist/wallet-client.js.map +1 -1
  78. package/dist/walletconnect.d.ts +22 -1
  79. package/dist/walletconnect.d.ts.map +1 -1
  80. package/dist/walletconnect.js +101 -8
  81. package/dist/walletconnect.js.map +1 -1
  82. package/package.json +17 -9
  83. package/src/anvil.ts +15 -18
  84. package/src/chain-meta.ts +8 -4
  85. package/src/chains.ts +145 -0
  86. package/src/client.ts +39 -254
  87. package/src/http-server.ts +67 -44
  88. package/src/index.ts +77 -53
  89. package/src/oauth.ts +270 -0
  90. package/src/preview.ts +3 -12
  91. package/src/tools/advanced.ts +195 -213
  92. package/src/tools/balance.ts +103 -108
  93. package/src/tools/contract-info.ts +163 -184
  94. package/src/tools/contract.ts +123 -144
  95. package/src/tools/ens.ts +290 -345
  96. package/src/tools/gas.ts +122 -170
  97. package/src/tools/index.ts +2 -2
  98. package/src/tools/logs.ts +45 -43
  99. package/src/tools/signatures.ts +68 -86
  100. package/src/tools/transactions.ts +194 -87
  101. package/src/types.ts +1 -15
  102. package/src/utils.ts +52 -110
  103. package/src/wallet-client.ts +31 -9
  104. package/src/walletconnect.ts +119 -7
@@ -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
+ }