Skip to content

Conversation

@danielpeng1
Copy link
Contributor

@danielpeng1 danielpeng1 commented Nov 20, 2025

Adding tx explanation for TxIntentMismatch errors for UTXO and ETH-like coins.

  • Implemented getTxExplanation method in both AbstractEthLikeNewCoins and AbstractUtxoCoin to generate detailed transaction explanations for error reporting.
    • Created txExplanation.ts to use in abstractUtxoCoin.ts and both verifyTransactions
  • Updated error handling in tx verification methods to include tx explanations in TxIntentMismatchError and its subclasses.
  • Added unit tests to validate the inclusion of tx explanations in error instances.

Ticket: WP-6608

@danielpeng1 danielpeng1 self-assigned this Nov 20, 2025
@danielpeng1 danielpeng1 marked this pull request as ready for review November 20, 2025 13:09
@danielpeng1 danielpeng1 requested review from a team as code owners November 20, 2025 13:09
txHex: txPrebuild.txHex,
txInfo: txPrebuild.txInfo,
});
return JSON.stringify(explanation, null, 2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will not work with bigint values

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do a bigint-safe JSON serialization. Can I create a test file modules/abstract-utxo/test/unit/txExplanation.ts to test this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to a return a string here anyway? we already have src/transaction/explainTransaction.ts already as well, so why are we adding another file here?

why can't the caller of this func (whoever that is) deal with the string conversion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the alternative to return the object (explanation)? Then the parent error class TxIntentMismatchError's constructor would handle the JSON.stringify for example?

regardless I can move this function to src/transaction/explainTransaction.ts

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the alternative to return the object (explanation)? Then the parent error class TxIntentMismatchError's constructor would handle the JSON.stringify for example?

yes

regardless I can move this function to src/transaction/explainTransaction.ts

I don't think we need a new function

Copy link
Contributor Author

@danielpeng1 danielpeng1 Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but then we would need to duplicate/in-line this try catch logic in each call site? In descriptor/verifyTransaction.ts, fixedScript/verifyTransaction.ts, abstractUtxoCoin.ts

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@danielpeng1 danielpeng1 Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this is returning a string again though? (instead of the explanation object)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes but at least it is relegated to the error class, so I'm not as concerned about the interface

you can implement it returning unknown or string | undefined, your call

async verifyTransaction<TNumber extends number | bigint = number>(
params: VerifyTransactionOptions<TNumber>
): Promise<boolean> {
const txExplanation = await getTxExplanation(this, params.txPrebuild);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is pretty wasteful if we only use it in the catch block - move it there

params: VerifyTransactionOptions<TNumber>,
descriptorMap: DescriptorMap
): Promise<boolean> {
const txExplanation = await getTxExplanation(coin, params.txPrebuild);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Copy link
Contributor

@OttoAllmendinger OttoAllmendinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better deal with the string explanation -> conversion in the sdk-core errors

txHex: txPrebuild.txHex,
txInfo: txPrebuild.txInfo,
});
return JSON.stringify(explanation, null, 2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to a return a string here anyway? we already have src/transaction/explainTransaction.ts already as well, so why are we adding another file here?

why can't the caller of this func (whoever that is) deal with the string conversion?

* @property {string | IRequestTracer | undefined} id - Transaction ID or request tracer for tracking
* @property {TransactionParams[]} txParams - Array of transaction parameters that were analyzed
* @property {string | undefined} txHex - The raw transaction in hexadecimal format
* @property {string | undefined} txExplanation - Stringified transaction explanation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better make this unknown

txParams: TransactionParams[],
txHex: string | undefined
txHex: string | undefined,
txExplanation?: string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unknown here

this.id = id;
this.txParams = txParams;
this.txHex = txHex;
this.txExplanation = txExplanation;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do some sort of safe stringification here (JSON.stringify with bigint substitution)

* @property {string | undefined} txHex - The raw transaction in hexadecimal format
* @property {string | undefined} txExplanation - Stringified transaction explanation
*/
export class TxIntentMismatchError extends BitGoJsError {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a static function here

static tryGetTxExplanation(coin: AbstractCoin, tx): Promise<string | undefined> {
   try {
      return bigintSafeStringify(coin.getExplanation(tx));
   } catch (e) {
     return String(e); // with tx etc
   }
 }

txHex: txPrebuild.txHex,
txInfo: txPrebuild.txInfo,
});
return JSON.stringify(explanation, null, 2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

async verifyTssTransaction(params: VerifyEthTransactionOptions): Promise<boolean> {
const { txParams, txPrebuild, wallet } = params;

const txExplanation = await this.getTxExplanation(txPrebuild);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we have this inside throwRecipientMismatch function ?
so that its only called when required

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and probably have the string conversion inside, instead of a new function

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants