Skip to content

Commit 076170e

Browse files
feat(abstract-utxo): test over all wallet types
Issue: BTC-2732
1 parent 45745c9 commit 076170e

File tree

1 file changed

+94
-69
lines changed

1 file changed

+94
-69
lines changed

modules/abstract-utxo/test/unit/txFormat.ts

Lines changed: 94 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,30 @@ import { AbstractUtxoCoin, TxFormat } from '../../src';
77

88
import { utxoCoins, defaultBitGo } from './util';
99

10+
type WalletType = 'hot' | 'cold' | 'custodial' | 'custodialPaired' | 'trading';
11+
type WalletSubType = 'distributedCustody';
12+
type WalletFlag = { name: string; value: string };
13+
1014
type WalletOptions = {
11-
type?: 'hot' | 'cold' | 'custodial' | 'custodialPaired' | 'trading';
12-
subType?: string;
13-
walletFlags?: Array<{ name: string; value: string }>;
15+
type?: WalletType;
16+
subType?: WalletSubType;
17+
walletFlags?: WalletFlag[];
1418
};
1519

20+
/**
21+
* Enumerates common wallet configurations for testing
22+
*/
23+
export function getWalletConfigurations(): Array<{ name: string; options: WalletOptions }> {
24+
return [
25+
{ name: 'hot wallet', options: { type: 'hot' } },
26+
{ name: 'cold wallet', options: { type: 'cold' } },
27+
{ name: 'custodial wallet', options: { type: 'custodial' } },
28+
{ name: 'distributedCustody wallet', options: { type: 'cold', subType: 'distributedCustody' } },
29+
{ name: 'musigKp wallet', options: { type: 'cold', walletFlags: [{ name: 'musigKp', value: 'true' }] } },
30+
{ name: 'hot musigKp wallet', options: { type: 'hot', walletFlags: [{ name: 'musigKp', value: 'true' }] } },
31+
];
32+
}
33+
1634
/**
1735
* Helper function to create a mock wallet for testing
1836
*/
@@ -35,115 +53,122 @@ export function getTxFormat(coin: AbstractUtxoCoin, wallet: Wallet, requestedFor
3553
}
3654

