Skip to content

Commit 44d2af8

Browse files
committed
feat(sdk-coin-bsc): support tokens for bsc
Ticket: BG-55766
1 parent 549fbbc commit 44d2af8

File tree

10 files changed

+260
-2
lines changed

10 files changed

+260
-2
lines changed

modules/bitgo/src/v2/coinFactory.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
AvaxP,
1414
Bch,
1515
Bsc,
16+
BscToken,
1617
Bsv,
1718
Btc,
1819
Btg,
@@ -181,6 +182,10 @@ function registerCoinConstructors(globalCoinFactory: CoinFactory): void {
181182
globalCoinFactory.register(name, coinConstructor);
182183
});
183184

185+
BscToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
186+
globalCoinFactory.register(name, coinConstructor);
187+
});
188+
184189
EosToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
185190
globalCoinFactory.register(name, coinConstructor);
186191
});

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import { AvaxP, TavaxP } from '@bitgo/sdk-coin-avaxp';
1212
export { AvaxP, TavaxP };
1313
import { Bch, Tbch } from '@bitgo/sdk-coin-bch';
1414
export { Bch, Tbch };
15-
import { Bsc, Tbsc } from '@bitgo/sdk-coin-bsc';
16-
export { Bsc, Tbsc };
15+
import { Bsc, Tbsc, BscToken } from '@bitgo/sdk-coin-bsc';
16+
export { Bsc, Tbsc, BscToken };
1717
import { Bsv, Tbsv } from '@bitgo/sdk-coin-bsv';
1818
export { Bsv, Tbsv };
1919
import { Btc, Tbtc } from '@bitgo/sdk-coin-btc';

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ describe('Coins', () => {
2020
'AlgoToken': 1,
2121
'AvaxCToken': 1,
2222
'PolygonToken': 1,
23+
'BscToken': 1,
2324
};
2425
Object.keys(BitGoJS.Coin)
2526
.filter((coinName) => !excludedKeys[coinName])
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/**
2+
* @prettier
3+
*/
4+
5+
import { Bsc } from './bsc';
6+
import { TransactionPrebuild } from '@bitgo/sdk-coin-eth';
7+
import { EthLikeTokenConfig, tokens, coins } from '@bitgo/statics';
8+
import { BitGoBase, CoinConstructor, NamedCoinConstructor } from '@bitgo/sdk-core';
9+
10+
export { EthLikeTokenConfig };
11+
12+
export class BscToken extends Bsc {
13+
public readonly tokenConfig: EthLikeTokenConfig;
14+
constructor(bitgo: BitGoBase, tokenConfig: EthLikeTokenConfig) {
15+
const staticsCoin = tokenConfig.network === 'Mainnet' ? coins.get('bsc') : coins.get('tbsc');
16+
super(bitgo, staticsCoin);
17+
this.tokenConfig = tokenConfig;
18+
}
19+
20+
static createTokenConstructor(config: EthLikeTokenConfig): CoinConstructor {
21+
return (bitgo: BitGoBase) => new BscToken(bitgo, config);
22+
}
23+
24+
static createTokenConstructors(): NamedCoinConstructor[] {
25+
const tokensCtors: NamedCoinConstructor[] = [];
26+
for (const token of [...tokens.bitcoin.bsc.tokens, ...tokens.testnet.bsc.tokens]) {
27+
const tokenConstructor = BscToken.createTokenConstructor(token);
28+
tokensCtors.push({ name: token.type, coinConstructor: tokenConstructor });
29+
tokensCtors.push({ name: token.tokenContractAddress, coinConstructor: tokenConstructor });
30+
}
31+
return tokensCtors;
32+
}
33+
34+
get type() {
35+
return this.tokenConfig.type;
36+
}
37+
38+
get name() {
39+
return this.tokenConfig.name;
40+
}
41+
42+
get coin() {
43+
return this.tokenConfig.coin;
44+
}
45+
46+
get network() {
47+
return this.tokenConfig.network;
48+
}
49+
50+
get tokenContractAddress() {
51+
return this.tokenConfig.tokenContractAddress;
52+
}
53+
54+
get decimalPlaces() {
55+
return this.tokenConfig.decimalPlaces;
56+
}
57+
58+
getChain(): string {
59+
return this.tokenConfig.type;
60+
}
61+
62+
getBaseChain() {
63+
return this.coin;
64+
}
65+
66+
getFullName(): string {
67+
return 'Bsc Token';
68+
}
69+
70+
getBaseFactor() {
71+
return Math.pow(10, this.tokenConfig.decimalPlaces);
72+
}
73+
74+
/**
75+
* Flag for sending value of 0
76+
* @returns {boolean} True if okay to send 0 value, false otherwise
77+
*/
78+
valuelessTransferAllowed() {
79+
return false;
80+
}
81+
82+
/**
83+
* Flag for sending data along with transactions
84+
* @returns {boolean} True if okay to send tx data (ETH), false otherwise
85+
*/
86+
transactionDataAllowed() {
87+
return false;
88+
}
89+
90+
isToken(): boolean {
91+
return true;
92+
}
93+
94+
verifyCoin(txPrebuild: TransactionPrebuild): boolean {
95+
return txPrebuild.coin === this.tokenConfig.coin && txPrebuild.token === this.tokenConfig.type;
96+
}
97+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './lib';
22
export * from './bsc';
33
export * from './tbsc';
4+
export * from './bscToken';
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import 'should';
2+
3+
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
4+
import { BscToken } from '../../src';
5+
import { BitGoAPI } from '@bitgo/sdk-api';
6+
7+
describe('Bsc Token:', function () {
8+
let bitgo: TestBitGoAPI;
9+
let bscTokenCoin;
10+
const tokenName = 'tbsc:busd';
11+
12+
before(function () {
13+
bitgo = TestBitGo.decorate(BitGoAPI, { env: 'test' });
14+
BscToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
15+
bitgo.safeRegister(name, coinConstructor);
16+
});
17+
bitgo.initializeTestVars();
18+
bscTokenCoin = bitgo.coin(tokenName);
19+
});
20+
21+
it('should return constants', function () {
22+
bscTokenCoin.getChain().should.equal('tbsc:busd');
23+
bscTokenCoin.getBaseChain().should.equal('tbsc');
24+
bscTokenCoin.getFullName().should.equal('Bsc Token');
25+
bscTokenCoin.getBaseFactor().should.equal(1e18);
26+
bscTokenCoin.type.should.equal(tokenName);
27+
bscTokenCoin.name.should.equal('Test Binance USD Token');
28+
bscTokenCoin.coin.should.equal('tbsc');
29+
bscTokenCoin.network.should.equal('Testnet');
30+
bscTokenCoin.decimalPlaces.should.equal(18);
31+
});
32+
});

