From 030444b70a89c7e290fb87ffff0f536c51f70a47 Mon Sep 17 00:00:00 2001 From: Jonathan Tzeng Date: Mon, 27 Jul 2026 11:42:59 -0700 Subject: [PATCH] Fix EVM wallets stuck at 50% sync when only evmScanApiKey is configured edge-currency-accountbased resolves the Etherscan API key for api.etherscan.io from the deprecated etherscanApiKey init option and throws before it considers evmScanApiKey, the option this repo documents and cleans. A build whose env.json carries only evmScanApiKey therefore sends a keyless request, which Etherscan V2 rejects with 'Missing/Invalid API Key'. The EvmScan adapter is the only adapter with a non-null fetchTxs on the 15 EVM networks routed through that host, so the rejected request leaves transaction history permanently unsynced. The token sync tracker averages balance and history progress, so a wallet with a complete balance and no history reports exactly 0.5 and the app shows it stuck at 50% forever. Supply the deprecated option from the supported one at the env layer, which takes effect without waiting on a plugin release. An explicitly configured etherscanApiKey still wins, and an empty evmScanApiKey adds nothing. --- CHANGELOG.md | 1 + src/__tests__/envConfig.test.ts | 44 +++++++++++++++++++++++++++++++++ src/envConfig.ts | 23 ++++++++++++++++- 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/__tests__/envConfig.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 685ea4ac0e3..1f5d3a8dd6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - changed: Sign MoonPay buy/sell widget URLs and bind them to the customer's IP via the info server, for MoonPay's on-ramp IP-matching security upgrade. - fixed: NYM max swaps from EVM wallets now report the correct limit error instead of an unsupported-route error (edge-exchange-plugins 2.52.1). - fixed: Exchange rate queries no longer request each chain's own asset twice, which had been inflating every rate query with duplicate pairs. +- fixed: EVM wallets no longer stall at 50% sync on a build whose env.json configures `evmScanApiKey` without the deprecated `etherscanApiKey`. ## 4.50.0 (2026-07-21) diff --git a/src/__tests__/envConfig.test.ts b/src/__tests__/envConfig.test.ts new file mode 100644 index 00000000000..c9732a60558 --- /dev/null +++ b/src/__tests__/envConfig.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from '@jest/globals' + +import { asEnvConfig } from '../envConfig' + +describe('asEnvConfig EVM api keys', () => { + it('supplies the deprecated etherscanApiKey from evmScanApiKey', () => { + // edge-currency-accountbased reads only `etherscanApiKey` for + // api.etherscan.io, and a keyless Etherscan V2 request is rejected, which + // leaves EVM wallets stuck at 50% sync: + const env = asEnvConfig({ ETHEREUM_INIT: { evmScanApiKey: ['key1'] } }) + expect(env.ETHEREUM_INIT).toMatchObject({ + evmScanApiKey: ['key1'], + etherscanApiKey: ['key1'] + }) + }) + + it('leaves an explicit etherscanApiKey alone', () => { + const env = asEnvConfig({ + ETHEREUM_INIT: { evmScanApiKey: ['key1'], etherscanApiKey: ['legacy'] } + }) + expect(env.ETHEREUM_INIT).toMatchObject({ + evmScanApiKey: ['key1'], + etherscanApiKey: ['legacy'] + }) + }) + + it('adds no key when evmScanApiKey is unconfigured', () => { + const env = asEnvConfig({ ETHEREUM_INIT: {} }) + expect(env.ETHEREUM_INIT).toMatchObject({ + evmScanApiKey: [], + etherscanApiKey: undefined + }) + }) + + it('applies to every EVM plugin init, not just ethereum', () => { + const env = asEnvConfig({ BASE_INIT: { evmScanApiKey: ['key1'] } }) + expect(env.BASE_INIT).toMatchObject({ etherscanApiKey: ['key1'] }) + }) + + it('keeps a disabled plugin init disabled', () => { + const env = asEnvConfig({ ETHEREUM_INIT: false }) + expect(env.ETHEREUM_INIT).toBe(false) + }) +}) diff --git a/src/envConfig.ts b/src/envConfig.ts index 20df307be82..7f688347ca9 100644 --- a/src/envConfig.ts +++ b/src/envConfig.ts @@ -35,11 +35,12 @@ function asCorePluginInit(cleaner: Cleaner): Cleaner { } } -const asEvmApiKeys = asObject({ +const asRawEvmApiKeys = asObject({ alethioApiKey: asOptional(asString, ''), amberdataApiKey: asOptional(asString, ''), blockchairApiKey: asOptional(asString, ''), drpcApiKey: asOptional(asString, ''), + etherscanApiKey: asOptional(asArray(asString)), evmScanApiKey: asOptional(asArray(asString), () => []), gasStationApiKey: asOptional(asString, ''), infuraProjectId: asOptional(asString, ''), @@ -48,6 +49,26 @@ const asEvmApiKeys = asObject({ quiknodeApiKey: asOptional(asString, '') }).withRest +/** + * Up to edge-currency-accountbased 4.86.2, key lookup for `api.etherscan.io` + * reads only the deprecated `etherscanApiKey` option and throws before it + * considers `evmScanApiKey`. Etherscan V2 rejects keyless requests, and its + * adapter is the sole source of transaction history on the EVM networks that + * route through that host, so a build supplying only `evmScanApiKey` leaves + * every one of those wallets stuck at 50% sync. Supply the deprecated name + * from the supported one so env.json only has to carry `evmScanApiKey`. + * + * Remove once the app depends on a release carrying the plugin-side fix. + */ +const asEvmApiKeys: Cleaner> = raw => { + const clean = asRawEvmApiKeys(raw) + if (clean.etherscanApiKey != null && clean.etherscanApiKey.length > 0) { + return clean + } + if (clean.evmScanApiKey.length === 0) return clean + return { ...clean, etherscanApiKey: clean.evmScanApiKey } +} + export const asEnvConfig = asObject({ // API keys: EDGE_API_KEY: asOptional(asString, ''),