Skip to content

Commit 36824ec

Browse files
Merge pull request #8963 from BitGo/CGD-1644
feat: onboard MPT tokens
2 parents 0dc6573 + 14474cb commit 36824ec

9 files changed

Lines changed: 185 additions & 3 deletions

File tree

modules/bitgo/src/v2/coinFactory.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
AdaTokenConfig,
3030
AlgoTokenConfig,
3131
TrxTokenConfig,
32+
XrpMptTokenConfig,
3233
XrpTokenConfig,
3334
SuiTokenConfig,
3435
AptTokenConfig,
@@ -222,6 +223,7 @@ import {
222223
XdcToken,
223224
Xlm,
224225
Xrp,
226+
XrpMptToken,
225227
XrpToken,
226228
Xtz,
227229
Zec,
@@ -543,6 +545,10 @@ export function registerCoinConstructors(coinFactory: CoinFactory, coinMap: Coin
543545
({ name, coinConstructor }) => coinFactory.register(name, coinConstructor)
544546
);
545547

548+
XrpMptToken.createTokenConstructors([...tokens.bitcoin.xrp.mptTokens, ...tokens.testnet.xrp.mptTokens]).forEach(
549+
({ name, coinConstructor }) => coinFactory.register(name, coinConstructor)
550+
);
551+
546552
AptToken.createTokenConstructors([...tokens.bitcoin.apt.tokens, ...tokens.testnet.apt.tokens]).forEach(
547553
({ name, coinConstructor }) => coinFactory.register(name, coinConstructor)
548554
);
@@ -1065,6 +1071,9 @@ export function getTokenConstructor(tokenConfig: TokenConfig): CoinConstructor |
10651071
return PolyxToken.createTokenConstructor(tokenConfig as PolyxTokenConfig);
10661072
case 'xrp':
10671073
case 'txrp':
1074+
if ('canTransfer' in tokenConfig) {
1075+
return XrpMptToken.createTokenConstructor(tokenConfig as XrpMptTokenConfig);
1076+
}
10681077
return XrpToken.createTokenConstructor(tokenConfig as XrpTokenConfig);
10691078
case 'apt':
10701079
case 'tapt':

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ import { Vet, Tvet, VetToken } from '@bitgo/sdk-coin-vet';
7272
import { Wemix, Twemix } from '@bitgo/sdk-coin-wemix';
7373
import { World, Tworld, WorldToken } from '@bitgo/sdk-coin-world';
7474
import { Xdc, Txdc, XdcToken } from '@bitgo/sdk-coin-xdc';
75-
import { Txrp, Xrp, XrpToken } from '@bitgo/sdk-coin-xrp';
75+
import { Txrp, Xrp, XrpMptToken, XrpToken } from '@bitgo/sdk-coin-xrp';
7676
import { Txtz, Xtz } from '@bitgo/sdk-coin-xtz';
7777
import { Tzec, Zec } from '@bitgo/sdk-coin-zec';
7878
import { Tzeta, Zeta } from '@bitgo/sdk-coin-zeta';
@@ -150,7 +150,7 @@ export { Trx, Ttrx };
150150
export { Vet, Tvet, VetToken };
151151
export { Xdc, Txdc, XdcToken };
152152
export { StellarToken, Txlm, Xlm };
153-
export { Txrp, Xrp, XrpToken };
153+
export { Txrp, Xrp, XrpMptToken, XrpToken };
154154
export { Txtz, Xtz };
155155
export { Tzec, Zec };
156156
export { Tzeta, Zeta };

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ describe('Coins', () => {
3535
TaoToken: 1,
3636
PolyxToken: 1,
3737
BeraToken: 1,
38+
XrpMptToken: 1,
3839
XrpToken: 1,
3940
Rune: 1,
4041
Trune: 1,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './register';
33
export * from './txrp';
44
export * from './xrp';
55
export * from './xrpToken';
6+
export * from './xrpMptToken';

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ import { BitGoBase } from '@bitgo/sdk-core';
22
import { Txrp } from './txrp';
33
import { Xrp } from './xrp';
44
import { XrpToken } from './xrpToken';
5+
import { XrpMptToken } from './xrpMptToken';
56

67
export const register = (sdk: BitGoBase): void => {
78
sdk.register('xrp', Xrp.createInstance);
89
sdk.register('txrp', Txrp.createInstance);
910
XrpToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
1011
sdk.register(name, coinConstructor);
1112
});
13+
XrpMptToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
14+
sdk.register(name, coinConstructor);
15+
});
1216
};
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { coins, XrpMptTokenConfig, tokens } from '@bitgo/statics';
2+
import { Xrp } from './xrp';
3+
import { BitGoBase, CoinConstructor, NamedCoinConstructor } from '@bitgo/sdk-core';
4+
5+
export class XrpMptToken extends Xrp {
6+
public readonly tokenConfig: XrpMptTokenConfig;
7+
8+
constructor(bitgo: BitGoBase, tokenConfig: XrpMptTokenConfig) {
9+
const staticsCoin = tokenConfig.network === 'Mainnet' ? coins.get('xrp') : coins.get('txrp');
10+
super(bitgo, staticsCoin);
11+
this.tokenConfig = tokenConfig;
12+
}
13+
14+
static createTokenConstructor(config: XrpMptTokenConfig): CoinConstructor {
15+
return (bitgo: BitGoBase) => new XrpMptToken(bitgo, config);
16+
}
17+
18+
static createTokenConstructors(
19+
tokenConfigs: XrpMptTokenConfig[] = [...tokens.bitcoin.xrp.mptTokens, ...tokens.testnet.xrp.mptTokens]
20+
): NamedCoinConstructor[] {
21+
return tokenConfigs.map((config) => ({
22+
name: config.type,
23+
coinConstructor: XrpMptToken.createTokenConstructor(config),
24+
}));
25+
}
26+
27+
get name(): string {
28+
return this.tokenConfig.name;
29+
}
30+
31+
get coin(): string {
32+
return this.tokenConfig.coin;
33+
}
34+
35+
get network(): string {
36+
return this.tokenConfig.network;
37+
}
38+
39+
get contractAddress(): string {
40+
return this.tokenConfig.contractAddress;
41+
}
42+
43+
get canTransfer(): boolean {
44+
return this.tokenConfig.canTransfer;
45+
}
46+
47+
get decimalPlaces(): number {
48+
return this.tokenConfig.decimalPlaces;
49+
}
50+
51+
getChain(): string {
52+
return this.tokenConfig.type;
53+
}
54+
55+
getBaseChain(): string {
56+
return this.coin;
57+
}
58+
59+
getFullName(): string {
60+
return 'XRP MPT Token';
61+
}
62+
63+
getBaseFactor(): number {
64+
return Math.pow(10, this.tokenConfig.decimalPlaces);
65+
}
66+
}

