Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): etherscan api v2 #4503

Merged
merged 3 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/eight-squids-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@wagmi/cli": minor
---

Updated Etherscan Plugin to use Etherscan API v2.
13 changes: 10 additions & 3 deletions packages/cli/src/plugins/blockExplorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export type BlockExplorerConfig = {
* @default 1_800_000 // 30m in ms
*/
cacheDuration?: number | undefined
/**
* Chain ID for block explorer. Appended to the request URL as query param `&chainId=${chainId}`.
*/
chainId?: number | undefined
/**
* Contracts to fetch ABIs for.
*/
Expand Down Expand Up @@ -63,6 +67,7 @@ export function blockExplorer(config: BlockExplorerConfig) {
apiKey,
baseUrl,
cacheDuration,
chainId,
contracts,
getAddress = ({ address }) => {
if (typeof address === 'string') return address
Expand Down Expand Up @@ -91,9 +96,11 @@ export function blockExplorer(config: BlockExplorerConfig) {
request({ address }) {
if (!address) throw new Error('address is required')
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}` : ''}`,
}
},
})
Expand Down
138 changes: 72 additions & 66 deletions packages/cli/src/plugins/etherscan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,73 +2,11 @@ import type { ContractConfig } from '../config.js'
import type { Compute } from '../types.js'
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',
}
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)
* - [__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
*/
apiKey: string
/**
Expand All @@ -78,11 +16,13 @@ export type EtherscanConfig<chainId extends number> = {
*/
cacheDuration?: number | undefined
/**
* 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 & {})
/**
* Contracts to fetch ABIs for.
*/
Expand All @@ -105,8 +45,9 @@ export function etherscan<chainId extends ChainId>(

return blockExplorer({
apiKey,
baseUrl: apiUrls[chainId as ChainId],
baseUrl: 'https://api.etherscan.io/v2/api',
cacheDuration,
chainId,
contracts,
getAddress({ address }) {
if (!address) throw new Error('address is required')
Expand All @@ -121,3 +62,68 @@ export function etherscan<chainId extends ChainId>(
name: 'Etherscan',
})
}

// 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
Loading
Loading