11import _ from 'lodash' ;
2- import * as utxolib from '@bitgo/utxo-lib' ;
32import {
43 BitGoBase ,
54 ErrorNoInputToRecover ,
@@ -9,32 +8,29 @@ import {
98 getIsUnsignedSweep ,
109 isTriple ,
1110 krsProviders ,
11+ Triple ,
1212} from '@bitgo/sdk-core' ;
13- import { getMainnet , networks } from '@bitgo/utxo-lib' ;
14- import { fixedScriptWallet } from '@bitgo/wasm-utxo' ;
13+ import { BIP32 , fixedScriptWallet } from '@bitgo/wasm-utxo' ;
1514
1615import { AbstractUtxoCoin } from '../abstractUtxoCoin' ;
1716import { signAndVerifyPsbt } from '../transaction/fixedScript/signTransaction' ;
1817import { generateAddressWithChainAndIndex } from '../address' ;
1918import { encodeTransaction } from '../transaction/decode' ;
2019import { getReplayProtectionPubkeys } from '../transaction/fixedScript/replayProtection' ;
21- import { isTestnetCoin , UtxoCoinName } from '../names' ;
22- import type { WalletUnspent } from '../unspent' ;
20+ import { getMainnetCoinName , UtxoCoinName } from '../names' ;
21+ import { parseOutputId , unspentSum , type WalletUnspent } from '../unspent' ;
2322
2423import { forCoin , RecoveryProvider } from './RecoveryProvider' ;
2524import { MempoolApi } from './mempoolApi' ;
2625import { CoingeckoApi } from './coingeckoApi' ;
27- import { createBackupKeyRecoveryPsbt , getRecoveryAmount , PsbtBackend , toPsbtToUtxolibPsbt } from './psbt' ;
26+ import { createBackupKeyRecoveryPsbt , getRecoveryAmount } from './psbt' ;
2827
29- type ScriptType2Of3 = utxolib . bitgo . outputScripts . ScriptType2Of3 ;
30- type ChainCode = utxolib . bitgo . ChainCode ;
31- type RootWalletKeys = utxolib . bitgo . RootWalletKeys ;
28+ type ScriptType2Of3 = fixedScriptWallet . OutputScriptType ;
29+ type ChainCode = fixedScriptWallet . ChainCode ;
3230type WalletUnspentJSON = WalletUnspent & {
3331 valueString : string ;
3432} ;
3533
36- const { getInternalChainCode, scriptTypeForChain, outputScripts, getExternalChainCode } = utxolib . bitgo ;
37-
3834// V1 only deals with BTC. 50 sat/vbyte is very arbitrary.
3935export const DEFAULT_RECOVERY_FEERATE_SAT_VBYTE_V1 = 50 ;
4036
@@ -120,7 +116,7 @@ export interface RecoverParams {
120116function getFormattedAddress (
121117 coin : AbstractUtxoCoin ,
122118 coinName : UtxoCoinName ,
123- walletKeys : RootWalletKeys ,
119+ walletKeys : fixedScriptWallet . RootWalletKeys ,
124120 chain : ChainCode ,
125121 addrIndex : number
126122) : string {
@@ -131,15 +127,18 @@ function getFormattedAddress(
131127 return format === 'cashaddr' ? address . split ( ':' ) [ 1 ] : address ;
132128}
133129
130+ function hasWitnessData ( scriptType : ScriptType2Of3 ) : boolean {
131+ return scriptType !== 'p2sh' ;
132+ }
133+
134134async function queryBlockchainUnspentsPath (
135135 coin : AbstractUtxoCoin ,
136136 params : RecoverParams ,
137- walletKeys : RootWalletKeys ,
137+ walletKeys : fixedScriptWallet . RootWalletKeys ,
138138 chain : ChainCode
139139) : Promise < WalletUnspent < bigint > [ ] > {
140- const scriptType = scriptTypeForChain ( chain ) ;
141- const fetchPrevTx =
142- ! utxolib . bitgo . outputScripts . hasWitnessData ( scriptType ) && getMainnet ( coin . network ) !== networks . zcash ;
140+ const scriptType = fixedScriptWallet . ChainCode . scriptType ( chain ) ;
141+ const fetchPrevTx = ! hasWitnessData ( scriptType ) && getMainnetCoinName ( coin . name ) !== 'zec' ;
143142 const recoveryProvider = params . recoveryProvider ?? forCoin ( coin . getChain ( ) , params . apiKey ) ;
144143 const MAX_SEQUENTIAL_ADDRESSES_WITHOUT_TXS = params . scan || 20 ;
145144 let numSequentialAddressesWithoutTxs = 0 ;
@@ -168,7 +167,7 @@ async function queryBlockchainUnspentsPath(
168167 const addressUnspents = await recoveryProvider . getUnspentsForAddresses ( [ formattedAddress ] ) ;
169168 const processedUnspents = await Promise . all (
170169 addressUnspents . map ( async ( u ) : Promise < WalletUnspent < bigint > > => {
171- const { txid, vout } = utxolib . bitgo . parseOutputId ( u . id ) ;
170+ const { txid, vout } = parseOutputId ( u . id ) ;
172171 let val = BigInt ( u . value ) ;
173172 if ( coin . amountType === 'bigint' ) {
174173 // blockchair returns the number with the correct precision, but in number format
@@ -246,6 +245,14 @@ export type BackupKeyRecoveryTransansaction = {
246245 recoveryAmountString : string ;
247246} ;
248247
248+ function getBip32Privkeys ( bitgo : BitGoBase , params : RecoverParams ) : Triple < BIP32 > {
249+ const keys = getBip32Keys ( bitgo , params , { requireBitGoXpub : true } ) ;
250+ if ( ! isTriple ( keys ) ) {
251+ throw new Error ( `expected key triple` ) ;
252+ }
253+ return keys . map ( ( k ) => BIP32 . from ( k . toBase58 ( ) ) ) as Triple < BIP32 > ;
254+ }
255+
249256/**
250257 * Builds a funds recovery transaction without BitGo.
251258 *
@@ -303,35 +310,43 @@ export async function backupKeyRecovery(
303310 const krsProvider = isKrsRecovery ? getKrsProvider ( coin , params . krsProvider ) : undefined ;
304311
305312 // check whether key material and password authenticate the users and return parent keys of all three keys of the wallet
306- const keys = getBip32Keys ( bitgo , params , { requireBitGoXpub : true } ) ;
307- if ( ! isTriple ( keys ) ) {
308- throw new Error ( `expected key triple` ) ;
309- }
310- const walletKeys = new utxolib . bitgo . RootWalletKeys ( keys , [
311- params . userKeyPath || utxolib . bitgo . RootWalletKeys . defaultPrefix ,
312- utxolib . bitgo . RootWalletKeys . defaultPrefix ,
313- utxolib . bitgo . RootWalletKeys . defaultPrefix ,
314- ] ) ;
313+ const keys = getBip32Privkeys ( bitgo , params ) ;
314+ const walletKeys = fixedScriptWallet . RootWalletKeys . from ( {
315+ triple : keys ,
316+ derivationPrefixes : [ params . userKeyPath || 'm/0/0' , 'm/0/0' , 'm/0/0' ] ,
317+ } ) ;
315318
316319 const unspents : WalletUnspent < bigint > [ ] = (
317320 await Promise . all (
318- outputScripts . scriptTypes2Of3
321+ fixedScriptWallet . outputScriptTypes
319322 . filter (
320- ( addressType ) => coin . supportsAddressType ( addressType ) && ! params . ignoreAddressTypes ?. includes ( addressType )
323+ ( addressType ) =>
324+ fixedScriptWallet . supportsScriptType ( coin . name , addressType ) &&
325+ ! params . ignoreAddressTypes ?. includes ( addressType )
321326 )
322327 . reduce (
323328 ( queries , addressType ) => [
324329 ...queries ,
325- queryBlockchainUnspentsPath ( coin , params , walletKeys , getExternalChainCode ( addressType ) ) ,
326- queryBlockchainUnspentsPath ( coin , params , walletKeys , getInternalChainCode ( addressType ) ) ,
330+ queryBlockchainUnspentsPath (
331+ coin ,
332+ params ,
333+ walletKeys ,
334+ fixedScriptWallet . ChainCode . value ( addressType , 'external' )
335+ ) ,
336+ queryBlockchainUnspentsPath (
337+ coin ,
338+ params ,
339+ walletKeys ,
340+ fixedScriptWallet . ChainCode . value ( addressType , 'internal' )
341+ ) ,
327342 ] ,
328343 [ ] as Promise < WalletUnspent < bigint > [ ] > [ ]
329344 )
330345 )
331346 ) . flat ( ) ;
332347
333348 // Execute the queries and gather the unspents
334- const totalInputAmount = utxolib . bitgo . unspentSum ( unspents , 'bigint' ) ;
349+ const totalInputAmount = unspentSum ( unspents ) ;
335350 if ( totalInputAmount <= BigInt ( 0 ) ) {
336351 throw new ErrorNoInputToRecover ( ) ;
337352 }
@@ -370,20 +385,12 @@ export async function backupKeyRecovery(
370385 }
371386 }
372387
373- // Use wasm-utxo for testnet coins only, utxolib for mainnet
374- const backend : PsbtBackend = isTestnetCoin ( coin . name ) ? 'wasm-utxo' : 'utxolib' ;
375- let psbt = createBackupKeyRecoveryPsbt (
376- coin . getChain ( ) ,
377- walletKeys ,
378- unspents ,
379- {
380- feeRateSatVB : feePerByte ,
381- recoveryDestination : params . recoveryDestination ,
382- keyRecoveryServiceFee : krsFee ,
383- keyRecoveryServiceFeeAddress : krsFeeAddress ,
384- } ,
385- backend
386- ) ;
388+ let psbt = createBackupKeyRecoveryPsbt ( coin . getChain ( ) , walletKeys , unspents , {
389+ feeRateSatVB : feePerByte ,
390+ recoveryDestination : params . recoveryDestination ,
391+ keyRecoveryServiceFee : krsFee ,
392+ keyRecoveryServiceFeeAddress : krsFeeAddress ,
393+ } ) ;
387394
388395 if ( isUnsignedSweep ) {
389396 return {
@@ -398,7 +405,7 @@ export async function backupKeyRecovery(
398405 const replayProtection = { publicKeys : getReplayProtectionPubkeys ( coin . name ) } ;
399406
400407 // Sign with user key first
401- psbt = signAndVerifyPsbt ( psbt , walletKeys . user , rootWalletKeysWasm , replayProtection ) ;
408+ psbt = signAndVerifyPsbt ( psbt , keys [ 0 ] , rootWalletKeysWasm , replayProtection ) ;
402409
403410 if ( isKrsRecovery ) {
404411 // The KRS provider keyternal solely supports P2SH, P2WSH, and P2SH-P2WSH input script types.
@@ -407,20 +414,14 @@ export async function backupKeyRecovery(
407414 // which hinders the integration of the latest BitGoJS SDK with PSBT signing support.
408415 txInfo . transactionHex =
409416 params . krsProvider === 'keyternal'
410- ? utxolib . bitgo . extractP2msOnlyHalfSignedTx ( toPsbtToUtxolibPsbt ( psbt , coin . name ) ) . toBuffer ( ) . toString ( 'hex' )
417+ ? Buffer . from ( psbt . getHalfSignedLegacyFormat ( ) ) . toString ( 'hex' )
411418 : encodeTransaction ( psbt ) . toString ( 'hex' ) ;
412419 } else {
413420 // Sign with backup key
414- psbt = signAndVerifyPsbt ( psbt , walletKeys . backup , rootWalletKeysWasm , replayProtection ) ;
421+ psbt = signAndVerifyPsbt ( psbt , keys [ 1 ] , rootWalletKeysWasm , replayProtection ) ;
415422 // Finalize and extract transaction
416423 psbt . finalizeAllInputs ( ) ;
417- if ( psbt instanceof utxolib . bitgo . UtxoPsbt ) {
418- txInfo . transactionHex = psbt . extractTransaction ( ) . toBuffer ( ) . toString ( 'hex' ) ;
419- } else if ( psbt instanceof fixedScriptWallet . BitGoPsbt ) {
420- txInfo . transactionHex = Buffer . from ( psbt . extractTransaction ( ) . toBytes ( ) ) . toString ( 'hex' ) ;
421- } else {
422- throw new Error ( 'expected a UtxoPsbt or BitGoPsbt object' ) ;
423- }
424+ txInfo . transactionHex = Buffer . from ( psbt . extractTransaction ( ) . toBytes ( ) ) . toString ( 'hex' ) ;
424425 }
425426
426427 if ( isKrsRecovery ) {
0 commit comments