Skip to content

Commit b05154e

Browse files
committed
feat: replace generic Error with TxIntentMismatchError
Ticket: WP-6189
1 parent d3b9823 commit b05154e

4 files changed

Lines changed: 175 additions & 37 deletions

File tree

modules/abstract-eth/src/abstractEthLikeNewCoins.ts

Lines changed: 99 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
PresignTransactionOptions as BasePresignTransactionOptions,
2929
Recipient,
3030
SignTransactionOptions as BaseSignTransactionOptions,
31+
TxIntentMismatchError,
3132
TransactionParams,
3233
TransactionPrebuild as BaseTransactionPrebuild,
3334
TransactionRecipient,
@@ -2767,6 +2768,7 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
27672768
* @param {TransactionPrebuild} params.txPrebuild - prebuild object returned by server
27682769
* @param {Wallet} params.wallet - Wallet object to obtain keys to verify against
27692770
* @returns {boolean}
2771+
* @throws {TxIntentMismatchError} if transaction validation fails
27702772
*/
27712773
async verifyTssTransaction(params: VerifyEthTransactionOptions): Promise<boolean> {
27722774
const { txParams, txPrebuild, wallet } = params;
@@ -2777,13 +2779,18 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
27772779
(txParams.type && ['acceleration', 'fillNonce', 'transferToken', 'tokenApproval'].includes(txParams.type))
27782780
)
27792781
) {
2780-
throw new Error(`missing txParams`);
2782+
throw new TxIntentMismatchError(`missing txParams`, '', [txParams], txPrebuild?.txHex || '');
27812783
}
27822784
if (!wallet || !txPrebuild) {
2783-
throw new Error(`missing params`);
2785+
throw new TxIntentMismatchError(`missing params`, '', [txParams], txPrebuild?.txHex || '');
27842786
}
27852787
if (txParams.hop && txParams.recipients && txParams.recipients.length > 1) {
2786-
throw new Error(`tx cannot be both a batch and hop transaction`);
2788+
throw new TxIntentMismatchError(
2789+
`tx cannot be both a batch and hop transaction`,
2790+
'',
2791+
[txParams],
2792+
txPrebuild.txHex || ''
2793+
);
27872794
}
27882795

27892796
if (txParams.type && ['transfer'].includes(txParams.type)) {
@@ -2798,21 +2805,41 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
27982805
const txJson = tx.toJson();
27992806
if (txJson.data === '0x') {
28002807
if (expectedAmount !== txJson.value) {
2801-
throw new Error('the transaction amount in txPrebuild does not match the value given by client');
2808+
throw new TxIntentMismatchError(
2809+
'the transaction amount in txPrebuild does not match the value given by client',
2810+
'',
2811+
[txParams],
2812+
txPrebuild.txHex || ''
2813+
);
28022814
}
28032815
if (expectedDestination.toLowerCase() !== txJson.to.toLowerCase()) {
2804-
throw new Error('destination address does not match with the recipient address');
2816+
throw new TxIntentMismatchError(
2817+
'destination address does not match with the recipient address',
2818+
'',
2819+
[txParams],
2820+
txPrebuild.txHex || ''
2821+
);
28052822
}
28062823
} else if (txJson.data.startsWith('0xa9059cbb')) {
28072824
const [recipientAddress, amount] = getRawDecoded(
28082825
['address', 'uint256'],
28092826
getBufferedByteCode('0xa9059cbb', txJson.data)
28102827
);
28112828
if (expectedAmount !== amount.toString()) {
2812-
throw new Error('the transaction amount in txPrebuild does not match the value given by client');
2829+
throw new TxIntentMismatchError(
2830+
'the transaction amount in txPrebuild does not match the value given by client',
2831+
'',
2832+
[txParams],
2833+
txPrebuild.txHex || ''
2834+
);
28132835
}
28142836
if (expectedDestination.toLowerCase() !== addHexPrefix(recipientAddress.toString()).toLowerCase()) {
2815-
throw new Error('destination address does not match with the recipient address');
2837+
throw new TxIntentMismatchError(
2838+
'destination address does not match with the recipient address',
2839+
'',
2840+
[txParams],
2841+
txPrebuild.txHex || ''
2842+
);
28162843
}
28172844
}
28182845
}
@@ -2829,6 +2856,7 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
28292856
* @param {TransactionPrebuild} params.txPrebuild - prebuild object returned by server
28302857
* @param {Wallet} params.wallet - Wallet object to obtain keys to verify against
28312858
* @returns {boolean}
2859+
* @throws {TxIntentMismatchError} if transaction validation fails
28322860
*/
28332861
async verifyTransaction(params: VerifyEthTransactionOptions): Promise<boolean> {
28342862
const ethNetwork = this.getNetwork();
@@ -2839,20 +2867,33 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
28392867
}
28402868