modules/sdk-coin-xrp/test/unit/xrpToken.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import 'should';
22

33
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
44
import { BitGoAPI } from '@bitgo/sdk-api';
5-
import { XrpToken } from '../../src';
5+
import { XrpMptToken, XrpToken } from '../../src';
66

77
describe('Xrp Tokens', function () {
88
let bitgo: TestBitGoAPI;
@@ -35,3 +35,52 @@ describe('Xrp Tokens', function () {
3535
xrpTokenCoin.currencyCode.should.equal('524C555344000000000000000000000000000000');
3636
});
3737
});
38+
39+
describe('Xrp MPT Tokens', function () {
40+
let bitgo: TestBitGoAPI;
41+
let mptCoin: XrpMptToken;
42+
43+
before(function () {
44+
bitgo = TestBitGo.decorate(BitGoAPI, { env: 'test' });
45+
XrpMptToken.createTokenConstructors().forEach(({ name, coinConstructor }) => {
46+
bitgo.safeRegister(name, coinConstructor);
47+
});
48+
bitgo.initializeTestVars();
49+
mptCoin = bitgo.coin('txrp:sec0') as XrpMptToken;
50+
});
51+
52+
it('should register all testnet MPT tokens', function () {
53+
const names = XrpMptToken.createTokenConstructors().map(({ name }) => name);
54+
names.should.containEql('txrp:sec0');
55+
names.should.containEql('txrp:sec2');
56+
names.should.containEql('txrp:wrapt');
57+
names.should.containEql('txrp:ntsec');
58+
names.should.containEql('txrp:feesec');
59+
});
60+
61+
it('should return constants for txrp:sec0', function () {
62+
mptCoin.getChain().should.equal('txrp:sec0');
63+
mptCoin.getBaseChain().should.equal('txrp');
64+
mptCoin.getFullName().should.equal('XRP MPT Token');
65+
mptCoin.getBaseFactor().should.equal(1); // assetScale 0 → 10^0
66+
mptCoin.coin.should.equal('txrp');
67+
mptCoin.network.should.equal('Testnet');
68+
mptCoin.decimalPlaces.should.equal(0);
69+
mptCoin.contractAddress.should.equal('01135794225BAA3A7F9DA001AF93FB258C517F50E20DE771');
70+
mptCoin.canTransfer.should.equal(true);
71+
});
72+
73+
it('should reflect canTransfer=false for non-transferable tokens', function () {
74+
const ntsec = bitgo.coin('txrp:ntsec') as XrpMptToken;
75+
ntsec.canTransfer.should.equal(false);
76+
ntsec.contractAddress.should.equal('01135791225BAA3A7F9DA001AF93FB258C517F50E20DE771');
77+
});
78+
79+
it('should return correct baseFactor for tokens with non-zero assetScale', function () {
80+
const sec2 = bitgo.coin('txrp:sec2') as XrpMptToken; // assetScale 2
81+
sec2.getBaseFactor().should.equal(100); // 10^2
82+
83+
const wrapt = bitgo.coin('txrp:wrapt') as XrpMptToken; // assetScale 8
84+
wrapt.getBaseFactor().should.equal(100000000); // 10^8
85+
});
86+
});

