Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 50 additions & 5 deletions modules/abstract-substrate/src/abstractSubstrateCoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ import {
RecoveryTxRequest,
SignedTransaction,
TssVerifyAddressOptions,
TxIntentMismatchRecipientError,
UnexpectedAddressError,
verifyEddsaTssWalletAddress,
VerifyTransactionOptions,
} from '@bitgo/sdk-core';
import { CoinFamily, BaseCoin as StaticsBaseCoin } from '@bitgo/statics';
import { KeyPair as SubstrateKeyPair, Transaction } from './lib';
import { NativeTransferBuilder } from './lib/nativeTransferBuilder';
import { DEFAULT_SUBSTRATE_PREFIX } from './lib/constants';
import { SignTransactionOptions, VerifiedTransactionParameters, Material } from './lib/iface';
import utils from './lib/utils';
Expand Down Expand Up @@ -132,12 +134,55 @@ export class SubstrateCoin extends BaseCoin {

/** @inheritDoc **/
async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
const { txParams } = params;
if (Array.isArray(txParams.recipients) && txParams.recipients.length > 1) {
throw new Error(
`${this.getChain()} doesn't support sending to more than 1 destination address within a single transaction. Try again, using only a single recipient.`
);
const { txParams, txPrebuild } = params;

if (!txParams) {
throw new Error('missing txParams');
}
if (!txPrebuild) {
throw new Error('missing txPrebuild');
}
if (!txPrebuild.txHex) {
throw new Error('missing txHex in txPrebuild');
}

const factory = this.getBuilder();
const txBuilder = factory.from(txPrebuild.txHex) as unknown as NativeTransferBuilder;
const txTo: string = txBuilder['_to'];
const txAmount: string = txBuilder['_amount'];
const isSweep: boolean = txBuilder['_sweepFreeBalance'] === true;
Comment thread
bhavesh-prasad marked this conversation as resolved.

if (txParams.recipients !== undefined) {
if (txParams.recipients.length === 0) {
throw new Error('missing recipients in txParams');
}
if (Array.isArray(txParams.recipients) && txParams.recipients.length > 1) {
throw new Error(
`${this.getChain()} doesn't support sending to more than 1 destination address within a single transaction. Try again, using only a single recipient.`
);
}

if (txParams.recipients[0].address !== txTo) {
throw new TxIntentMismatchRecipientError(
`Recipient address ${txParams.recipients[0].address} does not match transaction destination address ${txTo}`,
params.reqId,
[txParams],
txPrebuild.txHex,
[{ address: txTo, amount: txAmount }]
);
Comment thread
bhavesh-prasad marked this conversation as resolved.
}

if (!isSweep && txParams.recipients[0].amount !== txAmount) {
throw new TxIntentMismatchRecipientError(
`Recipient amount ${txParams.recipients[0].amount} does not match transaction amount ${txAmount}`,
params.reqId,
[txParams],
txPrebuild.txHex,
[{ address: txTo, amount: txAmount }]
);
Comment thread
bhavesh-prasad marked this conversation as resolved.
}
}

return true;
}

Expand Down
71 changes: 70 additions & 1 deletion modules/sdk-coin-polyx/test/unit/polyx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { POLYX_ADDRESS_FORMAT, TPOLYX_ADDRESS_FORMAT } from '../../src/lib/const
import * as sinon from 'sinon';
import * as testData from '../resources/wrwUsers';
import { afterEach } from 'mocha';
import { genesisHash, specVersion, txVersion } from '../resources';
import { genesisHash, specVersion, txVersion, rawTx } from '../resources';
import { TxIntentMismatchRecipientError } from '@bitgo/sdk-core';

describe('Polyx:', function () {
let bitgo: TestBitGoAPI;
Expand Down Expand Up @@ -199,4 +200,72 @@ describe('Polyx:', function () {
);
});
});

