@wagmi/cli
Version:
Manage and generate code from Ethereum ABIs
120 lines (114 loc) • 4 kB
text/typescript
import type { ContractConfig } from '../config.js'
import type { Compute } from '../types.js'
import { blockExplorer } from './blockExplorer.js'
const apiUrls = {
// Ethereum
[]: 'https://api.etherscan.io/api',
[]: 'https://api-goerli.etherscan.io/api',
[]: 'https://api-holesky.etherscan.io/api',
[]: 'https://api-sepolia.etherscan.io/api',
// Optimism
[]: 'https://api-optimistic.etherscan.io/api',
[]: 'https://api-goerli-optimistic.etherscan.io/api',
[]: 'https://api-sepolia-optimistic.etherscan.io/api',
// Base
[]: 'https://api-sepolia.basescan.org/api',
[]: 'https://api.basescan.org/api',
// Polygon
[]: 'https://api.polygonscan.com/api',
[]: 'https://api-testnet.polygonscan.com/api',
// Arbitrum
[]: 'https://api.arbiscan.io/api',
[]: 'https://api-goerli.arbiscan.io/api',
[]: 'https://api-sepolia.arbiscan.io/api',
// BNB Smart Chain
[]: 'https://api.bscscan.com/api',
[]: 'https://api-testnet.bscscan.com/api',
// Heco Chain
[]: 'https://api.hecoinfo.com/api',
[]: 'https://api-testnet.hecoinfo.com/api',
// Fantom
[]: 'https://api.ftmscan.com/api',
[]: 'https://api-testnet.ftmscan.com/api',
// Avalanche
[]: 'https://api.snowscan.xyz/api',
[]: 'https://api-testnet.snowscan.xyz/api',
// Celo
[]: 'https://api.celoscan.io/api',
[]: 'https://api-alfajores.celoscan.io/api',
// Fraxtal
[]: 'https://api.fraxscan.com/api',
[]: 'https://api-holesky.fraxscan.com/api',
// Gnosis
[]: 'https://api.gnosisscan.io/api',
// Blast
[]: 'https://api.blastscan.io/api',
}
type ChainId = keyof typeof apiUrls
export type EtherscanConfig<chainId extends number> = {
/**
* Etherscan API key.
*
* 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)
* - [__Celo__](https://celoscan.io/myapikey)
* - [__Fantom__](https://ftmscan.com/myapikey)
* - [__Heco Chain__](https://hecoinfo.com/myapikey)
* - [__Optimism__](https://optimistic.etherscan.io/myapikey)
* - [__Base__](https://basescan.org/myapikey)
* - [__Polygon__](https://polygonscan.com/myapikey)
* - [__Fraxtal__](https://fraxscan.com/myapikey)
* - [__Gnosis__](https://gnosisscan.io/myapikey)
* - [__Blast__](https://blastscan.io/myapikey)
*/
apiKey: string
/**
* Duration in milliseconds to cache ABIs.
*
* @default 1_800_000 // 30m in ms
*/
cacheDuration?: number | undefined
/**
* Chain id to use for fetching ABI.
*
* If `address` is an object, `chainId` is used to select the address.
*/
chainId: chainId
/**
* Contracts to fetch ABIs for.
*/
contracts: Compute<Omit<ContractConfig<ChainId, chainId>, 'abi'>>[]
}
/**
* Fetches contract ABIs from Etherscan.
*/
export function etherscan<chainId extends ChainId>(
config: EtherscanConfig<chainId>,
) {
const { apiKey, cacheDuration, chainId } = config
const contracts = config.contracts.map((x) => ({
...x,
address:
typeof x.address === 'string' ? { [chainId]: x.address } : x.address,
})) as Omit<ContractConfig, 'abi'>[]
return blockExplorer({
apiKey,
baseUrl: apiUrls[chainId as ChainId],
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.`,
)
return contractAddress
},
name: 'Etherscan',
})
}