Skip to content

Commit 4918ce9

Browse files
authored
Merge pull request #9167 from BitGo/CGD-2013-fix-xrp-mpt-token-enablement
fix(xrp): route MPT token enablement to enableMpt type via coin config [CGD-2013]
2 parents 8ed3e40 + d274c8e commit 4918ce9

5 files changed

Lines changed: 79 additions & 6 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const signWithPrivateKey = function (txHex, privateKey, options) {
5050
} catch (e) {
5151
try {
5252
tx = JSON.parse(txHex);
53-
} catch (e) {
53+
} catch (e2) {
5454
throw new Error('txHex needs to be either hex or JSON string for XRP');
5555
}
5656
}

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

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
UnexpectedAddressError,
2626
VerifyTransactionOptions,
2727
} from '@bitgo/sdk-core';
28-
import { coins, BaseCoin as StaticsBaseCoin, XrpCoin } from '@bitgo/statics';
28+
import { coins, BaseCoin as StaticsBaseCoin, XrpCoin, XrpMptCoin } from '@bitgo/statics';
2929
import * as rippleKeypairs from 'ripple-keypairs';
3030
import * as xrpl from 'xrpl';
3131

@@ -132,6 +132,8 @@ export class Xrp extends BaseCoin {
132132
return {
133133
requiresTokenEnablement: true,
134134
supportsMultipleTokenEnablements: false,
135+
getEnableTokenType: (tokenName: string) =>
136+
coins.has(tokenName) && coins.get(tokenName) instanceof XrpMptCoin ? 'enableMpt' : 'enabletoken',
135137
};
136138
}
137139

@@ -219,7 +221,7 @@ export class Xrp extends BaseCoin {
219221
try {
220222
transaction = JSON.parse(txHex);
221223
txHex = rippleBinaryCodec.encode(transaction);
222-
} catch (e) {
224+
} catch (e2) {
223225
throw new Error('txHex needs to be either hex or JSON string for XRP');
224226
}
225227
}
@@ -329,18 +331,25 @@ export class Xrp extends BaseCoin {
329331
} catch (e) {
330332
try {
331333
transaction = JSON.parse(txHex);
332-
} catch (e) {
334+
} catch (e2) {
333335
throw new Error('txHex needs to be either hex or JSON string for XRP');
334336
}
335337
}
336338

337339
return transaction.TransactionType;
338340
}
339341

