Skip to content

Commit 29bc58a

Browse files
fix: call verifytransaction coin method after fetching txrequest from server for eddsa coins
TICKET: WIN-5204
1 parent ec718c9 commit 29bc58a

File tree

10 files changed

+285
-7
lines changed

10 files changed

+285
-7
lines changed

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

+38
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,44 @@ export class Algo extends BaseCoin {
579579
}
580580

581581
async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
582+
const coinConfig = coins.get(this.getChain());
583+
const { txPrebuild, txParams } = params;
584+
585+
// Validate the presence of txHex
586+
const rawTx = txPrebuild.txHex;
587+
if (!rawTx) {
588+
throw new Error('Missing required tx prebuild property: txHex');
589+
}
590+
591+
// Parse the transaction
592+
const transaction = new AlgoLib.Transaction(coinConfig);
593+
transaction.fromRawTransaction(Buffer.from(rawTx, 'hex').toString('base64'));
594+
const explainedTx = transaction.explainTransaction();
595+
596+
// Validate recipients
597+
if (txParams.recipients) {
598+
const filteredRecipients = txParams.recipients.map((recipient) => ({
599+
address: recipient.address,
600+
amount: BigInt(recipient.amount),
601+
}));
602+
603+
const filteredOutputs = explainedTx.outputs.map((output) => ({
604+
address: output.address,
605+
amount: BigInt(output.amount),
606+
}));
607+
608+
if (!_.isEqual(filteredOutputs, filteredRecipients)) {
609+
throw new Error('Transaction outputs do not match the expected recipients in txParams.');
610+
}
611+
612+
// Validate total amount
613+
const totalAmount = txParams.recipients.reduce((sum, recipient) => sum.plus(recipient.amount), new BigNumber(0));
614+
615+
if (!totalAmount.isEqualTo(explainedTx.outputAmount)) {
616+
throw new Error('Transaction total amount does not match the expected total amount.');
617+
}
618+
}
619+
582620
return true;
583621
}
584622

modules/sdk-coin-algo/src/lib/transaction.ts

+19
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,25 @@ export class Transaction extends BaseTransaction {
252252
return result;
253253
}
254254

255+
/**
256+
* Sets this transaction payload
257+
*
258+
* @param rawTx - The raw transaction in hex format
259+
*/
260+
fromRawTransaction(rawTransaction: string): void {
261+
try {
262+
// Decode the raw transaction using Algorand SDK
263+
const decodedTx = algosdk.decodeUnsignedTransaction(Buffer.from(rawTransaction, 'hex'));
264+
265+
// Extract and set transaction details
266+
this._algoTransaction = decodedTx;
267+
this._sender = algosdk.encodeAddress(decodedTx.from.publicKey);
268+
this._signatures = []; // Reset signatures as this is a raw transaction
269+
} catch (e) {
270+
throw new Error('Invalid raw transaction: ' + e.message);
271+
}
272+
}
273+
255274
/**
256275
* Load the input and output data on this transaction.
257276
*/

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

+39-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
VerifyAddressOptions,
2626
VerifyTransactionOptions,
2727
} from '@bitgo/sdk-core';
28+
import _ from 'lodash';
2829

