Skip to content

Commit 4794167

Browse files
Merge pull request #7619 from BitGo/WIN-7914-1
fix: getTokenConstructor to work for testnet tokens + test cases
2 parents 17217ee + e4852bc commit 4794167

File tree

6 files changed

+105
-8
lines changed

6 files changed

+105
-8
lines changed

modules/bitgo/src/v2/coinFactory.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -904,31 +904,44 @@ export function getCoinConstructor(coinName: string): CoinConstructor | undefine
904904
}
905905
}
906906

907-
export const buildEthLikeChainToTestnetMap = (): Record<string, string> => {
908-
const map: Record<string, string> = {};
907+
export const buildEthLikeChainToTestnetMap = (): {
908+
mainnetToTestnetMap: Record<string, string>;
909+
testnetToMainnetMap: Record<string, string>;
910+
} => {
911+
const testnetToMainnetMap: Record<string, string> = {};
912+
const mainnetToTestnetMap: Record<string, string> = {};
909913

910914
const enabledEvmCoins = ['ip'];
911915

912916
// TODO: remove ip coin here and remove other evm coins from switch block, once changes are tested (Ticket: https://bitgoinc.atlassian.net/browse/WIN-7835)
913917
coins.forEach((coin) => {
914918
if (coin.network.type === NetworkType.TESTNET && !coin.isToken && enabledEvmCoins.includes(coin.family)) {
915919
if (coins.get(coin.family)?.features.includes(CoinFeature.SUPPORTS_ERC20)) {
916-
map[coin.family] = `${coin.name}`;
920+
mainnetToTestnetMap[coin.family] = `${coin.name}`;
921+
testnetToMainnetMap[coin.name] = `${coin.family}`;
917922
}
918923
}
919924
});
920925

921-
return map;
926+
return { mainnetToTestnetMap, testnetToMainnetMap };
922927
};
923928

924929
// TODO: add IP token here and test changes (Ticket: https://bitgoinc.atlassian.net/browse/WIN-7835)
925-
const ethLikeChainToTestnetMap: Record<string, string> = buildEthLikeChainToTestnetMap();
930+
const { mainnetToTestnetMap, testnetToMainnetMap } = buildEthLikeChainToTestnetMap();
926931

927932
export function getTokenConstructor(tokenConfig: TokenConfig): CoinConstructor | undefined {
928-
if (tokenConfig.coin in ethLikeChainToTestnetMap) {
933+
const testnetCoin = mainnetToTestnetMap[tokenConfig.coin];
934+
if (testnetCoin) {
929935
return EthLikeErc20Token.createTokenConstructor(tokenConfig as EthLikeTokenConfig, {
930936
Mainnet: tokenConfig.coin,
931-
Testnet: ethLikeChainToTestnetMap[tokenConfig.coin],
937+
Testnet: testnetCoin,
938+
});
939+
}
940+
const mainnetCoin = testnetToMainnetMap[tokenConfig.coin];
941+
if (mainnetCoin) {
942+
return EthLikeErc20Token.createTokenConstructor(tokenConfig as EthLikeTokenConfig, {
943+
Mainnet: mainnetCoin,
944+
Testnet: tokenConfig.coin,
932945
});
933946
}
934947
switch (tokenConfig.coin) {

modules/bitgo/test/v2/resources/amsTokenConfig.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,26 @@ export const reducedAmsTokenConfig = {
2121
contractAddress: '0x89a959b9184b4f8c8633646d5dfd049d2ebc983a',
2222
},
2323
],
24+
'tip:faketoken': [
25+
{
26+
id: 'a1b2c3d4-e5f6-4789-8abc-def123456789',
27+
fullName: 'Story Testnet Faketoken',
28+
name: 'tip:faketoken',
29+
prefix: '',
30+
suffix: 'TIP:FAKETOKEN',
31+
baseUnit: 'wei',
32+
kind: 'crypto',
33+
family: 'ip',
34+
isToken: true,
35+
additionalFeatures: [],
36+
excludedFeatures: [],
37+
decimalPlaces: 18,
38+
asset: 'tip:faketoken',
39+
network: {
40+
name: 'BaseChainTestnet',
41+
},
42+
primaryKeyCurve: 'secp256k1',
43+
contractAddress: '0x1234567890123456789012345678901234567890',
44+
},
45+
],
2446
};

modules/bitgo/test/v2/unit/ams/ams.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,36 @@ describe('Asset metadata service', () => {
100100
const coin = bitgo.coin(tokenName);
101101
should.exist(coin);
102102
});
103+
104+
it('should register a EVM coin token from statics library if available', async () => {
105+
const bitgo = TestBitGo.decorate(BitGo, { env: 'mock', microservicesUri, useAms: true } as BitGoOptions);
106+
bitgo.initializeTestVars();
107+
await bitgo.registerToken('tip:usdc');
108+
const coin = bitgo.coin('tip:usdc');
109+
should.exist(coin);
110+
});
111+
112+
it('should register an EVM coin token from AMS', async () => {
113+
const bitgo = TestBitGo.decorate(BitGo, { env: 'mock', microservicesUri, useAms: true } as BitGoOptions);
114+
bitgo.initializeTestVars();
115+
116+
const tokenName = 'tip:faketoken';
117+
118+
// Setup nocks for AMS API call
119+
nock(microservicesUri).get(`/api/v1/assets/name/${tokenName}`).reply(200, reducedAmsTokenConfig[tokenName][0]);
120+
121+
await bitgo.registerToken(tokenName);
122+
const coin = bitgo.coin(tokenName);
123+
should.exist(coin);
124+
coin.type.should.equal(tokenName);
125+
const staticsCoin = coin.getConfig();
126+
staticsCoin.name.should.equal('tip');
127+
staticsCoin.decimalPlaces.should.equal(18);
128+
// For EVM tokens, contractAddress is available on the statics coin
129+
if ('contractAddress' in staticsCoin && staticsCoin.contractAddress) {
130+
(staticsCoin.contractAddress as string).should.equal('0x1234567890123456789012345678901234567890');
131+
}
132+
staticsCoin.family.should.equal('ip');
133+
});
103134
});
104135
});

