web3-tools-mcp 1.3.4 → 1.5.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 (65) hide show
  1. package/README.md +63 -5
  2. package/dist/anvil.d.ts +34 -0
  3. package/dist/anvil.d.ts.map +1 -0
  4. package/dist/anvil.js +254 -0
  5. package/dist/anvil.js.map +1 -0
  6. package/dist/chain-meta.d.ts +27 -0
  7. package/dist/chain-meta.d.ts.map +1 -0
  8. package/dist/chain-meta.js +92 -0
  9. package/dist/chain-meta.js.map +1 -0
  10. package/dist/clear-signing.d.ts +62 -0
  11. package/dist/clear-signing.d.ts.map +1 -0
  12. package/dist/clear-signing.js +139 -0
  13. package/dist/clear-signing.js.map +1 -0
  14. package/dist/client.d.ts +3 -0
  15. package/dist/client.d.ts.map +1 -1
  16. package/dist/client.js +11 -0
  17. package/dist/client.js.map +1 -1
  18. package/dist/erc7730-index.json.gz +0 -0
  19. package/dist/index.js +25 -5
  20. package/dist/index.js.map +1 -1
  21. package/dist/preview.d.ts +58 -0
  22. package/dist/preview.d.ts.map +1 -0
  23. package/dist/preview.js +182 -0
  24. package/dist/preview.js.map +1 -0
  25. package/dist/tools/advanced.d.ts +67 -9
  26. package/dist/tools/advanced.d.ts.map +1 -1
  27. package/dist/tools/advanced.js +206 -67
  28. package/dist/tools/advanced.js.map +1 -1
  29. package/dist/tools/balance.d.ts +5 -5
  30. package/dist/tools/contract-info.d.ts +9 -9
  31. package/dist/tools/contract.d.ts +8 -8
  32. package/dist/tools/ens.d.ts +15 -15
  33. package/dist/tools/gas.d.ts +18 -18
  34. package/dist/tools/logs.d.ts +3 -3
  35. package/dist/tools/transactions.d.ts +11 -11
  36. package/dist/tools/transactions.d.ts.map +1 -1
  37. package/dist/tools/transactions.js +92 -113
  38. package/dist/tools/transactions.js.map +1 -1
  39. package/dist/utils.d.ts +13 -0
  40. package/dist/utils.d.ts.map +1 -1
  41. package/dist/utils.js +76 -0
  42. package/dist/utils.js.map +1 -1
  43. package/dist/wallet-client.d.ts +83 -0
  44. package/dist/wallet-client.d.ts.map +1 -0
  45. package/dist/wallet-client.js +357 -0
  46. package/dist/wallet-client.js.map +1 -0
  47. package/package.json +11 -9
  48. package/src/anvil.ts +323 -0
  49. package/src/chain-meta.ts +102 -0
  50. package/src/clear-signing.ts +206 -0
  51. package/src/client.ts +14 -0
  52. package/src/erc7730-index.json.gz +0 -0
  53. package/src/index.ts +26 -5
  54. package/src/preview.ts +265 -0
  55. package/src/tools/advanced.ts +239 -70
  56. package/src/tools/transactions.ts +98 -125
  57. package/src/utils.ts +95 -0
  58. package/src/wallet-client.ts +380 -0
  59. package/dist/wallet-server.d.ts +0 -35
  60. package/dist/wallet-server.d.ts.map +0 -1
  61. package/dist/wallet-server.js +0 -232
  62. package/dist/wallet-server.js.map +0 -1
  63. package/public/wallet-app.js +0 -677
  64. package/public/wallet.html +0 -723
  65. package/src/wallet-server.ts +0 -283