2930
interface SignTransactionOptions extends BaseSignTransactionOptions {
3031
txPrebuild: TransactionPrebuild;
@@ -110,7 +111,44 @@ export class Cspr extends BaseCoin {
110111
}
111112

112113
async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
113-
// TODO: Implement when available on the SDK.
114+
const coinConfig = coins.get(this.getChain());
115+
const { txPrebuild, txParams } = params;
116+
117+
// Validate the presence of txHex
118+
const rawTx = txPrebuild.txHex;
119+
if (!rawTx) {
120+
throw new Error('Missing required tx prebuild property: txHex');
121+
}
122+
123+
// Parse the transaction
124+
const transaction = new CsprLib.Transaction(coinConfig);
125+
transaction.fromRawTransaction(Buffer.from(rawTx, 'hex').toString('base64'));
126+
const explainedTx = transaction.explainTransaction();
127+
128+
// Validate recipients
129+
if (txParams.recipients) {
130+
const filteredRecipients = txParams.recipients.map((recipient) => ({
131+
address: recipient.address,
132+
amount: BigInt(recipient.amount),
133+
}));
134+
135+
const filteredOutputs = explainedTx.outputs.map((output) => ({
136+
address: output.address,
137+
amount: BigInt(output.amount),
138+
}));
139+
140+
if (!_.isEqual(filteredOutputs, filteredRecipients)) {
141+
throw new Error('Transaction outputs do not match the expected recipients in txParams.');
142+
}
143+
144+
// Validate total amount
145+
const totalAmount = txParams.recipients.reduce((sum, recipient) => sum.plus(recipient.amount), new BigNumber(0));
146+
147+
if (!totalAmount.isEqualTo(explainedTx.outputAmount)) {
148+
throw new Error('Transaction total amount does not match the expected total amount.');
149+
}
150+
}
151+
114152
return true;
115153
}
116154

modules/sdk-coin-cspr/src/lib/transaction.ts

+26
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,32 @@ export class Transaction extends BaseTransaction {
254254
}
255255
}
256256

257+
/**
258+
* Sets this transaction payload
259+
*
260+
* @param rawTx - The raw transaction in hex format
261+
*/
262+
async fromRawTransaction(rawTransaction: string): Promise<void> {
263+
try {
264+
// Decode the raw transaction using Casper's DeployUtil
265+
const deploy = DeployUtil.deployFromJson(JSON.parse(Buffer.from(rawTransaction, 'hex').toString()));
266+
267+
if (!deploy) {
268+
throw new Error('Failed to decode raw transaction');
269+
}
270+
271+
// Extract and set transaction details
272+
if (deploy.ok) {
273+
this._deploy = deploy.val;
274+
} else {
275+
throw new Error('Failed to decode raw transaction: ' + (deploy.err as any)?.message || 'Unknown error');
276+
}
277+
this._signatures = []; // Reset signatures as this is a raw transaction
278+
} catch (e) {
279+
throw new Error('Invalid raw transaction: ' + e.message);
280+
}
281+
}
282+
257283
get casperTx(): DeployUtil.Deploy {
258284
return this._deploy;
259285
}

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

+40-1
Original file line numberDiff line numberDiff line change
@@ -646,12 +646,51 @@ export class Dot extends BaseCoin {
646646
}
647647

648648
async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
649-
const { txParams } = params;
649+
const coinConfig = coins.get(this.getChain());
650+
const { txPrebuild, txParams } = params;
651+
652+
// Ensure only one recipient is allowed
650653
if (Array.isArray(txParams.recipients) && txParams.recipients.length > 1) {
651654
throw new Error(
652655
`${this.getChain()} doesn't support sending to more than 1 destination address within a single transaction. Try again, using only a single recipient.`
653656
);
654657
}
658+
659+
// Validate the presence of txHex
660+
const rawTx = txPrebuild.txHex;
661+
if (!rawTx) {
662+
throw new Error('Missing required tx prebuild property: txHex');
663+
}
664+
665+
// Parse the transaction
666+
const transaction = new Transaction(coinConfig);
667+
transaction.fromRawTransaction(Buffer.from(rawTx, 'hex').toString('base64'));
668+
const explainedTx = transaction.explainTransaction();
669+
670+
// Validate recipients
671+
if (txParams.recipients) {
672+
const filteredRecipients = txParams.recipients.map((recipient) => ({
673+
address: recipient.address,
674+
amount: BigInt(recipient.amount),
675+
}));
676+
677+
const filteredOutputs = explainedTx.outputs.map((output) => ({
678+
address: output.address,
679+
amount: BigInt(output.amount),
680+
}));
681+
682+
if (!_.isEqual(filteredOutputs, filteredRecipients)) {
683+
throw new Error('Transaction outputs do not match the expected recipients in txParams.');
684+
}
685+
686+
// Validate total amount
687+
const totalAmount = txParams.recipients.reduce((sum, recipient) => sum.plus(recipient.amount), new BigNumber(0));
688+
689+
if (!totalAmount.isEqualTo(explainedTx.outputAmount)) {
690+
throw new Error('Transaction total amount does not match the expected total amount.');
691+
}
692+
}
693+
655694
return true;
656695
}
657696

