Skip to content

Commit cf0937b

Browse files
committed
feat(statics): add XrpMptCoin class and token config for XRPL MPT
Ticket: CGD-1470 TICKET: CGD-1470
1 parent d68cfc4 commit cf0937b

5 files changed

Lines changed: 155 additions & 17 deletions

File tree

modules/sdk-coin-xrp/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"lodash": "^4.18.0",
4848
"ripple-binary-codec": "2.1.0",
4949
"ripple-keypairs": "2.0.0",
50-
"xrpl": "4.0.0"
50+
"xrpl": "4.6.0"
5151
},
5252
"devDependencies": {
5353
"@bitgo/sdk-api": "^1.80.1",

modules/statics/src/account.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,12 @@ export interface XrpCoinConstructorOptions extends AccountConstructorOptions {
137137
contractAddress: string;
138138
}
139139

140+
export interface XrpMptCoinConstructorOptions extends AccountConstructorOptions {
141+
mptIssuanceId: string; // 48-char hex MPTokenIssuanceID — stored as contractAddress
142+
canTransfer: boolean; // immutable lsfMPTCanTransfer (0x0008) flag
143+
assetScale: number; // immutable AssetScale from MPTokenIssuanceCreate
144+
}
145+
140146
export interface SuiCoinConstructorOptions extends AccountConstructorOptions {
141147
packageId: string;
142148
module: string;
@@ -607,6 +613,25 @@ export class XrpCoin extends AccountCoinToken {
607613
}
608614
}
609615

616+
/**
617+
* XRP Ledger Multi-Purpose Token (MPT) — MPTokensV1 amendment.
618+
* Identified by a 48-char hex MPTokenIssuanceID stored as contractAddress.
619+
* Uses account_objects (not account_lines). No issuer::currency pattern.
620+
* Named xrp:<token_name> — same pattern as trust-line tokens.
621+
*/
622+
export class XrpMptCoin extends AccountCoinToken {
623+
public readonly contractAddress: string; // MPTokenIssuanceID
624+
public readonly canTransfer: boolean; // immutable — set at MPTokenIssuanceCreate
625+
public readonly assetScale: number; // immutable — set at MPTokenIssuanceCreate
626+
627+
constructor(options: XrpMptCoinConstructorOptions) {
628+
super({ ...options });
629+
this.contractAddress = options.mptIssuanceId;
630+
this.canTransfer = options.canTransfer;
631+
this.assetScale = options.assetScale;
632+
}
633+
}
634+
610635
export class SuiCoin extends AccountCoinToken {
611636
public packageId: string;
612637
public module: string;
@@ -3313,6 +3338,85 @@ export function txrpToken(
33133338
);
33143339
}
33153340

3341+
/**
3342+
* Factory function for mainnet XRP MPT token instances.
3343+
*
3344+
* @param id uuid v4
3345+
* @param name unique identifier of the token, e.g. "xrp:my_mpt"
3346+
* @param fullName Complete human-readable name of the token
3347+
* @param mptIssuanceId 48-char hex MPTokenIssuanceID
3348+
* @param canTransfer immutable lsfMPTCanTransfer flag from MPTokenIssuanceCreate
3349+
* @param assetScale immutable display decimal places from MPTokenIssuanceCreate (also used as decimalPlaces)
3350+
* @param asset UnderlyingAsset enum value
3351+
* @param features Optional coin features
3352+
* @param network Optional network override (defaults to mainnet XRP)
3353+
*/
3354+
export function xrpMptToken(
3355+
id: string,
3356+
name: string,
3357+
fullName: string,
3358+
mptIssuanceId: string,
3359+
canTransfer: boolean,
3360+
assetScale: number,
3361+
asset: UnderlyingAsset,
3362+
features: CoinFeature[] = AccountCoin.DEFAULT_FEATURES,
3363+
prefix = '',
3364+
suffix: string = name.toUpperCase(),
3365+
network: AccountNetwork = Networks.main.xrp,
3366+
primaryKeyCurve: KeyCurve = KeyCurve.Secp256k1
3367+
) {
3368+
return Object.freeze(
3369+
new XrpMptCoin({
3370+
id,
3371+
name,
3372+
fullName,
3373+
network,
3374+
mptIssuanceId,
3375+
canTransfer,
3376+
assetScale,
3377+
prefix,
3378+
suffix,
3379+
features,
3380+
decimalPlaces: assetScale, // assetScale IS the display decimal places — same concept as decimalPlaces elsewhere
3381+
asset,
3382+
isToken: true,
3383+
primaryKeyCurve,
3384+
baseUnit: BaseUnit.XRP,
3385+
})
3386+
);
3387+
}
3388+
3389+
/**
3390+
* Factory function for testnet XRP MPT token instances.
3391+
*/
3392+
export function txrpMptToken(
3393+
id: string,
3394+
name: string,
3395+
fullName: string,
3396+
mptIssuanceId: string,
3397+
canTransfer: boolean,
3398+
assetScale: number,
3399+
asset: UnderlyingAsset,
3400+
features: CoinFeature[] = AccountCoin.DEFAULT_FEATURES,
3401+
prefix = '',
3402+
suffix: string = name.toUpperCase(),
3403+
network: AccountNetwork = Networks.test.xrp
3404+
) {
3405+
return xrpMptToken(
3406+
id,
3407+
name,
3408+
fullName,
3409+
mptIssuanceId,
3410+
canTransfer,
3411+
assetScale,
3412+
asset,
3413+
features,
3414+
prefix,
3415+
suffix,
3416+
network
3417+
);
3418+
}
3419+
33163420
/**
33173421
* Factory function for sui token instances.
33183422
*

modules/statics/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ export {
2525
TaoCoin,
2626
PolyxCoin,
2727
XrpCoin,
28+
XrpMptCoin,
29+
xrpMptToken,
30+
txrpMptToken,
2831
AptCoin,
2932
AptNFTCollection,
3033
Sip10Token,

modules/statics/src/tokenConfig.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import {
3333
VetToken,
3434
WorldERC20Token,
3535
XrpCoin,
36+
XrpMptCoin,
3637
ZkethERC20Token,
3738
VetNFTCollection,
3839
AdaToken,
@@ -106,6 +107,11 @@ export type XrpTokenConfig = BaseNetworkConfig & {
106107
contractAddress: string;
107108
};
108109

110+
export type XrpMptTokenConfig = BaseNetworkConfig & {
111+
contractAddress: string; // MPTokenIssuanceID (48-char hex)
112+
canTransfer: boolean; // immutable lsfMPTCanTransfer flag
113+
};
114+
109115
export type SuiTokenConfig = BaseNetworkConfig & {
110116
packageId: string;
111117
module: string;
@@ -180,6 +186,7 @@ export type TokenConfig =
180186
| AlgoTokenConfig
181187
| TrxTokenConfig
182188
| XrpTokenConfig
189+
| XrpMptTokenConfig
183190
| SuiTokenConfig
184191
| AptTokenConfig
185192
| AptNFTCollectionConfig
@@ -225,7 +232,7 @@ export interface TokenNetwork {
225232
hbar: { tokens: HbarTokenConfig[] };
226233
ada: { tokens: AdaTokenConfig[] };
227234
trx: { tokens: TrxTokenConfig[] };
228-
xrp: { tokens: XrpTokenConfig[] };
235+
xrp: { tokens: XrpTokenConfig[]; mptTokens: XrpMptTokenConfig[] };
229236
zketh: { tokens: EthLikeTokenConfig[] };
230237
sui: { tokens: SuiTokenConfig[] };
231238
tao: { tokens: TaoTokenConfig[] };
@@ -932,6 +939,26 @@ const getFormattedXrpTokens = (customCoinMap = coins) =>
932939
return acc;
933940
}, []);
934941

942+
function getXrpMptTokenConfig(coin: XrpMptCoin): XrpMptTokenConfig {
943+
return {
944+
type: coin.name,
945+
coin: coin.network.type === NetworkType.MAINNET ? 'xrp' : 'txrp',
946+
network: coin.network.type === NetworkType.MAINNET ? 'Mainnet' : 'Testnet',
947+
name: coin.fullName,
948+
decimalPlaces: coin.decimalPlaces, // equals coin.assetScale — set from assetScale in factory
949+
contractAddress: coin.contractAddress,
950+
canTransfer: coin.canTransfer,
951+
};
952+
}
953+
954+
const getFormattedXrpMptTokens = (customCoinMap = coins) =>
955+
customCoinMap.reduce((acc: XrpMptTokenConfig[], coin) => {
956+
if (coin instanceof XrpMptCoin) {
957+
acc.push(getXrpMptTokenConfig(coin));
958+
}
959+
return acc;
960+
}, []);
961+
935962
function getSuiTokenConfig(coin: SuiCoin): SuiTokenConfig {
936963
return {
937964
type: coin.name,
@@ -1373,6 +1400,7 @@ export const getFormattedTokensByNetwork = (network: 'Mainnet' | 'Testnet', coin
13731400
},
13741401
xrp: {
13751402
tokens: getFormattedXrpTokens(coinMap).filter((token) => token.network === network),
1403+
mptTokens: getFormattedXrpMptTokens(coinMap).filter((token) => token.network === network),
13761404
},
13771405
sui: {
13781406
tokens: getFormattedSuiTokens(coinMap).filter((token) => token.network === network),
@@ -1552,6 +1580,8 @@ export function getFormattedTokenConfigForCoin(coin: Readonly<BaseCoin>): TokenC
15521580
return getAdaTokenConfig(coin);
15531581
} else if (coin instanceof TronErc20Coin) {
15541582
return getTrxTokenConfig(coin);
1583+
} else if (coin instanceof XrpMptCoin) {
1584+
return getXrpMptTokenConfig(coin);
15551585
} else if (coin instanceof XrpCoin) {
15561586
return getXrpTokenConfig(coin);
15571587
} else if (coin instanceof SuiCoin) {

yarn.lock

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6998,12 +6998,12 @@
69986998
eventemitter3 "5.0.1"
69996999
ws "^8.13.0"
70007000

7001-
"@xrplf/secret-numbers@^1.0.0":
7002-
version "1.0.0"
7003-
resolved "https://registry.npmjs.org/@xrplf/secret-numbers/-/secret-numbers-1.0.0.tgz"
7004-
integrity sha512-qsCLGyqe1zaq9j7PZJopK+iGTGRbk6akkg6iZXJJgxKwck0C5x5Gnwlb1HKYGOwPKyrXWpV6a2YmcpNpUFctGg==
7001+
"@xrplf/secret-numbers@^2.0.0":
7002+
version "2.0.0"
7003+
resolved "https://registry.npmjs.org/@xrplf/secret-numbers/-/secret-numbers-2.0.0.tgz#36ffa45c41e78efc6179ca4fe9d950260103dbce"
7004+
integrity sha512-z3AOibRTE9E8MbjgzxqMpG1RNaBhQ1jnfhNCa1cGf2reZUJzPMYs4TggQTc7j8+0WyV3cr7y/U8Oz99SXIkN5Q==
70057005
dependencies:
7006-
"@xrplf/isomorphic" "^1.0.0"
7006+
"@xrplf/isomorphic" "^1.0.1"
70077007
ripple-keypairs "^2.0.0"
70087008

70097009
"@xtuc/ieee754@^1.2.0":
@@ -18352,10 +18352,10 @@ ripple-binary-codec@2.1.0:
1835218352
bignumber.js "^9.0.0"
1835318353
ripple-address-codec "^5.0.0"
1835418354

18355-
ripple-binary-codec@^2.1.0:
18356-
version "2.5.0"
18357-
resolved "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-2.5.0.tgz"
18358-
integrity sha512-n2EPs3YRX0/XE6zO8Mav/XFmI1wWmWraCRyCSb0fQ0Fkpv4kJ1tMhQXfX9E/DbLtyXbeogcoxYsQZtAmG8u+Ww==
18355+
ripple-binary-codec@^2.7.0:
18356+
version "2.7.0"
18357+
resolved "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-2.7.0.tgz#987448c14e3734f4161b0ccd9ff97624c25973f3"
18358+
integrity sha512-gEBqan5muVp+q7jgZ6aUniSyN+e4FKRzn9uFAeFSIW7IgvkezP1cUolNtpahQ+jvaSK/33hxZA7wNmn1mc330g==
1835918359
dependencies:
1836018360
"@xrplf/isomorphic" "^1.0.1"
1836118361
bignumber.js "^9.0.0"
@@ -21428,19 +21428,20 @@ xmlbuilder@~11.0.0:
2142821428
resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz"
2142921429
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
2143021430

21431-
xrpl@4.0.0:
21432-
version "4.0.0"
21433-
resolved "https://registry.npmjs.org/xrpl/-/xrpl-4.0.0.tgz"
21434-
integrity sha512-VZm1lQWHQ6PheAAFGdH+ISXKvqB2hZDQ0w4ZcdAEtmqZQXtSIVQHOKPz95rEgGANbos7+XClxJ73++joPhA8Cw==
21431+
xrpl@4.6.0:
21432+
version "4.6.0"
21433+
resolved "https://registry.npmjs.org/xrpl/-/xrpl-4.6.0.tgz#1df29a1f0157b115d9c8788d94b222d154d01154"
21434+
integrity sha512-0nXZfqDHRJ6bsDv1WtA9MdCYalMtXuxVa9mtLdqT3xypRKf2LwT5DbuGL/kHcVfuqk3B+ly+SFARlrnX+LHtRQ==
2143521435
dependencies:
2143621436
"@scure/bip32" "^1.3.1"
2143721437
"@scure/bip39" "^1.2.1"
2143821438
"@xrplf/isomorphic" "^1.0.1"
21439-
"@xrplf/secret-numbers" "^1.0.0"
21439+
"@xrplf/secret-numbers" "^2.0.0"
2144021440
bignumber.js "^9.0.0"
2144121441
eventemitter3 "^5.0.1"
21442+
fast-json-stable-stringify "^2.1.0"
2144221443
ripple-address-codec "^5.0.0"
21443-
ripple-binary-codec "^2.1.0"
21444+
ripple-binary-codec "^2.7.0"
2144421445
ripple-keypairs "^2.0.0"
2144521446

2144621447
xss@1.0.13:

0 commit comments

Comments
 (0)