diff --git a/modules/sdk-coin-iota/src/iota.ts b/modules/sdk-coin-iota/src/iota.ts index bcc4d581b0..48523f17c9 100644 --- a/modules/sdk-coin-iota/src/iota.ts +++ b/modules/sdk-coin-iota/src/iota.ts @@ -14,6 +14,7 @@ import { MPCType, PopulatedIntent, PrebuildTransactionWithIntentOptions, + TransactionRecipient, verifyEddsaTssWalletAddress, } from '@bitgo/sdk-core'; import { BaseCoin as StaticsBaseCoin, CoinFamily, coins } from '@bitgo/statics'; @@ -121,7 +122,21 @@ export class Iota extends BaseCoin { throw new Error('Tx not a transfer transaction'); } const txData = transaction.toJson() as TransferTxData; - if (!txData.recipients || !_.isEqual(txParams.recipients, txData.recipients)) { + + if (!txData.recipients) { + throw new Error('Tx recipients does not match with expected txParams recipients'); + } + + const normalizeRecipient = (recipient: TransactionRecipient) => + _.pick(recipient, ['address', 'amount', 'tokenName']); + const txDataRecipients = txData.recipients.map(normalizeRecipient); + const txParamsRecipients = txParams.recipients.map(normalizeRecipient); + + const allRecipientsMatch = txParamsRecipients.every((expectedRecipient) => + txDataRecipients.some((actualRecipient) => _.isEqual(expectedRecipient, actualRecipient)) + ); + + if (!allRecipientsMatch) { throw new Error('Tx recipients does not match with expected txParams recipients'); } } diff --git a/modules/sdk-coin-iota/test/unit/iota.ts b/modules/sdk-coin-iota/test/unit/iota.ts index 356c7af02f..1b3eccd920 100644 --- a/modules/sdk-coin-iota/test/unit/iota.ts +++ b/modules/sdk-coin-iota/test/unit/iota.ts @@ -201,6 +201,34 @@ describe('IOTA:', function () { ); }); + it('should verify transaction with recipients containing extra fields', async function () { + // The verification should only compare address, amount, and tokenName + // Extra fields should be ignored + const txBuilder = factory.getTransferBuilder(); + txBuilder.sender(testData.sender.address); + txBuilder.recipients(testData.recipients); + txBuilder.paymentObjects(testData.paymentObjects); + txBuilder.gasData(testData.gasData); + + const tx = (await txBuilder.build()) as TransferTransaction; + const txHex = Buffer.from(await tx.toBroadcastFormat(), 'base64').toString('hex'); + + // Add extra fields to recipients that should be ignored during comparison + const recipientsWithExtraFields = testData.recipients.map((r) => ({ + ...r, + extraField: 'should be ignored', + anotherField: 123, + })); + + should.equal( + await basecoin.verifyTransaction({ + txPrebuild: { txHex: txHex }, + txParams: { recipients: recipientsWithExtraFields }, + }), + true + ); + }); + it('should detect mismatched recipients', async function () { const txBuilder = factory.getTransferBuilder(); txBuilder.sender(testData.sender.address); @@ -210,11 +238,20 @@ describe('IOTA:', function () { const tx = (await txBuilder.build()) as TransferTransaction; const txHex = Buffer.from(await tx.toBroadcastFormat(), 'base64').toString('hex'); - assert.rejects( + + // Create mismatched recipients with different addresses/amounts + const mismatchedRecipients = [ + { + address: testData.addresses.validAddresses[2], // Different address + amount: '9999', // Different amount + }, + ]; + + await assert.rejects( async () => await basecoin.verifyTransaction({ txPrebuild: { txHex: txHex }, - txParams: { recipients: testData.recipients }, + txParams: { recipients: mismatchedRecipients }, }), /Tx recipients does not match with expected txParams recipients/ );