Conversation
WalkthroughUpdated Etherscan provider configuration to use API-style endpoints across multiple networks and corrected txlist URL construction by removing a redundant "api?" segment. No public APIs changed. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
💼 Build Files |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/extension/src/providers/ethereum/libs/activity-handlers/providers/etherscan/index.ts (1)
61-67: Hardcoded OKLink API key in client code.Shipping a live key inside the extension is a security/compliance risk (client‑side exposure, easy scraping, rate‑limit bleed). Move this to a server proxy, remote config, or build‑time secret injection with per‑env rotation.
Example (build‑time injection):
- headers = { 'OK-ACCESS-KEY': 'df87e7eb-061f-44b1-84bc-83722fad717c' }; + const oklinkKey = process.env.OKLINK_API_KEY ?? import.meta.env.VITE_OKLINK_API_KEY; + if (oklinkKey) headers = { 'OK-ACCESS-KEY': oklinkKey };Also consider gating XLayer support when no key is provided to avoid silent 401s.
🧹 Nitpick comments (7)
packages/extension/src/providers/ethereum/libs/activity-handlers/providers/etherscan/index.ts (4)
21-27: Build URLs via URL/URLSearchParams to avoid delimiter fragility.Relying on endpoints to end with
?or&is brittle across mixed providers (Etherscan v2, Blockscout, OKLink). Safer to append params programmatically and preserve any pre‑existing ones (e.g.,chainid).Apply:
return cacheFetch( { - // Note: would like to add offset=50 (i.e. results per page) but it seems to cause polygon API to hang - url: `${endpoint}module=account&action=txlist&address=${address}&sort=desc`, + // Note: would like to add offset=50 (i.e. results per page) but it seems to cause polygon API to hang + url: (() => { + const u = new URL(endpoint); + u.searchParams.set('module', 'account'); + u.searchParams.set('action', 'txlist'); + u.searchParams.set('address', address); + u.searchParams.set('sort', 'desc'); + return u.toString(); + })(), headers, }, TTL, ).then(res => {
29-31: Harden Etherscan/Blockscout response handling.Some Blockscout instances don’t return
isError; many usetxreceipt_status. Current logic would mark such entries as failed.Apply:
- if (res.status === '0') return []; + if (res?.status === '0' || res?.message === 'No transactions found') return []; @@ - status: tx.isError === '0' ? true : false, + status: + (tx as any).isError !== undefined + ? (tx as any).isError === '0' + : (tx as any).txreceipt_status === '1',Also applies to: 43-47
68-70: Nit: typo and naming.
enpoint→endpointfor clarity.- const enpoint = - NetworkEndpoints[network.name as keyof typeof NetworkEndpoints]; - const activities = await getAddressActivity(address, enpoint, headers); + const endpoint = + NetworkEndpoints[network.name as keyof typeof NetworkEndpoints]; + const activities = await getAddressActivity(address, endpoint, headers);
71-99: Nit: variable casing.Prefer
promises(lowercase) for local arrays of promises.- const Promises = activities.map(activity => { + const promises = activities.map(activity => { @@ - return Promise.all(Promises); + return Promise.all(promises);packages/extension/src/providers/ethereum/libs/activity-handlers/providers/etherscan/configs.ts (3)
4-7: Confirm intent: Ethereum now points to Blockscout, not Etherscan v2.This can alter fields (e.g., presence/absence of
isError). If the switch is intentional, ensure index.ts has the fallback logic for status derivation.If the goal is to fully consolidate on Etherscan v2, change to:
- [NetworkNames.Ethereum]: 'https://eth.blockscout.com/api?', + [NetworkNames.Ethereum]: 'https://api.etherscan.io/v2/api?chainid=1&',Otherwise, keep Blockscout but ensure consumers handle its nuances (see index.ts suggestion).
34-37: Etherscan v2 aggregator endpoints: consider API key support.Calls to
api.etherscan.io/v2/api?chainid=...are typically key‑gated/rate‑limited. If we expect real traffic, plumb an optionalapikeyquery param orX-API-Keyheader from config.Propose adding an optional per‑network
apiKeyProvider(or global) and appendingapikeyin index.ts when available.Also applies to: 41-43, 48-51, 62-63
3-73: Future‑proofing: move from stringly URLs to structured config.To avoid trailing delimiter errors and to support provider‑specific auth, consider:
- Store
baseUrlwithout query delimiters.- Store static
params(e.g.,{ chainid: '56' }).- Optional
headers/authStrategy.High‑level sketch:
// configs.ts type EndpointCfg = { baseUrl: string; params?: Record<string,string>; headers?: Record<string,string> }; const NetworkEndpoints: Record<NetworkNames, EndpointCfg> = { [NetworkNames.Binance]: { baseUrl: 'https://api.etherscan.io/v2/api', params: { chainid: '56' } }, // ... }; // index.ts const cfg = NetworkEndpoints[network.name]; const u = new URL(cfg.baseUrl); Object.entries(cfg.params ?? {}).forEach(([k,v]) => u.searchParams.set(k, v)); // then set module/action/address/sort; pass cfg.headers if any
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/extension/src/providers/ethereum/libs/activity-handlers/providers/etherscan/configs.ts(1 hunks)packages/extension/src/providers/ethereum/libs/activity-handlers/providers/etherscan/index.ts(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: buildAll
- GitHub Check: test
- GitHub Check: test
🔇 Additional comments (2)
packages/extension/src/providers/ethereum/libs/activity-handlers/providers/etherscan/index.ts (1)
24-24: Good catch: removed duplicate api? segment.This fixes double-
api?constructions when paired with new endpoints that already include query scaffolding.packages/extension/src/providers/ethereum/libs/activity-handlers/providers/etherscan/configs.ts (1)
65-66: No change required — /api/api? is valid for these explorers.Smoke tests: Cytonic testnet returned JSON with message "OK" and a non-empty result; AppLayer testnet returned JSON "No transactions found" (status "0") — both accept /api/api?. Keep entries in packages/extension/src/providers/ethereum/libs/activity-handlers/providers/etherscan/configs.ts (lines 65–66 and 69–71) unchanged.
| [NetworkNames.Ethereum]: 'https://eth.blockscout.com/api?', | ||
| [NetworkNames.Binance]: 'https://api.etherscan.io/v2/api?chainid=56&', | ||
| [NetworkNames.Matic]: 'https://polygon.blockscout.com/api?', | ||
| [NetworkNames.Moonbeam]: 'https://api.etherscan.io/v2/api?chainid=1284&', | ||
| [NetworkNames.Moonriver]: 'https://api.etherscan.io/v2/api?chainid=1285&', | ||
| [NetworkNames.KaruraEVM]: 'https://blockscout.karura.network/api?', | ||
| [NetworkNames.AstarEVM]: 'https://blockscout.com/astar/api?', | ||
| [NetworkNames.ShidenEVM]: 'https://blockscout.com/shiden/api?', | ||
| [NetworkNames.Optimism]: 'https://api.etherscan.io/v2/api?chainid=10&', | ||
| [NetworkNames.EdgeEVM]: 'https://edgscan.live/api?', | ||
| [NetworkNames.Rootstock]: 'https://blockscout.com/rsk/mainnet/api?', | ||
| [NetworkNames.RootstockTestnet]: | ||
| 'https://rootstock-testnet.blockscout.com/api?', | ||
| [NetworkNames.SkaleBlockBrawlers]: | ||
| 'https://frayed-decent-antares.explorer.mainnet.skalenodes.com/', | ||
| 'https://frayed-decent-antares.explorer.mainnet.skalenodes.com/api?', | ||
| [NetworkNames.SkaleCalypso]: | ||
| 'https://honorable-steel-rasalhague.explorer.mainnet.skalenodes.com/', | ||
| 'https://honorable-steel-rasalhague.explorer.mainnet.skalenodes.com/api?', | ||
| [NetworkNames.SkaleCryptoBlades]: | ||
| 'https://affectionate-immediate-pollux.explorer.mainnet.skalenodes.com/', | ||
| 'https://affectionate-immediate-pollux.explorer.mainnet.skalenodes.com/api?', | ||
| [NetworkNames.SkaleCryptoColosseum]: | ||
| 'https://haunting-devoted-deneb.explorer.mainnet.skalenodes.com/', | ||
| 'https://haunting-devoted-deneb.explorer.mainnet.skalenodes.com/api?', | ||
| [NetworkNames.SkaleEuropa]: | ||
| 'https://elated-tan-skat.explorer.mainnet.skalenodes.com/', | ||
| 'https://elated-tan-skat.explorer.mainnet.skalenodes.com/api?', | ||
| [NetworkNames.SkaleExorde]: | ||
| 'https://light-vast-diphda.explorer.mainnet.skalenodes.com/', | ||
| 'https://light-vast-diphda.explorer.mainnet.skalenodes.com/api?', | ||
| [NetworkNames.SkaleNebula]: | ||
| 'https://green-giddy-denebola.explorer.mainnet.skalenodes.com/', | ||
| 'https://green-giddy-denebola.explorer.mainnet.skalenodes.com/api?', | ||
| [NetworkNames.SkaleTitan]: | ||
| 'https://parallel-stormy-spica.explorer.mainnet.skalenodes.com/', | ||
| [NetworkNames.ZkSync]: 'https://block-explorer-api.mainnet.zksync.io/', | ||
| [NetworkNames.Arbitrum]: 'https://api.arbiscan.io/', | ||
| [NetworkNames.ArbitrumNova]: 'https://nova-explorer.arbitrum.io/', | ||
| [NetworkNames.Gnosis]: 'https://api.gnosisscan.io/', | ||
| [NetworkNames.Avalanche]: 'https://api.snowtrace.io/', | ||
| [NetworkNames.Aurora]: 'https://explorer.mainnet.aurora.dev/', | ||
| [NetworkNames.PuppyNet]: 'https://puppyscan.shib.io/', | ||
| [NetworkNames.Shibarium]: 'https://www.shibariumscan.io/', | ||
| [NetworkNames.MaticZK]: 'https://api-zkevm.polygonscan.com/', | ||
| [NetworkNames.Base]: 'https://api.basescan.org/', | ||
| [NetworkNames.Celo]: 'https://explorer.celo.org/mainnet/', | ||
| [NetworkNames.SyscoinNEVMTest]: 'https://explorer.tanenbaum.io/', | ||
| [NetworkNames.SyscoinNEVM]: 'https://explorer.syscoin.org/', | ||
| [NetworkNames.RolluxTest]: 'https://rollux.tanenbaum.io/', | ||
| [NetworkNames.Rollux]: 'https://explorer.rollux.com/', | ||
| [NetworkNames.Blast]: 'https://api.blastscan.io/', | ||
| [NetworkNames.Sanko]: 'https://explorer.sanko.xyz/', | ||
| [NetworkNames.Sonic]: 'https://api.sonicscan.org/', | ||
| [NetworkNames.Degen]: 'https://explorer.degen.tips/', | ||
| [NetworkNames.Ink]: 'https://explorer.inkonchain.com/', | ||
| [NetworkNames.ImmutableZkevm]: 'https://explorer.immutable.com/', | ||
| [NetworkNames.Rari]: 'https://mainnet.explorer.rarichain.org/', | ||
| [NetworkNames.Forma]: 'https://explorer.forma.art/', | ||
| [NetworkNames.XLayer]: 'https://www.oklink.com/api/v5/explorer/xlayer/', | ||
| [NetworkNames.Linea]: 'https://api.lineascan.build/', | ||
| [NetworkNames.MantaPacific]: 'https://pacific-explorer.manta.network/', | ||
| [NetworkNames.Mode]: 'https://explorer.mode.network/', | ||
| [NetworkNames.ProofOfPlayApex]: 'https://explorer.apex.proofofplay.com/', | ||
| [NetworkNames.Scroll]: 'https://api.scrollscan.com/', | ||
| [NetworkNames.Fraxtal]: 'https://api.fraxscan.com/', | ||
| [NetworkNames.Coti]: 'https://mainnet.cotiscan.io/', | ||
| 'https://parallel-stormy-spica.explorer.mainnet.skalenodes.com/api?', | ||
| [NetworkNames.ZkSync]: 'https://block-explorer-api.mainnet.zksync.io/api?', | ||
| [NetworkNames.Arbitrum]: 'https://api.etherscan.io/v2/api?chainid=42161&', | ||
| [NetworkNames.ArbitrumNova]: 'https://api.etherscan.io/v2/api?chainid=42170&', | ||
| [NetworkNames.Gnosis]: 'https://api.etherscan.io/v2/api?chainid=100&', | ||
| [NetworkNames.Avalanche]: 'https://api.snowtrace.io/api?', | ||
| [NetworkNames.Aurora]: 'https://explorer.mainnet.aurora.dev/api?', | ||
| [NetworkNames.PuppyNet]: 'https://puppyscan.shib.io/api?', | ||
| [NetworkNames.Shibarium]: 'https://www.shibariumscan.io/api?', | ||
| [NetworkNames.MaticZK]: 'https://api.etherscan.io/v2/api?chainid=1101&', | ||
| [NetworkNames.Base]: 'https://api.etherscan.io/v2/api?chainid=8453&', | ||
| [NetworkNames.Celo]: 'https://explorer.celo.org/mainnet/api?', | ||
| [NetworkNames.SyscoinNEVMTest]: 'https://explorer.tanenbaum.io/api?', | ||
| [NetworkNames.SyscoinNEVM]: 'https://explorer.syscoin.org/api?', | ||
| [NetworkNames.RolluxTest]: 'https://rollux.tanenbaum.io/api?', | ||
| [NetworkNames.Rollux]: 'https://explorer.rollux.com/api?', | ||
| [NetworkNames.Blast]: 'https://api.etherscan.io/v2/api?chainid=81457&', | ||
| [NetworkNames.Sanko]: 'https://explorer.sanko.xyz/api?', | ||
| [NetworkNames.Sonic]: 'https://api.etherscan.io/v2/api?chainid=146&', | ||
| [NetworkNames.Degen]: 'https://explorer.degen.tips/api?', | ||
| [NetworkNames.Ink]: 'https://explorer.inkonchain.com/api?', | ||
| [NetworkNames.ImmutableZkevm]: 'https://explorer.immutable.com/api?', | ||
| [NetworkNames.Rari]: 'https://mainnet.explorer.rarichain.org/api?', | ||
| [NetworkNames.Forma]: 'https://explorer.forma.art/api?', | ||
| [NetworkNames.XLayer]: 'https://www.oklink.com/api/v5/explorer/xlayer/api?', | ||
| [NetworkNames.Linea]: 'https://api.lineascan.build/api?', | ||
| [NetworkNames.MantaPacific]: 'https://pacific-explorer.manta.network/api?', | ||
| [NetworkNames.Mode]: 'https://explorer.mode.network/api?', | ||
| [NetworkNames.ProofOfPlayApex]: 'https://explorer.apex.proofofplay.com/api?', | ||
| [NetworkNames.Scroll]: 'https://api.scrollscan.com/api?', | ||
| [NetworkNames.Fraxtal]: 'https://api.etherscan.io/v2/api?chainid=252&', | ||
| [NetworkNames.Coti]: 'https://mainnet.cotiscan.io/api?', | ||
| [NetworkNames.CytonicTestnet]: | ||
| 'https://explorer-api.evm.testnet.cytonic.com/api/', | ||
| [NetworkNames.Derive]: 'https://explorer.derive.xyz/', | ||
| [NetworkNames.Conflux]: 'https://evmapi.confluxscan.org/', | ||
| [NetworkNames.Hemi]: 'https://explorer.hemi.xyz/', | ||
| [NetworkNames.AppLayerTestnet]: 'https://testnet-explorer.applayer.com/api/', | ||
| [NetworkNames.EthereumClassic]: 'https://etc.blockscout.com/', | ||
| [NetworkNames.TAC]: 'https://explorer.tac.build/', | ||
| 'https://explorer-api.evm.testnet.cytonic.com/api/api?', | ||
| [NetworkNames.Derive]: 'https://explorer.derive.xyz/api?', | ||
| [NetworkNames.Conflux]: 'https://evmapi.confluxscan.org/api?', | ||
| [NetworkNames.Hemi]: 'https://explorer.hemi.xyz/api?', | ||
| [NetworkNames.AppLayerTestnet]: | ||
| 'https://testnet-explorer.applayer.com/api/api?', | ||
| [NetworkNames.EthereumClassic]: 'https://etc.blockscout.com/api?', | ||
| [NetworkNames.TAC]: 'https://explorer.tac.build/api?', | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
Endpoint normalization looks coherent; verify provider mix and trailing delimiters.
- Trailing
?vs&is consistent with the new URL builder in this PR, but becomes unnecessary if we adopt URLSearchParams in index.ts. - Mixed providers: some networks moved to Blockscout (e.g., Ethereum, Polygon), others to Etherscan v2 aggregator with
chainid, and several custom explorers. This can change payload nuances.
Run this to sanity‑check all endpoints end with ? or & and to flag any double /api/api?:
If adopting URLSearchParams in index.ts, consider dropping the trailing delimiters entirely in this map to reduce config footguns.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
F="packages/extension/src/providers/ethereum/libs/activity-handlers/providers/etherscan/configs.ts"
python - <<'PY' "$F"
import re,sys,json
t=open(sys.argv[1]).read()
urls=re.findall(r'["\'](https?://[^"\']+)["\']',t)
bad=[u for u in urls if not (u.endswith('?') or u.endswith('&'))]
double=[u for u in urls if '/api/api?' in u]
print(json.dumps({"total":len(urls),"bad_endings":bad,"double_api":double},indent=2))
PYLength of output: 175
Fix duplicated /api/api? entries & consider removing trailing delimiters
- Sanity check: 58 endpoints; all end with
?or&. Two entries contain a duplicated/api/api?in packages/extension/src/providers/ethereum/libs/activity-handlers/providers/etherscan/configs.ts: - Action: replace the above with the correct path (e.g.,
/api?) or the provider's canonical base URL. - Optional: if index.ts will use URLSearchParams, remove trailing
?/&from this map to avoid config footguns.
Summary by CodeRabbit