describe('verifyTransaction', function () {
const transferTo = '5F8jxKE81GhFrphyfMFr5UjeAz5wS4AaZFmeFPnf8wTetD72';
const transferAmount = '2000000000';
const wrongAddress = '5GhbC6n2pUFrX98DwyPit67fB5AwQvVCwZ4j2HKA7a4dUK4y';

describe('transfer transaction', function () {
it('should return true when address and amount match', async function () {
const result = await baseCoin.verifyTransaction({
txPrebuild: { txHex: rawTx.transfer.signed },
txParams: { recipients: [{ address: transferTo, amount: transferAmount }] },
});
result.should.be.true();
});

it('should throw TxIntentMismatchRecipientError for address mismatch', async function () {
await baseCoin
.verifyTransaction({
txPrebuild: { txHex: rawTx.transfer.signed },
txParams: { recipients: [{ address: wrongAddress, amount: transferAmount }] },
})
.should.be.rejectedWith(TxIntentMismatchRecipientError);
});

it('should throw TxIntentMismatchRecipientError for amount mismatch', async function () {
await baseCoin
.verifyTransaction({
txPrebuild: { txHex: rawTx.transfer.signed },
txParams: { recipients: [{ address: transferTo, amount: '1' }] },
})
.should.be.rejectedWith(TxIntentMismatchRecipientError);
});
});

describe('guard cases', function () {
it('should throw when txHex is missing', async function () {
await baseCoin
.verifyTransaction({
txPrebuild: {},
txParams: { recipients: [{ address: transferTo, amount: transferAmount }] },
})
.should.be.rejectedWith('missing txHex in txPrebuild');
});

it('should throw when recipients has more than 1 entry', async function () {
await baseCoin
.verifyTransaction({
txPrebuild: { txHex: rawTx.transfer.signed },
txParams: {
recipients: [
{ address: transferTo, amount: transferAmount },
{ address: wrongAddress, amount: transferAmount },
],
},
})
.should.be.rejectedWith(/support sending to more than 1 destination address/);
});

it('should throw when recipients is an empty array', async function () {
await baseCoin
.verifyTransaction({
txPrebuild: { txHex: rawTx.transfer.signed },
txParams: { recipients: [] },
})
.should.be.rejectedWith('missing recipients in txParams');
});
});
});
});
91 changes: 90 additions & 1 deletion modules/sdk-coin-tao/test/unit/tao.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { BitGoAPI } from '@bitgo/sdk-api';
import { Tao, Ttao } from '../../src';
import * as sinon from 'sinon';
import * as testData from './fixtures';
import { txVersion, genesisHash, specVersion } from '../resources';
import { txVersion, genesisHash, specVersion, rawTx } from '../resources';
import { afterEach } from 'mocha';
import { TxIntentMismatchRecipientError } from '@bitgo/sdk-core';

describe('Tao:', function () {
let bitgo: TestBitGoAPI;
Expand Down Expand Up @@ -508,4 +509,92 @@ describe('Tao:', function () {
);
});
});

describe('verifyTransaction', function () {
const transferTo = '5EQZSJmHuFH8asYYJruSRwpJmE5aqSdhdiX9oxRbxujKUkTe';
const transferAmount = '2';
const sweepTo = '5EQZSJmHuFH8asYYJruSRwpJmE5aqSdhdiX9oxRbxujKUkTe';
const wrongAddress = '5Ffp1wJCPu4hzVDTo7XaMLqZSvSadyUQmxWPDw74CBjECSoq';

describe('transfer transaction', function () {
it('should return true when address and amount match', async function () {
const result = await baseCoin.verifyTransaction({
txPrebuild: { txHex: rawTx.transfer.signed },
txParams: { recipients: [{ address: transferTo, amount: transferAmount }] },
});
result.should.be.true();
});

it('should throw TxIntentMismatchRecipientError for address mismatch', async function () {
await baseCoin
.verifyTransaction({
txPrebuild: { txHex: rawTx.transfer.signed },
txParams: { recipients: [{ address: wrongAddress, amount: transferAmount }] },
})
.should.be.rejectedWith(TxIntentMismatchRecipientError);
});

it('should throw TxIntentMismatchRecipientError for amount mismatch', async function () {
await baseCoin
.verifyTransaction({
txPrebuild: { txHex: rawTx.transfer.signed },
txParams: { recipients: [{ address: transferTo, amount: '9999' }] },
})
.should.be.rejectedWith(TxIntentMismatchRecipientError);
});
});

describe('sweep transaction', function () {
it('should return true when address matches (amount check skipped for sweep)', async function () {
const result = await baseCoin.verifyTransaction({
txPrebuild: { txHex: rawTx.transferAll.signed },
txParams: { recipients: [{ address: sweepTo, amount: '9999999' }] },
});
result.should.be.true();
});

it('should throw TxIntentMismatchRecipientError when sweep address does not match', async function () {
await baseCoin
.verifyTransaction({
txPrebuild: { txHex: rawTx.transferAll.signed },
txParams: { recipients: [{ address: wrongAddress, amount: '0' }] },
})
.should.be.rejectedWith(TxIntentMismatchRecipientError);
});
});

describe('guard cases', function () {
it('should throw when txHex is missing', async function () {
await baseCoin
.verifyTransaction({
txPrebuild: {},
txParams: { recipients: [{ address: transferTo, amount: transferAmount }] },
})
.should.be.rejectedWith('missing txHex in txPrebuild');
});

it('should throw when recipients has more than 1 entry', async function () {
await baseCoin
.verifyTransaction({
txPrebuild: { txHex: rawTx.transfer.signed },
txParams: {
recipients: [
{ address: transferTo, amount: transferAmount },
{ address: wrongAddress, amount: transferAmount },
],
},
})
.should.be.rejectedWith(/doesn't support sending to more than 1 destination address/);
});

it('should throw when recipients is an empty array', async function () {
await baseCoin
.verifyTransaction({
txPrebuild: { txHex: rawTx.transfer.signed },
txParams: { recipients: [] },
})
.should.be.rejectedWith('missing recipients in txParams');
});
});
});
});
Loading