Skip to content
Merged
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
2 changes: 2 additions & 0 deletions modules/abstract-utxo/src/recovery/backupKeyRecovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
krsProviders,
} from '@bitgo/sdk-core';
import { getMainnet, networks } from '@bitgo/utxo-lib';
import { CoinName } from '@bitgo/wasm-utxo';

import { AbstractUtxoCoin } from '../abstractUtxoCoin';
import { signAndVerifyPsbt } from '../transaction/fixedScript/signPsbt';
Expand Down Expand Up @@ -377,6 +378,7 @@ export async function backupKeyRecovery(
recoveryDestination: params.recoveryDestination,
keyRecoveryServiceFee: krsFee,
keyRecoveryServiceFeeAddress: krsFeeAddress,
coinName: coin.getChain() as CoinName,
},
backend
);
Expand Down
21 changes: 14 additions & 7 deletions modules/abstract-utxo/src/recovery/crossChainRecovery.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as utxolib from '@bitgo/utxo-lib';
import { BIP32Interface, bip32 } from '@bitgo/secp256k1';
import { Dimensions } from '@bitgo/unspents';
import { fixedScriptWallet } from '@bitgo/wasm-utxo';
import { fixedScriptWallet, CoinName } from '@bitgo/wasm-utxo';
import { BitGoBase, IWallet, Keychain, Triple, Wallet } from '@bitgo/sdk-core';
import { decrypt } from '@bitgo/sdk-api';

Expand Down Expand Up @@ -388,14 +388,16 @@ function createSweepTransactionUtxolib<TNumber extends number | bigint = number>
* @param unspents
* @param targetAddress
* @param feeRateSatVB
* @param coinName - BitGo coin name (e.g. 'btc', 'tbtc', 'ltc')
* @return unsigned PSBT
*/
function createSweepTransactionWasm<TNumber extends number | bigint = number>(
network: utxolib.Network,
walletKeys: RootWalletKeys,
unspents: WalletUnspent<TNumber>[],
targetAddress: string,
feeRateSatVB: number
feeRateSatVB: number,
coinName: CoinName
): utxolib.bitgo.UtxoPsbt {
const inputValue = unspentSum<bigint>(
unspents.map((u) => ({ ...u, value: BigInt(u.value) })),
Expand All @@ -408,9 +410,8 @@ function createSweepTransactionWasm<TNumber extends number | bigint = number>(
addWalletInputsToWasmPsbt(wasmPsbt, unspentsBigint, walletKeys);

// Calculate dimensions using wasm-utxo Dimensions
const targetOutputScript = utxolib.address.toOutputScript(targetAddress, network);
const vsize = fixedScriptWallet.Dimensions.fromPsbt(wasmPsbt)
.plus(fixedScriptWallet.Dimensions.fromOutput(new Uint8Array(targetOutputScript)))
.plus(fixedScriptWallet.Dimensions.fromOutput(targetAddress, coinName))
.getVSize();
const fee = BigInt(Math.round(vsize * feeRateSatVB));

Expand All @@ -429,6 +430,7 @@ function createSweepTransactionWasm<TNumber extends number | bigint = number>(
* @param targetAddress
* @param feeRateSatVB
* @param backend - Which backend to use for PSBT creation (default: 'wasm-utxo')
* @param coinName - BitGo coin name (required for wasm-utxo backend)
* @return unsigned PSBT
*/
function createSweepTransaction<TNumber extends number | bigint = number>(
Expand All @@ -437,10 +439,14 @@ function createSweepTransaction<TNumber extends number | bigint = number>(
unspents: WalletUnspent<TNumber>[],
targetAddress: string,
feeRateSatVB: number,
backend: PsbtBackend = 'wasm-utxo'
backend: PsbtBackend = 'wasm-utxo',
coinName?: CoinName
): utxolib.bitgo.UtxoPsbt {
if (backend === 'wasm-utxo') {
return createSweepTransactionWasm(network, walletKeys, unspents, targetAddress, feeRateSatVB);
if (!coinName) {
throw new Error('coinName is required for wasm-utxo backend');
}
return createSweepTransactionWasm(network, walletKeys, unspents, targetAddress, feeRateSatVB, coinName);
} else {
return createSweepTransactionUtxolib(network, walletKeys, unspents, targetAddress, feeRateSatVB);
}
Expand Down Expand Up @@ -502,7 +508,8 @@ export async function recoverCrossChain<TNumber extends number | bigint = number
walletUnspents,
params.recoveryAddress,
feeRateSatVB,
backend
backend,
params.sourceCoin.getChain() as CoinName
);

// For unsigned recovery, return unsigned PSBT hex
Expand Down
16 changes: 10 additions & 6 deletions modules/abstract-utxo/src/recovery/psbt.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as utxolib from '@bitgo/utxo-lib';
import { Dimensions } from '@bitgo/unspents';
import { fixedScriptWallet, utxolibCompat } from '@bitgo/wasm-utxo';
import { fixedScriptWallet, utxolibCompat, CoinName } from '@bitgo/wasm-utxo';

type RootWalletKeys = utxolib.bitgo.RootWalletKeys;
type WalletUnspent<TNumber extends number | bigint> = utxolib.bitgo.WalletUnspent<TNumber>;
Expand Down Expand Up @@ -60,6 +60,8 @@ interface CreateBackupKeyRecoveryPsbtOptions {
keyRecoveryServiceFeeAddress: string | undefined;
/** Block height for Zcash networks (required to determine consensus branch ID) */
blockHeight?: number;
/** Coin name for wasm-utxo (e.g. 'btc', 'tbtc', 'ltc') */
coinName?: CoinName;
}

/**
Expand Down Expand Up @@ -243,21 +245,23 @@ function createBackupKeyRecoveryPsbtWasm(
unspents: WalletUnspent<bigint>[],
options: CreateBackupKeyRecoveryPsbtOptions
): utxolib.bitgo.UtxoPsbt {
const { feeRateSatVB, recoveryDestination, keyRecoveryServiceFee, keyRecoveryServiceFeeAddress } = options;
const { feeRateSatVB, recoveryDestination, keyRecoveryServiceFee, keyRecoveryServiceFeeAddress, coinName } = options;

if (!coinName) {
throw new Error('coinName is required for wasm-utxo backend');
}

// Create PSBT with wasm-utxo and add wallet inputs using shared utilities
const wasmPsbt = createEmptyWasmPsbt(network, rootWalletKeys, { blockHeight: options.blockHeight });
addWalletInputsToWasmPsbt(wasmPsbt, unspents, rootWalletKeys);

// Calculate dimensions using wasm-utxo Dimensions
const recoveryOutputScript = utxolib.address.toOutputScript(recoveryDestination, network);
let dimensions = fixedScriptWallet.Dimensions.fromPsbt(wasmPsbt).plus(
fixedScriptWallet.Dimensions.fromOutput(new Uint8Array(recoveryOutputScript))
fixedScriptWallet.Dimensions.fromOutput(recoveryDestination, coinName)
);

if (keyRecoveryServiceFeeAddress) {
const krsOutputScript = utxolib.address.toOutputScript(keyRecoveryServiceFeeAddress, network);
dimensions = dimensions.plus(fixedScriptWallet.Dimensions.fromOutput(new Uint8Array(krsOutputScript)));
dimensions = dimensions.plus(fixedScriptWallet.Dimensions.fromOutput(keyRecoveryServiceFeeAddress, coinName));
}

const approximateFee = BigInt(dimensions.getVSize() * feeRateSatVB);
Expand Down