3755
/**
38-
* Helper function to run a txFormat test with named arguments
56+
* Helper function to run a txFormat test with named arguments.
57+
* By default, iterates over all wallet configurations and all coins.
3958
*/
4059
function runTest(params: {
4160
description: string;
42-
walletOptions: WalletOptions;
43-
expectedTxFormat: TxFormat | undefined | ((coin: AbstractUtxoCoin) => TxFormat | undefined);
61+
expectedTxFormat:
62+
| TxFormat
63+
| undefined
64+
| ((coin: AbstractUtxoCoin, walletConfig: WalletOptions) => TxFormat | undefined);
4465
coinFilter?: (coin: AbstractUtxoCoin) => boolean;
66+
walletFilter?: (walletConfig: { name: string; options: WalletOptions }) => boolean;
4567
requestedTxFormat?: TxFormat;
4668
}): void {
4769
it(params.description, function () {
48-
for (const coin of utxoCoins) {
49-
// Skip coins that don't match the filter
50-
if (params.coinFilter && !params.coinFilter(coin)) {
70+
const walletConfigs = getWalletConfigurations();
71+
72+
for (const walletConfig of walletConfigs) {
73+
// Skip wallet configurations that don't match the filter
74+
if (params.walletFilter && !params.walletFilter(walletConfig)) {
5175
continue;
5276
}
5377

54-
const wallet = createMockWallet(coin, params.walletOptions);
55-
const txFormat = getTxFormat(coin, wallet, params.requestedTxFormat);
56-
57-
const expectedTxFormat =
58-
typeof params.expectedTxFormat === 'function' ? params.expectedTxFormat(coin) : params.expectedTxFormat;
59-
60-
assert.strictEqual(
61-
txFormat,
62-
expectedTxFormat,
63-
`${params.description} - ${coin.getChain()}: expected ${expectedTxFormat}, got ${txFormat}`
64-
);
78+
for (const coin of utxoCoins) {
79+
// Skip coins that don't match the filter
80+
if (params.coinFilter && !params.coinFilter(coin)) {
81+
continue;
82+
}
83+
84+
const wallet = createMockWallet(coin, walletConfig.options);
85+
const txFormat = getTxFormat(coin, wallet, params.requestedTxFormat);
86+
87+
const expectedTxFormat =
88+
typeof params.expectedTxFormat === 'function'
89+
? params.expectedTxFormat(coin, walletConfig.options)
90+
: params.expectedTxFormat;
91+
92+
assert.strictEqual(
93+
txFormat,
94+
expectedTxFormat,
95+
`${params.description} - ${
96+
walletConfig.name
97+
} - ${coin.getChain()}: expected ${expectedTxFormat}, got ${txFormat}`
98+
);
99+
}
65100
}
66101
});
67102
}
68103

69104
describe('txFormat', function () {
70105
describe('getDefaultTxFormat', function () {
71-
// Testnet hot wallets default to PSBT (except ZCash)
106+
// ZCash never defaults to PSBT
72107
runTest({
73-
description: 'should default to psbt for testnet hot wallets (except zcash)',
74-
walletOptions: { type: 'hot' },
75-
expectedTxFormat: (coin) => {
76-
const isZcash = utxolib.getMainnet(coin.network) === utxolib.networks.zcash;
77-
// ZCash is excluded from PSBT default due to PSBT support issues (BTC-1322)
78-
return isZcash ? undefined : 'psbt';
79-
},
80-
coinFilter: (coin) => utxolib.isTestnet(coin.network),
108+
description: 'should never return psbt for zcash',
109+
coinFilter: (coin) => utxolib.getMainnet(coin.network) === utxolib.networks.zcash,
110+
expectedTxFormat: undefined,
81111
});
82112

83-
// Mainnet Bitcoin hot wallets default to PSBT
113+
// All non-ZCash testnet wallets default to PSBT
84114
runTest({
85-
description: 'should default to psbt for mainnet bitcoin hot wallets',
86-
walletOptions: { type: 'hot' },
87-
expectedTxFormat: 'psbt',
115+
description: 'should always return psbt for testnet (non-zcash)',
88116
coinFilter: (coin) =>
89-
utxolib.isMainnet(coin.network) && utxolib.getMainnet(coin.network) === utxolib.networks.bitcoin,
117+
utxolib.isTestnet(coin.network) && utxolib.getMainnet(coin.network) !== utxolib.networks.zcash,
118+
expectedTxFormat: 'psbt',
90119
});
91120

92-
// Mainnet non-Bitcoin hot wallets do NOT default to PSBT
121+
// DistributedCustody wallets default to PSBT (mainnet only, testnet already covered)
93122
runTest({
94-
description: 'should not default to psbt for mainnet non-bitcoin hot wallets',
95-
walletOptions: { type: 'hot' },
96-
expectedTxFormat: undefined,
123+
description: 'should return psbt for distributedCustody wallets on mainnet',
97124
coinFilter: (coin) =>
98-
utxolib.isMainnet(coin.network) && utxolib.getMainnet(coin.network) !== utxolib.networks.bitcoin,
125+
utxolib.isMainnet(coin.network) && utxolib.getMainnet(coin.network) !== utxolib.networks.zcash,
126+
walletFilter: (w) => w.options.subType === 'distributedCustody',
127+
expectedTxFormat: 'psbt',
99128
});
100129

101-
// Cold wallets default to PSBT
130+
// MuSig2 wallets default to PSBT (mainnet only, testnet already covered)
102131
runTest({
103-
description: 'should default to psbt for testnet cold wallets as well',
104-
walletOptions: { type: 'cold' },
105-
coinFilter: (coin) => utxolib.isTestnet(coin.network),
106-
expectedTxFormat: (coin) => {
107-
const isZcash = utxolib.getMainnet(coin.network) === utxolib.networks.zcash;
108-
// ZCash is excluded from PSBT default due to PSBT support issues (BTC-1322)
109-
return isZcash ? undefined : 'psbt';
110-
},
132+
description: 'should return psbt for wallets with musigKp flag on mainnet',
133+
coinFilter: (coin) =>
134+
utxolib.isMainnet(coin.network) && utxolib.getMainnet(coin.network) !== utxolib.networks.zcash,
135+
walletFilter: (w) => Boolean(w.options.walletFlags?.some((f) => f.name === 'musigKp' && f.value === 'true')),
136+
expectedTxFormat: 'psbt',
111137
});
112138

113-
// DistributedCustody wallets default to PSBT
139+
// Mainnet Bitcoin hot wallets default to PSBT
114140
runTest({
115-
description: 'should default to psbt for distributedCustody wallets',
116-
walletOptions: { type: 'cold', subType: 'distributedCustody' },
117-
expectedTxFormat: (coin) => {
118-
const isZcash = utxolib.getMainnet(coin.network) === utxolib.networks.zcash;
119-
// ZCash is excluded from PSBT default due to PSBT support issues (BTC-1322)
120-
return isZcash ? undefined : 'psbt';
121-
},
141+
description: 'should return psbt for mainnet bitcoin hot wallets',
142+
coinFilter: (coin) =>
143+
utxolib.isMainnet(coin.network) && utxolib.getMainnet(coin.network) === utxolib.networks.bitcoin,
144+
walletFilter: (w) => w.options.type === 'hot',
145+
expectedTxFormat: 'psbt',
122146
});
123147

124-
// Wallets with musigKp flag default to PSBT
148+
// Other mainnet wallets do NOT default to PSBT
125149
runTest({
126-
description: 'should default to psbt for wallets with musigKp flag',
127-
walletOptions: { type: 'cold', walletFlags: [{ name: 'musigKp', value: 'true' }] },
128-
expectedTxFormat: (coin) => {
129-
const isZcash = utxolib.getMainnet(coin.network) === utxolib.networks.zcash;
130-
// ZCash is excluded from PSBT default due to PSBT support issues (BTC-1322)
131-
return isZcash ? undefined : 'psbt';
150+
description: 'should return undefined for other mainnet wallets',
151+
coinFilter: (coin) =>
152+
utxolib.isMainnet(coin.network) && utxolib.getMainnet(coin.network) !== utxolib.networks.zcash,
153+
walletFilter: (w) => {
154+
const isHotBitcoin = w.options.type === 'hot'; // This will be bitcoin hot wallets
155+
const isDistributedCustody = w.options.subType === 'distributedCustody';
156+
const hasMusigKpFlag = Boolean(w.options.walletFlags?.some((f) => f.name === 'musigKp' && f.value === 'true'));
157+
// Only test "other" wallets - exclude the special cases
158+
return !isHotBitcoin && !isDistributedCustody && !hasMusigKpFlag;
132159
},
160+
expectedTxFormat: undefined,
133161
});
134162

135-
// Explicitly specified legacy format is respected
163+
// Test explicitly requested formats
136164
runTest({
137-
description: 'should respect explicitly specified legacy txFormat',
138-
walletOptions: { type: 'hot' },
165+
description: 'should respect explicitly requested legacy format',
139166
expectedTxFormat: 'legacy',
140167
requestedTxFormat: 'legacy',
141168
});
142169

143-
// Explicitly specified psbt format is respected
144170
runTest({
145-
description: 'should respect explicitly specified psbt txFormat',
146-
walletOptions: { type: 'cold' },
171+
description: 'should respect explicitly requested psbt format',
147172
expectedTxFormat: 'psbt',
148173
requestedTxFormat: 'psbt',
149174
});

0 commit comments

Comments
 (0)