Skip to content

fix: call verifytransaction coin method after fetching txrequest from… #6014

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
38 changes: 38 additions & 0 deletions modules/sdk-coin-algo/src/algo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,44 @@ export class Algo extends BaseCoin {
}

async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
const coinConfig = coins.get(this.getChain());
const { txPrebuild, txParams } = params;

// Validate the presence of txHex
const rawTx = txPrebuild.txHex;
if (!rawTx) {
throw new Error('Missing required tx prebuild property: txHex');
}

// Parse the transaction
const transaction = new AlgoLib.Transaction(coinConfig);
transaction.fromRawTransaction(Buffer.from(rawTx, 'hex').toString('base64'));
const explainedTx = transaction.explainTransaction();

// Validate recipients
if (txParams.recipients) {
const filteredRecipients = txParams.recipients.map((recipient) => ({
address: recipient.address,
amount: BigInt(recipient.amount),
}));

const filteredOutputs = explainedTx.outputs.map((output) => ({
address: output.address,
amount: BigInt(output.amount),
}));

if (!_.isEqual(filteredOutputs, filteredRecipients)) {
throw new Error('Transaction outputs do not match the expected recipients in txParams.');
}

// Validate total amount
const totalAmount = txParams.recipients.reduce((sum, recipient) => sum.plus(recipient.amount), new BigNumber(0));

if (!totalAmount.isEqualTo(explainedTx.outputAmount)) {
throw new Error('Transaction total amount does not match the expected total amount.');
}
}

return true;
}

Expand Down
19 changes: 19 additions & 0 deletions modules/sdk-coin-algo/src/lib/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,25 @@ export class Transaction extends BaseTransaction {
return result;
}

/**
* Sets this transaction payload
*
* @param rawTx - The raw transaction in hex format
*/
fromRawTransaction(rawTransaction: string): void {
try {
// Decode the raw transaction using Algorand SDK
const decodedTx = algosdk.decodeUnsignedTransaction(Buffer.from(rawTransaction, 'hex'));

// Extract and set transaction details
this._algoTransaction = decodedTx;
this._sender = algosdk.encodeAddress(decodedTx.from.publicKey);
this._signatures = []; // Reset signatures as this is a raw transaction
} catch (e) {
throw new Error('Invalid raw transaction: ' + e.message);
}
}

/**
* Load the input and output data on this transaction.
*/
Expand Down
40 changes: 39 additions & 1 deletion modules/sdk-coin-cspr/src/cspr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
VerifyAddressOptions,
VerifyTransactionOptions,
} from '@bitgo/sdk-core';
import _ from 'lodash';

interface SignTransactionOptions extends BaseSignTransactionOptions {
txPrebuild: TransactionPrebuild;
Expand Down Expand Up @@ -110,7 +111,44 @@ export class Cspr extends BaseCoin {
}

async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
// TODO: Implement when available on the SDK.
const coinConfig = coins.get(this.getChain());
const { txPrebuild, txParams } = params;

// Validate the presence of txHex
const rawTx = txPrebuild.txHex;
if (!rawTx) {
throw new Error('Missing required tx prebuild property: txHex');
}

// Parse the transaction
const transaction = new CsprLib.Transaction(coinConfig);
transaction.fromRawTransaction(Buffer.from(rawTx, 'hex').toString('base64'));
const explainedTx = transaction.explainTransaction();

// Validate recipients
if (txParams.recipients) {
const filteredRecipients = txParams.recipients.map((recipient) => ({
address: recipient.address,
amount: BigInt(recipient.amount),
}));

const filteredOutputs = explainedTx.outputs.map((output) => ({
address: output.address,
amount: BigInt(output.amount),
}));

if (!_.isEqual(filteredOutputs, filteredRecipients)) {
throw new Error('Transaction outputs do not match the expected recipients in txParams.');
}

// Validate total amount
const totalAmount = txParams.recipients.reduce((sum, recipient) => sum.plus(recipient.amount), new BigNumber(0));

if (!totalAmount.isEqualTo(explainedTx.outputAmount)) {
throw new Error('Transaction total amount does not match the expected total amount.');
}
}

return true;
}

Expand Down
26 changes: 26 additions & 0 deletions modules/sdk-coin-cspr/src/lib/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,32 @@ export class Transaction extends BaseTransaction {
}
}

