Skip to content

Commit 436e08b

Browse files
feat: destination tag validation in xrp isWalletAddress
TICKET: WIN-4102
1 parent fab30f8 commit 436e08b

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,9 +358,34 @@ export class Xrp extends BaseCoin {
358358
throw new InvalidAddressError(`address verification failure: address "${address}" is not valid`);
359359
}
360360

361+
const accountInfoParams = {
362+
method: 'account_info',
363+
params: [
364+
{
365+
account: address,
366+
ledger_index: 'current',
367+
queue: true,
368+
strict: true,
369+
signer_lists: true,
370+
},
371+
],
372+
};
373+
374+
const accountInfo = (await this.bitgo.post(this.getRippledUrl()).send(accountInfoParams)).body;
375+
376+
if (accountInfo?.result?.account_data?.Flags == null) {
377+
throw new Error('Invalid account information: Flags field is missing.');
378+
}
379+
380+
const flags = xrpl.parseAccountRootFlags(accountInfo.result.account_data.Flags);
381+
361382
const addressDetails = utils.getAddressDetails(address);
362383
const rootAddressDetails = utils.getAddressDetails(rootAddress);
363384

385+
if (flags.lsfRequireDestTag && addressDetails.destinationTag == null) {
386+
throw new InvalidAddressError(`Invalid Address: Destination Tag is required for address "${address}".`);
387+
}
388+
364389
if (addressDetails.address !== rootAddressDetails.address) {
365390
throw new UnexpectedAddressError(
366391
`address validation failure: ${addressDetails.address} vs. ${rootAddressDetails.address}`

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

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,4 +474,111 @@ describe('XRP:', function () {
474474
await token.verifyTransaction({ txParams, txPrebuild }).should.be.rejectedWith('txrp:usd is not supported');
475475
});
476476
});
477+
478+
describe('Unit Tests for isWalletAddress function', function () {
479+
it('should return true for valid wallet address with matching root address', async function () {
480+
sinon.stub(basecoin.bitgo, 'post').returns({
481+
send: sinon.stub().resolves({
482+
body: {
483+
result: {
484+
account_data: {
485+
Flags: 0, // Include the Flags field
486+
},
487+
},
488+
},
489+
}),
490+
});
491+
492+
const validAddress = 'r2udSsspYjWSoUZxzxLzV6RxGcbygngJ8?dt=1893500718';
493+
const rootAddress = 'r2udSsspYjWSoUZxzxLzV6RxGcbygngJ8';
494+
495+
const result = await basecoin.isWalletAddress({ address: validAddress, rootAddress });
496+
result.should.be.true();
497+
});
498+
499+
it('should throw InvalidAddressError if the address is invalid', async function () {
500+
const invalidAddress = 'invalidAddress';
501+
const rootAddress = 'r2udSsspYjWSoUZxzxLzV6RxGcbygngJ8';
502+
503+
sinon.stub(basecoin, 'isValidAddress').returns(false);
504+
505+
await assert.rejects(async () => basecoin.isWalletAddress({ address: invalidAddress, rootAddress }), {
506+
name: 'InvalidAddressError',
507+
message: `address verification failure: address "${invalidAddress}" is not valid`,
508+
});
509+
});
510+
511+
it('should throw InvalidAddressError if destinationTag is required but not provided', async function () {
512+
const addressWithoutTag = 'r2udSsspYjWSoUZxzxLzV6RxGcbygngJ8';
513+
const rootAddress = 'r2udSsspYjWSoUZxzxLzV6RxGcbygngJ8';
514+
515+
sinon.stub(basecoin, 'isValidAddress').returns(true);
516+
sinon.stub(basecoin.bitgo, 'post').returns({
517+
send: sinon.stub().resolves({
518+
body: {
519+
result: {
520+
account_data: {
521+
Flags: 0x00020000, // lsfRequireDestTag
522+
},
523+
},
524+
},
525+
}),
526+
});
527+
528+
await assert.rejects(async () => basecoin.isWalletAddress({ address: addressWithoutTag, rootAddress }), {
529+
name: 'InvalidAddressError',
530+
message: 'Invalid Address: Destination Tag is required for address "r2udSsspYjWSoUZxzxLzV6RxGcbygngJ8".',
531+
});
532+
});
533+
534+
it('should throw UnexpectedAddressError if the root address does not match', async function () {
535+
sinon.stub(basecoin.bitgo, 'post').returns({
536+
send: sinon.stub().resolves({
537+
body: {
538+
result: {
539+
account_data: {
540+
Flags: 0, // Include the Flags field
541+
},
542+
},
543+
},
544+
}),
545+
});
546+
547+
const validAddress = 'r2udSsspYjWSoUZxzxLzV6RxGcbygngJ8?dt=1893500718';
548+
const mismatchedRootAddress = 'rBfhJ6HopLW69xK83nyShdNxC3uggjs46K';
549+
550+
await assert.rejects(
551+
async () => basecoin.isWalletAddress({ address: validAddress, rootAddress: mismatchedRootAddress }),
552+
{
553+
name: 'UnexpectedAddressError',
554+
message: `address validation failure: ${validAddress.split('?')[0]} vs. ${mismatchedRootAddress}`,
555+
}
556+
);
557+
});
558+
559+
it('should handle missing Flags field gracefully', async function () {
560+
const validAddress = 'r2udSsspYjWSoUZxzxLzV6RxGcbygngJ8?dt=1893500718';
561+
const rootAddress = 'r2udSsspYjWSoUZxzxLzV6RxGcbygngJ8';
562+
563+
sinon.stub(basecoin, 'isValidAddress').returns(true);
564+
sinon.stub(basecoin.bitgo, 'post').returns({
565+
send: sinon.stub().resolves({
566+
body: {
567+
result: {
568+
account_data: {}, // Flags field is missing
569+
},
570+
},
571+
}),
572+
});
573+
574+
await assert.rejects(async () => basecoin.isWalletAddress({ address: validAddress, rootAddress }), {
575+
name: 'Error',
576+
message: 'Invalid account information: Flags field is missing.',
577+
});
578+
});
579+
580+
afterEach(function () {
581+
sinon.restore();
582+
});
583+
});
477584
});

0 commit comments

Comments
 (0)