28412869
if (!txParams?.recipients || !txPrebuild?.recipients || !wallet) {
2842-
throw new Error(`missing params`);
2870+
throw new TxIntentMismatchError(`missing params`, '', [txParams], txPrebuild?.txHex || '');
28432871
}
28442872
if (txParams.hop && txParams.recipients.length > 1) {
2845-
throw new Error(`tx cannot be both a batch and hop transaction`);
2873+
throw new TxIntentMismatchError(
2874+
`tx cannot be both a batch and hop transaction`,
2875+
'',
2876+
[txParams],
2877+
txPrebuild.txHex || ''
2878+
);
28462879
}
28472880
if (txPrebuild.recipients.length > 1) {
2848-
throw new Error(
2849-
`${this.getChain()} doesn't support sending to more than 1 destination address within a single transaction. Try again, using only a single recipient.`
2881+
throw new TxIntentMismatchError(
2882+
`${this.getChain()} doesn't support sending to more than 1 destination address within a single transaction. Try again, using only a single recipient.`,
2883+
'',
2884+
[txParams],
2885+
txPrebuild.txHex || ''
28502886
);
28512887
}
28522888
if (txParams.hop && txPrebuild.hopTransaction) {
28532889
// Check recipient amount for hop transaction
28542890
if (txParams.recipients.length !== 1) {
2855-
throw new Error(`hop transaction only supports 1 recipient but ${txParams.recipients.length} found`);
2891+
throw new TxIntentMismatchError(
2892+
`hop transaction only supports 1 recipient but ${txParams.recipients.length} found`,
2893+
'',
2894+
[txParams],
2895+
txPrebuild.txHex || ''
2896+
);
28562897
}
28572898

28582899
// Check tx sends to hop address
@@ -2862,7 +2903,12 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
28622903
const expectedHopAddress = optionalDeps.ethUtil.stripHexPrefix(decodedHopTx.getSenderAddress().toString());
28632904
const actualHopAddress = optionalDeps.ethUtil.stripHexPrefix(txPrebuild.recipients[0].address);
28642905
if (expectedHopAddress.toLowerCase() !== actualHopAddress.toLowerCase()) {
2865-
throw new Error('recipient address of txPrebuild does not match hop address');
2906+
throw new TxIntentMismatchError(
2907+
'recipient address of txPrebuild does not match hop address',
2908+
'',
2909+
[txParams],
2910+
txPrebuild.txHex || ''
2911+
);
28662912
}
28672913

28682914
// Convert TransactionRecipient array to Recipient array
@@ -2880,16 +2926,24 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
28802926
if (txParams.tokenName) {
28812927
const expectedTotalAmount = new BigNumber(0);
28822928
if (!expectedTotalAmount.isEqualTo(txPrebuild.recipients[0].amount)) {
2883-
throw new Error('batch token transaction amount in txPrebuild should be zero for token transfers');
2929+
throw new TxIntentMismatchError(
2930+
'batch token transaction amount in txPrebuild should be zero for token transfers',
2931+
'',
2932+
[txParams],
2933+
txPrebuild.txHex || ''
2934+
);
28842935
}
28852936
} else {
28862937
let expectedTotalAmount = new BigNumber(0);
28872938
for (let i = 0; i < txParams.recipients.length; i++) {
28882939
expectedTotalAmount = expectedTotalAmount.plus(txParams.recipients[i].amount);
28892940
}
28902941
if (!expectedTotalAmount.isEqualTo(txPrebuild.recipients[0].amount)) {
2891-
throw new Error(
2892-
'batch transaction amount in txPrebuild received from BitGo servers does not match txParams supplied by client'
2942+
throw new TxIntentMismatchError(
2943+
'batch transaction amount in txPrebuild received from BitGo servers does not match txParams supplied by client',
2944+
'',
2945+
[txParams],
2946+
txPrebuild.txHex || ''
28932947
);
28942948
}
28952949
}
@@ -2900,29 +2954,52 @@ export abstract class AbstractEthLikeNewCoins extends AbstractEthLikeCoin {
29002954
!batcherContractAddress ||
29012955
batcherContractAddress.toLowerCase() !== txPrebuild.recipients[0].address.toLowerCase()
29022956
) {
2903-
throw new Error('recipient address of txPrebuild does not match batcher address');
2957+
throw new TxIntentMismatchError(
2958+
'recipient address of txPrebuild does not match batcher address',
2959+
'',
2960+
[txParams],
2961+
txPrebuild.txHex || ''
2962+
);
29042963
}
29052964
} else {
29062965
// Check recipient address and amount for normal transaction
29072966
if (txParams.recipients.length !== 1) {
2908-
throw new Error(`normal transaction only supports 1 recipient but ${txParams.recipients.length} found`);
2967+
throw new TxIntentMismatchError(
2968+
`normal transaction only supports 1 recipient but ${txParams.recipients.length} found`,
2969+
'',
2970+
[txParams],
2971+
txPrebuild.txHex || ''
2972+
);
29092973
}
29102974
const expectedAmount = new BigNumber(txParams.recipients[0].amount);
29112975
if (!expectedAmount.isEqualTo(txPrebuild.recipients[0].amount)) {
2912-
throw new Error(
2913-
'normal transaction amount in txPrebuild received from BitGo servers does not match txParams supplied by client'
2976+
throw new TxIntentMismatchError(
2977+
'normal transaction amount in txPrebuild received from BitGo servers does not match txParams supplied by client',
2978+
'',
2979+
[txParams],
2980+
txPrebuild.txHex || ''
29142981
);
29152982
}
29162983
if (
29172984
this.isETHAddress(txParams.recipients[0].address) &&
29182985
txParams.recipients[0].address !== txPrebuild.recipients[0].address
29192986
) {
2920-
throw new Error('destination address in normal txPrebuild does not match that in txParams supplied by client');
2987+
throw new TxIntentMismatchError(
2988+
'destination address in normal txPrebuild does not match that in txParams supplied by client',
2989+
'',
2990+
[txParams],
2991+
txPrebuild.txHex || ''
2992+
);
29212993
}
29222994
}
29232995
// Check coin is correct for all transaction types
29242996
if (!this.verifyCoin(txPrebuild)) {
2925-
throw new Error(`coin in txPrebuild did not match that in txParams supplied by client`);
2997+
throw new TxIntentMismatchError(
2998+
`coin in txPrebuild did not match that in txParams supplied by client`,
2999+
'',
3000+
[txParams],
3001+
txPrebuild.txHex || ''
3002+
);
29263003
}
29273004
return true;
29283005
}

modules/abstract-utxo/src/abstractUtxoCoin.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,7 @@ export abstract class AbstractUtxoCoin extends BaseCoin {
632632
* @param params.verification.keychains Pass keychains manually rather than fetching them by id
633633
* @param params.verification.addresses Address details to pass in for out-of-band verification
634634
* @returns {boolean}
635+
* @throws {TxIntentMismatchError} if transaction validation fails
635636
*/
636637
async verifyTransaction<TNumber extends number | bigint = number>(
637638
params: VerifyTransactionOptions<TNumber>

modules/abstract-utxo/src/transaction/descriptor/verifyTransaction.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as utxolib from '@bitgo/utxo-lib';
2-
import { ITransactionRecipient, VerifyTransactionOptions } from '@bitgo/sdk-core';
2+
import { ITransactionRecipient, TxIntentMismatchError, VerifyTransactionOptions } from '@bitgo/sdk-core';
33
import { DescriptorMap } from '@bitgo/utxo-core/descriptor';
44

55
import { AbstractUtxoCoin, BaseOutput, BaseParsedTransactionOutputs } from '../../abstractUtxoCoin';
@@ -66,6 +66,8 @@ export function assertValidTransaction(
6666
* @param coin
6767
* @param params
6868
* @param descriptorMap
69+
* @returns {boolean} True if verification passes
70+
* @throws {TxIntentMismatchError} if transaction validation fails
6971
*/
7072
export async function verifyTransaction(
7173
coin: AbstractUtxoCoin,
@@ -74,7 +76,12 @@ export async function verifyTransaction(
7476
): Promise<boolean> {
7577
const tx = coin.decodeTransactionFromPrebuild(params.txPrebuild);
7678
if (!(tx instanceof utxolib.bitgo.UtxoPsbt)) {
77-
throw new Error('unexpected transaction type');
79+
throw new TxIntentMismatchError(
80+
'unexpected transaction type',
81+
params.reqId || '',
82+
[params.txParams],
83+
params.txPrebuild.txHex || ''
84+
);
7885
}
7986
assertValidTransaction(tx, descriptorMap, params.txParams.recipients ?? [], tx.network);
8087
return true;

0 commit comments

Comments
 (0)