/**
* Sets this transaction payload
*
* @param rawTx - The raw transaction in hex format
*/
async fromRawTransaction(rawTransaction: string): Promise<void> {
try {
// Decode the raw transaction using Casper's DeployUtil
const deploy = DeployUtil.deployFromJson(JSON.parse(Buffer.from(rawTransaction, 'hex').toString()));

if (!deploy) {
throw new Error('Failed to decode raw transaction');
}

// Extract and set transaction details
if (deploy.ok) {
this._deploy = deploy.val;
} else {
throw new Error('Failed to decode raw transaction: ' + (deploy.err as any)?.message || 'Unknown error');
}
this._signatures = []; // Reset signatures as this is a raw transaction
} catch (e) {
throw new Error('Invalid raw transaction: ' + e.message);
}
}

get casperTx(): DeployUtil.Deploy {
return this._deploy;
}
Expand Down
43 changes: 41 additions & 2 deletions modules/sdk-coin-dot/src/dot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -646,12 +646,51 @@ export class Dot extends BaseCoin {
}

async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
const { txParams } = params;
const coinConfig = coins.get(this.getChain());
const { txPrebuild, txParams } = params;

// Ensure only one recipient is allowed
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.`
`${this.getChain()} doesn't support sending to more than one destination address within a single transaction. Use only a single recipient.`
);
}

// Validate the presence of txHex
const rawTx = txPrebuild.txHex;
if (!rawTx) {
throw new Error('Missing required tx prebuild property: txHex');
}

// Parse the transaction
const transaction = new Transaction(coinConfig);
transaction.fromRawTransaction(Buffer.from(rawTx, 'hex').toString('base64'));
const explainedTx = transaction.explainTransaction();

// Validate recipients
if (txParams.recipients) {
const filteredRecipients = txParams.recipients.map((recipient) => ({
address: recipient.address,
amount: BigInt(recipient.amount),
}));

const filteredOutputs = explainedTx.outputs.map((output) => ({
address: output.address,
amount: BigInt(output.amount),
}));

if (!_.isEqual(filteredOutputs, filteredRecipients)) {
throw new Error('Transaction outputs do not match the expected recipients in txParams.');
}

// Validate total amount
const totalAmount = txParams.recipients.reduce((sum, recipient) => sum.plus(recipient.amount), new BigNumber(0));

if (!totalAmount.isEqualTo(explainedTx.outputAmount)) {
throw new Error('Transaction total amount does not match the expected total amount.');
}
}

return true;
}

Expand Down
23 changes: 23 additions & 0 deletions modules/sdk-coin-dot/src/lib/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,29 @@ export class Transaction extends BaseTransaction {
];
}

/**
* Sets this transaction payload
*
* @param rawTx - The raw transaction in hex format
*/
fromRawTransaction(rawTransaction: string): void {
try {
// Decode the raw transaction using the Polkadot txwrapper
const decodedTx = decode(rawTransaction, {
metadataRpc: this._dotTransaction.metadataRpc,
registry: this._registry,
isImmortalEra: utils.isZeroHex(this._dotTransaction.era),
}) as unknown as DecodedTx;

// Extract and set transaction details
this._sender = decodedTx.address;
this._dotTransaction = decodedTx as unknown as UnsignedTransaction;
this._signatures = []; // Reset signatures as this is a raw transaction
} catch (e) {
throw new Error('Invalid raw transaction: ' + e.message);
}
}