modules/statics/src/account.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,11 @@ export class Erc20CompatibleAccountCoin extends ContractAddressDefinedToken {
181181
*/
182182
export class CeloCoin extends ContractAddressDefinedToken {}
183183

184+
/**
185+
* The BSC blockchain supports tokens of the ERC20 standard similar to ETH ERC20 tokens.
186+
*/
187+
export class BscCoin extends ContractAddressDefinedToken {}
188+
184189
/**
185190
* The Stellar network supports tokens (non-native assets)
186191
* XLM is also known as the native asset.
@@ -699,6 +704,76 @@ export function tceloToken(
699704
return celoToken(name, fullName, decimalPlaces, contractAddress, asset, features, prefix, suffix, network);
700705
}
701706

707+
/**
708+
* Factory function for celo token instances.
709+
*
710+
* @param name unique identifier of the token
711+
* @param fullName Complete human-readable name of the token
712+
* @param decimalPlaces Number of decimal places this token supports (divisibility exponent)
713+
* @param contractAddress Contract address of this token
714+
* @param asset Asset which this coin represents. This is the same for both mainnet and testnet variants of a coin.
715+
* @param prefix? Optional token prefix. Defaults to empty string
716+
* @param suffix? Optional token suffix. Defaults to token name.
717+
* @param network? Optional token network. Defaults to BSC main network.
718+
* @param features? Features of this coin. Defaults to the DEFAULT_FEATURES defined in `AccountCoin`
719+
* @param primaryKeyCurve The elliptic curve for this chain/token
720+
*/
721+
export function bscToken(
722+
name: string,
723+
fullName: string,
724+
decimalPlaces: number,
725+
contractAddress: string,
726+
asset: UnderlyingAsset,
727+
features: CoinFeature[] = AccountCoin.DEFAULT_FEATURES,
728+
prefix = '',
729+
suffix: string = name.toUpperCase(),
730+
network: EthereumNetwork = Networks.main.bsc,
731+
primaryKeyCurve: KeyCurve = KeyCurve.Secp256k1
732+
) {
733+
return Object.freeze(
734+
new BscCoin({
735+
name,
736+
fullName,
737+
network,
738+
contractAddress,
739+
prefix,
740+
suffix,
741+
features,
742+
decimalPlaces,
743+
asset,
744+
isToken: true,
745+
primaryKeyCurve,
746+
})
747+
);
748+
}
749+
750+
/**
751+
* Factory function for testnet bsc token instances.
752+
*
753+
* @param name unique identifier of the token
754+
* @param fullName Complete human-readable name of the token
755+
* @param decimalPlaces Number of decimal places this token supports (divisibility exponent)
756+
* @param contractAddress Contract address of this token
757+
* @param asset Asset which this coin represents. This is the same for both mainnet and testnet variants of a coin.
758+
* @param prefix? Optional token prefix. Defaults to empty string
759+
* @param suffix? Optional token suffix. Defaults to token name.
760+
* @param network? Optional token network. Defaults to the testnet BSC network.
761+
* @param features? Features of this coin. Defaults to the DEFAULT_FEATURES defined in `AccountCoin`
762+
*/
763+
export function tbscToken(
764+
name: string,
765+
fullName: string,
766+
decimalPlaces: number,
767+
contractAddress: string,
768+
asset: UnderlyingAsset,
769+
features: CoinFeature[] = AccountCoin.DEFAULT_FEATURES,
770+
prefix = '',
771+
suffix: string = name.toUpperCase(),
772+
network: EthereumNetwork = Networks.test.bsc
773+
) {
774+
return bscToken(name, fullName, decimalPlaces, contractAddress, asset, features, prefix, suffix, network);
775+
}
776+
702777
/**
703778
* Factory function for Stellar token instances.
704779
*

modules/statics/src/base.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -859,6 +859,10 @@ export enum UnderlyingAsset {
859859
'avaxc:usdc' = 'avaxc:usdc',
860860
'avaxc:link' = 'avaxc:link',
861861

862+
// BSC Token BEP-20
863+
'bsc:busd' = 'bsc:busd',
864+
'tbsc:busd' = 'tbsc:busd',
865+
862866
// Polygon testnet tokens
863867
'tpolygon:derc20' = 'tpolygon:derc20',
864868
'tpolygon:link' = 'tpolygon:link',

modules/statics/src/coins.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
AccountCoin,
44
algoToken,
55
avaxErc20,
6+
bscToken,
67
celoToken,
78
eosToken,
89
erc20,
@@ -15,6 +16,7 @@ import {
1516
stellarToken,
1617
talgoToken,
1718
tavaxErc20,
19+
tbscToken,
1820
tceloToken,
1921
teosToken,
2022
terc20,
@@ -845,6 +847,13 @@ export const coins = CoinMap.fromCoins([
845847
erc20('zrx', '0x Token', 18, '0xe41d2489571d322189246dafa5ebde1f4699f498', UnderlyingAsset.ZRX),
846848
erc20('zusd', 'Z.com Usd', 6, '0xc56c2b7e71b54d38aab6d52e94a04cbfa8f604fa', UnderlyingAsset.ZUSD),
847849
celoToken('cusd', 'Celo USD', 18, '0x765de816845861e75a25fca122bb6898b8b1282a', UnderlyingAsset.CUSD),
850+
bscToken(
851+
'bsc:busd',
852+
'Binance USD Token',
853+
18,
854+
'0xe9e7cea3dedca5984780bafc599bd69add087d56',
855+
UnderlyingAsset['bsc:busd']
856+
),
848857
ofcerc20('ofc1inch', '1Inch Token', 18, UnderlyingAsset['1INCH']),
849858
ofcerc20('ofcusdc', 'USD Coin', 6, UnderlyingAsset.USDC),
850859
ofcerc20('ofcaave', 'Aave', 18, UnderlyingAsset.AAVE),
@@ -1648,6 +1657,13 @@ export const coins = CoinMap.fromCoins([
16481657
terc20('tdai', 'Test DAI', 18, '0x4f96fe3b7a6cf9725f59d353f723c1bdb64ca6aa', UnderlyingAsset.TERC20),
16491658
terc20('trif', 'Test RIF Token', 18, '0x19f64674d8a5b4e652319f5e239efd3bc969a1fe', UnderlyingAsset.RIF),
16501659
tceloToken('tcusd', 'Test Celo USD Token', 18, '0x874069fa1eb16d44d622f2e0ca25eea172369bc1', UnderlyingAsset.CUSD),
1660+
tbscToken(
1661+
'tbsc:busd',
1662+
'Test Binance USD Token',
1663+
18,
1664+
'0x78867bbeef44f2326bf8ddd1941a4439382ef2a7',
1665+
UnderlyingAsset['tbsc:busd']
1666+
),
16511667
erc721('erc721:witch', 'Crypto Coven', '0x5180db8f5c931aae63c74266b211f580155ecac8'),
16521668
terc721('terc721:bitgoerc721', 'Test BITGO ERC 721 Token', '0x8397b091514c1f7bebb9dea6ac267ea23b570605'),
16531669
terc721('terc1155:bitgoerc1155', 'Test BITGO ERC 1155 Token', '0x87cd6a40640befdd96e563b788a6b1fb3e07a186'),

modules/statics/src/tokenConfig.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
SolCoin,
99
HederaToken,
1010
PolygonERC20Token,
11+
BscCoin,
1112
} from './account';
1213
import { CoinKind } from './base';
1314
import { coins } from './coins';
@@ -77,6 +78,9 @@ export interface Tokens {
7778
polygon: {
7879
tokens: EthLikeTokenConfig[];
7980
};
81+
bsc: {
82+
tokens: EthLikeTokenConfig[];
83+
};
8084
sol: {
8185
tokens: SolTokenConfig[];
8286
};
@@ -100,6 +104,9 @@ export interface Tokens {
100104
celo: {
101105
tokens: CeloTokenConfig[];
102106
};
107+
bsc: {
108+
tokens: EthLikeTokenConfig[];
109+
};
103110
eos: {
104111
tokens: EosTokenConfig[];
105112
};
@@ -200,6 +207,20 @@ const formattedCeloTokens = coins.reduce((acc: CeloTokenConfig[], coin) => {
200207
return acc;
201208
}, []);
202209

210+
const formattedBscTokens = coins.reduce((acc: EthLikeTokenConfig[], coin) => {
211+
if (coin instanceof BscCoin) {
212+
acc.push({
213+
type: coin.name,
214+
coin: coin.network.type === NetworkType.MAINNET ? 'bsc' : 'tbsc',
215+
network: coin.network.type === NetworkType.MAINNET ? 'Mainnet' : 'Testnet',
216+
name: coin.fullName,
217+
tokenContractAddress: coin.contractAddress.toString().toLowerCase(),
218+
decimalPlaces: coin.decimalPlaces,
219+
});
220+
}
221+
return acc;
222+
}, []);
223+
203224
const formattedEosTokens = coins.reduce((acc: EosTokenConfig[], coin) => {
204225
if (coin instanceof EosCoin) {
205226
acc.push({
@@ -301,6 +322,9 @@ export const tokens: Tokens = {
301322
celo: {
302323
tokens: formattedCeloTokens.filter((token) => token.network === 'Mainnet'),
303324
},
325+
bsc: {
326+
tokens: formattedBscTokens.filter((token) => token.network === 'Mainnet'),
327+
},
304328
eos: {
305329
tokens: formattedEosTokens.filter((token) => token.network === 'Mainnet'),
306330
},
@@ -334,6 +358,9 @@ export const tokens: Tokens = {
334358
celo: {
335359
tokens: formattedCeloTokens.filter((token) => token.network === 'Testnet'),
336360
},
361+
bsc: {
362+
tokens: formattedBscTokens.filter((token) => token.network === 'Testnet'),
363+
},
337364
eos: {
338365
tokens: formattedEosTokens.filter((token) => token.network === 'Testnet'),
339366
},

0 commit comments

Comments
 (0)