package/README.md CHANGED
@@ -6,6 +6,7 @@ A Model Context Protocol (MCP) server for blockchain interactions using [viem](h
6
6
 
7
7
  - Multi-chain support (Ethereum, Arbitrum, Avalanche, Base, BNB Chain, Gnosis, Sonic, Optimism, Polygon, zkSync Era, Linea, Unichain)
8
8
  - **🔐 Transaction Signing via Browser Wallet** (MetaMask, Rabby, Coinbase Wallet)
9
+ - Clear signing: transactions are decoded, named and simulated before you approve them
9
10
  - Smart contract interactions (read & write functions, ABI retrieval, source code)
10
11
  - Contract simulation & gas estimation (simulate transactions, estimate costs)
11
12
  - Real-time gas price tracking (legacy & EIP-1559)
@@ -46,6 +47,10 @@ All API keys are optional. The server uses public RPCs by default. Add keys to u
46
47
  - `--infura-api-key` or `INFURA_API_KEY` - Additional RPC provider
47
48
  - `--custom-rpc` - Custom RPC URLs as JSON
48
49
 
50
+ **Wallet relay (all optional):**
51
+ - `WALLET_SERVER_URL` + `WALLET_TOKEN` - Use a hosted relay instead of a local one
52
+ - `PORT`, `HOST`, `WALLET_PUBLIC_URL` - Read by the relay itself when you host it
53
+
49
54
  **Get free API keys:**
50
55
  - Etherscan: [etherscan.io/apis](https://etherscan.io/apis)
51
56
  - Hypersync: [hypersync.xyz](https://hypersync.xyz)
@@ -79,7 +84,7 @@ npx web3-tools-mcp
79
84
  | zkSync Era | 324 | ✅ |
80
85
  | Linea | 59144 | ✅ |
81
86
  | Unichain | 130 | ✅ |
82
- | Localhost | 31337 | ❌ |
87
+ | Localhost | 1337 | ❌ |
83
88
 
84
89
  ## 🔐 Browser Wallet Integration
85
90
 
@@ -88,12 +93,63 @@ The server includes a built-in wallet interface for secure transaction signing w
88
93
  ### How It Works
89
94
 
90
95
  1. When you use a transaction tool (e.g., `send_native_token`), the server automatically:
91
- - Starts a local web server on `http://localhost:3456`
96
+ - Starts a local wallet relay on `http://127.0.0.1:3456` (the next free port up to 3460)
92
97
  - Opens your browser to connect your wallet (MetaMask, Rabby, Coinbase Wallet, etc.)
93
98
  2. You connect your wallet once in the browser
94
- 3. Transaction requests appear in the browser for approval
99
+ 3. The transaction is decoded and simulated, then the request appears in the browser for approval
95
100
  4. Sign or reject transactions directly in your wallet
96
101
 
102
+ The browser page and the MCP server are both clients of the relay, authenticated with a
103
+ pairing token that travels in the URL fragment (`#t=…`) of the link the server prints.
104
+
105
+ **One relay per machine.** Every local MCP process looks for an existing relay on ports
106
+ 3456-3460 before starting one, so all your editor sessions share a single wallet page —
107
+ connect once, and a transaction from any session lands in that tab. They authenticate with
108
+ a token kept in `~/.config/web3-tools-mcp/relay-token` (mode 0600); delete it to rotate.
109
+
110
+ ### Before You Sign
111
+
112
+ Each request shows what the transaction actually does, not just calldata:
113
+
114
+ - **What it means** — for contracts in the [Ledger ERC-7730 registry](https://github.com/LedgerHQ/clear-signing-erc7730-registry)
115
+ (651 contracts, 3253 selectors), the protocol's own intent and field labels: *Supply ·
116
+ Aave · Amount to supply: 250 USDC*, rather than `supply(address,uint256,address,uint16)`.
117
+ The index ships gzipped (~60KB) and a weekly workflow opens a PR when it changes.
118
+ - **Decoded call** — otherwise the function name and named arguments, from the verified ABI
119
+ (Sourcify / Etherscan) or, for unverified contracts, from selectors recovered from bytecode
120
+ via [WhatsABI](https://github.com/shazow/whatsabi). Proxies resolve to their implementation.
121
+ - **Token amounts** — formatted with on-chain decimals and symbol; unlimited approvals are
122
+ flagged explicitly.
123
+ - **Simulation** — `eth_simulateV1` (falling back to `eth_call` + `estimateGas`) reports the
124
+ gas estimate and every ERC20 transfer the transaction would cause, marked in/out for your
125
+ account. A reverting transaction is shown as such before you can approve it.
126
+
127
+ ### Hosting the Wallet Page
128
+
129
+ The relay lives in its own workspace, [`wallet/`](wallet), and depends only on express, cors
130
+ and ws (~4MB installed, against ~180MB for the MCP server). It deploys on its own to any host
131
+ that keeps a Node process alive and supports WebSockets — Render, Railway, Fly:
132
+
133
+ ```bash
134
+ # On the host, from wallet/ — PORT is provided by the platform
135
+ WALLET_TOKEN=<long random string> WALLET_PUBLIC_URL=https://wallet.example.com npm start
136
+ ```
137
+
138
+ Then point the MCP server at it:
139
+
140
+ ```bash
141
+ export WALLET_SERVER_URL=https://wallet.example.com
142
+ export WALLET_TOKEN=<the same token>
143
+ npx web3-tools-mcp
144
+ ```
145
+
146
+ `render.yaml` deploys the relay as-is (`rootDir: wallet`, so only its dependencies are
147
+ installed). Anyone holding `WALLET_TOKEN` can send your browser transactions to sign, so
148
+ treat it like a password and always serve the page over HTTPS.
149
+
150
+ Locally nothing changes: the MCP server embeds the same relay and `npm run start:wallet`
151
+ runs it from the workspace.
152
+
97
153
  ### Supported Wallets
98
154
 
99
155
  - MetaMask
@@ -103,12 +159,12 @@ The server includes a built-in wallet interface for secure transaction signing w
103
159
 
104
160
  ### Manual Access
105
161
 
106
- Visit `http://localhost:3456` anytime to:
162
+ Visit the wallet URL printed by the server (`wallet_status` also returns it) anytime to:
107
163
  - Check wallet connection status
108
164
  - See pending transactions
109
165
  - View transaction history
110
166
 
111
- **Note:** The wallet server runs automatically when the MCP server starts. No additional setup required!
167
+ **Note:** The relay starts automatically with the MCP server. No additional setup required.
112
168
 
113
169
  ## Available Tools
114
170
 
@@ -116,6 +172,7 @@ Visit `http://localhost:3456` anytime to:
116
172
  - `get_function_signature` - Generate 4-byte function selectors
117
173
  - `get_event_signature` - Generate 32-byte event topic0 hashes
118
174
  - `get_error_signature` - Generate 4-byte error selectors
175
+ - `encode_function_data` - Encode a call's calldata from its ABI and arguments
119
176
 
120
177
  ### Contract Info
121
178
  - `get_contract_abi` - Get ABI with proxy detection and verification status
@@ -155,6 +212,7 @@ Visit `http://localhost:3456` anytime to:
155
212
  - `get_storage_at` - Read storage slots with type decoding
156
213
  - `get_block_info` - Get block data (timestamp, hash, etc.)
157
214
  - `trace_transaction` - Trace execution (call tree, VM, state diff)
215
+ - `debug_call` - Trace a call without broadcasting it, falling back to a local anvil fork
158
216
 
159
217
  ## Advanced Configuration
160
218
 
@@ -0,0 +1,34 @@
1
+ import { ChildProcess } from 'child_process';
2
+ import { createPublicClient } from 'viem';
3
+ export interface AnvilInstance {
4
+ process: ChildProcess;
5
+ port: number;
6
+ rpcUrl: string;
7
+ client: ReturnType<typeof createPublicClient>;
8
+ }
9
+ export interface AnvilConfig {
10
+ forkUrl: string;
11
+ forkBlockNumber?: bigint;
12
+ port?: number;
13
+ chainId?: number;
14
+ }
15
+ export declare function isAnvilInstalled(): Promise<boolean>;
16
+ export declare function startAnvil(config: AnvilConfig): Promise<AnvilInstance>;
17
+ export declare function stopAnvil(instance: AnvilInstance): void;
18
+ export declare function withAnvil<T>(config: AnvilConfig, fn: (instance: AnvilInstance) => Promise<T>): Promise<T>;
19
+ export declare function traceTransactionWithAnvil(forkUrl: string, transactionHash: string, blockNumber: bigint, tracer?: 'callTracer' | 'prestateTracer' | 'stateDiffTracer', chainName?: string): Promise<unknown>;
20
+ export interface SimulateCallParams {
21
+ to: string;
22
+ data?: string;
23
+ from?: string;
24
+ value?: bigint;
25
+ gas?: bigint;
26
+ }
27
+ export declare function simulateCallWithTrace(forkUrl: string, params: SimulateCallParams, blockNumber?: bigint, tracer?: 'callTracer' | 'prestateTracer'): Promise<{
28
+ result: string;
29
+ trace: unknown;
30
+ gasUsed: bigint;
31
+ success: boolean;
32
+ revertReason?: string;
33
+ }>;
34
+ //# sourceMappingURL=anvil.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anvil.d.ts","sourceRoot":"","sources":["../src/anvil.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,YAAY,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,EAAE,kBAAkB,EAAQ,MAAM,MAAM,CAAA;AAG/C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,YAAY,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAA;CAC9C;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAGD,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAMzD;AAuBD,wBAAsB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAsF5E;AAGD,wBAAgB,SAAS,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI,CAIvD;AAGD,wBAAsB,SAAS,CAAC,CAAC,EAC/B,MAAM,EAAE,WAAW,EACnB,EAAE,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAC1C,OAAO,CAAC,CAAC,CAAC,CAOZ;AAQD,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,EACnB,MAAM,GAAE,YAAY,GAAG,gBAAgB,GAAG,iBAAgC,EAC1E,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAiDlB;AAGD,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,kBAAkB,EAC1B,WAAW,CAAC,EAAE,MAAM,EACpB,MAAM,GAAE,YAAY,GAAG,gBAA+B,GACrD,OAAO,CAAC;IACT,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAC,CAmFD"}
package/dist/anvil.js ADDED
@@ -0,0 +1,254 @@
1
+ import { spawn } from 'child_process';
2
+ import { createPublicClient, http } from 'viem';
3
+ import { mainnet } from 'viem/chains';
4
+ // Check if anvil is installed
5
+ export async function isAnvilInstalled() {
6
+ return new Promise(resolve => {
7
+ const proc = spawn('anvil', ['--version'], { shell: true });
8
+ proc.on('close', code => resolve(code === 0));
9
+ proc.on('error', () => resolve(false));
10
+ });
11
+ }
12
+ // Find an available port
13
+ async function findAvailablePort(startPort = 8545) {
14
+ const net = await import('net');
15
+ return new Promise((resolve, reject) => {
16
+ const server = net.createServer();
17
+ server.listen(startPort, () => {
18
+ const address = server.address();
19
+ if (address && typeof address === 'object') {
20
+ server.close(() => resolve(address.port));
21
+ }
22
+ else {
23
+ server.close(() => reject(new Error('Could not get port')));
24
+ }
25
+ });
26
+ server.on('error', () => {
27
+ // Port in use, try next one
28
+ findAvailablePort(startPort + 1).then(resolve).catch(reject);
29
+ });
30
+ });
31
+ }
32
+ // Start an Anvil instance
33
+ export async function startAnvil(config) {
34
+ const installed = await isAnvilInstalled();
35
+ if (!installed) {
36
+ throw new Error('Anvil is not installed. Please install Foundry: https://book.getfoundry.sh/getting-started/installation');
37
+ }
38
+ const port = config.port ?? (await findAvailablePort());
39
+ const rpcUrl = `http://127.0.0.1:${port}`;
40
+ const args = ['--fork-url', config.forkUrl, '--port', port.toString(), '--silent'];
41
+ if (config.forkBlockNumber !== undefined) {
42
+ args.push('--fork-block-number', config.forkBlockNumber.toString());
43
+ }
44
+ if (config.chainId !== undefined) {
45
+ args.push('--chain-id', config.chainId.toString());
46
+ }
47
+ return new Promise((resolve, reject) => {
48
+ const proc = spawn('anvil', args, {
49
+ shell: true,
50
+ stdio: ['ignore', 'pipe', 'pipe']
51
+ });
52
+ let stderr = '';
53
+ proc.stderr?.on('data', data => {
54
+ stderr += data.toString();
55
+ });
56
+ proc.on('error', err => {
57
+ reject(new Error(`Failed to start Anvil: ${err.message}`));
58
+ });
59
+ proc.on('close', code => {
60
+ if (code !== 0 && code !== null) {
61
+ reject(new Error(`Anvil exited with code ${code}: ${stderr}`));
62
+ }
63
+ });
64
+ // Wait for Anvil to be ready by polling the RPC
65
+ const maxAttempts = 50; // 5 seconds
66
+ let attempts = 0;
67
+ const checkReady = async () => {
68
+ attempts++;
69
+ try {
70
+ const response = await fetch(rpcUrl, {
71
+ method: 'POST',
72
+ headers: { 'Content-Type': 'application/json' },
73
+ body: JSON.stringify({
74
+ jsonrpc: '2.0',
75
+ method: 'eth_blockNumber',
76
+ params: [],
77
+ id: 1
78
+ })
79
+ });
80
+ if (response.ok) {
81
+ // Add small delay to ensure all RPC methods are ready (including debug_*)
82
+ await new Promise(r => setTimeout(r, 100));
83
+ const client = createPublicClient({
84
+ chain: mainnet, // Chain is overridden by fork anyway
85
+ transport: http(rpcUrl)
86
+ });
87
+ resolve({ process: proc, port, rpcUrl, client });
88
+ }
89
+ else if (attempts < maxAttempts) {
90
+ setTimeout(checkReady, 100);
91
+ }
92
+ else {
93
+ proc.kill();
94
+ reject(new Error('Anvil failed to start within timeout'));
95
+ }
96
+ }
97
+ catch {
98
+ if (attempts < maxAttempts) {
99
+ setTimeout(checkReady, 100);
100
+ }
101
+ else {
102
+ proc.kill();
103
+ reject(new Error('Anvil failed to start within timeout'));
104
+ }
105
+ }
106
+ };
107
+ // Start checking after a small delay
108
+ setTimeout(checkReady, 200);
109
+ });
110
+ }
111
+ // Stop an Anvil instance
112
+ export function stopAnvil(instance) {
113
+ if (instance.process && !instance.process.killed) {
114
+ instance.process.kill('SIGTERM');
115
+ }
116
+ }
117
+ // Helper to run a function with a temporary Anvil instance
118
+ export async function withAnvil(config, fn) {
119
+ const instance = await startAnvil(config);
120
+ try {
121
+ return await fn(instance);
122
+ }
123
+ finally {
124
+ stopAnvil(instance);
125
+ }
126
+ }
127
+ // Archive RPC URLs for chains that need historical state access
128
+ const ARCHIVE_RPCS = {
129
+ arbitrum: 'https://arbitrum-one.arpc.x.superfluid.dev'
130
+ };
131
+ // Trace a transaction using Anvil fork
132
+ export async function traceTransactionWithAnvil(forkUrl, transactionHash, blockNumber, tracer = 'callTracer', chainName) {
133
+ // Use archive RPC if available for better historical state access
134
+ const archiveUrl = chainName && ARCHIVE_RPCS[chainName] ? ARCHIVE_RPCS[chainName] : forkUrl;
135
+ // First, fetch the original transaction from the RPC
136
+ const fetchClient = createPublicClient({
137
+ chain: mainnet,
138
+ transport: http(forkUrl)
139
+ });
140
+ const tx = await fetchClient.getTransaction({
141
+ hash: transactionHash
142
+ });
143
+ if (!tx) {
144
+ throw new Error(`Transaction ${transactionHash} not found`);
145
+ }
146
+ // Fork at the block BEFORE the transaction to replay it accurately
147
+ // Note: This requires an archival RPC that has historical state
148
+ const forkBlock = blockNumber > 0n ? blockNumber - 1n : 0n;
149
+ return withAnvil({
150
+ forkUrl: archiveUrl,
151
+ forkBlockNumber: forkBlock
152
+ }, async (instance) => {
153
+ // Use debug_traceCall to simulate and trace the transaction
154
+ // Note: This replays the tx against the post-block state, which may differ slightly
155
+ // from the original execution if state changed within the block
156
+ const trace = await instance.client.request({
157
+ method: 'debug_traceCall',
158
+ params: [
159
+ {
160
+ from: tx.from,
161
+ to: tx.to,
162
+ data: tx.input,
163
+ value: tx.value ? `0x${tx.value.toString(16)}` : '0x0',
164
+ gas: tx.gas ? `0x${tx.gas.toString(16)}` : undefined
165
+ },
166
+ 'latest',
167
+ { tracer }
168
+ ]
169
+ });
170
+ return trace;
171
+ });
172
+ }
173
+ export async function simulateCallWithTrace(forkUrl, params, blockNumber, tracer = 'callTracer') {
174
+ return withAnvil({
175
+ forkUrl,
176
+ forkBlockNumber: blockNumber
177
+ }, async (instance) => {
178
+ // First, try a regular call to get the result
179
+ let result = '0x';
180
+ let success = true;
181
+ let revertReason;
182
+ let gasUsed = 0n;
183
+ try {
184
+ // Use eth_call to get the result
185
+ result = (await instance.client.request({
186
+ method: 'eth_call',
187
+ params: [
188
+ {
189
+ to: params.to,
190
+ data: params.data,
191
+ from: params.from ?? '0x0000000000000000000000000000000000000000',
192
+ value: params.value ? `0x${params.value.toString(16)}` : undefined,
193
+ gas: params.gas ? `0x${params.gas.toString(16)}` : undefined
194
+ },
195
+ 'latest'
196
+ ]
197
+ }));
198
+ // Estimate gas
199
+ try {
200
+ gasUsed = await instance.client.estimateGas({
201
+ to: params.to,
202
+ data: params.data,
203
+ account: params.from,
204
+ value: params.value
205
+ });
206
+ }
207
+ catch {
208
+ // Ignore gas estimation errors
209
+ }
210
+ }
211
+ catch (error) {
212
+ success = false;
213
+ const errorMessage = error.message;
214
+ // Try to extract revert reason
215
+ const revertMatch = errorMessage.match(/revert(?:ed)?[:\s]*(.+?)(?:\n|$)/i);
216
+ if (revertMatch) {
217
+ revertReason = revertMatch[1].trim();
218
+ }
219
+ else {
220
+ revertReason = errorMessage;
221
+ }
222
+ }
223
+ // Get the trace using debug_traceCall
224
+ let trace = null;
225
+ try {
226
+ trace = await instance.client.request({
227
+ method: 'debug_traceCall',
228
+ params: [
229
+ {
230
+ to: params.to,
231
+ data: params.data,
232
+ from: params.from ?? '0x0000000000000000000000000000000000000000',
233
+ value: params.value ? `0x${params.value.toString(16)}` : undefined,
234
+ gas: params.gas ? `0x${params.gas.toString(16)}` : undefined
235
+ },
236
+ 'latest',
237
+ { tracer }
238
+ ]
239
+ });
240
+ }
241
+ catch (traceError) {
242
+ // If tracing fails, include the error
243
+ trace = { error: traceError.message };
244
+ }
245
+ return {
246
+ result,
247
+ trace,
248
+ gasUsed,
249
+ success,
250
+ revertReason
251
+ };
252
+ });
253
+ }
254
+ //# sourceMappingURL=anvil.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anvil.js","sourceRoot":"","sources":["../src/anvil.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * On-chain metadata every layer of the preview needs: contract ABIs, token symbols and
3
+ * human labels for addresses. Kept apart from the preview and the clear-signing registry
4
+ * so both can use it without importing each other.
5
+ */
6
+ import { type Abi } from 'viem';
7
+ import type { ChainName } from './types.js';
8
+ export declare function loadAbi(chain: ChainName, address: string): Promise<{
9
+ abi: Abi;
10
+ source: "verified" | "guessed";
11
+ proxy?: string;
12
+ name?: string;
13
+ }>;
14
+ export declare function tokenMeta(chain: ChainName, token: string): Promise<{
15
+ symbol?: string;
16
+ decimals?: number;
17
+ }>;
18
+ /**
19
+ * Human label for an address: token symbol first (more recognisable than the contract
20
+ * name — "USDC" beats "FiatTokenProxy"), then the verified contract name. EOAs get none,
21
+ * and the bytecode check keeps us from asking explorers about plain wallets.
22
+ */
23
+ export declare function addressLabel(chain: ChainName, address: string): Promise<string | undefined>;
24
+ /** uint256 max and anything near it means "infinite" in an approval. */
25
+ export declare const MAX_UINT256: bigint;
26
+ export declare const UNLIMITED_THRESHOLD: bigint;
27
+ //# sourceMappingURL=chain-meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chain-meta.d.ts","sourceRoot":"","sources":["../src/chain-meta.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,KAAK,GAAG,EAA8B,MAAM,MAAM,CAAA;AAG3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAW3C,wBAAsB,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM;SAJvB,GAAG;YAAU,UAAU,GAAG,SAAS;YAAU,MAAM;WAAS,MAAM;GAmCzG;AAED,wBAAsB,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM;aAnClB,MAAM;eAAa,MAAM;GAuDrE;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAiBjG;AAGD,wEAAwE;AACxE,eAAO,MAAM,WAAW,QAAoB,CAAA;AAC5C,eAAO,MAAM,mBAAmB,QAAmB,CAAA"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * On-chain metadata every layer of the preview needs: contract ABIs, token symbols and
3
+ * human labels for addresses. Kept apart from the preview and the clear-signing registry
4
+ * so both can use it without importing each other.
5
+ */
6
+ import { parseAbiItem } from 'viem';
7
+ import { whatsabi } from '@shazow/whatsabi';
8
+ import { getClientManager } from './client.js';
9
+ const ERC20_META_ABI = [
10
+ parseAbiItem('function decimals() view returns (uint8)'),
11
+ parseAbiItem('function symbol() view returns (string)')
12
+ ];
13
+ const abiCache = new Map();
14
+ const labelCache = new Map();
15
+ const metaCache = new Map();
16
+ export async function loadAbi(chain, address) {
17
+ const key = `${chain}:${address.toLowerCase()}`;
18
+ const cached = abiCache.get(key);
19
+ if (cached)
20
+ return cached;
21
+ const clientManager = getClientManager();
22
+ const client = clientManager.getClient(chain);
23
+ const etherscanApiKey = clientManager.getConfig().etherscanApiKey;
24
+ const loaders = [new whatsabi.loaders.SourcifyABILoader({ chainId: clientManager.getChainId(chain) })];
25
+ if (etherscanApiKey) {
26
+ loaders.push(new whatsabi.loaders.EtherscanV2ABILoader({ apiKey: etherscanApiKey, chainId: clientManager.getChainId(chain) }));
27
+ }
28
+ const result = await whatsabi.autoload(address, {
29
+ provider: client,
30
+ abiLoader: new whatsabi.loaders.MultiABILoader(loaders),
31
+ signatureLookup: new whatsabi.loaders.OpenChainSignatureLookup(),
32
+ followProxies: true
33
+ });
34
+ // Bytecode-guessed ABIs carry no argument names; verified ones do.
35
+ const source = result.abi.some((item) => item.type === 'function' && item.inputs?.some((i) => i.name)) ? 'verified' : 'guessed';
36
+ const loaded = {
37
+ abi: result.abi,
38
+ source: source,
39
+ proxy: result.address !== address ? result.address : undefined,
40
+ name: result.contractResult?.name ?? undefined
41
+ };
42
+ abiCache.set(key, loaded);
43
+ return loaded;
44
+ }
45
+ export async function tokenMeta(chain, token) {
46
+ const key = `${chain}:${token.toLowerCase()}`;
47
+ const cached = metaCache.get(key);
48
+ if (cached)
49
+ return cached;
50
+ const client = getClientManager().getClient(chain);
51
+ const [decimals, symbol] = await client.multicall({
52
+ contracts: [
53
+ { address: token, abi: ERC20_META_ABI, functionName: 'decimals' },
54
+ { address: token, abi: ERC20_META_ABI, functionName: 'symbol' }
55
+ ],
56
+ ...(chain === 'localhost' && { deployless: true })
57
+ });
58
+ const meta = {
59
+ decimals: decimals.status === 'success' ? Number(decimals.result) : undefined,
60
+ symbol: symbol.status === 'success' ? symbol.result : undefined
61
+ };
62
+ metaCache.set(key, meta);
63
+ return meta;
64
+ }
65
+ /**
66
+ * Human label for an address: token symbol first (more recognisable than the contract
67
+ * name — "USDC" beats "FiatTokenProxy"), then the verified contract name. EOAs get none,
68
+ * and the bytecode check keeps us from asking explorers about plain wallets.
69
+ */
70
+ export async function addressLabel(chain, address) {
71
+ const key = `${chain}:${address.toLowerCase()}`;
72
+ if (labelCache.has(key))
73
+ return labelCache.get(key);
74
+ let label;
75
+ try {
76
+ const code = await getClientManager().getClient(chain).getBytecode({ address: address });
77
+ if (code && code !== '0x') {
78
+ label = (await tokenMeta(chain, address).catch(() => ({ symbol: undefined }))).symbol;
79
+ if (!label)
80
+ label = (await loadAbi(chain, address)).name;
81
+ }
82
+ }
83
+ catch {
84
+ // Unknown address — show it bare rather than failing the preview.
85
+ }
86
+ labelCache.set(key, label);
87
+ return label;
88
+ }
89
+ /** uint256 max and anything near it means "infinite" in an approval. */
90
+ export const MAX_UINT256 = (1n << 256n) - 1n;
91
+ export const UNLIMITED_THRESHOLD = MAX_UINT256 / 2n;
92
+ //# sourceMappingURL=chain-meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chain-meta.js","sourceRoot":"","sources":["../src/chain-meta.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import type { ChainName } from './types.js';
2
+ /**
3
+ * Clear signing from the Ledger ERC-7730 registry: for a known contract and selector it
4
+ * says what the transaction *means* ("Swap", "Approve spending") and labels each field the
5
+ * way the protocol intends, instead of showing raw ABI argument names.
6
+ *
7
+ * The index is built from the registry by scripts/build-erc7730-index.mjs.
8
+ */
9
+ interface IndexField {
10
+ label?: string;
11
+ path?: string;
12
+ format?: string;
13
+ /** tokenAmount: `tokenPath` points at the field holding the token address. */
14
+ params?: {
15
+ tokenPath?: string;
16
+ tokenAddress?: string;
17
+ [k: string]: unknown;
18
+ };
19
+ }
20
+ interface IndexSelector {
21
+ name: string;
22
+ intent: string;
23
+ /** Full signature, needed to ABI-decode the calldata these paths address. */
24
+ signature?: string;
25
+ fields: IndexField[];
26
+ }
27
+ interface IndexEntry {
28
+ protocol: string;
29
+ chainIds: number[];
30
+ selectors: Record<string, IndexSelector>;
31
+ }
32
+ export interface ClearSigningField {
33
+ label: string;
34
+ format: string;
35
+ value: string;
36
+ address?: string;
37
+ name?: string;
38
+ }
39
+ export interface ClearSigningInfo {
40
+ protocol: string;
41
+ intent: string;
42
+ functionName: string;
43
+ fields: ClearSigningField[];
44
+ }
45
+ export declare function protocolLabel(slug: string): string;
46
+ interface TxContext {
47
+ to: string;
48
+ value?: string;
49
+ from?: string;
50
+ }
51
+ /** Registry entry for a contract, if it is listed for this chain. */
52
+ export declare function lookupContract(chain: ChainName, address: string): IndexEntry | undefined;
53
+ /**
54
+ * Clear-signing info for a transaction, or null when the contract/selector is unlisted.
55
+ * `decodedArgs` is supplied by the caller, which has already decoded the calldata against
56
+ * the contract's real ABI — the registry's own signature is only a fallback for naming.
57
+ */
58
+ export declare function resolveClearSigning(chain: ChainName, tx: TxContext & {
59
+ data?: string;
60
+ }, decodedArgs: Record<string, unknown>): Promise<ClearSigningInfo | null>;
61
+ export {};
62
+ //# sourceMappingURL=clear-signing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clear-signing.d.ts","sourceRoot":"","sources":["../src/clear-signing.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAE3C;;;;;;GAMG;AAEH,UAAU,UAAU;IAElB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;CAC7E;AAED,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,EAAE,UAAU,EAAE,CAAA;CACrB;AAED,UAAU,UAAU;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;CACzC;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,iBAAiB,EAAE,CAAA;CAC5B;AAkCD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAiBD,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AA+DD,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAKxF;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,SAAS,EAChB,EAAE,EAAE,SAAS,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EACjC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAiBlC"}