From f86f3aae426629460ad535ddba2fa95b32596b9d Mon Sep 17 00:00:00 2001 From: Otto Allmendinger Date: Tue, 18 Nov 2025 10:46:23 +0100 Subject: [PATCH] feat(abstract-utxo): prohibit legacy tx format on testnet This adds a safeguard to prevent use of the deprecated legacy tx format on testnet environments. The new ErrorDeprecatedTxFormat class provides clear messaging to users about using PSBT instead. BTC-2732 Co-authored-by: llm-git --- modules/abstract-utxo/src/abstractUtxoCoin.ts | 10 +++++++++ modules/abstract-utxo/test/unit/txFormat.ts | 21 +++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/modules/abstract-utxo/src/abstractUtxoCoin.ts b/modules/abstract-utxo/src/abstractUtxoCoin.ts index 058079f62d..a0c740aeb2 100644 --- a/modules/abstract-utxo/src/abstractUtxoCoin.ts +++ b/modules/abstract-utxo/src/abstractUtxoCoin.ts @@ -97,6 +97,12 @@ export type TxFormat = // While this prevents us to fully verify the transaction fee, we have other checks in place to ensure the fee is within bounds. | 'psbt-lite'; +export class ErrorDeprecatedTxFormat extends Error { + constructor(txFormat: TxFormat) { + super(`SDK support for txFormat=${txFormat} is deprecated on this environment. Please use psbt instead.`); + } +} + type UtxoCustomSigningFunction = { (params: { coin: IBaseCoin; @@ -979,6 +985,10 @@ export abstract class AbstractUtxoCoin extends BaseCoin { getDefaultTxFormat(wallet: Wallet, requestedFormat?: TxFormat): TxFormat | undefined { // If format is explicitly requested, use it if (requestedFormat !== undefined) { + if (isTestnet(this.network) && requestedFormat === 'legacy') { + throw new ErrorDeprecatedTxFormat(requestedFormat); + } + return requestedFormat; } diff --git a/modules/abstract-utxo/test/unit/txFormat.ts b/modules/abstract-utxo/test/unit/txFormat.ts index 0911d0e93a..f0e79fdc65 100644 --- a/modules/abstract-utxo/test/unit/txFormat.ts +++ b/modules/abstract-utxo/test/unit/txFormat.ts @@ -3,7 +3,7 @@ import * as assert from 'assert'; import * as utxolib from '@bitgo/utxo-lib'; import { Wallet } from '@bitgo/sdk-core'; -import { AbstractUtxoCoin, TxFormat } from '../../src'; +import { AbstractUtxoCoin, ErrorDeprecatedTxFormat, TxFormat } from '../../src'; import { utxoCoins, defaultBitGo } from './util'; @@ -151,7 +151,8 @@ describe('txFormat', function () { // Test explicitly requested formats runTest({ - description: 'should respect explicitly requested legacy format', + description: 'should respect explicitly requested legacy format on mainnet', + coinFilter: (coin) => utxolib.isMainnet(coin.network), expectedTxFormat: 'legacy', requestedTxFormat: 'legacy', }); @@ -167,5 +168,21 @@ describe('txFormat', function () { expectedTxFormat: 'psbt-lite', requestedTxFormat: 'psbt-lite', }); + + // Test that legacy format is prohibited on testnet + it('should throw ErrorDeprecatedTxFormat when legacy format is requested on testnet', function () { + for (const coin of utxoCoins) { + if (!utxolib.isTestnet(coin.network)) { + continue; + } + + const wallet = createMockWallet(coin, { type: 'hot' }); + assert.throws( + () => getTxFormat(coin, wallet, 'legacy'), + ErrorDeprecatedTxFormat, + `Expected ErrorDeprecatedTxFormat for ${coin.getChain()}` + ); + } + }); }); });