1- import { BaseCoin , BitGoAPI , MethodNotImplementedError } from 'bitgo' ;
1+ import { BaseCoin , BitGoAPI , MethodNotImplementedError , MPCRecoveryOptions } from 'bitgo' ;
22
33import { AbstractEthLikeNewCoins } from '@bitgo/abstract-eth' ;
44import { AbstractUtxoCoin } from '@bitgo/abstract-utxo' ;
55
66import assert from 'assert' ;
77
8- import {
9- isEddsaCoin ,
10- isEthLikeCoin ,
11- isFormattedOfflineVaultTxInfo ,
12- isUtxoCoin ,
13- } from '../../../shared/coinUtils' ;
14- import {
15- DEFAULT_MUSIG_ETH_GAS_PARAMS ,
16- getReplayProtectionOptions ,
17- } from '../../../shared/recoveryUtils' ;
8+ import { isEddsaCoin , isEthLikeCoin , isFormattedOfflineVaultTxInfo , isUtxoCoin } from '../../../shared/coinUtils' ;
9+ import { DEFAULT_MUSIG_ETH_GAS_PARAMS , getReplayProtectionOptions } from '../../../shared/recoveryUtils' ;
10+
1811import { EnclavedExpressClient } from '../clients/enclavedExpressClient' ;
19- import { MasterApiSpecRouteRequest } from '../routers/masterApiSpec' ;
12+ import {
13+ EvmRecoveryOptions ,
14+ MasterApiSpecRouteRequest ,
15+ ScriptType2Of3 ,
16+ SolanaRecoveryOptions ,
17+ UtxoRecoveryOptions ,
18+ } from '../routers/masterApiSpec' ;
2019import { recoverEddsaWallets } from './recoverEddsaWallets' ;
2120import { EnvironmentName } from '../../../shared/types' ;
2221import logger from '../../../logger' ;
22+ import { CoinFamily } from '@bitgo/statics' ;
23+ import { type SolRecoveryOptions } from '@bitgo/sdk-coin-sol' ;
2324
2425interface RecoveryParams {
2526 userKey : string ;
@@ -34,17 +35,77 @@ interface EnclavedRecoveryParams {
3435 backupPub : string ;
3536 apiKey : string ;
3637 unsignedSweepPrebuildTx : any ; // TODO: type this properly once we have the SDK types
37- coinSpecificParams ?: Record < string , undefined > ;
38+ coinSpecificParams ?: EvmRecoveryOptions | UtxoRecoveryOptions | SolanaRecoveryOptions ;
3839 walletContractAddress : string ;
3940}
4041
42+ // Type guards for recovery options
43+ function isUtxoRecoveryOptions ( params : any ) : params is UtxoRecoveryOptions {
44+ return (
45+ params &&
46+ ( params . ignoreAddressTypes !== undefined ||
47+ params . userKeyPath !== undefined ||
48+ params . feeRate !== undefined )
49+ ) ;
50+ }
51+
52+ function isEthRecoveryOptions ( params : any ) : params is EvmRecoveryOptions {
53+ return (
54+ params &&
55+ ( params . gasPrice !== undefined ||
56+ params . gasLimit !== undefined ||
57+ params . eip1559 !== undefined ||
58+ params . replayProtectionOptions !== undefined )
59+ ) ;
60+ }
61+
62+ function isSolanaRecoveryOptions ( params : any ) : params is SolanaRecoveryOptions {
63+ return (
64+ params &&
65+ ( params . tokenContractAddress !== undefined ||
66+ params . closeAtaAddress !== undefined ||
67+ params . recoveryDestinationAtaAddress !== undefined ||
68+ params . programId !== undefined )
69+ ) ;
70+ }
71+
72+ // Validation function to ensure correct params are used with correct coin types
73+ function validateRecoveryParams (
74+ sdkCoin : BaseCoin ,
75+ params ?: EvmRecoveryOptions | UtxoRecoveryOptions | SolanaRecoveryOptions ,
76+ ) {
77+ if ( ! params ) {
78+ return ;
79+ }
80+
81+ if ( isUtxoCoin ( sdkCoin ) ) {
82+ if ( isEthRecoveryOptions ( params ) || isSolanaRecoveryOptions ( params ) ) {
83+ throw new Error ( 'Invalid parameters provided for UTXO coin recovery' ) ;
84+ }
85+ }
86+
87+ if ( isEthLikeCoin ( sdkCoin ) ) {
88+ if ( isUtxoRecoveryOptions ( params ) || isSolanaRecoveryOptions ( params ) ) {
89+ throw new Error ( 'Invalid parameters provided for ETH-like coin recovery' ) ;
90+ }
91+ }
92+
93+ if ( isEddsaCoin ( sdkCoin ) ) {
94+ if ( isUtxoRecoveryOptions ( params ) || isEthRecoveryOptions ( params ) ) {
95+ throw new Error ( 'Invalid parameters provided for Solana coin recovery' ) ;
96+ }
97+ }
98+ }
99+
41100async function handleEthLikeRecovery (
42101 sdkCoin : BaseCoin ,
43102 commonRecoveryParams : RecoveryParams ,
44103 enclavedExpressClient : any ,
45104 params : EnclavedRecoveryParams ,
46105 env : EnvironmentName ,
47106) {
107+ // Validate that we have correct parameters for ETH recovery
108+ validateRecoveryParams ( sdkCoin , params . coinSpecificParams ) ;
48109 try {
49110 const { gasLimit, gasPrice, maxFeePerGas, maxPriorityFeePerGas } = DEFAULT_MUSIG_ETH_GAS_PARAMS ;
50111 const unsignedSweepPrebuildTx = await ( sdkCoin as AbstractEthLikeNewCoins ) . recover ( {
@@ -76,13 +137,34 @@ async function handleEddsaRecovery(
76137 enclavedExpressClient : EnclavedExpressClient ,
77138 params : EnclavedRecoveryParams ,
78139) {
140+ // Validate that we have the correct parameters for Solana recovery
141+ validateRecoveryParams ( sdkCoin , params . coinSpecificParams ) ;
79142 const { recoveryDestination, userKey } = commonRecoveryParams ;
80143 try {
81- const unsignedSweepPrebuildTx = await recoverEddsaWallets ( bitgo , sdkCoin , {
144+ const options : MPCRecoveryOptions = {
82145 bitgoKey : userKey ,
83146 recoveryDestination,
84147 apiKey : params . apiKey ,
85- } ) ;
148+ } ;
149+ let unsignedSweepPrebuildTx : Awaited < ReturnType < typeof recoverEddsaWallets > > ;
150+ if ( sdkCoin . getFamily ( ) === CoinFamily . SOL ) {
151+ const solanaParams = params . coinSpecificParams as SolanaRecoveryOptions ;
152+ console . log ( params ) ;
153+ const solanaRecoveryOptions : SolRecoveryOptions = { ...options }
154+ solanaRecoveryOptions . recoveryDestinationAtaAddress = solanaParams . recoveryDestinationAtaAddress ;
155+ solanaRecoveryOptions . closeAtaAddress = solanaParams . closeAtaAddress ;
156+ solanaRecoveryOptions . tokenContractAddress = solanaParams . tokenContractAddress ;
157+ solanaRecoveryOptions . programId = solanaParams . programId ;
158+ if ( solanaParams . durableNonce ) {
159+ solanaRecoveryOptions . durableNonce = {
160+ publicKey : solanaParams . durableNonce . publicKey ,
161+ secretKey : solanaParams . durableNonce . secretKey ,
162+ } ;
163+ }
164+ unsignedSweepPrebuildTx = await recoverEddsaWallets ( bitgo , sdkCoin , solanaRecoveryOptions ) ;
165+ } else {
166+ unsignedSweepPrebuildTx = await recoverEddsaWallets ( bitgo , sdkCoin , options ) ;
167+ }
86168 logger . info ( 'Unsigned sweep tx: ' , JSON . stringify ( unsignedSweepPrebuildTx , null , 2 ) ) ;
87169
88170 return await enclavedExpressClient . recoveryMPC ( {
@@ -168,7 +250,7 @@ export async function handleRecoveryWalletOnPrem(
168250 apiKey : '' ,
169251 walletContractAddress : '' ,
170252 unsignedSweepPrebuildTx : undefined ,
171- coinSpecificParams : undefined ,
253+ coinSpecificParams : coinSpecificParams ?. sol ,
172254 } ,
173255 ) ;
174256 } else {
@@ -219,7 +301,7 @@ export async function handleRecoveryWalletOnPrem(
219301 backupPub,
220302 apiKey,
221303 unsignedSweepPrebuildTx : undefined ,
222- coinSpecificParams : undefined ,
304+ coinSpecificParams : coinSpecificParams ?. evmRecoveryOptions ,
223305 walletContractAddress,
224306 } ,
225307 bitgo . env as EnvironmentName ,
@@ -234,9 +316,9 @@ export async function handleRecoveryWalletOnPrem(
234316 userKey : userPub ,
235317 backupKey : backupPub ,
236318 bitgoKey : bitgoPub ,
237- ignoreAddressTypes : coinSpecificParams ?. ignoreAddressTypes ?? [ ] ,
238- scan : coinSpecificParams ?. addressScan ,
239- feeRate : coinSpecificParams ?. feeRate ,
319+ ignoreAddressTypes : ( coinSpecificParams ?. utxo ?. ignoreAddressTypes as ScriptType2Of3 [ ] ) ?? [ ] ,
320+ scan : coinSpecificParams ?. utxo ?. scan ,
321+ feeRate : coinSpecificParams ?. utxo ?. feeRate ,
240322 recoveryDestination : recoveryDestinationAddress ,
241323 apiKey,
242324 } ) ;
0 commit comments