diff --git a/CHANGELOG.md b/CHANGELOG.md index de9335409..a87c5fbf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ ## 4.64.0 (2025-11-14) - changed: Upgrade @polkadot/api to latest to support Asset Hub migration +- removed: Removed 'api.bscscan.com' server due to deprecation ## 4.63.2 (2025-11-13) diff --git a/src/ethereum/ethereumTypes.ts b/src/ethereum/ethereumTypes.ts index 3e91bd132..1e9640755 100644 --- a/src/ethereum/ethereumTypes.ts +++ b/src/ethereum/ethereumTypes.ts @@ -117,23 +117,92 @@ export interface EthereumNetworkInfo { decoyAddressConfig?: DecoyAddressConfig } -const asNetworkAdaptorConfigType = asValue( - 'amberdata-rpc', - 'blockbook', - 'blockbook-ws', - 'blockchair', - 'blockcypher', - 'evmscan', - 'filfox', - 'pulsechain-scan', - 'rpc' -) -const asNetworkAdaptorConfig = asObject({ - type: asNetworkAdaptorConfigType, +const asAmberdataAdapterConfig: Cleaner< + import('./networkAdapters/AmberdataAdapter').AmberdataAdapterConfig +> = asObject({ + type: asValue('amberdata-rpc'), + amberdataBlockchainId: asString, + servers: asArray(asString) +}) + +const asBlockbookAdapterConfig: Cleaner< + import('./networkAdapters/BlockbookAdapter').BlockbookAdapterConfig +> = asObject({ + type: asValue('blockbook'), + servers: asArray(asString) +}) + +const asBlockbookWsAdapterConnection: Cleaner< + import('./networkAdapters/BlockbookWsAdapter').BlockbookWsAdapterConnection +> = asObject({ + url: asString, + keyType: asOptional(asValue('nowNodesApiKey')) +}) + +const asBlockbookWsAdapterConfig: Cleaner< + import('./networkAdapters/BlockbookWsAdapter').BlockbookWsAdapterConfig +> = asObject({ + type: asValue('blockbook-ws'), + connections: asArray(asBlockbookWsAdapterConnection) +}) + +const asBlockchairAdapterConfig: Cleaner< + import('./networkAdapters/BlockchairAdapter').BlockchairAdapterConfig +> = asObject({ + type: asValue('blockchair'), + servers: asArray(asString) +}) + +const asBlockcypherAdapterConfig: Cleaner< + import('./networkAdapters/BlockcypherAdapter').BlockcypherAdapterConfig +> = asObject({ + type: asValue('blockcypher'), + servers: asArray(asString) +}) + +const asEvmScanAdapterConfig: Cleaner< + import('./networkAdapters/EvmScanAdapter').EvmScanAdapterConfig +> = asObject({ + type: asValue('evmscan'), + servers: asArray(asString), + version: asValue(1, 2), + gastrackerSupport: asOptional(asBoolean) +}) + +const asFilfoxAdapterConfig: Cleaner< + import('./networkAdapters/FilfoxAdapter').FilfoxAdapterConfig +> = asObject({ + type: asValue('filfox'), + servers: asArray(asString) +}) + +const asPulsechainScanAdapterConfig: Cleaner< + import('./networkAdapters/PulsechainScanAdapter').PulsechainScanAdapterConfig +> = asObject({ + type: asValue('pulsechain-scan'), + servers: asArray(asString) +}) + +const asRpcAdapterConfig: Cleaner< + import('./networkAdapters/RpcAdapter').RpcAdapterConfig +> = asObject({ + type: asValue('rpc'), servers: asArray(asString), ethBalCheckerContract: asOptional(asString) }) +const asNetworkAdaptorConfig: Cleaner = asEither( + asAmberdataAdapterConfig, + asBlockbookAdapterConfig, + asBlockbookWsAdapterConfig, + asBlockchairAdapterConfig, + asBlockcypherAdapterConfig, + asEvmScanAdapterConfig, + asFilfoxAdapterConfig, + asPulsechainScanAdapterConfig, + asRpcAdapterConfig +) + /** * Other Methods from EthereumTools */ diff --git a/src/ethereum/fees/feeProviders.ts b/src/ethereum/fees/feeProviders.ts index 586b7f019..621288880 100644 --- a/src/ethereum/fees/feeProviders.ts +++ b/src/ethereum/fees/feeProviders.ts @@ -222,11 +222,12 @@ export const fetchFeesFromEvmScan = async ( const apiKey = `&apikey=${ Array.isArray(scanApiKey) ? pickRandom(scanApiKey, 1)[0] : scanApiKey ?? '' }` - // Use Etherscan v2 API when targeting etherscan.io, otherwise use the network-specific scan API format + // Use version from config to determine URL format (server URLs already include full paths) const chainId = networkInfo.chainParams.chainId - const url = server.includes('etherscan.io') - ? `${server}/v2/api?chainid=${chainId}&module=gastracker&action=gasoracle${apiKey}` - : `${server}/api?module=gastracker&action=gasoracle${apiKey}` + const url = + evmScanConfig.version === 2 + ? `${server}?chainid=${chainId}&module=gastracker&action=gasoracle${apiKey}` + : `${server}?module=gastracker&action=gasoracle${apiKey}` const fetchResponse = await fetch(url) if (!fetchResponse.ok) @@ -346,6 +347,8 @@ export const getEvmScanApiKey = ( log: EdgeLog, serverUrl: string ): string | string[] | undefined => { + // TODO: This is total BS. We should just have a key associated with a domain name. + // This is getting out of hand. const { evmScanApiKey, etherscanApiKey, bscscanApiKey, polygonscanApiKey } = initOptions diff --git a/src/ethereum/info/abstractInfo.ts b/src/ethereum/info/abstractInfo.ts index 34b8f3016..f0421c659 100644 --- a/src/ethereum/info/abstractInfo.ts +++ b/src/ethereum/info/abstractInfo.ts @@ -47,12 +47,13 @@ const networkInfo: EthereumNetworkInfo = { networkAdapterConfigs: [ { type: 'rpc', - servers: ['https://api.mainnet.abs.xyz '] + servers: ['https://api.mainnet.abs.xyz'] }, { type: 'evmscan', gastrackerSupport: true, - servers: ['https://api.abscan.org/'] + servers: ['https://api.etherscan.io/v2/api'], + version: 2 } ], uriNetworks: ['abstract'], diff --git a/src/ethereum/info/amoyInfo.ts b/src/ethereum/info/amoyInfo.ts index f8919baf5..048bf8a8e 100644 --- a/src/ethereum/info/amoyInfo.ts +++ b/src/ethereum/info/amoyInfo.ts @@ -58,7 +58,8 @@ const networkInfo: EthereumNetworkInfo = { { type: 'evmscan', gastrackerSupport: true, - servers: ['https://api-amoy.polygonscan.com/'] + servers: ['https://api-amoy.polygonscan.com/v2/api'], + version: 2 } ], uriNetworks: ['amoy'], diff --git a/src/ethereum/info/arbitrumInfo.ts b/src/ethereum/info/arbitrumInfo.ts index 163635282..24b887c19 100644 --- a/src/ethereum/info/arbitrumInfo.ts +++ b/src/ethereum/info/arbitrumInfo.ts @@ -152,7 +152,8 @@ const networkInfo: EthereumNetworkInfo = { { type: 'evmscan', gastrackerSupport: true, - servers: ['https://api.etherscan.io', 'https://api.arbiscan.io'] + servers: ['https://api.etherscan.io/v2/api'], + version: 2 }, { type: 'blockchair', diff --git a/src/ethereum/info/avalancheInfo.ts b/src/ethereum/info/avalancheInfo.ts index 98d2ecd0f..a4075d56a 100644 --- a/src/ethereum/info/avalancheInfo.ts +++ b/src/ethereum/info/avalancheInfo.ts @@ -195,14 +195,19 @@ const networkInfo: EthereumNetworkInfo = { ], ethBalCheckerContract: '0xd023d153a0dfa485130ecfde2faa7e612ef94818' }, + { + type: 'evmscan', + gastrackerSupport: true, + servers: ['https://api.etherscan.io/v2/api'], + version: 2 + }, { type: 'evmscan', gastrackerSupport: true, servers: [ - 'https://api.etherscan.io', - 'https://api.avascan.info/v2/network/mainnet/evm/43114/etherscan', - 'https://api.snowscan.xyz' - ] + 'https://api.avascan.info/v2/network/mainnet/evm/43114/etherscan/api' + ], + version: 1 } ], uriNetworks: ['avalanche'], diff --git a/src/ethereum/info/baseInfo.ts b/src/ethereum/info/baseInfo.ts index 17121033a..6404869a1 100644 --- a/src/ethereum/info/baseInfo.ts +++ b/src/ethereum/info/baseInfo.ts @@ -62,7 +62,6 @@ const networkInfo: EthereumNetworkInfo = { type: 'rpc', servers: [ 'https://base-mainnet.public.blastapi.io', - 'https://rpc.ankr.com/base', 'https://lb.drpc.org/ogrpc?network=base&dkey={{drpcApiKey}}' ], ethBalCheckerContract: '0x3ba5A41eA17fd4950a641a057dC0bEb8E8ff1521' @@ -70,7 +69,8 @@ const networkInfo: EthereumNetworkInfo = { { type: 'evmscan', gastrackerSupport: true, - servers: ['https://api.etherscan.io', 'https://api.basescan.org'] + servers: ['https://api.etherscan.io/v2/api'], + version: 2 }, { type: 'blockchair', diff --git a/src/ethereum/info/binancesmartchainInfo.ts b/src/ethereum/info/binancesmartchainInfo.ts index 3f10f2148..dc6bd3681 100644 --- a/src/ethereum/info/binancesmartchainInfo.ts +++ b/src/ethereum/info/binancesmartchainInfo.ts @@ -154,7 +154,8 @@ const networkInfo: EthereumNetworkInfo = { { type: 'evmscan', gastrackerSupport: true, - servers: ['https://api.etherscan.io', 'https://api.bscscan.com'] + servers: ['https://api.etherscan.io/v2/api'], + version: 2 } ], uriNetworks: ['smartchain'], diff --git a/src/ethereum/info/bobevmInfo.ts b/src/ethereum/info/bobevmInfo.ts index d8a448f67..f008239de 100644 --- a/src/ethereum/info/bobevmInfo.ts +++ b/src/ethereum/info/bobevmInfo.ts @@ -56,7 +56,8 @@ const networkInfo: EthereumNetworkInfo = { { type: 'evmscan', gastrackerSupport: true, - servers: ['https://explorer.gobob.xyz'] + servers: ['https://explorer.gobob.xyz/api'], + version: 1 } ], uriNetworks: [], diff --git a/src/ethereum/info/botanixInfo.ts b/src/ethereum/info/botanixInfo.ts index 808577a4f..a39f933a9 100644 --- a/src/ethereum/info/botanixInfo.ts +++ b/src/ethereum/info/botanixInfo.ts @@ -57,8 +57,9 @@ const networkInfo: EthereumNetworkInfo = { type: 'evmscan', gastrackerSupport: true, servers: [ - 'https://api.routescan.io/v2/network/mainnet/evm/3637/etherscan' - ] + 'https://api.routescan.io/v2/network/mainnet/evm/3637/etherscan/api' + ], + version: 1 } ], uriNetworks: [], diff --git a/src/ethereum/info/celoInfo.ts b/src/ethereum/info/celoInfo.ts index 9c67b5cf4..c038d96a7 100644 --- a/src/ethereum/info/celoInfo.ts +++ b/src/ethereum/info/celoInfo.ts @@ -71,14 +71,20 @@ const networkInfo: EthereumNetworkInfo = { servers: [ 'https://forno.celo.org', 'https://rpc.ankr.com/celo', - 'https://celo-mainnet-rpc.allthatnode.com', 'https://lb.drpc.org/ogrpc?network=celo&dkey={{drpcApiKey}}' ] }, { type: 'evmscan', gastrackerSupport: false, - servers: ['https://api.etherscan.io', 'https://explorer.celo.org/mainnet'] + servers: ['https://api.etherscan.io/v2/api'], + version: 2 + }, + { + type: 'evmscan', + gastrackerSupport: false, + servers: ['https://explorer.celo.org/mainnet/api'], + version: 1 } ], uriNetworks: ['celo'], diff --git a/src/ethereum/info/ethereumInfo.ts b/src/ethereum/info/ethereumInfo.ts index ade2157b2..8945f5c3e 100644 --- a/src/ethereum/info/ethereumInfo.ts +++ b/src/ethereum/info/ethereumInfo.ts @@ -1173,7 +1173,8 @@ const networkInfo: EthereumNetworkInfo = { { type: 'evmscan', gastrackerSupport: true, - servers: ['https://api.etherscan.io'] + servers: ['https://api.etherscan.io/v2/api'], + version: 2 }, { type: 'blockbook', diff --git a/src/ethereum/info/ethereumclassicInfo.ts b/src/ethereum/info/ethereumclassicInfo.ts index 74fa027f1..6003812ef 100644 --- a/src/ethereum/info/ethereumclassicInfo.ts +++ b/src/ethereum/info/ethereumclassicInfo.ts @@ -84,18 +84,14 @@ const networkInfo: EthereumNetworkInfo = { networkAdapterConfigs: [ { type: 'rpc', - servers: [ - 'https://etc.rivet.link', - 'https://geth-de.etc-network.info', - 'https://geth-at.etc-network.info', - 'https://etc.etcdesktop.com' - ], + servers: ['https://etc.rivet.link', 'https://etc.etcdesktop.com'], ethBalCheckerContract: '0xfC701A6b65e1BcF59fb3BDbbe5cb41f35FC7E009' }, { type: 'evmscan', gastrackerSupport: true, - servers: ['https://etc.blockscout.com'] + servers: ['https://etc.blockscout.com/api'], + version: 1 }, { type: 'blockbook', diff --git a/src/ethereum/info/ethereumpowInfo.ts b/src/ethereum/info/ethereumpowInfo.ts index 0196cffb3..8e71da72e 100644 --- a/src/ethereum/info/ethereumpowInfo.ts +++ b/src/ethereum/info/ethereumpowInfo.ts @@ -60,13 +60,6 @@ const networkInfo: EthereumNetworkInfo = { { type: 'rpc', servers: ['https://mainnet.ethereumpow.org'] - }, - { - type: 'evmscan', - gastrackerSupport: true, - servers: [ - // TODO: - ] } ], diff --git a/src/ethereum/info/fantomInfo.ts b/src/ethereum/info/fantomInfo.ts index e3bd66b3e..9b7f39b11 100644 --- a/src/ethereum/info/fantomInfo.ts +++ b/src/ethereum/info/fantomInfo.ts @@ -299,7 +299,8 @@ const networkInfo: EthereumNetworkInfo = { { type: 'evmscan', gastrackerSupport: false, - servers: ['https://ftmscout.com/'] + servers: ['https://ftmscout.com/api'], + version: 1 } ], uriNetworks: ['fantom'], diff --git a/src/ethereum/info/holeskyInfo.ts b/src/ethereum/info/holeskyInfo.ts index 1600a8cc0..718029cbc 100644 --- a/src/ethereum/info/holeskyInfo.ts +++ b/src/ethereum/info/holeskyInfo.ts @@ -76,7 +76,8 @@ const networkInfo: EthereumNetworkInfo = { { type: 'evmscan', gastrackerSupport: true, - servers: ['https://api.etherscan.io'] + servers: ['https://api.etherscan.io/v2/api'], + version: 2 } ], diff --git a/src/ethereum/info/hyperEvmInfo.ts b/src/ethereum/info/hyperEvmInfo.ts index 2861e0bd3..83d0e2290 100644 --- a/src/ethereum/info/hyperEvmInfo.ts +++ b/src/ethereum/info/hyperEvmInfo.ts @@ -125,14 +125,16 @@ const networkInfo: EthereumNetworkInfo = { servers: [ 'https://rpc.hyperliquid.xyz/evm', 'https://rpc.hypurrscan.io', - 'https://hyperliquid-json-rpc.stakely.io', - 'https://hyperliquid-json-rpc.stakely.io' + 'https://rpc.hyperlend.finance' ] }, { type: 'evmscan', gastrackerSupport: true, - servers: ['https://api.routescan.io/v2/network/mainnet/evm/999/etherscan'] + servers: [ + 'https://api.routescan.io/v2/network/mainnet/evm/999/etherscan/api' + ], + version: 1 } ], uriNetworks: [], diff --git a/src/ethereum/info/optimismInfo.ts b/src/ethereum/info/optimismInfo.ts index 36747a959..42facf912 100644 --- a/src/ethereum/info/optimismInfo.ts +++ b/src/ethereum/info/optimismInfo.ts @@ -190,7 +190,8 @@ const networkInfo: EthereumNetworkInfo = { { type: 'evmscan', gastrackerSupport: true, - servers: ['https://api.etherscan.io'] + servers: ['https://api.etherscan.io/v2/api'], + version: 2 } ], uriNetworks: ['optimism'], diff --git a/src/ethereum/info/polygonInfo.ts b/src/ethereum/info/polygonInfo.ts index a5a892faf..99e48c655 100644 --- a/src/ethereum/info/polygonInfo.ts +++ b/src/ethereum/info/polygonInfo.ts @@ -185,9 +185,6 @@ const networkInfo: EthereumNetworkInfo = { type: 'rpc', servers: [ 'https://polygon-rpc.com/', - 'https://rpc.polycat.finance', - 'https://rpc-mainnet.maticvigil.com', - 'https://matic-mainnet.chainstacklabs.com', 'https://rpc.ankr.com/polygon', 'https://poly.api.pocket.network', 'https://rpc-mainnet.matic.quiknode.pro/{{quiknodeApiKey}}/', @@ -198,7 +195,8 @@ const networkInfo: EthereumNetworkInfo = { { type: 'evmscan', gastrackerSupport: true, - servers: ['https://api.etherscan.io', 'https://api.polygonscan.com'] + servers: ['https://api.etherscan.io/v2/api'], + version: 2 } ], uriNetworks: ['polygon'], diff --git a/src/ethereum/info/pulsechainInfo.ts b/src/ethereum/info/pulsechainInfo.ts index cb6532899..fb400e574 100644 --- a/src/ethereum/info/pulsechainInfo.ts +++ b/src/ethereum/info/pulsechainInfo.ts @@ -64,7 +64,8 @@ const networkInfo: EthereumNetworkInfo = { { type: 'evmscan', gastrackerSupport: false, - servers: ['https://api.scan.pulsechain.com'] + servers: ['https://api.scan.pulsechain.com/api'], + version: 1 }, { type: 'pulsechain-scan', diff --git a/src/ethereum/info/rskInfo.ts b/src/ethereum/info/rskInfo.ts index eca6e209c..6ebc9999c 100644 --- a/src/ethereum/info/rskInfo.ts +++ b/src/ethereum/info/rskInfo.ts @@ -62,7 +62,8 @@ const networkInfo: EthereumNetworkInfo = { { type: 'evmscan', gastrackerSupport: false, - servers: ['https://blockscout.com/rsk/mainnet'] + servers: ['https://blockscout.com/rsk/mainnet/api'], + version: 1 } ], uriNetworks: ['rsk', 'rbtc'], diff --git a/src/ethereum/info/sepoliaInfo.ts b/src/ethereum/info/sepoliaInfo.ts index 85853c0b6..3ab16a18a 100644 --- a/src/ethereum/info/sepoliaInfo.ts +++ b/src/ethereum/info/sepoliaInfo.ts @@ -74,7 +74,8 @@ const networkInfo: EthereumNetworkInfo = { { type: 'evmscan', gastrackerSupport: true, - servers: ['https://api.etherscan.io'] + servers: ['https://api.etherscan.io/v2/api'], + version: 2 } ], diff --git a/src/ethereum/info/sonicInfo.ts b/src/ethereum/info/sonicInfo.ts index fa88a1b4d..bfcc66d7b 100644 --- a/src/ethereum/info/sonicInfo.ts +++ b/src/ethereum/info/sonicInfo.ts @@ -145,7 +145,8 @@ const networkInfo: EthereumNetworkInfo = { { type: 'evmscan', gastrackerSupport: true, - servers: ['https://api.etherscan.io', 'https://api.sonicscan.org'] + servers: ['https://api.etherscan.io/v2/api'], + version: 2 } ], uriNetworks: ['sonic'], diff --git a/src/ethereum/info/zksyncInfo.ts b/src/ethereum/info/zksyncInfo.ts index 58e8a55c6..4e0d36d34 100644 --- a/src/ethereum/info/zksyncInfo.ts +++ b/src/ethereum/info/zksyncInfo.ts @@ -97,11 +97,14 @@ const networkInfo: EthereumNetworkInfo = { { type: 'evmscan', gastrackerSupport: true, - servers: [ - 'https://api.etherscan.io', - 'https://block-explorer-api.mainnet.zksync.io', - 'https://api-era.zksync.network' - ] + servers: ['https://api.etherscan.io/v2/api'], + version: 2 + }, + { + type: 'evmscan', + gastrackerSupport: true, + servers: ['https://block-explorer-api.mainnet.zksync.io/api'], + version: 1 } ], uriNetworks: ['zksync'], diff --git a/src/ethereum/networkAdapters/EvmScanAdapter.ts b/src/ethereum/networkAdapters/EvmScanAdapter.ts index c60140fe1..0287e2064 100644 --- a/src/ethereum/networkAdapters/EvmScanAdapter.ts +++ b/src/ethereum/networkAdapters/EvmScanAdapter.ts @@ -59,6 +59,7 @@ const NUM_TRANSACTIONS_TO_QUERY = 50 export interface EvmScanAdapterConfig { type: 'evmscan' servers: string[] + version: 1 | 2 /** Whether the API supports gastracker module */ gastrackerSupport?: boolean @@ -301,14 +302,14 @@ export class EvmScanAdapter extends NetworkAdapter { // Determine if we should use v2 API let url: string - if (server.includes('etherscan.io')) { + if (this.config.version === 2) { // For etherscan.io API - use v2 with chainId - url = `${server}/v2/api?chainid=${chainId}${ + url = `${server}?chainid=${chainId}${ cmd.startsWith('?') ? cmd.replace('?', '&') : cmd }` } else { // For non-etherscan APIs like blockscout, continue using the old format - url = `${server}/api${cmd}` + url = `${server}${cmd}` } const response = await this.ethEngine.fetchCors(`${url}${apiKeyParam}`) @@ -324,7 +325,9 @@ export class EvmScanAdapter extends NetworkAdapter { 'status' in cleanData && cleanData.status === '0' && typeof cleanData.result === 'string' && - cleanData.result.match(/Max calls|rate limit/) != null + (cleanData.result.match(/Max calls|rate limit/) != null || + cleanData.result.match(/Free API access is temporarily unavailable/) != + null) ) { throw new RateLimitError(`fetchGetEtherscan rate limit for ${server}`) } diff --git a/test/ethereum/fees/ethFeeHistory.test.ts b/test/ethereum/fees/ethFeeHistory.test.ts index 202fc03d8..58d8bebaa 100644 --- a/test/ethereum/fees/ethFeeHistory.test.ts +++ b/test/ethereum/fees/ethFeeHistory.test.ts @@ -269,7 +269,8 @@ describe('fetchFeesFromFeeHistory integration', function () { { type: 'evmscan', gastrackerSupport: true, - servers: ['https://api.etherscan.io'] + servers: ['https://api.etherscan.io'], + version: 2 } ] }