340-
verifyTxType(txPrebuildDecoded: TransactionExplanation, txHexPrebuild: string | undefined): void {
342+
verifyTxType(txPrebuildDecoded: TransactionExplanation, txHexPrebuild: string | undefined, isMpt = false): void {
341343
if (!txHexPrebuild) throw new Error('Missing txHexPrebuild to verify token type for enabletoken tx');
342344
const transactionType = this.getTransactionTypeRawTxHex(txHexPrebuild);
343345
if (transactionType === undefined) throw new Error('Missing TransactionType on token enablement tx');
346+
347+
if (isMpt) {
348+
if (transactionType !== XrpTransactionType.MPTokenAuthorize)
349+
throw new Error(`tx type ${transactionType} does not match expected type MPTokenAuthorize`);
350+
return;
351+
}
352+
344353
if (transactionType !== XrpTransactionType.TrustSet)
345354
throw new Error(`tx type ${transactionType} does not match expected type TrustSet`);
346355
// decoded payload type could come as undefined or any of the enabletoken like types but never as something else like Send, etc
@@ -438,6 +447,13 @@ export class Xrp extends BaseCoin {
438447

439448
// Explaining a tx strips out certain data, for extra measurement we're checking vs the explained tx
440449
// but also vs the tx pre explained.
450+
if (txParams.type === 'enableMpt') {
451+
if (verification?.verifyTokenEnablement) {
452+
this.verifyTxType(explanation, txPrebuild.txHex, true);
453+
}
454+
return true;
455+
}
456+
441457
if (txParams.type === 'enabletoken' && verification?.verifyTokenEnablement) {
442458
this.verifyTxType(explanation, txPrebuild.txHex);
443459
this.verifyActivationAddress(txParams, explanation);

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

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,38 @@ describe('XRP:', function () {
158158
unsignedExplanation.fee.fee.should.equal('45');
159159
});
160160

161+
it('should explain an MPTokenAuthorize transaction from hex', async function () {
162+
const factory = getMptBuilderFactory(testData.MPT_ISSUANCE_ID);
163+
const sender = testData.TEST_MULTI_SIG_ACCOUNT.address.split('?')[0];
164+
165+
const builder = factory.getMPTokenAuthorizeBuilder();
166+
builder.sender(sender);
167+
builder.mptIssuanceId(testData.MPT_ISSUANCE_ID);
168+
builder.sequence(1600000);
169+
builder.fee('12');
170+
builder.flags(2147483648);
171+
172+
const txHex = (await builder.build()).toBroadcastFormat();
173+
const explanation = await basecoin.explainTransaction({ txHex });
174+
175+
explanation.id.should.be.a.String();
176+
explanation.fee.fee.should.equal('12');
177+
});
178+
179+
it('should explain an MPTokenAuthorize transaction from JSON string', async function () {
180+
const txJson = JSON.stringify({
181+
TransactionType: 'MPTokenAuthorize',
182+
Account: 'rBSpCz8PafXTJHppDcNnex7dYnbe3tSuFG',
183+
MPTokenIssuanceID: testData.MPT_ISSUANCE_ID,
184+
Fee: '12',
185+
Sequence: 1600000,
186+
Flags: 2147483648,
187+
});
188+
const explanation = await basecoin.explainTransaction({ txHex: txJson });
189+
190+
explanation.fee.fee.should.equal('12');
191+
});
192+
161193
it('should be able to sign an XRP transaction', async function () {
162194
const txPrebuild = {
163195
txHex:
@@ -1038,4 +1070,27 @@ describe('XRP:', function () {
10381070
);
10391071
});
10401072
});
1073+
1074+
describe('getTokenEnablementConfig', function () {
1075+
it('should return enableMpt for a registered MPT token', function () {
1076+
const config = basecoin.getTokenEnablementConfig();
1077+
config.getEnableTokenType('txrp:feesec').should.equal('enableMpt');
1078+
});
1079+
1080+
it('should return enabletoken for a registered IOU token', function () {
1081+
const config = basecoin.getTokenEnablementConfig();
1082+
config.getEnableTokenType('txrp:rlusd').should.equal('enabletoken');
1083+
});
1084+
1085+
it('should return enabletoken for an unknown token name', function () {
1086+
const config = basecoin.getTokenEnablementConfig();
1087+
config.getEnableTokenType('txrp:unknown-token').should.equal('enabletoken');
1088+
});
1089+
1090+
it('should require token enablement and not support multiple enablements in one tx', function () {
1091+
const config = basecoin.getTokenEnablementConfig();
1092+
config.requiresTokenEnablement.should.equal(true);
1093+
config.supportsMultipleTokenEnablements.should.equal(false);
1094+
});
1095+
});
10411096
});

modules/sdk-core/src/bitgo/baseCoin/iBaseCoin.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,7 @@ export interface TokenEnablementConfig {
608608
requiresTokenEnablement: boolean;
609609
supportsMultipleTokenEnablements: boolean;
610610
validateWallet?: (walletType: string) => void;
611+
getEnableTokenType?: (tokenName: string) => string;
611612
}
612613

613614
export interface MessagePrep {

modules/sdk-core/src/bitgo/wallet/wallet.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3975,7 +3975,8 @@ export class Wallet implements IWallet {
39753975

39763976
const buildParams: PrebuildTransactionOptions = _.pick(params, this.prebuildWhitelistedParams());
39773977
if (!buildParams.type) {
3978-
buildParams.type = 'enabletoken';
3978+
const tokenName = params.enableTokens[0]?.name;
3979+
buildParams.type = (tokenName && teConfig.getEnableTokenType?.(tokenName)) ?? 'enabletoken';
39793980
}
39803981
// Check if we build with intent
39813982
if (this._wallet.multisigType === 'tss') {

0 commit comments

Comments
 (0)