Skip to content

Commit 4287e7a

Browse files
committed
feat: automate ERC20 automation
WIN-6598 TICKET: WIN-6598 chore: add EthLikeErc20 to index.ts for export TICKET: WIN-6598 chore: remove tokenConfig changes and rename feature flag TICKET: WIN-6598 chore: create CoinNames on the go instead of generic TICKET: WIN-6598 chore: fetch coinNames from coin name at runtime TICKET: WIN-6598 feat: one loop for coin and token registration TICKET: WIN-6598 chore: add the browser test for the new token class TICKET: WIN-6598
1 parent ed0b391 commit 4287e7a

File tree

8 files changed

+154
-9
lines changed

8 files changed

+154
-9
lines changed

modules/bitgo/src/v2/coinFactory.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Near, TNear, Nep141Token } from '@bitgo/sdk-coin-near';
99
import { SolToken } from '@bitgo/sdk-coin-sol';
1010
import { TrxToken } from '@bitgo/sdk-coin-trx';
1111
import { CoinFactory, CoinConstructor } from '@bitgo/sdk-core';
12+
import { EthLikeErc20Token } from '@bitgo/sdk-coin-evm';
1213
import {
1314
CoinMap,
1415
coins,
@@ -532,6 +533,20 @@ export function registerCoinConstructors(coinFactory: CoinFactory, coinMap: Coin
532533
VetToken.createTokenConstructors().forEach(({ name, coinConstructor }) =>
533534
coinFactory.register(name, coinConstructor)
534535
);
536+
537+
// Generic ERC20 token registration for coins with SUPPORTS_ERC20 feature
538+
coins
539+
.filter((coin) => coin.features.includes(CoinFeature.SUPPORTS_ERC20) && !coin.isToken)
540+
.forEach((coin) => {
541+
const coinNames = {
542+
Mainnet: `${coin.name}`,
543+
Testnet: `t${coin.name}`,
544+
};
545+
546+
EthLikeErc20Token.createTokenConstructors(coinNames).forEach(({ name, coinConstructor }) => {
547+
coinFactory.register(name, coinConstructor);
548+
});
549+
});
535550
}
536551

537552
export function getCoinConstructor(coinName: string): CoinConstructor | undefined {

modules/bitgo/src/v2/coins/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import { Dot, Tdot } from '@bitgo/sdk-coin-dot';
3131
import { Eos, EosToken, Teos } from '@bitgo/sdk-coin-eos';
3232
import { Etc, Tetc } from '@bitgo/sdk-coin-etc';
3333
import { Erc20Token, Erc721Token, Eth, Gteth, Hteth, Teth } from '@bitgo/sdk-coin-eth';
34-
import { EvmCoin } from '@bitgo/sdk-coin-evm';
34+
import { EvmCoin, EthLikeErc20Token } from '@bitgo/sdk-coin-evm';
3535
import { Flr, Tflr } from '@bitgo/sdk-coin-flr';
3636
import { Ethw } from '@bitgo/sdk-coin-ethw';
3737
import { EthLikeCoin, TethLikeCoin } from '@bitgo/sdk-coin-ethlike';
@@ -108,7 +108,7 @@ export { Erc20Token, Erc721Token, Eth, Gteth, Hteth, Teth };
108108
export { Ethw };
109109
export { EthLikeCoin, TethLikeCoin };
110110
export { Etc, Tetc };
111-
export { EvmCoin };
111+
export { EvmCoin, EthLikeErc20Token };
112112
export { Flr, Tflr };
113113
export { Hash, Thash };
114114
export { Hbar, Thbar };

modules/bitgo/test/browser/browser.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ describe('Coins', () => {
5252
CosmosToken: 1,
5353
CosmosSharedCoin: 1,
5454
VetToken: 1,
55+
EthLikeErc20Token: 1,
5556
};
5657
Object.keys(BitGoJS.Coin)
5758
.filter((coinName) => !excludedKeys[coinName])
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* @prettier
3+
*/
4+
import { coins, EthLikeTokenConfig } from '@bitgo/statics';
5+
import { BitGoBase, CoinConstructor, common, MPCAlgorithm, NamedCoinConstructor } from '@bitgo/sdk-core';
6+
import { CoinNames, EthLikeToken, recoveryBlockchainExplorerQuery } from '@bitgo/abstract-eth';
7+
import { TransactionBuilder } from './lib';
8+
import assert from 'assert';
9+
10+
export class EthLikeErc20Token extends EthLikeToken {
11+
public readonly tokenConfig: EthLikeTokenConfig;
12+
private readonly coinNames: CoinNames;
13+
14+
constructor(bitgo: BitGoBase, tokenConfig: EthLikeTokenConfig, coinNames: CoinNames) {
15+
super(bitgo, tokenConfig, coinNames);
16+
this.coinNames = coinNames;
17+
}
18+
19+
static createTokenConstructor(config: EthLikeTokenConfig, coinNames: CoinNames): CoinConstructor {
20+
return (bitgo: BitGoBase) => new this(bitgo, config, coinNames);
21+
}
22+
23+
static createTokenConstructors(coinNames: CoinNames): NamedCoinConstructor[] {
24+
return super.createTokenConstructors(coinNames);
25+
}
26+
27+
protected getTransactionBuilder(): TransactionBuilder {
28+
return new TransactionBuilder(coins.get(this.getBaseChain()));
29+
}
30+
31+
getMPCAlgorithm(): MPCAlgorithm {
32+
return 'ecdsa';
33+
}
34+
35+
supportsTss(): boolean {
36+
return true;
37+
}
38+
39+
async recoveryBlockchainExplorerQuery(query: Record<string, string>): Promise<Record<string, unknown>> {
40+
const family = this.getFamily();
41+
const evmConfig = common.Environments[this.bitgo.getEnv()].evm;
42+
assert(
43+
evmConfig && this.getFamily() in evmConfig,
44+
`env config is missing for ${this.getFamily()} in ${this.bitgo.getEnv()}`
45+
);
46+
const explorerUrl = evmConfig[family].baseUrl;
47+
const apiToken = evmConfig[family].apiToken;
48+
return await recoveryBlockchainExplorerQuery(query, explorerUrl as string, apiToken);
49+
}
50+
51+
//TODO: implement a way to return the coin family name or coin name instead of standard ERC20 Token.
52+
getFullName(): string {
53+
return 'ERC20 Token';
54+
}
55+
}

modules/sdk-coin-evm/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './evmCoin';
22
export * from './lib';
33
export * from './register';
4+
export * from './ethLikeErc20Token';

modules/sdk-coin-evm/src/register.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { BitGoBase } from '@bitgo/sdk-core';
22
import { CoinFeature, coins, NetworkType } from '@bitgo/statics';
33
import { EvmCoin } from './evmCoin';
4+
import { EthLikeErc20Token } from './ethLikeErc20Token';
45

56
export const registerAll = (sdk: BitGoBase): void => {
67
coins
@@ -14,12 +15,25 @@ export const registerAll = (sdk: BitGoBase): void => {
1415
};
1516

1617
export const register = (coinFamily: string, sdk: BitGoBase): void => {
17-
if (coins.get(coinFamily).features.includes(CoinFeature.SHARED_EVM_SDK)) {
18-
coins
19-
.filter((coin) => coin.family === coinFamily && !coin.isToken)
20-
.forEach((coin) => {
18+
const coinFeatures = coins.get(coinFamily).features;
19+
coins
20+
.filter((coin) => coin.family === coinFamily && !coin.isToken)
21+
.forEach((coin) => {
22+
// Handle SHARED_EVM_SDK registration
23+
if (coinFeatures.includes(CoinFeature.SHARED_EVM_SDK)) {
2124
sdk.register(coin.name, EvmCoin.createInstance);
22-
});
23-
//TODO: add token registration after EVM Token Optimisation is implemented
24-
}
25+
}
26+
27+
// Handle SUPPORTS_ERC20 registration
28+
if (coinFeatures.includes(CoinFeature.SUPPORTS_ERC20)) {
29+
const coinNames = {
30+
Mainnet: `${coin.name}`,
31+
Testnet: `${coin.name}`,
32+
};
33+
34+
EthLikeErc20Token.createTokenConstructors(coinNames).forEach(({ name, coinConstructor }) => {
35+
sdk.register(name, coinConstructor);
36+
});
37+
}
38+
});
2539
};

modules/statics/src/account.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,12 @@ export class AdaCoin extends AccountCoinToken {
431431
}
432432
}
433433

434+
export class EthLikeERC20Token extends ContractAddressDefinedToken {
435+
constructor(options: Erc20ConstructorOptions) {
436+
super(options);
437+
}
438+
}
439+
434440
/**
435441
* The AVAX C Chain network support tokens
436442
* AVAX C Chain Tokens are ERC20 coins
@@ -797,6 +803,54 @@ export function gasTankAccount(
797803
);
798804
}
799805

806+
/**
807+
* Factory function for ethLikeErc20 token instances.
808+
*
809+
* @param id uuid v4
810+
* @param name unique identifier of the token
811+
* @param fullName Complete human-readable name of the token
812+
* @param decimalPlaces Number of decimal places this token supports
813+
* @param contractAddress Contract address of this token
814+
* @param asset Asset which this coin represents
815+
* @param network Optional token network
816+
* @param coinNames The parent coin names for mainnet and testnet
817+
* @param features Features of this coin
818+
* @param prefix Optional token prefix
819+
* @param suffix Optional token suffix
820+
* @param primaryKeyCurve The elliptic curve for this chain/token
821+
*/
822+
export function erc20Token(
823+
id: string,
824+
name: string,
825+
fullName: string,
826+
decimalPlaces: number,
827+
contractAddress: string,
828+
asset: UnderlyingAsset,
829+
network: AccountNetwork,
830+
features: CoinFeature[] = [...AccountCoin.DEFAULT_FEATURES, CoinFeature.EIP1559],
831+
prefix = '',
832+
suffix: string = name.toUpperCase(),
833+
primaryKeyCurve: KeyCurve = KeyCurve.Secp256k1
834+
) {
835+
return Object.freeze(
836+
new EthLikeERC20Token({
837+
id,
838+
name,
839+
fullName,
840+
network,
841+
contractAddress,
842+
decimalPlaces,
843+
asset,
844+
features,
845+
prefix,
846+
suffix,
847+
primaryKeyCurve,
848+
isToken: true,
849+
baseUnit: BaseUnit.ETH,
850+
})
851+
);
852+
}
853+
800854
/**
801855
* Factory function for erc20 token instances.
802856
*

modules/statics/src/base.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,11 @@ export enum CoinFeature {
397397
*/
398398
SHARED_EVM_SDK = 'shared-evm-sdk',
399399

400+
/**
401+
* This coin supports erc20 tokens
402+
*/
403+
SUPPORTS_ERC20 = 'supports-erc20-token',
404+
400405
/**
401406
* This coin is a Cosmos coin and should use shared Cosmos SDK module
402407
*/

0 commit comments

Comments
 (0)