modules/statics/src/coins.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,17 @@ export function createToken(token: AmsTokenConfig): Readonly<BaseCoin> | undefin
137137
];
138138

139139
switch (family) {
140+
case erc20ChainToNameMap[family]:
141+
return initializer(
142+
...commonArgs.slice(0, 4), // id, name, fullName, decimalPlaces
143+
token.contractAddress || token.tokenAddress, // contractAddress
144+
token.asset,
145+
token.network,
146+
token.features,
147+
token.prefix,
148+
token.suffix,
149+
token.primaryKeyCurve
150+
);
140151
case 'arbeth':
141152
case 'avaxc':
142153
case 'baseeth':
@@ -152,7 +163,6 @@ export function createToken(token: AmsTokenConfig): Readonly<BaseCoin> | undefin
152163
case 'opeth':
153164
case 'polygon':
154165
case 'trx':
155-
case erc20ChainToNameMap[family]:
156166
return initializer(
157167
...commonArgs.slice(0, 4), // id, name, fullName, decimalPlaces
158168
token.contractAddress || token.tokenAddress, // contractAddress

modules/statics/src/tokenConfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1385,6 +1385,8 @@ export function getFormattedTokenConfigForCoin(coin: Readonly<BaseCoin>): TokenC
13851385
return getJettonTokenConfig(coin);
13861386
} else if (coin instanceof FlrERC20Token) {
13871387
return getFlrTokenConfig(coin);
1388+
} else if (coin instanceof EthLikeERC20Token) {
1389+
return getEthLikeTokenConfig(coin);
13881390
}
13891391
return undefined;
13901392
}

modules/statics/test/unit/coins.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ import {
3535
reducedAmsTokenConfig,
3636
reducedTokenConfigForAllChains,
3737
} from './resources/amsTokenConfig';
38+
import { EthLikeErc20Token } from '../../../sdk-coin-evm/src';
39+
import { allCoinsAndTokens } from '../../src/allCoinsAndTokens';
3840

3941
interface DuplicateCoinObject {
4042
name: string;
@@ -1280,4 +1282,21 @@ describe('create token map using config details', () => {
12801282
}
12811283
}
12821284
});
1285+
1286+
it('should create tokens for all EVM coins using createToken', () => {
1287+
const evmCoinTokens = allCoinsAndTokens
1288+
.filter((coin) => coin.isToken && coins.get(coin.family)?.features.includes(CoinFeature.SUPPORTS_ERC20))
1289+
.map((coin) => coin);
1290+
1291+
for (const coin of evmCoinTokens) {
1292+
const token = createToken(coin);
1293+
token?.should.not.be.undefined();
1294+
token?.name.should.eql(coin.name);
1295+
token?.family.should.eql(coin.family);
1296+
token?.decimalPlaces.should.eql(coin.decimalPlaces);
1297+
if (token instanceof EthLikeErc20Token) {
1298+
(token as EthLikeErc20Token).tokenContractAddress.should.eql(coin?.contractAddress);
1299+
}
1300+
}
1301+
});
12831302
});

0 commit comments

Comments
 (0)