@wagmi/cli
Advanced tools
Comparing version 2.1.22 to 2.2.0
@@ -23,3 +23,3 @@ import { camelCase } from 'change-case'; | ||
export function blockExplorer(config) { | ||
const { apiKey, baseUrl, cacheDuration, contracts, getAddress = ({ address }) => { | ||
const { apiKey, baseUrl, cacheDuration, chainId, contracts, getAddress = ({ address }) => { | ||
if (typeof address === 'string') | ||
@@ -51,3 +51,3 @@ return address; | ||
return { | ||
url: `${baseUrl}?module=contract&action=getabi&address=${getAddress({ | ||
url: `${baseUrl}?${chainId ? `chainId=${chainId}&` : ''}module=contract&action=getabi&address=${getAddress({ | ||
address, | ||
@@ -54,0 +54,0 @@ })}${apiKey ? `&apikey=${apiKey}` : ''}`, |
@@ -1,48 +0,8 @@ | ||
import { blockExplorer } from './blockExplorer.js'; | ||
const apiUrls = { | ||
// Ethereum | ||
[1]: 'https://api.etherscan.io/api', | ||
[5]: 'https://api-goerli.etherscan.io/api', | ||
[17_000]: 'https://api-holesky.etherscan.io/api', | ||
[11_155_111]: 'https://api-sepolia.etherscan.io/api', | ||
// Optimism | ||
[10]: 'https://api-optimistic.etherscan.io/api', | ||
[420]: 'https://api-goerli-optimistic.etherscan.io/api', | ||
[11_155_420]: 'https://api-sepolia-optimistic.etherscan.io/api', | ||
// Base | ||
[84532]: 'https://api-sepolia.basescan.org/api', | ||
[8453]: 'https://api.basescan.org/api', | ||
// Polygon | ||
[137]: 'https://api.polygonscan.com/api', | ||
[80_001]: 'https://api-testnet.polygonscan.com/api', | ||
[80_002]: 'https://api-amoy.polygonscan.com/api', | ||
// Arbitrum | ||
[42_161]: 'https://api.arbiscan.io/api', | ||
[421_613]: 'https://api-goerli.arbiscan.io/api', | ||
[421_614]: 'https://api-sepolia.arbiscan.io/api', | ||
// BNB Smart Chain | ||
[56]: 'https://api.bscscan.com/api', | ||
[97]: 'https://api-testnet.bscscan.com/api', | ||
// Heco Chain | ||
[128]: 'https://api.hecoinfo.com/api', | ||
[256]: 'https://api-testnet.hecoinfo.com/api', | ||
// Sonic | ||
[146]: 'https://api.sonicscan.org/api', | ||
// Fantom | ||
[250]: 'https://api.ftmscan.com/api', | ||
[4_002]: 'https://api-testnet.ftmscan.com/api', | ||
// Avalanche | ||
[43_114]: 'https://api.snowscan.xyz/api', | ||
[43_113]: 'https://api-testnet.snowscan.xyz/api', | ||
// Celo | ||
[42_220]: 'https://api.celoscan.io/api', | ||
[44_787]: 'https://api-alfajores.celoscan.io/api', | ||
// Fraxtal | ||
[252]: 'https://api.fraxscan.com/api', | ||
[2_522]: 'https://api-holesky.fraxscan.com/api', | ||
// Gnosis | ||
[100]: 'https://api.gnosisscan.io/api', | ||
// Blast | ||
[81_457]: 'https://api.blastscan.io/api', | ||
}; | ||
import { mkdir, writeFile } from 'node:fs/promises'; | ||
import { Address as AddressSchema } from 'abitype/zod'; | ||
import { camelCase } from 'change-case'; | ||
import { join } from 'pathe'; | ||
import { z } from 'zod'; | ||
import { fromZodError } from '../errors.js'; | ||
import { fetch, getCacheDir } from './fetch.js'; | ||
/** | ||
@@ -52,3 +12,3 @@ * Fetches contract ABIs from Etherscan. | ||
export function etherscan(config) { | ||
const { apiKey, cacheDuration, chainId } = config; | ||
const { apiKey, cacheDuration = 1_800_000, chainId, tryFetchProxyImplementation = false, } = config; | ||
const contracts = config.contracts.map((x) => ({ | ||
@@ -58,20 +18,114 @@ ...x, | ||
})); | ||
return blockExplorer({ | ||
apiKey, | ||
baseUrl: apiUrls[chainId], | ||
const name = 'Etherscan'; | ||
const getCacheKey = ({ contract, }) => { | ||
if (typeof contract.address === 'string') | ||
return `${camelCase(name)}:${contract.address}`; | ||
return `${camelCase(name)}:${JSON.stringify(contract.address)}`; | ||
}; | ||
return fetch({ | ||
cacheDuration, | ||
contracts, | ||
getAddress({ address }) { | ||
if (!address) | ||
name, | ||
getCacheKey, | ||
async parse({ response }) { | ||
const json = await response.json(); | ||
const parsed = await GetAbiResponse.safeParseAsync(json); | ||
if (!parsed.success) | ||
throw fromZodError(parsed.error, { prefix: 'Invalid response' }); | ||
if (parsed.data.status === '0') | ||
throw new Error(parsed.data.result); | ||
return parsed.data.result; | ||
}, | ||
async request(contract) { | ||
if (!contract.address) | ||
throw new Error('address is required'); | ||
if (typeof address === 'string') | ||
return address; | ||
const contractAddress = address[chainId]; | ||
if (!contractAddress) | ||
throw new Error(`No address found for chainId "${chainId}". Make sure chainId "${chainId}" is set as an address.`); | ||
return contractAddress; | ||
const resolvedAddress = (() => { | ||
if (!contract.address) | ||
throw new Error('address is required'); | ||
if (typeof contract.address === 'string') | ||
return contract.address; | ||
const contractAddress = contract.address[chainId]; | ||
if (!contractAddress) | ||
throw new Error(`No address found for chainId "${chainId}". Make sure chainId "${chainId}" is set as an address.`); | ||
return contractAddress; | ||
})(); | ||
const options = { | ||
address: resolvedAddress, | ||
apiKey, | ||
chainId, | ||
}; | ||
let abi; | ||
const implementationAddress = await (async () => { | ||
if (!tryFetchProxyImplementation) | ||
return; | ||
const json = await globalThis | ||
.fetch(buildUrl({ ...options, action: 'getsourcecode' })) | ||
.then((res) => res.json()); | ||
const parsed = await GetSourceCodeResponse.safeParseAsync(json); | ||
if (!parsed.success) | ||
throw fromZodError(parsed.error, { prefix: 'Invalid response' }); | ||
if (parsed.data.status === '0') | ||
throw new Error(parsed.data.result); | ||
if (!parsed.data.result[0]) | ||
return; | ||
abi = parsed.data.result[0].ABI; | ||
return parsed.data.result[0].Implementation; | ||
})(); | ||
if (abi) { | ||
const cacheDir = getCacheDir(); | ||
await mkdir(cacheDir, { recursive: true }); | ||
const cacheKey = getCacheKey({ contract }); | ||
const cacheFilePath = join(cacheDir, `${cacheKey}.json`); | ||
await writeFile(cacheFilePath, `${JSON.stringify({ abi, timestamp: Date.now() + cacheDuration }, undefined, 2)}\n`); | ||
} | ||
return { | ||
url: buildUrl({ | ||
...options, | ||
action: 'getabi', | ||
address: implementationAddress || resolvedAddress, | ||
}), | ||
}; | ||
}, | ||
name: 'Etherscan', | ||
}); | ||
} | ||
function buildUrl(options) { | ||
const baseUrl = 'https://api.etherscan.io/v2/api'; | ||
const { action, address, apiKey, chainId } = options; | ||
return `${baseUrl}?${chainId ? `chainId=${chainId}&` : ''}module=contract&action=${action}&address=${address}${apiKey ? `&apikey=${apiKey}` : ''}`; | ||
} | ||
const GetAbiResponse = z.discriminatedUnion('status', [ | ||
z.object({ | ||
status: z.literal('1'), | ||
message: z.literal('OK'), | ||
result: z.string().transform((val) => JSON.parse(val)), | ||
}), | ||
z.object({ | ||
status: z.literal('0'), | ||
message: z.literal('NOTOK'), | ||
result: z.string(), | ||
}), | ||
]); | ||
const GetSourceCodeResponse = z.discriminatedUnion('status', [ | ||
z.object({ | ||
status: z.literal('1'), | ||
message: z.literal('OK'), | ||
result: z.array(z.discriminatedUnion('Proxy', [ | ||
z.object({ | ||
ABI: z.string().transform((val) => JSON.parse(val)), | ||
Implementation: AddressSchema, | ||
Proxy: z.literal('1'), | ||
}), | ||
z.object({ | ||
ABI: z.string().transform((val) => JSON.parse(val)), | ||
Implementation: z.string(), | ||
Proxy: z.literal('0'), | ||
}), | ||
])), | ||
}), | ||
z.object({ | ||
status: z.literal('0'), | ||
message: z.literal('NOTOK'), | ||
result: z.string(), | ||
}), | ||
]); | ||
//# sourceMappingURL=etherscan.js.map |
@@ -9,3 +9,3 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises'; | ||
async contracts() { | ||
const cacheDir = join(homedir(), '.wagmi-cli/plugins/fetch/cache'); | ||
const cacheDir = getCacheDir(); | ||
await mkdir(cacheDir, { recursive: true }); | ||
@@ -53,2 +53,5 @@ const timestamp = Date.now() + cacheDuration; | ||
} | ||
export function getCacheDir() { | ||
return join(homedir(), '.wagmi-cli/plugins/fetch/cache'); | ||
} | ||
//# sourceMappingURL=fetch.js.map |
@@ -1,2 +0,2 @@ | ||
export const version = '2.1.22'; | ||
export const version = '2.2.0'; | ||
//# sourceMappingURL=version.js.map |
@@ -20,2 +20,6 @@ import type { Address } from 'viem'; | ||
/** | ||
* Chain ID for block explorer. Appended to the request URL as query param `&chainId=${chainId}`. | ||
*/ | ||
chainId?: number | undefined; | ||
/** | ||
* Contracts to fetch ABIs for. | ||
@@ -22,0 +26,0 @@ */ |
import type { ContractConfig } from '../config.js'; | ||
import type { Compute } from '../types.js'; | ||
declare const apiUrls: { | ||
1: string; | ||
5: string; | ||
17000: string; | ||
11155111: string; | ||
10: string; | ||
420: string; | ||
11155420: string; | ||
84532: string; | ||
8453: string; | ||
137: string; | ||
80001: string; | ||
80002: string; | ||
42161: string; | ||
421613: string; | ||
421614: string; | ||
56: string; | ||
97: string; | ||
128: string; | ||
256: string; | ||
146: string; | ||
250: string; | ||
4002: string; | ||
43114: string; | ||
43113: string; | ||
42220: string; | ||
44787: string; | ||
252: string; | ||
2522: string; | ||
100: string; | ||
81457: string; | ||
}; | ||
type ChainId = keyof typeof apiUrls; | ||
export type EtherscanConfig<chainId extends number> = { | ||
@@ -40,17 +7,3 @@ /** | ||
* | ||
* API keys are specific per network and include testnets (e.g. Ethereum Mainnet and Goerli share same API key). Create or manage keys: | ||
* - [__Ethereum__](https://etherscan.io/myapikey) | ||
* - [__Arbitrum__](https://arbiscan.io/myapikey) | ||
* - [__Avalanche__](https://snowscan.xyz/myapikey) | ||
* - [__BNB Smart Chain__](https://bscscan.com/myapikey) | ||
* - [__Base__](https://basescan.org/myapikey) | ||
* - [__Blast__](https://blastscan.io/myapikey) | ||
* - [__Celo__](https://celoscan.io/myapikey) | ||
* - [__Fantom__](https://ftmscan.com/myapikey) | ||
* - [__Fraxtal__](https://fraxscan.com/myapikey) | ||
* - [__Gnosis__](https://gnosisscan.io/myapikey) | ||
* - [__Heco Chain__](https://hecoinfo.com/myapikey) | ||
* - [__Optimism__](https://optimistic.etherscan.io/myapikey) | ||
* - [__Polygon__](https://polygonscan.com/myapikey) | ||
* - [__Sonic__](https://sonicscan.org/myapikey) | ||
* Create or manage keys at https://etherscan.io/myapikey | ||
*/ | ||
@@ -65,7 +18,9 @@ apiKey: string; | ||
/** | ||
* Chain id to use for fetching ABI. | ||
* Chain ID to use for fetching ABI. | ||
* | ||
* If `address` is an object, `chainId` is used to select the address. | ||
* | ||
* View supported chains on the [Etherscan docs](https://docs.etherscan.io/etherscan-v2/getting-started/supported-chains). | ||
*/ | ||
chainId: chainId; | ||
chainId: (chainId extends ChainId ? chainId : never) | (ChainId & {}); | ||
/** | ||
@@ -75,2 +30,8 @@ * Contracts to fetch ABIs for. | ||
contracts: Compute<Omit<ContractConfig<ChainId, chainId>, 'abi'>>[]; | ||
/** | ||
* Whether to try fetching proxy implementation address of the contract | ||
* | ||
* @default false | ||
*/ | ||
tryFetchProxyImplementation?: boolean | undefined; | ||
}; | ||
@@ -100,3 +61,4 @@ /** | ||
}; | ||
type ChainId = 1 | 11155111 | 17000 | 56 | 97 | 137 | 80002 | 1101 | 2442 | 8453 | 84532 | 42161 | 42170 | 421614 | 59144 | 59141 | 250 | 4002 | 81457 | 168587773 | 10 | 11155420 | 43114 | 43113 | 199 | 1028 | 42220 | 44787 | 25 | 252 | 2522 | 100 | 255 | 2358 | 5000 | 5003 | 1284 | 1285 | 1287 | 204 | 5611 | 534352 | 534351 | 167000 | 167009 | 1111 | 1112 | 324 | 300 | 660279 | 37714555429 | 50 | 51 | 33139 | 33111 | 480 | 4801 | 50104 | 531050104 | 146 | 57054; | ||
export {}; | ||
//# sourceMappingURL=etherscan.d.ts.map |
@@ -37,2 +37,3 @@ import type { ContractConfig, Plugin } from '../config.js'; | ||
address?: ContractConfig['address'] | undefined; | ||
name: ContractConfig['name']; | ||
}) => { | ||
@@ -55,3 +56,4 @@ url: RequestInfo; | ||
export declare function fetch(config: FetchConfig): FetchResult; | ||
export declare function getCacheDir(): string; | ||
export {}; | ||
//# sourceMappingURL=fetch.d.ts.map |
@@ -1,2 +0,1 @@ | ||
import type * as chain from 'viem/chains'; | ||
import type { ContractConfig } from '../config.js'; | ||
@@ -18,3 +17,3 @@ import type { Compute } from '../types.js'; | ||
*/ | ||
chainId: chainId; | ||
chainId: (chainId extends ChainId ? chainId : never) | (ChainId & {}); | ||
/** | ||
@@ -46,4 +45,4 @@ * Contracts to fetch ABIs for. | ||
}; | ||
type ChainId = typeof chain.mainnet.id | typeof chain.goerli.id | 11155111 | typeof chain.arbitrumGoerli.id | typeof chain.arbitrum.id | 592 | typeof chain.aurora.id | typeof chain.auroraTestnet.id | typeof chain.avalanche.id | typeof chain.avalancheFuji.id | 56 | 97 | 288 | 28 | 534 | typeof chain.canto.id | typeof chain.celoAlfajores.id | 62320 | typeof chain.celo.id | typeof chain.gnosisChiado.id | 103090 | 53935 | 335 | 44 | 43 | 432204 | 432201 | 246 | 73799 | typeof chain.evmos.id | typeof chain.evmosTestnet.id | 122 | 486217935 | 192837465 | 356256156 | typeof chain.gnosis.id | 71402 | 71401 | 420420 | 420666 | 8217 | 1001 | 82 | 83 | 1287 | 1284 | 1285 | 62621 | 42262 | 42261 | 23295 | 311752642 | 4216137055 | typeof chain.optimism.id | 28528 | typeof chain.optimismGoerli.id | 300 | 99 | 77 | 11297108109 | 11297108099 | typeof chain.polygon.id | typeof chain.polygonMumbai.id | typeof chain.polygonAmoy.id | 336 | 57 | 5700 | 40 | 41 | 8 | 106 | 11111 | 51 | 7001; | ||
type ChainId = 1 | 17000 | 5 | 11155111 | 3 | 4 | 10 | 100 | 100009 | 100010 | 1001 | 10200 | 10242 | 10243 | 1030 | 103090 | 105105 | 106 | 10849 | 10850 | 1088 | 1101 | 111000 | 11111 | 1115 | 11155420 | 1116 | 11235 | 1127469 | 11297108099 | 11297108109 | 1149 | 122 | 1284 | 1285 | 1287 | 12898 | 1291 | 1313161554 | 1313161555 | 13337 | 13381 | 1339 | 137 | 14 | 1433 | 1516 | 16180 | 16350 | 167005 | 167006 | 17069 | 1890 | 1891 | 19 | 19011 | 192837465 | 2000 | 200810 | 200901 | 2017 | 2021 | 202401 | 2037 | 2038 | 2044 | 2047 | 2048 | 205205 | 212 | 216 | 222000222 | 2221 | 2222 | 223 | 22776 | 23294 | 23295 | 2358 | 2442 | 246 | 25 | 250 | 252 | 2522 | 255 | 25925 | 26100 | 28 | 28528 | 288 | 295 | 30 | 300 | 311752642 | 314 | 314159 | 32769 | 33101 | 33111 | 333000333 | 335 | 336 | 34443 | 35441 | 35443 | 356256156 | 369 | 3737 | 37714555429 | 383414847825 | 39797 | 40 | 4000 | 41 | 4157 | 420 | 4200 | 420420 | 420666 | 42161 | 421611 | 421613 | 4216137055 | 421614 | 42170 | 42220 | 42261 | 42262 | 42766 | 43 | 43113 | 43114 | 432201 | 432204 | 4337 | 44 | 44787 | 46 | 486217935 | 48899 | 48900 | 49797 | 5000 | 5003 | 51 | 534 | 534351 | 534352 | 53935 | 54211 | 56 | 57 | 570 | 5700 | 57000 | 5845 | 59141 | 59144 | 592 | 59902 | 61 | 6119 | 62320 | 62621 | 62831 | 6321 | 6322 | 641230 | 648 | 660279 | 666666666 | 69 | 690 | 7000 | 7001 | 7078815900 | 710420 | 71401 | 71402 | 7171 | 723107 | 73799 | 764984 | 7668 | 7672 | 767368 | 77 | 7700 | 7701 | 7771 | 7777777 | 78430 | 78431 | 78432 | 8 | 80001 | 80002 | 82 | 8217 | 83 | 841 | 842 | 8453 | 84531 | 84532 | 888 | 9000 | 9001 | 919 | 957 | 96 | 97 | 970 | 99 | 9977 | 999 | 9996 | 999999999; | ||
export {}; | ||
//# sourceMappingURL=sourcify.d.ts.map |
@@ -1,2 +0,2 @@ | ||
export declare const version = "2.1.22"; | ||
export declare const version = "2.2.0"; | ||
//# sourceMappingURL=version.d.ts.map |
{ | ||
"name": "@wagmi/cli", | ||
"description": "Manage and generate code from Ethereum ABIs", | ||
"version": "2.1.22", | ||
"version": "2.2.0", | ||
"license": "MIT", | ||
@@ -6,0 +6,0 @@ "repository": { |
@@ -26,2 +26,6 @@ import { camelCase } from 'change-case' | ||
/** | ||
* Chain ID for block explorer. Appended to the request URL as query param `&chainId=${chainId}`. | ||
*/ | ||
chainId?: number | undefined | ||
/** | ||
* Contracts to fetch ABIs for. | ||
@@ -67,2 +71,3 @@ */ | ||
cacheDuration, | ||
chainId, | ||
contracts, | ||
@@ -96,5 +101,7 @@ getAddress = ({ address }) => { | ||
return { | ||
url: `${baseUrl}?module=contract&action=getabi&address=${getAddress({ | ||
address, | ||
})}${apiKey ? `&apikey=${apiKey}` : ''}`, | ||
url: `${baseUrl}?${chainId ? `chainId=${chainId}&` : ''}module=contract&action=getabi&address=${getAddress( | ||
{ | ||
address, | ||
}, | ||
)}${apiKey ? `&apikey=${apiKey}` : ''}`, | ||
} | ||
@@ -101,0 +108,0 @@ }, |
@@ -0,53 +1,14 @@ | ||
import { mkdir, writeFile } from 'node:fs/promises' | ||
import type { Abi } from 'abitype' | ||
import { Address as AddressSchema } from 'abitype/zod' | ||
import { camelCase } from 'change-case' | ||
import { join } from 'pathe' | ||
import type { Address } from 'viem' | ||
import { z } from 'zod' | ||
import type { ContractConfig } from '../config.js' | ||
import { fromZodError } from '../errors.js' | ||
import type { Compute } from '../types.js' | ||
import { blockExplorer } from './blockExplorer.js' | ||
import { fetch, getCacheDir } from './fetch.js' | ||
const apiUrls = { | ||
// Ethereum | ||
[1]: 'https://api.etherscan.io/api', | ||
[5]: 'https://api-goerli.etherscan.io/api', | ||
[17_000]: 'https://api-holesky.etherscan.io/api', | ||
[11_155_111]: 'https://api-sepolia.etherscan.io/api', | ||
// Optimism | ||
[10]: 'https://api-optimistic.etherscan.io/api', | ||
[420]: 'https://api-goerli-optimistic.etherscan.io/api', | ||
[11_155_420]: 'https://api-sepolia-optimistic.etherscan.io/api', | ||
// Base | ||
[84532]: 'https://api-sepolia.basescan.org/api', | ||
[8453]: 'https://api.basescan.org/api', | ||
// Polygon | ||
[137]: 'https://api.polygonscan.com/api', | ||
[80_001]: 'https://api-testnet.polygonscan.com/api', | ||
[80_002]: 'https://api-amoy.polygonscan.com/api', | ||
// Arbitrum | ||
[42_161]: 'https://api.arbiscan.io/api', | ||
[421_613]: 'https://api-goerli.arbiscan.io/api', | ||
[421_614]: 'https://api-sepolia.arbiscan.io/api', | ||
// BNB Smart Chain | ||
[56]: 'https://api.bscscan.com/api', | ||
[97]: 'https://api-testnet.bscscan.com/api', | ||
// Heco Chain | ||
[128]: 'https://api.hecoinfo.com/api', | ||
[256]: 'https://api-testnet.hecoinfo.com/api', | ||
// Sonic | ||
[146]: 'https://api.sonicscan.org/api', | ||
// Fantom | ||
[250]: 'https://api.ftmscan.com/api', | ||
[4_002]: 'https://api-testnet.ftmscan.com/api', | ||
// Avalanche | ||
[43_114]: 'https://api.snowscan.xyz/api', | ||
[43_113]: 'https://api-testnet.snowscan.xyz/api', | ||
// Celo | ||
[42_220]: 'https://api.celoscan.io/api', | ||
[44_787]: 'https://api-alfajores.celoscan.io/api', | ||
// Fraxtal | ||
[252]: 'https://api.fraxscan.com/api', | ||
[2_522]: 'https://api-holesky.fraxscan.com/api', | ||
// Gnosis | ||
[100]: 'https://api.gnosisscan.io/api', | ||
// Blast | ||
[81_457]: 'https://api.blastscan.io/api', | ||
} | ||
type ChainId = keyof typeof apiUrls | ||
export type EtherscanConfig<chainId extends number> = { | ||
@@ -57,17 +18,3 @@ /** | ||
* | ||
* API keys are specific per network and include testnets (e.g. Ethereum Mainnet and Goerli share same API key). Create or manage keys: | ||
* - [__Ethereum__](https://etherscan.io/myapikey) | ||
* - [__Arbitrum__](https://arbiscan.io/myapikey) | ||
* - [__Avalanche__](https://snowscan.xyz/myapikey) | ||
* - [__BNB Smart Chain__](https://bscscan.com/myapikey) | ||
* - [__Base__](https://basescan.org/myapikey) | ||
* - [__Blast__](https://blastscan.io/myapikey) | ||
* - [__Celo__](https://celoscan.io/myapikey) | ||
* - [__Fantom__](https://ftmscan.com/myapikey) | ||
* - [__Fraxtal__](https://fraxscan.com/myapikey) | ||
* - [__Gnosis__](https://gnosisscan.io/myapikey) | ||
* - [__Heco Chain__](https://hecoinfo.com/myapikey) | ||
* - [__Optimism__](https://optimistic.etherscan.io/myapikey) | ||
* - [__Polygon__](https://polygonscan.com/myapikey) | ||
* - [__Sonic__](https://sonicscan.org/myapikey) | ||
* Create or manage keys at https://etherscan.io/myapikey | ||
*/ | ||
@@ -82,7 +29,9 @@ apiKey: string | ||
/** | ||
* Chain id to use for fetching ABI. | ||
* Chain ID to use for fetching ABI. | ||
* | ||
* If `address` is an object, `chainId` is used to select the address. | ||
* | ||
* View supported chains on the [Etherscan docs](https://docs.etherscan.io/etherscan-v2/getting-started/supported-chains). | ||
*/ | ||
chainId: chainId | ||
chainId: (chainId extends ChainId ? chainId : never) | (ChainId & {}) | ||
/** | ||
@@ -92,2 +41,8 @@ * Contracts to fetch ABIs for. | ||
contracts: Compute<Omit<ContractConfig<ChainId, chainId>, 'abi'>>[] | ||
/** | ||
* Whether to try fetching proxy implementation address of the contract | ||
* | ||
* @default false | ||
*/ | ||
tryFetchProxyImplementation?: boolean | undefined | ||
} | ||
@@ -101,3 +56,8 @@ | ||
) { | ||
const { apiKey, cacheDuration, chainId } = config | ||
const { | ||
apiKey, | ||
cacheDuration = 1_800_000, | ||
chainId, | ||
tryFetchProxyImplementation = false, | ||
} = config | ||
@@ -110,19 +70,195 @@ const contracts = config.contracts.map((x) => ({ | ||
return blockExplorer({ | ||
apiKey, | ||
baseUrl: apiUrls[chainId as ChainId], | ||
const name = 'Etherscan' | ||
const getCacheKey: Parameters<typeof fetch>[0]['getCacheKey'] = ({ | ||
contract, | ||
}) => { | ||
if (typeof contract.address === 'string') | ||
return `${camelCase(name)}:${contract.address}` | ||
return `${camelCase(name)}:${JSON.stringify(contract.address)}` | ||
} | ||
return fetch({ | ||
cacheDuration, | ||
contracts, | ||
getAddress({ address }) { | ||
if (!address) throw new Error('address is required') | ||
if (typeof address === 'string') return address | ||
const contractAddress = address[chainId] | ||
if (!contractAddress) | ||
throw new Error( | ||
`No address found for chainId "${chainId}". Make sure chainId "${chainId}" is set as an address.`, | ||
name, | ||
getCacheKey, | ||
async parse({ response }) { | ||
const json = await response.json() | ||
const parsed = await GetAbiResponse.safeParseAsync(json) | ||
if (!parsed.success) | ||
throw fromZodError(parsed.error, { prefix: 'Invalid response' }) | ||
if (parsed.data.status === '0') throw new Error(parsed.data.result) | ||
return parsed.data.result | ||
}, | ||
async request(contract) { | ||
if (!contract.address) throw new Error('address is required') | ||
const resolvedAddress = (() => { | ||
if (!contract.address) throw new Error('address is required') | ||
if (typeof contract.address === 'string') return contract.address | ||
const contractAddress = contract.address[chainId] | ||
if (!contractAddress) | ||
throw new Error( | ||
`No address found for chainId "${chainId}". Make sure chainId "${chainId}" is set as an address.`, | ||
) | ||
return contractAddress | ||
})() | ||
const options = { | ||
address: resolvedAddress, | ||
apiKey, | ||
chainId, | ||
} | ||
let abi: Abi | undefined | ||
const implementationAddress = await (async () => { | ||
if (!tryFetchProxyImplementation) return | ||
const json = await globalThis | ||
.fetch(buildUrl({ ...options, action: 'getsourcecode' })) | ||
.then((res) => res.json()) | ||
const parsed = await GetSourceCodeResponse.safeParseAsync(json) | ||
if (!parsed.success) | ||
throw fromZodError(parsed.error, { prefix: 'Invalid response' }) | ||
if (parsed.data.status === '0') throw new Error(parsed.data.result) | ||
if (!parsed.data.result[0]) return | ||
abi = parsed.data.result[0].ABI | ||
return parsed.data.result[0].Implementation as Address | ||
})() | ||
if (abi) { | ||
const cacheDir = getCacheDir() | ||
await mkdir(cacheDir, { recursive: true }) | ||
const cacheKey = getCacheKey({ contract }) | ||
const cacheFilePath = join(cacheDir, `${cacheKey}.json`) | ||
await writeFile( | ||
cacheFilePath, | ||
`${JSON.stringify({ abi, timestamp: Date.now() + cacheDuration }, undefined, 2)}\n`, | ||
) | ||
return contractAddress | ||
} | ||
return { | ||
url: buildUrl({ | ||
...options, | ||
action: 'getabi', | ||
address: implementationAddress || resolvedAddress, | ||
}), | ||
} | ||
}, | ||
name: 'Etherscan', | ||
}) | ||
} | ||
function buildUrl(options: { | ||
action: 'getabi' | 'getsourcecode' | ||
address: Address | ||
apiKey: string | ||
chainId: ChainId | undefined | ||
}) { | ||
const baseUrl = 'https://api.etherscan.io/v2/api' | ||
const { action, address, apiKey, chainId } = options | ||
return `${baseUrl}?${chainId ? `chainId=${chainId}&` : ''}module=contract&action=${action}&address=${address}${apiKey ? `&apikey=${apiKey}` : ''}` | ||
} | ||
const GetAbiResponse = z.discriminatedUnion('status', [ | ||
z.object({ | ||
status: z.literal('1'), | ||
message: z.literal('OK'), | ||
result: z.string().transform((val) => JSON.parse(val) as Abi), | ||
}), | ||
z.object({ | ||
status: z.literal('0'), | ||
message: z.literal('NOTOK'), | ||
result: z.string(), | ||
}), | ||
]) | ||
const GetSourceCodeResponse = z.discriminatedUnion('status', [ | ||
z.object({ | ||
status: z.literal('1'), | ||
message: z.literal('OK'), | ||
result: z.array( | ||
z.discriminatedUnion('Proxy', [ | ||
z.object({ | ||
ABI: z.string().transform((val) => JSON.parse(val) as Abi), | ||
Implementation: AddressSchema, | ||
Proxy: z.literal('1'), | ||
}), | ||
z.object({ | ||
ABI: z.string().transform((val) => JSON.parse(val) as Abi), | ||
Implementation: z.string(), | ||
Proxy: z.literal('0'), | ||
}), | ||
]), | ||
), | ||
}), | ||
z.object({ | ||
status: z.literal('0'), | ||
message: z.literal('NOTOK'), | ||
result: z.string(), | ||
}), | ||
]) | ||
// Supported chains | ||
// https://docs.etherscan.io/etherscan-v2/getting-started/supported-chains | ||
type ChainId = | ||
| 1 // Ethereum Mainnet | ||
| 11155111 // Sepolia Testnet | ||
| 17000 // Holesky Testnet | ||
| 56 // BNB Smart Chain Mainnet | ||
| 97 // BNB Smart Chain Testnet | ||
| 137 // Polygon Mainnet | ||
| 80002 // Polygon Amoy Testnet | ||
| 1101 // Polygon zkEVM Mainnet | ||
| 2442 // Polygon zkEVM Cardona Testnet | ||
| 8453 // Base Mainnet | ||
| 84532 // Base Sepolia Testnet | ||
| 42161 // Arbitrum One Mainnet | ||
| 42170 // Arbitrum Nova Mainnet | ||
| 421614 // Arbitrum Sepolia Testnet | ||
| 59144 // Linea Mainnet | ||
| 59141 // Linea Sepolia Testnet | ||
| 250 // Fantom Opera Mainnet | ||
| 4002 // Fantom Testnet | ||
| 81457 // Blast Mainnet | ||
| 168587773 // Blast Sepolia Testnet | ||
| 10 // OP Mainnet | ||
| 11155420 // OP Sepolia Testnet | ||
| 43114 // Avalanche C-Chain | ||
| 43113 // Avalanche Fuji Testnet | ||
| 199 // BitTorrent Chain Mainnet | ||
| 1028 // BitTorrent Chain Testnet | ||
| 42220 // Celo Mainnet | ||
| 44787 // Celo Alfajores Testnet | ||
| 25 // Cronos Mainnet | ||
| 252 // Fraxtal Mainnet | ||
| 2522 // Fraxtal Testnet | ||
| 100 // Gnosis | ||
| 255 // Kroma Mainnet | ||
| 2358 // Kroma Sepolia Testnet | ||
| 5000 // Mantle Mainnet | ||
| 5003 // Mantle Sepolia Testnet | ||
| 1284 // Moonbeam Mainnet | ||
| 1285 // Moonriver Mainnet | ||
| 1287 // Moonbase Alpha Testnet | ||
| 204 // opBNB Mainnet | ||
| 5611 // opBNB Testnet | ||
| 534352 // Scroll Mainnet | ||
| 534351 // Scroll Sepolia Testnet | ||
| 167000 // Taiko Mainnet | ||
| 167009 // Taiko Hekla L2 Testnet | ||
| 1111 // WEMIX3.0 Mainnet | ||
| 1112 // WEMIX3.0 Testnet | ||
| 324 // zkSync Mainnet | ||
| 300 // zkSync Sepolia Testnet | ||
| 660279 // Xai Mainnet | ||
| 37714555429 // Xai Sepolia Testnet | ||
| 50 // XDC Mainnet | ||
| 51 // XDC Apothem Testnet | ||
| 33139 // ApeChain Mainnet | ||
| 33111 // ApeChain Curtis Testnet | ||
| 480 // World Mainnet | ||
| 4801 // World Sepolia Testnet | ||
| 50104 // Sophon Mainnet | ||
| 531050104 // Sophon Sepolia Testnet | ||
| 146 // Sonic Mainnet | ||
| 57054 // Sonic Blaze Testnet |
@@ -45,2 +45,3 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises' | ||
address?: ContractConfig['address'] | undefined | ||
name: ContractConfig['name'] | ||
}) => | ||
@@ -73,3 +74,3 @@ | { url: RequestInfo; init?: RequestInit | undefined } | ||
async contracts() { | ||
const cacheDir = join(homedir(), '.wagmi-cli/plugins/fetch/cache') | ||
const cacheDir = getCacheDir() | ||
await mkdir(cacheDir, { recursive: true }) | ||
@@ -125,1 +126,5 @@ | ||
} | ||
export function getCacheDir() { | ||
return join(homedir(), '.wagmi-cli/plugins/fetch/cache') | ||
} |
import { Abi as AbiSchema } from 'abitype/zod' | ||
import type { Address } from 'viem' | ||
import type * as chain from 'viem/chains' | ||
import { z } from 'zod' | ||
@@ -25,3 +24,3 @@ | ||
*/ | ||
chainId: chainId | ||
chainId: (chainId extends ChainId ? chainId : never) | (ChainId & {}) | ||
/** | ||
@@ -98,75 +97,220 @@ * Contracts to fetch ABIs for. | ||
type ChainId = | ||
| typeof chain.mainnet.id | ||
| typeof chain.goerli.id | ||
| 11155111 | ||
| typeof chain.arbitrumGoerli.id | ||
| typeof chain.arbitrum.id | ||
| 592 | ||
| typeof chain.aurora.id | ||
| typeof chain.auroraTestnet.id | ||
| typeof chain.avalanche.id | ||
| typeof chain.avalancheFuji.id | ||
| 56 | ||
| 97 | ||
| 288 | ||
| 28 | ||
| 534 | ||
| typeof chain.canto.id | ||
| typeof chain.celoAlfajores.id | ||
| 62320 | ||
| typeof chain.celo.id | ||
| typeof chain.gnosisChiado.id | ||
| 103090 | ||
| 53935 | ||
| 335 | ||
| 44 | ||
| 43 | ||
| 432204 | ||
| 432201 | ||
| 246 | ||
| 73799 | ||
| typeof chain.evmos.id | ||
| typeof chain.evmosTestnet.id | ||
| 122 | ||
| 486217935 | ||
| 192837465 | ||
| 356256156 | ||
| typeof chain.gnosis.id | ||
| 71402 | ||
| 71401 | ||
| 420420 | ||
| 420666 | ||
| 8217 | ||
| 1001 | ||
| 82 | ||
| 83 | ||
| 1287 | ||
| 1284 | ||
| 1285 | ||
| 62621 | ||
| 42262 | ||
| 42261 | ||
| 23295 | ||
| 311752642 | ||
| 4216137055 | ||
| typeof chain.optimism.id | ||
| 28528 | ||
| typeof chain.optimismGoerli.id | ||
| 300 | ||
| 99 | ||
| 77 | ||
| 11297108109 | ||
| 11297108099 | ||
| typeof chain.polygon.id | ||
| typeof chain.polygonMumbai.id | ||
| typeof chain.polygonAmoy.id | ||
| 336 | ||
| 57 | ||
| 5700 | ||
| 40 | ||
| 41 | ||
| 8 | ||
| 106 | ||
| 11111 | ||
| 51 | ||
| 7001 | ||
| 1 // Ethereum Mainnet | ||
| 17000 // Ethereum Testnet Holesky | ||
| 5 // Ethereum Testnet Goerli | ||
| 11155111 // Ethereum Testnet Sepolia | ||
| 3 // Ethereum Testnet Ropsten | ||
| 4 // Ethereum Testnet Rinkeby | ||
| 10 // OP Mainnet | ||
| 100 // Gnosis | ||
| 100009 // VeChain | ||
| 100010 // VeChain Testnet | ||
| 1001 // Kaia Kairos Testnet | ||
| 10200 // Gnosis Chiado Testnet | ||
| 10242 // Arthera Mainnet | ||
| 10243 // Arthera Testnet | ||
| 1030 // Conflux eSpace | ||
| 103090 // Crystaleum | ||
| 105105 // Stratis Mainnet | ||
| 106 // Velas EVM Mainnet | ||
| 10849 // Lamina1 | ||
| 10850 // Lamina1 Identity | ||
| 1088 // Metis Andromeda Mainnet | ||
| 1101 // Polygon zkEVM | ||
| 111000 // Siberium Test Network | ||
| 11111 // WAGMI | ||
| 1115 // Core Blockchain Testnet | ||
| 11155420 // OP Sepolia Testnet | ||
| 1116 // Core Blockchain Mainnet | ||
| 11235 // Haqq Network | ||
| 1127469 // Tiltyard Subnet | ||
| 11297108099 // Palm Testnet | ||
| 11297108109 // Palm | ||
| 1149 // Symplexia Smart Chain | ||
| 122 // Fuse Mainnet | ||
| 1284 // Moonbeam | ||
| 1285 // Moonriver | ||
| 1287 // Moonbase Alpha | ||
| 12898 // PlayFair Testnet Subnet | ||
| 1291 // Swisstronik Testnet | ||
| 1313161554 // Aurora Mainnet | ||
| 1313161555 // Aurora Testnet | ||
| 13337 // Beam Testnet | ||
| 13381 // Phoenix Mainnet | ||
| 1339 // Elysium Mainnet | ||
| 137 // Polygon Mainnet | ||
| 14 // Flare Mainnet | ||
| 1433 // Rikeza Network Mainnet | ||
| 1516 // Story Odyssey Testnet | ||
| 16180 // PLYR PHI | ||
| 16350 // Incentiv Devnet | ||
| 167005 // Taiko Grimsvotn L2 | ||
| 167006 // Taiko Eldfell L3 | ||
| 17069 // Garnet Holesky | ||
| 1890 // Lightlink Phoenix Mainnet | ||
| 1891 // Lightlink Pegasus Testnet | ||
| 19 // Songbird Canary-Network | ||
| 19011 // HOME Verse Mainnet | ||
| 192837465 // Gather Mainnet Network | ||
| 2000 // Dogechain Mainnet | ||
| 200810 // Bitlayer Testnet | ||
| 200901 // Bitlayer Mainnet | ||
| 2017 // Adiri | ||
| 2021 // Edgeware EdgeEVM Mainnet | ||
| 202401 // YMTECH-BESU Testnet | ||
| 2037 // Kiwi Subnet | ||
| 2038 // Shrapnel Testnet | ||
| 2044 // Shrapnel Subnet | ||
| 2047 // Stratos Testnet | ||
| 2048 // Stratos | ||
| 205205 // Auroria Testnet | ||
| 212 // MAPO Makalu | ||
| 216 // Happychain Testnet | ||
| 222000222 // Kanazawa | ||
| 2221 // Kava Testnet | ||
| 2222 // Kava | ||
| 223 // B2 Mainnet | ||
| 22776 // MAP Protocol | ||
| 23294 // Oasis Sapphire | ||
| 23295 // Oasis Sapphire Testnet | ||
| 2358 // Kroma Sepolia | ||
| 2442 // Polygon zkEVM Cardona Testnet | ||
| 246 // Energy Web Chain | ||
| 25 // Cronos Mainnet | ||
| 250 // Fantom Opera | ||
| 252 // Fraxtal | ||
| 2522 // Fraxtal Testnet | ||
| 255 // Kroma | ||
| 25925 // Bitkub Chain Testnet | ||
| 26100 // Ferrum Quantum Portal Network | ||
| 28 // Boba Network Rinkeby Testnet | ||
| 28528 // Optimism Bedrock (Goerli Alpha Testnet) | ||
| 288 // Boba Network | ||
| 295 // Hedera Mainnet | ||
| 30 // Rootstock Mainnet | ||
| 300 // zkSync Sepolia Testnet | ||
| 311752642 // OneLedger Mainnet | ||
| 314 // Filecoin - Mainnet | ||
| 314159 // Filecoin - Calibration testnet | ||
| 32769 // Zilliqa EVM | ||
| 33101 // Zilliqa EVM Testnet | ||
| 33111 // Curtis | ||
| 333000333 // Meld | ||
| 335 // DFK Chain Test | ||
| 336 // Shiden | ||
| 34443 // Mode | ||
| 35441 // Q Mainnet | ||
| 35443 // Q Testnet | ||
| 356256156 // Gather Testnet Network | ||
| 369 // PulseChain | ||
| 3737 // Crossbell | ||
| 37714555429 // Xai Testnet v2 | ||
| 383414847825 // Zeniq | ||
| 39797 // Energi Mainnet | ||
| 40 // Telos EVM Mainnet | ||
| 4000 // Ozone Chain Mainnet | ||
| 41 // Telos EVM Testnet | ||
| 4157 // CrossFi Testnet | ||
| 420 // Optimism Goerli Testnet | ||
| 4200 // Merlin Mainnet | ||
| 420420 // Kekchain | ||
| 420666 // Kekchain (kektest) | ||
| 42161 // Arbitrum One | ||
| 421611 // Arbitrum Rinkeby | ||
| 421613 // Arbitrum Goerli | ||
| 4216137055 // OneLedger Testnet Frankenstein | ||
| 421614 // Arbitrum Sepolia | ||
| 42170 // Arbitrum Nova | ||
| 42220 // Celo Mainnet | ||
| 42261 // Oasis Emerald Testnet | ||
| 42262 // Oasis Emerald | ||
| 42766 // ZKFair Mainnet | ||
| 43 // Darwinia Pangolin Testnet | ||
| 43113 // Avalanche Fuji Testnet | ||
| 43114 // Avalanche C-Chain | ||
| 432201 // Dexalot Subnet Testnet | ||
| 432204 // Dexalot Subnet | ||
| 4337 // Beam | ||
| 44 // Crab Network | ||
| 44787 // Celo Alfajores Testnet | ||
| 46 // Darwinia Network | ||
| 486217935 // Gather Devnet Network | ||
| 48899 // Zircuit Testnet | ||
| 48900 // Zircuit Mainnet | ||
| 49797 // Energi Testnet | ||
| 5000 // Mantle | ||
| 5003 // Mantle Sepolia Testnet | ||
| 51 // XDC Apothem Network | ||
| 534 // Candle | ||
| 534351 // Scroll Sepolia Testnet | ||
| 534352 // Scroll | ||
| 53935 // DFK Chain | ||
| 54211 // Haqq Chain Testnet | ||
| 56 // BNB Smart Chain Mainnet | ||
| 57 // Syscoin Mainnet | ||
| 570 // Rollux Mainnet | ||
| 5700 // Syscoin Tanenbaum Testnet | ||
| 57000 // Rollux Testnet | ||
| 5845 // Tangle | ||
| 59141 // Linea Sepolia | ||
| 59144 // Linea | ||
| 592 // Astar | ||
| 59902 // Metis Sepolia Testnet | ||
| 61 // Ethereum Classic | ||
| 6119 // UPTN | ||
| 62320 // Celo Baklava Testnet | ||
| 62621 // MultiVAC Mainnet | ||
| 62831 // PLYR TAU Testnet | ||
| 6321 // Aura Euphoria Testnet | ||
| 6322 // Aura Mainnet | ||
| 641230 // Bear Network Chain Mainnet | ||
| 648 // Endurance Smart Chain Mainnet | ||
| 660279 // Xai Mainnet | ||
| 666666666 // Degen Chain | ||
| 69 // Optimism Kovan | ||
| 690 // Redstone | ||
| 7000 // ZetaChain Mainnet | ||
| 7001 // ZetaChain Testnet | ||
| 7078815900 // Mekong | ||
| 710420 // Tiltyard Mainnet Subnet | ||
| 71401 // Godwoken Testnet v1 | ||
| 71402 // Godwoken Mainnet | ||
| 7171 // Bitrock Mainnet | ||
| 723107 // TixChain Testnet | ||
| 73799 // Energy Web Volta Testnet | ||
| 764984 // Lamina1 Testnet | ||
| 7668 // The Root Network - Mainnet | ||
| 7672 // The Root Network - Porcini Testnet | ||
| 767368 // Lamina1 Identity Testnet | ||
| 77 // POA Network Sokol | ||
| 7700 // Canto | ||
| 7701 // Canto Tesnet | ||
| 7771 // Bitrock Testnet | ||
| 7777777 // Zora | ||
| 78430 // Amplify Subnet | ||
| 78431 // Bulletin Subnet | ||
| 78432 // Conduit Subnet | ||
| 8 // Ubiq | ||
| 80001 // Mumbai | ||
| 80002 // Amoy | ||
| 82 // Meter Mainnet | ||
| 8217 // Kaia Mainnet | ||
| 83 // Meter Testnet | ||
| 841 // Taraxa Mainnet | ||
| 842 // Taraxa Testnet | ||
| 8453 // Base | ||
| 84531 // Base Goerli Testnet | ||
| 84532 // Base Sepolia Testnet | ||
| 888 // Wanchain | ||
| 9000 // Evmos Testnet | ||
| 9001 // Evmos | ||
| 919 // Mode Testnet | ||
| 957 // Lyra Chain | ||
| 96 // Bitkub Chain | ||
| 97 // BNB Smart Chain Testnet | ||
| 970 // Oort Mainnet | ||
| 99 // POA Network Core | ||
| 9977 // Mind Smart Chain Testnet | ||
| 999 // Wanchain Testnet | ||
| 9996 // Mind Smart Chain Mainnet | ||
| 999999999 // Zora Sepolia Testnet |
@@ -1,1 +0,1 @@ | ||
export const version = '2.1.22' | ||
export const version = '2.2.0' |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
530173
5393
3