Skip to content

Commit 999455b

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
1 parent 46fd5c6 commit 999455b

File tree

7 files changed

+154
-3
lines changed

7 files changed

+154
-3
lines changed

modules/bitgo/src/v2/coinFactory.ts

Lines changed: 16 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,21 @@ 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 IS_ERC20_TOKEN feature
538+
coins
539+
.filter((coin) => coin.features.includes(CoinFeature.IS_ERC20_TOKEN))
540+
.forEach((coin) => {
541+
//TODO: Decide if it should stay here, maybe move to network or coin itself.
542+
const coinNames = {
543+
Mainnet: `${coin.network.name}`,
544+
Testnet: `t${coin.network.name}`,
545+
};
546+
547+
EthLikeErc20Token.createTokenConstructors(coinNames).forEach(({ name, coinConstructor }) => {
548+
coinFactory.register(name, coinConstructor);
549+
});
550+
});
535551
}
536552

537553
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 };
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: 16 additions & 1 deletion
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
@@ -20,6 +21,20 @@ export const register = (coinFamily: string, sdk: BitGoBase): void => {
2021
.forEach((coin) => {
2122
sdk.register(coin.name, EvmCoin.createInstance);
2223
});
23-
//TODO: add token registration after EVM Token Optimisation is implemented
24+
}
25+
if (coins.get(coinFamily).features.includes(CoinFeature.IS_ERC20_TOKEN)) {
26+
coins
27+
.filter((coin) => coin.family === coinFamily && coin.isToken)
28+
.forEach((coin) => {
29+
//TODO: Decide if it should stay here, maybe move to network or coin itself.
30+
const coinNames = {
31+
Mainnet: `${coin.network.name}`,
32+
Testnet: `t${coin.network.name}`,
33+
};
34+
35+
EthLikeErc20Token.createTokenConstructors(coinNames).forEach(({ name, coinConstructor }) => {
36+
sdk.register(name, coinConstructor);
37+
});
38+
});
2439
}
2540
};

modules/statics/src/account.ts

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

434+
export class EthLikeERC20Token extends ContractAddressDefinedToken {
435+
public readonly coinNames: { Mainnet: string; Testnet: string };
436+
437+
constructor(options: Erc20ConstructorOptions & { coinNames: { Mainnet: string; Testnet: string } }) {
438+
super(options);
439+
this.coinNames = options.coinNames;
440+
}
441+
}
442+
434443
/**
435444
* The AVAX C Chain network support tokens
436445
* AVAX C Chain Tokens are ERC20 coins
@@ -797,6 +806,56 @@ export function gasTankAccount(
797806
);
798807
}
799808

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

modules/statics/src/base.ts

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

398+
/**
399+
* This coin supports erc20 tokens
400+
*/
401+
IS_ERC20_TOKEN = 'is-erc20-token',
402+
398403
/**
399404
* This coin is a Cosmos coin and should use shared Cosmos SDK module
400405
*/

0 commit comments

Comments
 (0)