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.
- package/CHANGELOG.md +95 -0
- package/README.md +328 -234
- package/dist/anvil.d.ts +1 -1
- package/dist/anvil.d.ts.map +1 -1
- package/dist/anvil.js +11 -9
- package/dist/anvil.js.map +1 -1
- package/dist/chain-meta.d.ts.map +1 -1
- package/dist/chain-meta.js +7 -3
- package/dist/chain-meta.js.map +1 -1
- package/dist/chains.d.ts +960 -0
- package/dist/chains.d.ts.map +1 -0
- package/dist/chains.js +108 -0
- package/dist/chains.js.map +1 -0
- package/dist/client.d.ts +10 -1717
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +33 -218
- package/dist/client.js.map +1 -1
- package/dist/http-server.d.ts +6 -0
- package/dist/http-server.d.ts.map +1 -1
- package/dist/http-server.js +55 -40
- package/dist/http-server.js.map +1 -1
- package/dist/index.js +50 -29
- package/dist/index.js.map +1 -1
- package/dist/oauth.d.ts +31 -0
- package/dist/oauth.d.ts.map +1 -0
- package/dist/oauth.js +227 -0
- package/dist/oauth.js.map +1 -0
- package/dist/preview.d.ts.map +1 -1
- package/dist/preview.js +2 -2
- package/dist/preview.js.map +1 -1
- package/dist/tools/advanced.d.ts +4 -4
- package/dist/tools/advanced.d.ts.map +1 -1
- package/dist/tools/advanced.js +228 -247
- package/dist/tools/advanced.js.map +1 -1
- package/dist/tools/balance.d.ts +1 -1
- package/dist/tools/balance.d.ts.map +1 -1
- package/dist/tools/balance.js +97 -103
- package/dist/tools/balance.js.map +1 -1
- package/dist/tools/contract-info.d.ts +3 -3
- package/dist/tools/contract-info.d.ts.map +1 -1
- package/dist/tools/contract-info.js +150 -161
- package/dist/tools/contract-info.js.map +1 -1
- package/dist/tools/contract.d.ts +4 -4
- package/dist/tools/contract.d.ts.map +1 -1
- package/dist/tools/contract.js +91 -110
- package/dist/tools/contract.js.map +1 -1
- package/dist/tools/ens.d.ts +6 -6
- package/dist/tools/ens.d.ts.map +1 -1
- package/dist/tools/ens.js +239 -297
- package/dist/tools/ens.js.map +1 -1
- package/dist/tools/gas.d.ts +4 -4
- package/dist/tools/gas.d.ts.map +1 -1
- package/dist/tools/gas.js +90 -138
- package/dist/tools/gas.js.map +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/logs.d.ts +1 -1
- package/dist/tools/logs.d.ts.map +1 -1
- package/dist/tools/logs.js +36 -37
- package/dist/tools/logs.js.map +1 -1
- package/dist/tools/signatures.d.ts.map +1 -1
- package/dist/tools/signatures.js +67 -89
- package/dist/tools/signatures.js.map +1 -1
- package/dist/tools/transactions.d.ts +35 -5
- package/dist/tools/transactions.d.ts.map +1 -1
- package/dist/tools/transactions.js +148 -74
- package/dist/tools/transactions.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +35 -98
- package/dist/utils.js.map +1 -1
- package/dist/wallet-client.d.ts +11 -1
- package/dist/wallet-client.d.ts.map +1 -1
- package/dist/wallet-client.js +27 -9
- package/dist/wallet-client.js.map +1 -1
- package/dist/walletconnect.d.ts +22 -1
- package/dist/walletconnect.d.ts.map +1 -1
- package/dist/walletconnect.js +101 -8
- package/dist/walletconnect.js.map +1 -1
- package/package.json +17 -9
- package/src/anvil.ts +15 -18
- package/src/chain-meta.ts +8 -4
- package/src/chains.ts +145 -0
- package/src/client.ts +39 -254
- package/src/http-server.ts +67 -44
- package/src/index.ts +77 -53
- package/src/oauth.ts +270 -0
- package/src/preview.ts +3 -12
- package/src/tools/advanced.ts +195 -213
- package/src/tools/balance.ts +103 -108
- package/src/tools/contract-info.ts +163 -184
- package/src/tools/contract.ts +123 -144
- package/src/tools/ens.ts +290 -345
- package/src/tools/gas.ts +122 -170
- package/src/tools/index.ts +2 -2
- package/src/tools/logs.ts +45 -43
- package/src/tools/signatures.ts +68 -86
- package/src/tools/transactions.ts +194 -87
- package/src/types.ts +1 -15
- package/src/utils.ts +52 -110
- package/src/wallet-client.ts +31 -9
- package/src/walletconnect.ts +119 -7
package/src/tools/contract.ts
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import { type AbiFunction, type Address, isAddress, parseAbiItem } from
|
|
2
|
-
import { z } from
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { convertArgumentsToTypes, createTool, formatResponse } from
|
|
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
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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 ||
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
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
|
-
|
|
152
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
+
}
|