diff --git a/modules/abstract-utxo/src/address/fixedScript.ts b/modules/abstract-utxo/src/address/fixedScript.ts index d5ae2e4f56..0f927ebc23 100644 --- a/modules/abstract-utxo/src/address/fixedScript.ts +++ b/modules/abstract-utxo/src/address/fixedScript.ts @@ -14,6 +14,7 @@ import { import * as utxolib from '@bitgo/utxo-lib'; import { bitgo } from '@bitgo/utxo-lib'; import { bip32 } from '@bitgo/secp256k1'; +import * as wasmUtxo from '@bitgo/wasm-utxo'; type ScriptType2Of3 = bitgo.outputScripts.ScriptType2Of3; @@ -56,6 +57,19 @@ export function generateAddressWithChainAndIndex( index: number, format: CreateAddressFormat | undefined ): string { + if (utxolib.isTestnet(network)) { + // Convert CreateAddressFormat to AddressFormat for wasm-utxo + // 'base58' -> 'default', 'cashaddr' -> 'cashaddr' + const wasmFormat = format === 'base58' ? 'default' : format; + return wasmUtxo.fixedScriptWallet.address( + keychains.map((k) => k.pub) as [string, string, string], + chain, + index, + network, + wasmFormat + ); + } + const path = '0/0/' + chain + '/' + index; const hdNodes = keychains.map(({ pub }) => bip32.fromBase58(pub)); const derivedKeys = hdNodes.map((hdNode) => hdNode.derivePath(sanitizeLegacyPath(path)).publicKey); diff --git a/modules/abstract-utxo/test/unit/address.ts b/modules/abstract-utxo/test/unit/address.ts index 80d4bb7b06..1fa2964b09 100644 --- a/modules/abstract-utxo/test/unit/address.ts +++ b/modules/abstract-utxo/test/unit/address.ts @@ -116,6 +116,38 @@ function run(coin: AbstractUtxoCoin) { }); }); + it('respects format parameter', function () { + // Only test coins that actually support multiple address formats (BCH/BCHA) + // These are the only coins where the format parameter matters + const cashaddrPrefixes: Record = { + bch: 'bitcoincash:', + tbch: 'bchtest:', + bcha: 'ecash:', + tbcha: 'ectest:', + }; + + const expectedPrefix = cashaddrPrefixes[coin.getChain()]; + if (!expectedPrefix) { + this.skip(); + } + + const chain = chainCodes[0]; + const params = { keychains, chain }; + + // Generate with cashaddr format + const addressCashaddr = generateAddress(coin.network, { ...params, format: 'cashaddr' }); + coin.isValidAddress(addressCashaddr).should.eql(true); + addressCashaddr.should.startWith(expectedPrefix, `cashaddr should start with ${expectedPrefix}`); + + // Generate with base58 format explicitly + const addressBase58 = generateAddress(coin.network, { ...params, format: 'base58' }); + coin.isValidAddress(addressBase58).should.eql(true); + addressBase58.should.not.match(/.*:.*/, 'base58 should not contain colon separator'); + + // Verify formats produce different strings + addressCashaddr.should.not.equal(addressBase58, 'cashaddr and base58 should produce different address strings'); + }); + utxoCoins.forEach((otherCoin) => { it(`has expected address compatability with ${otherCoin.getChain()}`, async function () { getParameters().forEach((p) => { diff --git a/modules/abstract-utxo/test/unit/util/unspents.ts b/modules/abstract-utxo/test/unit/util/unspents.ts index dbb8cd7a29..24ac209f68 100644 --- a/modules/abstract-utxo/test/unit/util/unspents.ts +++ b/modules/abstract-utxo/test/unit/util/unspents.ts @@ -1,5 +1,6 @@ import * as utxolib from '@bitgo/utxo-lib'; import { getSeed } from '@bitgo/sdk-test'; +import * as wasmUtxo from '@bitgo/wasm-utxo'; import { getReplayProtectionAddresses } from '../../../src'; @@ -31,6 +32,9 @@ export function getWalletAddress( chain = defaultChain, index = 0 ): string { + if (utxolib.isTestnet(network)) { + return wasmUtxo.fixedScriptWallet.address(walletKeys, chain, index, network); + } return utxolib.address.fromOutputScript(getOutputScript(walletKeys, chain, index).scriptPubKey, network); } @@ -49,8 +53,7 @@ export function mockWalletUnspent( if (chain === undefined) { throw new Error(`unspent chain must be set`); } - const derived = getOutputScript(walletKeys, chain, index); - const deriveAddress = utxolib.address.fromOutputScript(derived.scriptPubKey, network); + const deriveAddress = getWalletAddress(network, walletKeys, chain, index); if (address) { if (address !== deriveAddress) { throw new Error(`derivedAddress mismatch: ${address} derived=${deriveAddress}`);