@@ -26,6 +26,12 @@ import {
2626 MPCAlgorithm ,
2727 EDDSAMethods ,
2828 EDDSAMethodTypes ,
29+ MPCTx ,
30+ MPCUnsignedTx ,
31+ RecoveryTxRequest ,
32+ MPCSweepTxs ,
33+ MPCSweepRecoveryOptions ,
34+ MPCTxs ,
2935 MultisigType ,
3036 multisigTypes ,
3137} from '@bitgo/sdk-core' ;
@@ -85,11 +91,6 @@ interface RecoveryOptions {
8591 scan ?: number ;
8692}
8793
88- interface NearTx {
89- serializedTx : string ;
90- scanIndex : number ;
91- }
92-
9394interface NearTxBuilderParamsFromNode {
9495 nonce : number ;
9596 blockHash : string ;
@@ -327,7 +328,7 @@ export class Near extends BaseCoin {
327328 * Builds a funds recovery transaction without BitGo
328329 * @param params
329330 */
330- async recover ( params : RecoveryOptions ) : Promise < NearTx > {
331+ async recover ( params : RecoveryOptions ) : Promise < MPCTx | MPCSweepTxs > {
331332 if ( ! params . bitgoKey ) {
332333 throw new Error ( 'missing bitgoKey' ) ;
333334 }
@@ -397,9 +398,9 @@ export class Near extends BaseCoin {
397398 . receiverId ( params . recoveryDestination )
398399 . recentBlockHash ( blockHash )
399400 . amount ( netAmount . toFixed ( ) ) ;
400-
401+ const unsignedTransaction = ( await txBuilder . build ( ) ) as Transaction ;
402+ let serializedTx = unsignedTransaction . toBroadcastFormat ( ) ;
401403 if ( ! isUnsignedSweep ) {
402- const unsignedTransaction = ( await txBuilder . build ( ) ) as Transaction ;
403404 // Sign the txn
404405 /* ***************** START **************************************/
405406 // TODO(BG-51092): This looks like a common part which can be extracted out too
@@ -451,15 +452,120 @@ export class Near extends BaseCoin {
451452 ) ;
452453 const publicKeyObj = { pub : accountId } ;
453454 txBuilder . addSignature ( publicKeyObj as PublicKey , signatureHex ) ;
454- }
455455
456- const completedTransaction = await txBuilder . build ( ) ;
457- const serializedTx = completedTransaction . toBroadcastFormat ( ) ;
456+ const completedTransaction = await txBuilder . build ( ) ;
457+ serializedTx = completedTransaction . toBroadcastFormat ( ) ;
458+ } else {
459+ const value = new BigNumber ( netAmount ) ; // Use the calculated netAmount for the transaction
460+ const walletCoin = this . getChain ( ) ;
461+ const inputs = [
462+ {
463+ address : accountId , // The sender's account ID
464+ valueString : value . toString ( ) ,
465+ value : value . toNumber ( ) ,
466+ } ,
467+ ] ;
468+ const outputs = [
469+ {
470+ address : params . recoveryDestination , // The recovery destination address
471+ valueString : value . toString ( ) ,
472+ coinName : walletCoin ,
473+ } ,
474+ ] ;
475+ const spendAmount = value . toString ( ) ;
476+ const parsedTx = { inputs : inputs , outputs : outputs , spendAmount : spendAmount , type : '' } ;
477+ const feeInfo = { fee : totalGasWithPadding . toNumber ( ) , feeString : totalGasWithPadding . toFixed ( ) } ; // Include gas fees
478+
479+ const transaction : MPCTx = {
480+ serializedTx : serializedTx , // Serialized unsigned transaction
481+ scanIndex : i , // Current index in the scan
482+ coin : walletCoin ,
483+ signableHex : unsignedTransaction . signablePayload . toString ( 'hex' ) , // Hex payload for signing
484+ derivationPath : currPath , // Derivation path for the account
485+ parsedTx : parsedTx ,
486+ feeInfo : feeInfo ,
487+ coinSpecific : { commonKeychain : bitgoKey } , // Include block hash for NEAR
488+ } ;
489+
490+ const transactions : MPCUnsignedTx [ ] = [ { unsignedTx : transaction , signatureShares : [ ] } ] ;
491+ const txRequest : RecoveryTxRequest = {
492+ transactions : transactions ,
493+ walletCoin : walletCoin ,
494+ } ;
495+ return { txRequests : [ txRequest ] } ;
496+ }
458497 return { serializedTx : serializedTx , scanIndex : i } ;
459498 }
460499 throw new Error ( 'Did not find an address with funds to recover' ) ;
461500 }
462501
502+ async createBroadcastableSweepTransaction ( params : MPCSweepRecoveryOptions ) : Promise < MPCTxs > {
503+ const req = params . signatureShares ;
504+ const broadcastableTransactions : MPCTx [ ] = [ ] ;
505+ let lastScanIndex = 0 ;
506+
507+ for ( let i = 0 ; i < req . length ; i ++ ) {
508+ const MPC = await EDDSAMethods . getInitializedMpcInstance ( ) ;
509+ const transaction = req [ i ] . txRequest . transactions [ 0 ] . unsignedTx ;
510+
511+ // Validate signature shares
512+ if ( ! req [ i ] . ovc || ! req [ i ] . ovc [ 0 ] . eddsaSignature ) {
513+ throw new Error ( 'Missing signature(s)' ) ;
514+ }
515+ const signature = req [ i ] . ovc [ 0 ] . eddsaSignature ;
516+
517+ // Validate signable hex
518+ if ( ! transaction . signableHex ) {
519+ throw new Error ( 'Missing signable hex' ) ;
520+ }
521+ const messageBuffer = Buffer . from ( transaction . signableHex ! , 'hex' ) ;
522+ const result = MPC . verify ( messageBuffer , signature ) ;
523+ if ( ! result ) {
524+ throw new Error ( 'Invalid signature' ) ;
525+ }
526+
527+ // Prepare the signature in hex format
528+ const signatureHex = Buffer . concat ( [ Buffer . from ( signature . R , 'hex' ) , Buffer . from ( signature . sigma , 'hex' ) ] ) ;
529+
530+ // Validate transaction-specific fields
531+ if ( ! transaction . coinSpecific ?. commonKeychain ) {
532+ throw new Error ( 'Missing common keychain' ) ;
533+ }
534+ const commonKeychain = transaction . coinSpecific ! . commonKeychain ! as string ;
535+
536+ if ( ! transaction . derivationPath ) {
537+ throw new Error ( 'Missing derivation path' ) ;
538+ }
539+ const derivationPath = transaction . derivationPath as string ;
540+
541+ // Derive account ID and sender address
542+ const accountId = MPC . deriveUnhardened ( commonKeychain , derivationPath ) . slice ( 0 , 64 ) ;
543+ const txnBuilder = this . getBuilder ( ) . from ( transaction . serializedTx as string ) ;
544+
545+ // Add the signature
546+ const nearKeyPair = new NearKeyPair ( { pub : accountId } ) ;
547+ txnBuilder . addSignature ( { pub : nearKeyPair . getKeys ( ) . pub } , signatureHex ) ;
548+
549+ // Finalize and serialize the transaction
550+ const signedTransaction = await txnBuilder . build ( ) ;
551+ const serializedTx = signedTransaction . toBroadcastFormat ( ) ;
552+
553+ // Add the signed transaction to the list
554+ broadcastableTransactions . push ( {
555+ serializedTx : serializedTx ,
556+ scanIndex : transaction . scanIndex ,
557+ } ) ;
558+
559+ // Update the last scan index if applicable
560+ if ( i === req . length - 1 && transaction . coinSpecific ! . lastScanIndex ) {
561+ lastScanIndex = transaction . coinSpecific ! . lastScanIndex as number ;
562+ }
563+ }
564+
565+ // Return the broadcastable transactions and the last scan index
566+ return { transactions : broadcastableTransactions , lastScanIndex } ;
567+ }
568+
463569 /**
464570 * Make a request to one of the public EOS nodes available
465571 * @param params.payload
0 commit comments