modules/sdk-coin-dot/src/lib/transaction.ts

+23
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,29 @@ export class Transaction extends BaseTransaction {
448448
];
449449
}
450450

451+
/**
452+
* Sets this transaction payload
453+
*
454+
* @param rawTx - The raw transaction in hex format
455+
*/
456+
fromRawTransaction(rawTransaction: string): void {
457+
try {
458+
// Decode the raw transaction using the Polkadot txwrapper
459+
const decodedTx = decode(rawTransaction, {
460+
metadataRpc: this._dotTransaction.metadataRpc,
461+
registry: this._registry,
462+
isImmortalEra: utils.isZeroHex(this._dotTransaction.era),
463+
}) as unknown as DecodedTx;
464+
465+
// Extract and set transaction details
466+
this._sender = decodedTx.address;
467+
this._dotTransaction = decodedTx as unknown as UnsignedTransaction;
468+
this._signatures = []; // Reset signatures as this is a raw transaction
469+
} catch (e) {
470+
throw new Error('Invalid raw transaction: ' + e.message);
471+
}
472+
}
473+
451474
private decodeInputsAndOutputsForBatch(decodedTx: DecodedTx) {
452475
const sender = decodedTx.address;
453476
this._inputs = [];

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

+34-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import BigNumber from 'bignumber.js';
3636

3737
import { ExplainTransactionOptions, StxSignTransactionOptions, StxTransactionExplanation } from './types';
3838
import { StxLib } from '.';
39-
import { TransactionBuilderFactory } from './lib';
39+
import { Transaction, TransactionBuilderFactory } from './lib';
4040
import { TransactionBuilder } from './lib/transactionBuilder';
4141
import { findContractTokenNameUsingContract, findTokenNameByContract, getAddressDetails } from './lib/utils';
4242
import {
@@ -52,6 +52,7 @@ import {
5252
} from './lib/iface';
5353
import { TransferBuilder } from './lib/transferBuilder';
5454
import { FungibleTokenTransferBuilder } from './lib/fungibleTokenTransferBuilder';
55+
import _ from 'lodash';
5556

5657
export class Stx extends BaseCoin {
5758
protected readonly _staticsCoin: Readonly<StaticsBaseCoin>;
@@ -101,12 +102,43 @@ export class Stx extends BaseCoin {
101102
}
102103

103104
async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
104-
const { txParams } = params;
105+
const coinConfig = coins.get(this.getChain());
106+
const { txPrebuild: txPrebuild, txParams: txParams } = params;
105107
if (Array.isArray(txParams.recipients) && txParams.recipients.length > 1) {
106108
throw new Error(
107109
`${this.getChain()} doesn't support sending to more than 1 destination address within a single transaction. Try again, using only a single recipient.`
108110
);
109111
}
112+
const transaction = new Transaction(coinConfig);
113+
const rawTx = txPrebuild.txHex;
114+
if (!rawTx) {
115+
throw new Error('missing required tx prebuild property txHex');
116+
}
117+
118+
transaction.fromRawTransaction(Buffer.from(rawTx, 'hex').toString('base64'));
119+
const explainedTx = transaction.explainTransaction();
120+
if (txParams.recipients !== undefined) {
121+
const filteredRecipients = txParams.recipients.map((recipient) => ({
122+
address: recipient.address,
123+
amount: BigInt(recipient.amount),
124+
}));
125+
126+
const filteredOutputs = explainedTx.outputs.map((output) => ({
127+
address: output.address,
128+
amount: BigInt(output.amount),
129+
}));
130+
131+
if (!_.isEqual(filteredOutputs, filteredRecipients)) {
132+
throw new Error('Transaction outputs do not match the expected recipients in txParams.');
133+
}
134+
135+
// Validate total amount
136+
const totalAmount = txParams.recipients.reduce((sum, recipient) => sum.plus(recipient.amount), new BigNumber(0));
137+
138+
if (!totalAmount.isEqualTo(explainedTx.outputAmount)) {
139+
throw new Error('Transaction total amount does not match the expected total amount.');
140+
}
141+
}
110142
return true;
111143
}
112144

modules/sdk-coin-xtz/src/lib/transaction.ts

+20
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,26 @@ export class Transaction extends BaseTransaction {
158158
});
159159
}
160160