modules/statics/src/allCoinsAndTokens.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import {
4848
ttronToken,
4949
tvetNFTCollection,
5050
tworldErc20,
51+
txrpMptToken,
5152
txrpToken,
5253
tzkethErc20,
5354
vetNFTCollection,
@@ -7607,6 +7608,51 @@ export const allCoinsAndTokens = [
76077608
'straitsx.com',
76087609
UnderlyingAsset['txrp:xsgd']
76097610
),
7611+
txrpMptToken(
7612+
'b8016d32-12d1-4bbd-b0f7-5e543557c0f7',
7613+
'txrp:sec0',
7614+
'Test Security Token 0dp',
7615+
'01135794225BAA3A7F9DA001AF93FB258C517F50E20DE771',
7616+
true,
7617+
0,
7618+
UnderlyingAsset['txrp:sec0']
7619+
),
7620+
txrpMptToken(
7621+
'5df3833f-74c4-4607-bccf-1bc061b1b092',
7622+
'txrp:sec2',
7623+
'Test Security Token 2dp',
7624+
'0113578F225BAA3A7F9DA001AF93FB258C517F50E20DE771',
7625+
true,
7626+
2,
7627+
UnderlyingAsset['txrp:sec2']
7628+
),
7629+
txrpMptToken(
7630+
'44d89e3d-4938-41bf-bc4d-a8fcd3ea34b2',
7631+
'txrp:wrapt',
7632+
'Test Wrapped Token',
7633+
'01135790225BAA3A7F9DA001AF93FB258C517F50E20DE771',
7634+
true,
7635+
8,
7636+
UnderlyingAsset['txrp:wrapt']
7637+
),
7638+
txrpMptToken(
7639+
'18c9a83b-b147-41cb-9586-e8e966948422',
7640+
'txrp:ntsec',
7641+
'Test Non-Transferable Security',
7642+
'01135791225BAA3A7F9DA001AF93FB258C517F50E20DE771',
7643+
false,
7644+
2,
7645+
UnderlyingAsset['txrp:ntsec']
7646+
),
7647+
txrpMptToken(
7648+
'2bad14c2-e6b8-41ea-a204-17ceeb501daf',
7649+
'txrp:feesec',
7650+
'Test Fee Security Token',
7651+
'01135792225BAA3A7F9DA001AF93FB258C517F50E20DE771',
7652+
true,
7653+
6,
7654+
UnderlyingAsset['txrp:feesec']
7655+
),
76107656
suiToken(
76117657
'f26941b7-1110-4aa7-a2bc-29807297a51c',
76127658
'sui:deep',

modules/statics/src/base.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3726,6 +3726,12 @@ export enum UnderlyingAsset {
37263726
'xrp:fiuaxrp' = 'xrp:fiuaxrp',
37273727
// XRP testnet tokens
37283728
'txrp:xsgd' = 'txrp:xsgd',
3729+
// XRP MPT testnet tokens
3730+
'txrp:sec0' = 'txrp:sec0',
3731+
'txrp:sec2' = 'txrp:sec2',
3732+
'txrp:wrapt' = 'txrp:wrapt',
3733+
'txrp:ntsec' = 'txrp:ntsec',
3734+
'txrp:feesec' = 'txrp:feesec',
37293735

37303736
// Sui tokens
37313737
'sui:deep' = 'sui:deep',

0 commit comments

Comments
 (0)