private decodeInputsAndOutputsForBatch(decodedTx: DecodedTx) {
const sender = decodedTx.address;
this._inputs = [];
Expand Down
37 changes: 35 additions & 2 deletions modules/sdk-coin-stx/src/stx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ import { ClarityType, cvToString, cvToValue } from '@stacks/transactions';

import { ExplainTransactionOptions, StxSignTransactionOptions, StxTransactionExplanation } from './types';
import { StxLib } from '.';
import { TransactionBuilderFactory } from './lib';
import { Transaction, TransactionBuilderFactory } from './lib';
import { TransactionBuilder } from './lib/transactionBuilder';
import { findTokenNameByContract } from './lib/utils';
import _ from 'lodash';
import BigNumber from 'bignumber.js';

export class Stx extends BaseCoin {
protected readonly _staticsCoin: Readonly<StaticsBaseCoin>;
Expand Down Expand Up @@ -67,12 +69,43 @@ export class Stx extends BaseCoin {
}

async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
const { txParams } = params;
const coinConfig = coins.get(this.getChain());
const { txPrebuild: txPrebuild, txParams: 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 transaction = new Transaction(coinConfig);
const rawTx = txPrebuild.txHex;
if (!rawTx) {
throw new Error('missing required tx prebuild property txHex');
}

transaction.fromRawTransaction(Buffer.from(rawTx, 'hex').toString('base64'));
const explainedTx = transaction.explainTransaction();
if (txParams.recipients !== undefined) {
const filteredRecipients = txParams.recipients.map((recipient) => ({
address: recipient.address,
amount: BigInt(recipient.amount),
}));

const filteredOutputs = explainedTx.outputs.map((output) => ({
address: output.address,
amount: BigInt(output.amount),
}));

if (!_.isEqual(filteredOutputs, filteredRecipients)) {
throw new Error('Transaction outputs do not match the expected recipients in txParams.');
}

// Validate total amount
const totalAmount = txParams.recipients.reduce((sum, recipient) => sum.plus(recipient.amount), new BigNumber(0));

if (!totalAmount.isEqualTo(explainedTx.outputAmount)) {
throw new Error('Transaction total amount does not match the expected total amount.');
}
}
return true;
}

Expand Down
20 changes: 20 additions & 0 deletions modules/sdk-coin-xtz/src/lib/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,26 @@ export class Transaction extends BaseTransaction {
});
}

/**
* Sets this transaction payload
*
* @param rawTx - The raw transaction in hex format
*/
async fromRawTransaction(rawTransaction: string): Promise<void> {
try {
// Decode the raw transaction using Taquito's local-forging library
const decodedTx = localForger.parse(rawTransaction);

// Extract and set transaction details
this._encodedTransaction = rawTransaction;
this._parsedTransaction = await decodedTx;
this._source = (await decodedTx).contents[0]?.source || '';
this._signatures = []; // Reset signatures as this is a raw transaction
} catch (e) {
throw new Error('Invalid raw transaction: ' + e.message);
}
}

/**
* Record the most important fields for a Transaction operation.
*
Expand Down
41 changes: 39 additions & 2 deletions modules/sdk-coin-xtz/src/xtz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {
import { bip32 } from '@bitgo/secp256k1';
import { CoinFamily, coins, BaseCoin as StaticsBaseCoin } from '@bitgo/statics';
import BigNumber from 'bignumber.js';
import { Interface, KeyPair, TransactionBuilder, Utils } from './lib';
import { Interface, KeyPair, Transaction, TransactionBuilder, Utils } from './lib';
import _ from 'lodash';

export class Xtz extends BaseCoin {
protected readonly _staticsCoin: Readonly<StaticsBaseCoin>;
Expand Down Expand Up @@ -116,12 +117,48 @@ export class Xtz extends BaseCoin {
}

async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
const { txParams } = params;
const coinConfig = coins.get(this.getChain());
const { txPrebuild, 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.`
);
}
// Validate the presence of txHex
const rawTx = txPrebuild.txHex;
if (!rawTx) {
throw new Error('Missing required tx prebuild property: txHex');
}

// Parse the transaction
const transaction = new Transaction(coinConfig);
transaction.fromRawTransaction(Buffer.from(rawTx, 'hex').toString('base64'));
const explainedTx = transaction.explainTransaction();

// Validate recipients
if (txParams.recipients) {
const filteredRecipients = txParams.recipients.map((recipient) => ({
address: recipient.address,
amount: BigInt(recipient.amount),
}));

const filteredOutputs = explainedTx.outputs.map((output) => ({
address: output.address,
amount: BigInt(output.amount),
}));

if (!_.isEqual(filteredOutputs, filteredRecipients)) {
throw new Error('Transaction outputs do not match the expected recipients in txParams.');
}

// Validate total amount
const totalAmount = txParams.recipients.reduce((sum, recipient) => sum.plus(recipient.amount), new BigNumber(0));

if (!totalAmount.isEqualTo(explainedTx.outputAmount)) {
throw new Error('Transaction total amount does not match the expected total amount.');
}
}

return true;
}

Expand Down
Loading