Skip to content

Commit c89d9c0

Browse files
Merge pull request #710 from BitGo/CECHO-962
feat: remove dependency for code changes for evm like coins and onboard tempo tokens
2 parents 04686fd + 2da091d commit c89d9c0

8 files changed

Lines changed: 1028 additions & 661 deletions

File tree

docs/auto-generate-allCoinMetas.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Plan: Auto-generate `allCoinMetas` entries from SDK for EVM recovery coins
2+
3+
## Context
4+
5+
Currently, every time a new EVM chain is added to the `@bitgo/statics` SDK with `EVM_UNSIGNED_SWEEP_RECOVERY` or `EVM_NON_BITGO_RECOVERY` features, a developer must also manually add a corresponding entry in `allCoinMetas` inside `src/helpers/config.ts`. Without that entry, `assertMetadata()` returns false and the coin is silently excluded from recovery flows.
6+
7+
The goal is to auto-generate a sensible default `CoinMetadata` from the SDK coin object for any EVM recovery coin not already explicitly defined in `allCoinMetas`. After this change, an SDK bump alone is sufficient to surface new EVM recovery coins — no changes to this repo are needed. Manual entries in `allCoinMetas` remain supported as overrides.
8+
9+
---
10+
11+
## Critical File
12+
13+
- `src/helpers/config.ts` — only file to modify
14+
15+
## Key insight: deriving `ApiKeyProvider` from the SDK
16+
17+
Two-step lookup for `ApiKeyProvider`:
18+
19+
**Step 1**`Environments[env].evm?.[coinName]?.baseUrl` from `@bitgo/sdk-core`
20+
Covers all coins using `SHARED_EVM_SDK` (the standard path for new EVM chains). Example:
21+
- `inketh` (prod): `https://explorer.inkonchain.com/api``explorer.inkonchain.com`
22+
- `tinketh` (test): `https://explorer-sepolia.inkonchain.com/api``explorer-sepolia.inkonchain.com`
23+
24+
**Step 2** (fallback) — `coin.network.explorerUrl` from `@bitgo/statics` (already imported)
25+
31 testnet coins (`tunieth`, `thoodeth`, `thppeth`, etc.) have `EVM_NON_BITGO_RECOVERY` but are
26+
NOT in the testnet `evm` map — they all have `explorerUrl` set. This fallback covers them.
27+
Example: `thoodeth``https://explorer.testnet.chain.robinhood.com/tx/``explorer.testnet.chain.robinhood.com`
28+
29+
`@bitgo/sdk-core` is a safe import in `src/` — it's already a transitive dependency via
30+
`@bitgo/sdk-coin-ada` (imported in `src/utils/types.ts`).
31+
32+
---
33+
34+
## Implementation
35+
36+
### Step 1 — Add `generateEvmCoinMeta` helper (after the `allCoinMetas` closing brace, before `assertMetadata`)
37+
38+
Add import at the top of config.ts:
39+
```typescript
40+
import { Environments } from '@bitgo/sdk-core';
41+
```
42+
43+
```typescript
44+
function generateEvmCoinMeta(coin: {
45+
name: string;
46+
fullName: string;
47+
network: { type: NetworkType; explorerUrl?: string };
48+
}): CoinMetadata {
49+
const isTestnet = coin.network.type === NetworkType.TESTNET;
50+
// Testnet coins typically start with 't'; use the mainnet name for the icon
51+
const iconName = isTestnet && coin.name.startsWith('t') ? coin.name.slice(1) : coin.name;
52+
// Step 1: check Environments evm map (SHARED_EVM_SDK coins)
53+
const env = isTestnet ? 'test' : 'prod';
54+
const evmBaseUrl = Environments[env].evm?.[coin.name]?.baseUrl;
55+
// Step 2: fall back to statics explorerUrl (covers testnet coins missing from evm map)
56+
const fallbackUrl = coin.network.explorerUrl;
57+
const rawUrl = evmBaseUrl ?? fallbackUrl;
58+
const apiKeyProvider = rawUrl ? new URL(rawUrl).hostname : undefined;
59+
return {
60+
Title: coin.name.toUpperCase(),
61+
Description: coin.fullName,
62+
value: coin.name,
63+
Icon: iconName,
64+
...(apiKeyProvider && { ApiKeyProvider: apiKeyProvider }),
65+
defaultGasLimit: '500,000',
66+
defaultGasLimitNum: 500000,
67+
defaultMaxFeePerGas: 20,
68+
defaultMaxPriorityFeePerGas: 10,
69+
};
70+
}
71+
```
72+
73+
### Step 2 — Modify the `coins.forEach` loop (lines 1567-1588)
74+
75+
Replace the existing loop with a version that auto-generates metadata instead of gating on `assertMetadata`:
76+
77+
```typescript
78+
coins.forEach(coin => {
79+
if (coin.isToken) return;
80+
81+
const name = coin.name;
82+
const isTestnet = coin.network.type === NetworkType.TESTNET;
83+
const hasUnsignedSweep = coin.features.includes(CoinFeature.EVM_UNSIGNED_SWEEP_RECOVERY);
84+
const hasNonBitgo = coin.features.includes(CoinFeature.EVM_NON_BITGO_RECOVERY);
85+
86+
if (!hasUnsignedSweep && !hasNonBitgo) return;
87+
88+
// Auto-generate metadata for any coin not already explicitly defined
89+
if (!Object.prototype.hasOwnProperty.call(allCoinMetas, name)) {
90+
allCoinMetas[name] = generateEvmCoinMeta(coin);
91+
}
92+
93+
if (hasUnsignedSweep) {
94+
if (isTestnet) testEvmUnsignedSweepCoins.push(name);
95+
else prodEvmUnsignedSweepCoins.push(name);
96+
}
97+
98+
if (hasNonBitgo) {
99+
if (isTestnet) testEvmNonBitgoRecoveryCoins.push(name);
100+
else prodEvmNonBitgoRecoveryCoins.push(name);
101+
}
102+
});
103+
```
104+
105+
### Step 3 — Remove `assertMetadata` function (now unused)
106+
107+
Delete lines 1552–1558. The auto-generation in step 2 replaces its gating role.
108+
109+
---
110+
111+
## Default values rationale
112+
113+
| Field | Default | Reason |
114+
|---|---|---|
115+
| `Icon` | strip `t` prefix for testnets | Matches existing pattern (`tinketh``inketh`) |
116+
| `ApiKeyProvider` | `Environments[env].evm?.[coin].baseUrl` then `coin.network.explorerUrl` | evm map covers SHARED_EVM_SDK coins; explorerUrl fallback covers 31 testnet coins missing from evm map; both are undefined only for `phrs` (which has a TODO in the SDK) |
117+
| `defaultGasLimit` | `'500,000'` / `500000` | Conservative safe default |
118+
| `defaultMaxFeePerGas` | `20` | Matches most explicit entries |
119+
| `defaultMaxPriorityFeePerGas` | `10` | Matches most explicit entries |
120+
121+
Explicit entries in `allCoinMetas` are checked first via `hasOwnProperty`, so any custom override (different gas limits, custom `ApiKeyProvider`, `isTssSupported`, etc.) is always respected.
122+
123+
---
124+
125+
## Verification
126+
127+
### Pre-verification: temporarily add `tempo` to `node_modules/@bitgo/sdk-core` evm map
128+
129+
`tempo` already has an explicit `allCoinMetas` entry (so it won't be auto-generated normally).
130+
To test the auto-generation path end-to-end, temporarily:
131+
1. Add `tempo: { baseUrl: 'https://dummy.alchemy.com/api' }` to the `evm` prod block in
132+
`node_modules/@bitgo/sdk-core/dist/src/bitgo/environments.js`
133+
2. Temporarily **delete** the `tempo` entry from `allCoinMetas` in `config.ts`
134+
3. Run verification script (below) — confirm `tempo` gets auto-generated with the correct `ApiKeyProvider: 'dummy.alchemy.com'`
135+
4. Restore both changes
136+
137+
### After making changes, run inline Node.js snippets to verify:
138+
139+
**1. TypeScript type-check**
140+
```bash
141+
npx tsc --noEmit
142+
```
143+
144+
**2. UI smoke-test**
145+
- Start dev server, switch to testnet, open Unsigned Sweep and Non-BitGo Recovery
146+
- Confirm existing EVM coins appear with correct gas defaults
147+
- Confirm new auto-generated coins (e.g. `tunieth`, `thoodeth`) now appear in coin dropdown

electron/main/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ import { registerAll as EVMCoinRegisterAll } from '@bitgo/sdk-coin-evm';
9393
import { CoinFeature, coins } from '@bitgo/statics';
9494
import { Xtz, Txtz } from '@bitgo/sdk-coin-xtz';
9595
import { Ton, Tton, JettonToken } from '@bitgo/sdk-coin-ton';
96+
import { Tempo, Tip20Token } from '@bitgo/sdk-coin-tempo';
9697

9798
const bip32 = BIP32Factory(ecc);
9899

@@ -231,6 +232,8 @@ sdk.register('xtz', Xtz.createInstance);
231232
sdk.register('txtz', Txtz.createInstance);
232233
sdk.register('ton', Ton.createInstance);
233234
sdk.register('tton', Tton.createInstance);
235+
sdk.register('tempo', Tempo.createInstance);
236+
sdk.register('ttempo', Tempo.createInstance);
234237
EVMCoinRegisterAll(sdk);
235238

236239
Erc20Token.createTokenConstructors().forEach(({ name, coinConstructor }) => {
@@ -272,6 +275,9 @@ VetToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
272275
JettonToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
273276
sdk.register(name, coinConstructor);
274277
});
278+
Tip20Token.createTokenConstructors().forEach(({ name, coinConstructor }) => {
279+
sdk.register(name, coinConstructor);
280+
});
275281

276282
function handleSdkError(e: unknown): string {
277283
if (typeof e === 'string' && e !== null) {

0 commit comments

Comments
 (0)