Skip to content

Commit 20a798b

Browse files
feat: enhance coin name retrieval by adding dynamic lookup for chain IDs
Ticket: WIN-8082
1 parent b5f002e commit 20a798b

File tree

3 files changed

+124
-81
lines changed

3 files changed

+124
-81
lines changed

modules/abstract-eth/src/abstractEthLikeNewCoins.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ import { getDerivationPath } from '@bitgo/sdk-lib-mpc';
4848
import { bip32 } from '@bitgo/secp256k1';
4949
import {
5050
BaseCoin as StaticsBaseCoin,
51+
ChainIdNotFoundError,
5152
CoinFeature,
52-
CoinMap,
5353
coins,
5454
EthereumNetwork as EthLikeNetwork,
5555
ethGasConfigs,
@@ -545,7 +545,10 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
545545
* @returns {EthLikeCommon.default}
546546
*/
547547
static getCustomChainCommon(chainId: number): EthLikeCommon.default {
548-
const coinName = CoinMap.coinNameFromChainId(chainId);
548+
const coinName = coins.coinNameFromChainId(chainId);
549+
if (!coinName) {
550+
throw new ChainIdNotFoundError(chainId);
551+
}
549552
const coin = coins.get(coinName);
550553
const ethLikeCommon = getCommon(coin.network as EthLikeNetwork);
551554
return ethLikeCommon;

modules/statics/src/errors.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,10 @@ export class ConflictingCoinFeaturesError extends BitGoStaticsError {
7676
Object.setPrototypeOf(this, ConflictingCoinFeaturesError.prototype);
7777
}
7878
}
79+
80+
export class ChainIdNotFoundError extends BitGoStaticsError {
81+
public constructor(chainId: number) {
82+
super(`chain '${chainId}' not found`);
83+
Object.setPrototypeOf(this, CoinNotDefinedError.prototype);
84+
}
85+
}

modules/statics/src/map.ts

Lines changed: 112 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { BaseCoin } from './base';
22
import { DuplicateCoinDefinitionError, CoinNotDefinedError, DuplicateCoinIdDefinitionError } from './errors';
33
import { ContractAddressDefinedToken, NFTCollectionIdDefinedToken } from './account';
4+
import { EthereumNetwork } from './networks';
45

56
export class CoinMap {
67
private readonly _map = new Map<string, Readonly<BaseCoin>>();
@@ -12,6 +13,8 @@ export class CoinMap {
1213
private readonly _coinByContractAddress = new Map<string, Readonly<BaseCoin>>();
1314
// map of coin by NFT collection ID -> the key is the (t)family:nftCollectionID
1415
private readonly _coinByNftCollectionID = new Map<string, Readonly<BaseCoin>>();
16+
// Lazily initialized cache for chainId to coin name mapping (derived from network definitions)
17+
private _coinByChainId: Map<number, string> | null = null;
1518

1619
private constructor() {
1720
// Do not instantiate
@@ -75,85 +78,115 @@ export class CoinMap {
7578
this.addCoin(coin);
7679
}
7780

78-
static coinNameFromChainId(chainId: number): string {
79-
const ethLikeCoinFromChainId: Record<number, string> = {
80-
1: 'eth',
81-
42: 'teth',
82-
5: 'gteth',
83-
560048: 'hteth',
84-
10001: 'ethw',
85-
80002: 'tpolygon',
86-
137: 'polygon',
87-
56: 'bsc',
88-
97: 'tbsc',
89-
42161: 'arbeth',
90-
421614: 'tarbeth',
91-
10: 'opeth',
92-
11155420: 'topeth',
93-
1116: 'coredao',
94-
1114: 'tcoredao',
95-
248: 'oas',
96-
9372: 'toas',
97-
14: 'flr',
98-
114: 'tflr',
99-
19: 'sgb',
100-
16: 'tsgb',
101-
1111: 'wemix',
102-
1112: 'twemix',
103-
50: 'xdc',
104-
51: 'txdc',
105-
80094: 'bera',
106-
80069: 'tbera',
107-
42220: 'celo',
108-
11142220: 'tcelo',
109-
2222: 'kava',
110-
2221: 'tkava',
111-
43114: 'avax',
112-
43113: 'tavax',
113-
100: 'gno',
114-
130: 'uni',
115-
324: 'zketh',
116-
8453: 'baseeth',
117-
84532: 'tbaseeth',
118-
30143: 'mon',
119-
10143: 'tmon',
120-
480: 'world',
121-
4801: 'tworld',
122-
5031: 'somi',
123-
50312: 'tstt',
124-
1868: 'soneium',
125-
1946: 'tsoneium',
126-
33111: 'tapechain',
127-
33139: 'apechain',
128-
688688: 'tphrs',
129-
102030: 'ctc',
130-
102031: 'tctc',
131-
998: 'thypeevm',
132-
999: 'hypeevm',
133-
16602: 'tog',
134-
16661: 'og',
135-
9746: 'txpl',
136-
9745: 'xpl',
137-
14601: 'tsonic',
138-
146: 'sonic',
139-
1328: 'tseievm',
140-
1329: 'seievm',
141-
1001: 'tkaia',
142-
8217: 'kaia',
143-
1270: 'tirys',
144-
59141: 'tlineaeth',
145-
59144: 'lineaeth',
146-
1315: 'tip',
147-
1514: 'ip',
148-
545: 'tflow',
149-
747: 'flow',
150-
98867: 'tplume',
151-
98866: 'plume',
152-
6342: 'tmegaeth',
153-
295: 'hbarevm',
154-
296: 'thbarevm',
155-
};
156-
return ethLikeCoinFromChainId[chainId];
81+
/**
82+
* Hardcoded mapping for backward compatibility.
83+
*/
84+
private static readonly LEGACY_CHAIN_ID_MAP: Record<number, string> = {
85+
1: 'eth',
86+
42: 'teth',
87+
5: 'gteth',
88+
560048: 'hteth',
89+
10001: 'ethw',
90+
80002: 'tpolygon',
91+
137: 'polygon',
92+
56: 'bsc',
93+
97: 'tbsc',
94+
42161: 'arbeth',
95+
421614: 'tarbeth',
96+
10: 'opeth',
97+
11155420: 'topeth',
98+
1116: 'coredao',
99+
1114: 'tcoredao',
100+
248: 'oas',
101+
9372: 'toas',
102+
14: 'flr',
103+
114: 'tflr',
104+
19: 'sgb',
105+
16: 'tsgb',
106+
1111: 'wemix',
107+
1112: 'twemix',
108+
50: 'xdc',
109+
51: 'txdc',
110+
80094: 'bera',
111+
80069: 'tbera',
112+
42220: 'celo',
113+
11142220: 'tcelo',
114+
2222: 'kava',
115+
2221: 'tkava',
116+
43114: 'avax',
117+
43113: 'tavax',
118+
100: 'gno',
119+
130: 'uni',
120+
324: 'zketh',
121+
8453: 'baseeth',
122+
84532: 'tbaseeth',
123+
30143: 'mon',
124+
10143: 'tmon',
125+
480: 'world',
126+
4801: 'tworld',
127+
5031: 'somi',
128+
50312: 'tstt',
129+
1868: 'soneium',
130+
1946: 'tsoneium',
131+
33111: 'tapechain',
132+
33139: 'apechain',
133+
688688: 'tphrs',
134+
102030: 'ctc',
135+
102031: 'tctc',
136+
998: 'thypeevm',
137+
999: 'hypeevm',
138+
16602: 'tog',
139+
16661: 'og',
140+
9746: 'txpl',
141+
9745: 'xpl',
142+
14601: 'tsonic',
143+
146: 'sonic',
144+
1328: 'tseievm',
145+
1329: 'seievm',
146+
1001: 'tkaia',
147+
8217: 'kaia',
148+
1270: 'tirys',
149+
59141: 'tlineaeth',
150+
59144: 'lineaeth',
151+
1315: 'tip',
152+
1514: 'ip',
153+
545: 'tflow',
154+
747: 'flow',
155+
98867: 'tplume',
156+
98866: 'plume',
157+
6342: 'tmegaeth',
158+
295: 'hbarevm',
159+
296: 'thbarevm',
160+
};
161+
162+
private buildChainIdMap(): Map<number, string> {
163+
const chainIdMap = new Map<number, string>();
164+
this._map.forEach((coin, coinName) => {
165+
// Skip tokens - they share the same chainId as their parent chain
166+
if (coin.isToken) {
167+
return;
168+
}
169+
const network = coin.network;
170+
if ('chainId' in network && typeof (network as EthereumNetwork).chainId === 'number') {
171+
const chainId = (network as EthereumNetwork).chainId;
172+
if (!chainIdMap.has(chainId)) {
173+
chainIdMap.set(chainId, coinName);
174+
}
175+
}
176+
});
177+
return chainIdMap;
178+
}
179+
180+
public coinNameFromChainId(chainId: number): string | undefined {
181+
const coinName = CoinMap.LEGACY_CHAIN_ID_MAP[chainId];
182+
if (coinName) {
183+
return coinName;
184+
}
185+
186+
if (this._coinByChainId === null) {
187+
this._coinByChainId = this.buildChainIdMap();
188+
}
189+
return this._coinByChainId.get(chainId);
157190
}
158191

159192
/**

0 commit comments

Comments
 (0)