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,7 +1,7 @@
1
1
  import { isAddress } from 'viem'
2
2
  import { z } from 'zod'
3
+ import { getClientManager, SUPPORTED_CHAINS } from '../client.js'
3
4
  import type { ChainName } from '../types.js'
4
- import { CHAINS, getClientManager, SUPPORTED_CHAINS } from '../client.js'
5
5
  import { createTool, formatResponse } from '../utils.js'
6
6
 
7
7
  // Type for raw Etherscan contract info
@@ -44,11 +44,7 @@ function getCacheKey(chainId: number, address: string): string {
44
44
  }
45
45
 
46
46
  // Shared helper to fetch contract info from Etherscan (with caching)
47
- async function fetchContractInfo(
48
- chainId: number,
49
- address: string,
50
- etherscanApiKey: string
51
- ): Promise<EtherscanContractInfo> {
47
+ async function fetchContractInfo(chainId: number, address: string, etherscanApiKey: string): Promise<EtherscanContractInfo> {
52
48
  // Check cache first
53
49
  const cacheKey = getCacheKey(chainId, address)
54
50
  const cached = contractCache.get(cacheKey)
@@ -98,9 +94,7 @@ export default {
98
94
  chain: z.enum(SUPPORTED_CHAINS).describe('The blockchain network to use'),
99
95
  address: z.string().describe('The contract address to get ABI for'),
100
96
  include: z
101
- .array(
102
- z.enum(['abi', 'implementationAbi', 'metadata', 'compilation', 'creation', 'stats'])
103
- )
97
+ .array(z.enum(['abi', 'implementationAbi', 'metadata', 'compilation', 'creation', 'stats']))
104
98
  .optional()
105
99
  .describe(
106
100
  'Optional: specify which data to include. Options: "abi" (contract ABI), "implementationAbi" (proxy implementation ABI), "metadata" (name, verification), "compilation" (compiler, optimization), "creation" (creator, tx), "stats" (function/event counts). Defaults to ["abi", "metadata"] for minimal context usage.'
@@ -124,8 +118,7 @@ export default {
124
118
  const include = args.include || ['abi', 'metadata']
125
119
  const includeSet = new Set(include)
126
120
 
127
- const chains = CHAINS(config)
128
- const chainId = chains[args.chain as ChainName].chain.id
121
+ const chainId = getClientManager().getChainId(args.chain as ChainName)
129
122
 
130
123
  // Use shared helper to fetch contract info
131
124
  const contractInfo = await fetchContractInfo(chainId, args.address, config.etherscanApiKey)
@@ -153,7 +146,7 @@ export default {
153
146
  chain: args.chain,
154
147
  chainId,
155
148
  address: args.address,
156
- etherscanUrl: `https://${clientManager.getEtherscanDomain(args.chain as ChainName)}/address/${args.address}`
149
+ etherscanUrl: clientManager.explorerUrl(args.chain as ChainName, `/address/${args.address}`)
157
150
  }
158
151
 
159
152
  // Add metadata if requested
@@ -292,201 +285,189 @@ export default {
292
285
  const config = (clientManager as any).config
293
286
 
294
287
  if (!config.etherscanApiKey) {
295
- throw new Error(
296
- 'Etherscan API key is required. Use --etherscan-api-key or set ETHERSCAN_API_KEY environment variable.'
297
- )
288
+ throw new Error('Etherscan API key is required. Use --etherscan-api-key or set ETHERSCAN_API_KEY environment variable.')
298
289
  }
299
290
 
300
291
  if (!isAddress(args.address)) {
301
292
  throw new Error('Invalid contract address')
302
293
  }
303
294
 
304
- const chains = CHAINS(config)
305
- const chainId = chains[args.chain as ChainName].chain.id
295
+ const chainId = getClientManager().getChainId(args.chain as ChainName)
306
296
 
307
- try {
308
- // Fetch main contract source using shared helper
309
- const contractInfo = await fetchContractInfo(chainId, args.address, config.etherscanApiKey)
297
+ // Fetch main contract source using shared helper
298
+ const contractInfo = await fetchContractInfo(chainId, args.address, config.etherscanApiKey)
310
299
 
311
- if (!contractInfo) {
312
- throw new Error('No contract information found')
313
- }
300
+ if (!contractInfo) {
301
+ throw new Error('No contract information found')
302
+ }
314
303
 
315
- const isVerified = contractInfo.SourceCode !== ''
316
- const isProxy = contractInfo.Proxy === '1'
317
-
318
- if (!isVerified) {
319
- return formatResponse({
320
- success: false,
321
- chain: args.chain,
322
- chainId,
323
- address: args.address,
324
- isVerified: false,
325
- message: 'Contract source code is not verified on Etherscan',
326
- etherscanUrl: `https://${clientManager.getEtherscanDomain(args.chain as ChainName)}/address/${args.address}`
327
- })
328
- }
304
+ const isVerified = contractInfo.SourceCode !== ''
305
+ const isProxy = contractInfo.Proxy === '1'
329
306
 
330
- // Parse source code
331
- let sourceFiles: Record<string, string> = {}
332
- let sourceCodeString = contractInfo.SourceCode
307
+ if (!isVerified) {
308
+ return formatResponse({
309
+ success: false,
310
+ chain: args.chain,
311
+ chainId,
312
+ address: args.address,
313
+ isVerified: false,
314
+ message: 'Contract source code is not verified on Etherscan',
315
+ etherscanUrl: clientManager.explorerUrl(args.chain as ChainName, `/address/${args.address}`)
316
+ })
317
+ }
333
318
 
334
- // Handle multi-file sources (wrapped in {{ }} or [ ])
335
- if (sourceCodeString.startsWith('{{') || sourceCodeString.startsWith('[')) {
336
- try {
337
- // Remove outer braces/brackets if present
338
- if (sourceCodeString.startsWith('{{')) {
339
- sourceCodeString = sourceCodeString.slice(1, -1)
340
- }
341
- const parsed = JSON.parse(sourceCodeString)
342
-
343
- if (parsed.sources) {
344
- // Standard JSON format
345
- sourceFiles = Object.fromEntries(
346
- Object.entries(parsed.sources).map(([path, data]: [string, any]) => [path, data.content || ''])
347
- )
348
- } else if (typeof parsed === 'object') {
349
- // Simple object format
350
- sourceFiles = parsed
351
- }
352
- } catch {
353
- // If parsing fails, treat as single file
354
- sourceFiles = { [contractInfo.ContractName + '.sol']: sourceCodeString }
319
+ // Parse source code
320
+ let sourceFiles: Record<string, string> = {}
321
+ let sourceCodeString = contractInfo.SourceCode
322
+
323
+ // Handle multi-file sources (wrapped in {{ }} or [ ])
324
+ if (sourceCodeString.startsWith('{{') || sourceCodeString.startsWith('[')) {
325
+ try {
326
+ // Remove outer braces/brackets if present
327
+ if (sourceCodeString.startsWith('{{')) {
328
+ sourceCodeString = sourceCodeString.slice(1, -1)
329
+ }
330
+ const parsed = JSON.parse(sourceCodeString)
331
+
332
+ if (parsed.sources) {
333
+ // Standard JSON format
334
+ sourceFiles = Object.fromEntries(
335
+ Object.entries(parsed.sources).map(([path, data]: [string, any]) => [path, data.content || ''])
336
+ )
337
+ } else if (typeof parsed === 'object') {
338
+ // Simple object format
339
+ sourceFiles = parsed
355
340
  }
356
- } else {
357
- // Single file source
358
- sourceFiles = { [contractInfo.ContractName + '.sol']: sourceCodeString }
341
+ } catch {
342
+ // If parsing fails, treat as single file
343
+ sourceFiles = { [`${contractInfo.ContractName}.sol`]: sourceCodeString }
359
344
  }
345
+ } else {
346
+ // Single file source
347
+ sourceFiles = { [`${contractInfo.ContractName}.sol`]: sourceCodeString }
348
+ }
360
349
 
361
- // Calculate file stats for summary mode
362
- const fileStats = Object.entries(sourceFiles).map(([path, content]) => ({
363
- path,
364
- lines: (content as string).split('\n').length,
365
- size: (content as string).length
366
- }))
350
+ // Calculate file stats for summary mode
351
+ const fileStats = Object.entries(sourceFiles).map(([path, content]) => ({
352
+ path,
353
+ lines: (content as string).split('\n').length,
354
+ size: (content as string).length
355
+ }))
367
356
 
368
- const result: Record<string, unknown> = {
369
- success: true,
370
- chain: args.chain,
371
- chainId,
372
- address: args.address,
373
- isVerified: true,
374
- isProxy,
375
- contractName: contractInfo.ContractName,
376
- compilerVersion: contractInfo.CompilerVersion,
377
- optimizationUsed: contractInfo.OptimizationUsed === '1',
378
- optimizationRuns: parseInt(contractInfo.Runs) || 0,
379
- evmVersion: contractInfo.EVMVersion,
380
- licenseType: contractInfo.LicenseType,
381
- constructorArguments: contractInfo.ConstructorArguments,
382
- fileCount: Object.keys(sourceFiles).length,
383
- totalLines: fileStats.reduce((sum, stat) => sum + stat.lines, 0),
384
- etherscanUrl: `https://${clientManager.getEtherscanDomain(args.chain as ChainName)}/address/${args.address}#code`
385
- }
357
+ const result: Record<string, unknown> = {
358
+ success: true,
359
+ chain: args.chain,
360
+ chainId,
361
+ address: args.address,
362
+ isVerified: true,
363
+ isProxy,
364
+ contractName: contractInfo.ContractName,
365
+ compilerVersion: contractInfo.CompilerVersion,
366
+ optimizationUsed: contractInfo.OptimizationUsed === '1',
367
+ optimizationRuns: parseInt(contractInfo.Runs, 10) || 0,
368
+ evmVersion: contractInfo.EVMVersion,
369
+ licenseType: contractInfo.LicenseType,
370
+ constructorArguments: contractInfo.ConstructorArguments,
371
+ fileCount: Object.keys(sourceFiles).length,
372
+ totalLines: fileStats.reduce((sum, stat) => sum + stat.lines, 0),
373
+ etherscanUrl: clientManager.explorerUrl(args.chain as ChainName, `/address/${args.address}#code`)
374
+ }
386
375
 
387
- // Add source based on includeSource parameter
388
- if (args.includeSource === 'full') {
389
- result.sourceFiles = sourceFiles
390
- } else if (args.includeSource === 'summary') {
391
- result.files = fileStats
392
- }
393
- // 'none' mode: no source files added
394
-
395
- // If proxy and implementation requested, fetch implementation source
396
- if (
397
- isProxy &&
398
- args.includeImplementation &&
399
- contractInfo.Implementation &&
400
- isAddress(contractInfo.Implementation)
401
- ) {
402
- try {
403
- const implInfo = await fetchContractInfo(chainId, contractInfo.Implementation, config.etherscanApiKey)
404
-
405
- if (implInfo && implInfo.SourceCode !== '') {
406
- // Parse implementation source code
407
- let implSourceFiles: Record<string, string> = {}
408
- let implSourceCodeString = implInfo.SourceCode
409
-
410
- if (implSourceCodeString.startsWith('{{') || implSourceCodeString.startsWith('[')) {
411
- try {
412
- if (implSourceCodeString.startsWith('{{')) {
413
- implSourceCodeString = implSourceCodeString.slice(1, -1)
414
- }
415
- const parsed = JSON.parse(implSourceCodeString)
416
-
417
- if (parsed.sources) {
418
- implSourceFiles = Object.fromEntries(
419
- Object.entries(parsed.sources).map(([path, data]: [string, any]) => [path, data.content || ''])
420
- )
421
- } else if (typeof parsed === 'object') {
422
- implSourceFiles = parsed
423
- }
424
- } catch {
425
- implSourceFiles = { [implInfo.ContractName + '.sol']: implSourceCodeString }
376
+ // Add source based on includeSource parameter
377
+ if (args.includeSource === 'full') {
378
+ result.sourceFiles = sourceFiles
379
+ } else if (args.includeSource === 'summary') {
380
+ result.files = fileStats
381
+ }
382
+ // 'none' mode: no source files added
383
+
384
+ // If proxy and implementation requested, fetch implementation source
385
+ if (isProxy && args.includeImplementation && contractInfo.Implementation && isAddress(contractInfo.Implementation)) {
386
+ try {
387
+ const implInfo = await fetchContractInfo(chainId, contractInfo.Implementation, config.etherscanApiKey)
388
+
389
+ if (implInfo && implInfo.SourceCode !== '') {
390
+ // Parse implementation source code
391
+ let implSourceFiles: Record<string, string> = {}
392
+ let implSourceCodeString = implInfo.SourceCode
393
+
394
+ if (implSourceCodeString.startsWith('{{') || implSourceCodeString.startsWith('[')) {
395
+ try {
396
+ if (implSourceCodeString.startsWith('{{')) {
397
+ implSourceCodeString = implSourceCodeString.slice(1, -1)
398
+ }
399
+ const parsed = JSON.parse(implSourceCodeString)
400
+
401
+ if (parsed.sources) {
402
+ implSourceFiles = Object.fromEntries(
403
+ Object.entries(parsed.sources).map(([path, data]: [string, any]) => [path, data.content || ''])
404
+ )
405
+ } else if (typeof parsed === 'object') {
406
+ implSourceFiles = parsed
426
407
  }
427
- } else {
428
- implSourceFiles = { [implInfo.ContractName + '.sol']: implSourceCodeString }
408
+ } catch {
409
+ implSourceFiles = { [`${implInfo.ContractName}.sol`]: implSourceCodeString }
429
410
  }
411
+ } else {
412
+ implSourceFiles = { [`${implInfo.ContractName}.sol`]: implSourceCodeString }
413
+ }
430
414
 
431
- // Calculate implementation file stats
432
- const implFileStats = Object.entries(implSourceFiles).map(([path, content]) => ({
433
- path,
434
- lines: (content as string).split('\n').length,
435
- size: (content as string).length
436
- }))
437
-
438
- const implResult: Record<string, unknown> = {
439
- address: contractInfo.Implementation,
440
- contractName: implInfo.ContractName,
441
- compilerVersion: implInfo.CompilerVersion,
442
- optimizationUsed: implInfo.OptimizationUsed === '1',
443
- optimizationRuns: parseInt(implInfo.Runs) || 0,
444
- evmVersion: implInfo.EVMVersion,
445
- licenseType: implInfo.LicenseType,
446
- fileCount: Object.keys(implSourceFiles).length,
447
- totalLines: implFileStats.reduce((sum, stat) => sum + stat.lines, 0),
448
- etherscanUrl: `https://${clientManager.getEtherscanDomain(args.chain as ChainName)}/address/${contractInfo.Implementation}#code`
449
- }
415
+ // Calculate implementation file stats
416
+ const implFileStats = Object.entries(implSourceFiles).map(([path, content]) => ({
417
+ path,
418
+ lines: (content as string).split('\n').length,
419
+ size: (content as string).length
420
+ }))
421
+
422
+ const implResult: Record<string, unknown> = {
423
+ address: contractInfo.Implementation,
424
+ contractName: implInfo.ContractName,
425
+ compilerVersion: implInfo.CompilerVersion,
426
+ optimizationUsed: implInfo.OptimizationUsed === '1',
427
+ optimizationRuns: parseInt(implInfo.Runs, 10) || 0,
428
+ evmVersion: implInfo.EVMVersion,
429
+ licenseType: implInfo.LicenseType,
430
+ fileCount: Object.keys(implSourceFiles).length,
431
+ totalLines: implFileStats.reduce((sum, stat) => sum + stat.lines, 0),
432
+ etherscanUrl: clientManager.explorerUrl(args.chain as ChainName, `/address/${contractInfo.Implementation}#code`)
433
+ }
450
434
 
451
- // Add implementation source based on includeSource parameter
452
- if (args.includeSource === 'full') {
453
- implResult.sourceFiles = implSourceFiles
454
- } else if (args.includeSource === 'summary') {
455
- implResult.files = implFileStats
456
- }
435
+ // Add implementation source based on includeSource parameter
436
+ if (args.includeSource === 'full') {
437
+ implResult.sourceFiles = implSourceFiles
438
+ } else if (args.includeSource === 'summary') {
439
+ implResult.files = implFileStats
440
+ }
457
441
 
458
- result.implementation = implResult
442
+ result.implementation = implResult
459
443
 
460
- // Update cache with implementation source (rawInfo already cached by fetchContractInfo)
461
- if (contractInfo.Implementation) {
462
- const implCacheKey = getCacheKey(chainId, contractInfo.Implementation)
463
- const existingImpl = contractCache.get(implCacheKey)!
464
- contractCache.set(implCacheKey, {
465
- ...existingImpl,
466
- sourceFiles: implSourceFiles
467
- })
468
- }
444
+ // Update cache with implementation source (rawInfo already cached by fetchContractInfo)
445
+ if (contractInfo.Implementation) {
446
+ const implCacheKey = getCacheKey(chainId, contractInfo.Implementation)
447
+ const existingImpl = contractCache.get(implCacheKey)!
448
+ contractCache.set(implCacheKey, {
449
+ ...existingImpl,
450
+ sourceFiles: implSourceFiles
451
+ })
469
452
  }
470
- } catch (error) {
471
- // Implementation fetch failed, continue without it
472
- result.implementationError = error instanceof Error ? error.message : 'Failed to fetch implementation source'
473
453
  }
474
- } else if (isProxy && contractInfo.Implementation) {
475
- result.implementationAddress = contractInfo.Implementation
454
+ } catch (error) {
455
+ // Implementation fetch failed, continue without it
456
+ result.implementationError = error instanceof Error ? error.message : 'Failed to fetch implementation source'
476
457
  }
458
+ } else if (isProxy && contractInfo.Implementation) {
459
+ result.implementationAddress = contractInfo.Implementation
460
+ }
477
461
 
478
- // Update cache with main contract source (rawInfo already cached by fetchContractInfo)
479
- const cacheKey = getCacheKey(chainId, args.address)
480
- const existing = contractCache.get(cacheKey)!
481
- contractCache.set(cacheKey, {
482
- ...existing,
483
- sourceFiles
484
- })
462
+ // Update cache with main contract source (rawInfo already cached by fetchContractInfo)
463
+ const cacheKey = getCacheKey(chainId, args.address)
464
+ const existing = contractCache.get(cacheKey)!
465
+ contractCache.set(cacheKey, {
466
+ ...existing,
467
+ sourceFiles
468
+ })
485
469
 
486
- return formatResponse(result)
487
- } catch (error) {
488
- throw new Error(`Failed to fetch source code: ${error}`)
489
- }
470
+ return formatResponse(result)
490
471
  }
491
472
  ),
492
473
 
@@ -505,8 +486,7 @@ export default {
505
486
  }),
506
487
  async (args) => {
507
488
  const clientManager = getClientManager()
508
- const chains = CHAINS((clientManager as any).config)
509
- const chainId = chains[args.chain as ChainName].chain.id
489
+ const chainId = clientManager.getChainId(args.chain as ChainName)
510
490
 
511
491
  if (!isAddress(args.address)) {
512
492
  throw new Error('Invalid contract address')
@@ -528,8 +508,7 @@ export default {
528
508
  chain: args.chain,
529
509
  chainId,
530
510
  address: args.address,
531
- message:
532
- 'Contract not in cache. Use get_contract_source_code with includeSource="full" first to cache the contract.'
511
+ message: 'Contract not in cache. Use get_contract_source_code with includeSource="full" first to cache the contract.'
533
512
  })
534
513
  }
535
514