161+
/**
162+
* Sets this transaction payload
163+
*
164+
* @param rawTx - The raw transaction in hex format
165+
*/
166+
async fromRawTransaction(rawTransaction: string): Promise<void> {
167+
try {
168+
// Decode the raw transaction using Taquito's local-forging library
169+
const decodedTx = localForger.parse(rawTransaction);
170+
171+
// Extract and set transaction details
172+
this._encodedTransaction = rawTransaction;
173+
this._parsedTransaction = await decodedTx;
174+
this._source = (await decodedTx).contents[0]?.source || '';
175+
this._signatures = []; // Reset signatures as this is a raw transaction
176+
} catch (e) {
177+
throw new Error('Invalid raw transaction: ' + e.message);
178+
}
179+
}
180+
161181
/**
162182
* Record the most important fields for a Transaction operation.
163183
*

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

+39-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import {
1616
import { bip32 } from '@bitgo/secp256k1';
1717
import { CoinFamily, coins, BaseCoin as StaticsBaseCoin } from '@bitgo/statics';
1818
import BigNumber from 'bignumber.js';
19-
import { Interface, KeyPair, TransactionBuilder, Utils } from './lib';
19+
import { Interface, KeyPair, Transaction, TransactionBuilder, Utils } from './lib';
20+
import _ from 'lodash';
2021

2122
export class Xtz extends BaseCoin {
2223
protected readonly _staticsCoin: Readonly<StaticsBaseCoin>;
@@ -116,12 +117,48 @@ export class Xtz extends BaseCoin {
116117
}
117118

118119
async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
119-
const { txParams } = params;
120+
const coinConfig = coins.get(this.getChain());
121+
const { txPrebuild, txParams } = params;
120122
if (Array.isArray(txParams.recipients) && txParams.recipients.length > 1) {
121123
throw new Error(
122124
`${this.getChain()} doesn't support sending to more than 1 destination address within a single transaction. Try again, using only a single recipient.`
123125
);
124126
}
127+
// Validate the presence of txHex
128+
const rawTx = txPrebuild.txHex;
129+
if (!rawTx) {
130+
throw new Error('Missing required tx prebuild property: txHex');
131+
}
132+
133+
// Parse the transaction
134+
const transaction = new Transaction(coinConfig);
135+
transaction.fromRawTransaction(Buffer.from(rawTx, 'hex').toString('base64'));
136+
const explainedTx = transaction.explainTransaction();
137+
138+
// Validate recipients
139+
if (txParams.recipients) {
140+
const filteredRecipients = txParams.recipients.map((recipient) => ({
141+
address: recipient.address,
142+
amount: BigInt(recipient.amount),
143+
}));
144+
145+
const filteredOutputs = explainedTx.outputs.map((output) => ({
146+
address: output.address,
147+
amount: BigInt(output.amount),
148+
}));
149+
150+
if (!_.isEqual(filteredOutputs, filteredRecipients)) {
151+
throw new Error('Transaction outputs do not match the expected recipients in txParams.');
152+
}
153+
154+
// Validate total amount
155+
const totalAmount = txParams.recipients.reduce((sum, recipient) => sum.plus(recipient.amount), new BigNumber(0));
156+
157+
if (!totalAmount.isEqualTo(explainedTx.outputAmount)) {
158+
throw new Error('Transaction total amount does not match the expected total amount.');
159+
}
160+
}
161+
125162
return true;
126163
}
127164

0 commit comments

Comments
 (0)