Skip to content

fix: etherscan urls#758

Merged
kvhnuke merged 1 commit intodevop/release-v-2-13from
devop/migrate-etherscan-v2
Sep 25, 2025
Merged

fix: etherscan urls#758
kvhnuke merged 1 commit intodevop/release-v-2-13from
devop/migrate-etherscan-v2

Conversation

@kvhnuke
Copy link
Contributor

@kvhnuke kvhnuke commented Sep 22, 2025

Summary by CodeRabbit

  • New Features
    • None
  • Bug Fixes
    • Corrected Etherscan API URL construction to prevent request failures and ensure reliable transaction history retrieval.
  • Refactor
    • Standardized explorer endpoints across many networks to use API-style routes, improving consistency and reducing edge-case errors when fetching activity.
  • Chores
    • Updated network configurations to align with Etherscan-like APIs, enhancing compatibility for Ethereum, Binance, Polygon, Arbitrum, Moonbeam, Skale, and others.

@coderabbitai
Copy link

coderabbitai bot commented Sep 22, 2025

Walkthrough

Updated 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

Cohort / File(s) Summary of Changes
Etherscan Config Endpoints
packages/extension/src/providers/ethereum/libs/activity-handlers/providers/etherscan/configs.ts
Replaced explorer base URLs with API-form endpoints (e.g., '/api?', '/api/api?'), adding chain ID parameters where applicable across numerous networks, including Ethereum, BSC, Polygon, Arbitrum, Moonbeam, Skale, Conflux, EthereumClassic, TAC, and others.
Etherscan Tx URL Builder
packages/extension/src/providers/ethereum/libs/activity-handlers/providers/etherscan/index.ts
Corrected txlist API URL composition by removing an extra 'api?' path segment. No other logic changes.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • SemajaM
  • SemajaM8
  • NickKelly1

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
Title Check ✅ Passed The title “fix: etherscan urls” clearly and concisely summarizes the main change of correcting and standardizing Etherscan API endpoints across networks, matching the primary updates in the code without extraneous detail. It follows a conventional commit style and accurately reflects the fixes implemented.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch devop/migrate-etherscan-v2

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Sep 22, 2025

💼 Build Files
chrome: enkrypt-chrome-c8edc6e6.zip
firefox: enkrypt-firefox-c8edc6e6.zip

💉 Virus total analysis
chrome: c8edc6e6
firefox: c8edc6e6

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 use txreceipt_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.

enpointendpoint for 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 optional apikey query param or X-API-Key header from config.

Propose adding an optional per‑network apiKeyProvider (or global) and appending apikey in 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 baseUrl without 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

📥 Commits

Reviewing files that changed from the base of the PR and between a3f5aab and c8edc6e.

📒 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.

Comment on lines +4 to 73
[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?',
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 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))
PY

Length 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.

@kvhnuke kvhnuke changed the base branch from develop to devop/release-v-2-13 September 25, 2025 18:50
@kvhnuke kvhnuke merged commit 73d525d into devop/release-v-2-13 Sep 25, 2025
5 checks passed
@kvhnuke kvhnuke deleted the devop/migrate-etherscan-v2 branch September 25, 2025 18:51
This was referenced Sep 25, 2025
@coderabbitai coderabbitai bot mentioned this pull request Nov 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant