1
+ import { AnyBip32Wallet , Bip32WalletAccount , InMemoryWallet , WalletType } from '../types' ;
1
2
import { Cardano , Serialization } from '@cardano-sdk/core' ;
2
3
import { Cip30DataSignature } from '@cardano-sdk/dapp-connector' ;
3
4
import { CustomError } from 'ts-custom-error' ;
4
- import { InMemoryWallet , WalletType } from '../types' ;
5
5
import { KeyAgent , KeyPurpose , SignDataContext , TrezorConfig , errors } from '@cardano-sdk/key-management' ;
6
6
import { KeyAgentFactory } from './KeyAgentFactory' ;
7
7
import { Logger } from 'ts-log' ;
@@ -83,6 +83,24 @@ export class SigningCoordinator<WalletMetadata extends {}, AccountMetadata exten
83
83
this . #logger = contextLogger ( logger , 'SigningCoordinator' ) ;
84
84
}
85
85
86
+ /**
87
+ * Gets the appropriate TrezorConfig for the given wallet.
88
+ *
89
+ * This allows wallets to specify only the properties they want to override
90
+ * (e.g., derivationType) while inheriting global settings (e.g., communicationType, manifest)
91
+ */
92
+ #getTrezorConfig( wallet : AnyBip32Wallet < WalletMetadata , AccountMetadata > ) : TrezorConfig {
93
+ const trezorConfig =
94
+ wallet . type === WalletType . Trezor && 'trezorConfig' in wallet . metadata
95
+ ? ( wallet . metadata as { trezorConfig ?: Partial < TrezorConfig > } ) . trezorConfig
96
+ : undefined ;
97
+
98
+ return {
99
+ ...this . #hwOptions, // Global defaults (communicationType, manifest, etc.)
100
+ ...( trezorConfig || { } ) // Wallet-specific overrides (derivationType, etc.)
101
+ } ;
102
+ }
103
+
86
104
async signTransaction (
87
105
{ tx, signContext, options } : SignTransactionProps ,
88
106
requestContext : RequestContext < WalletMetadata , AccountMetadata >
@@ -127,11 +145,8 @@ export class SigningCoordinator<WalletMetadata extends {}, AccountMetadata exten
127
145
if ( ! emitter$ . observed ) {
128
146
return reject ( new WrongTargetError ( 'Not expecting sign requests at this time' ) ) ;
129
147
}
130
- const account = request . requestContext . wallet . accounts . find (
131
- ( { accountIndex, purpose = KeyPurpose . STANDARD } ) =>
132
- accountIndex === request . requestContext . accountIndex && request . requestContext . purpose === purpose
133
- ) ;
134
148
149
+ const account = this . #findAccount( request ) ;
135
150
if ( ! account ) {
136
151
return reject (
137
152
new errors . ProofGenerationError (
@@ -144,67 +159,107 @@ export class SigningCoordinator<WalletMetadata extends {}, AccountMetadata exten
144
159
...request ,
145
160
reject : async ( reason : string ) => reject ( new errors . AuthenticationError ( reason ) )
146
161
} ;
147
- emitter$ . next (
162
+
163
+ const signRequest =
148
164
request . walletType === WalletType . InMemory
149
- ? ( {
150
- ...commonRequestProps ,
151
- sign : async ( passphrase : Uint8Array , options ?: SignOptions ) =>
152
- bubbleResolveReject (
153
- async ( ) => {
154
- const wallet = request . requestContext . wallet as InMemoryWallet < WalletMetadata , AccountMetadata > ;
155
- try {
156
- const result = await sign (
157
- await this . #keyAgentFactory. InMemory ( {
158
- accountIndex : account . accountIndex ,
159
- chainId : request . requestContext . chainId ,
160
- encryptedRootPrivateKeyBytes : [
161
- ...Buffer . from ( wallet . encryptedSecrets . rootPrivateKeyBytes , 'hex' )
162
- ] ,
163
- extendedAccountPublicKey : account . extendedAccountPublicKey ,
164
- getPassphrase : async ( ) => passphrase ,
165
- purpose : account . purpose || KeyPurpose . STANDARD
166
- } )
167
- ) ;
168
- clearPassphrase ( passphrase ) ;
169
- return result ;
170
- } catch ( error ) {
171
- clearPassphrase ( passphrase ) ;
172
- return throwMaybeWrappedWithNoRejectError ( error , options ) ;
173
- }
174
- } ,
175
- resolve ,
176
- reject
177
- ) ,
178
- walletType : request . walletType
179
- } as Req )
180
- : ( {
181
- ...commonRequestProps ,
182
- sign : async ( ) : Promise < R > =>
183
- bubbleResolveReject (
184
- async ( options ?: SignOptions ) =>
185
- sign (
186
- request . walletType === WalletType . Ledger
187
- ? await this . #keyAgentFactory. Ledger ( {
188
- accountIndex : request . requestContext . accountIndex ,
189
- chainId : request . requestContext . chainId ,
190
- communicationType : this . #hwOptions. communicationType ,
191
- extendedAccountPublicKey : account . extendedAccountPublicKey ,
192
- purpose : account . purpose || KeyPurpose . STANDARD
193
- } )
194
- : await this . #keyAgentFactory. Trezor ( {
195
- accountIndex : request . requestContext . accountIndex ,
196
- chainId : request . requestContext . chainId ,
197
- extendedAccountPublicKey : account . extendedAccountPublicKey ,
198
- purpose : account . purpose || KeyPurpose . STANDARD ,
199
- trezorConfig : this . #hwOptions
200
- } )
201
- ) . catch ( ( error ) => throwMaybeWrappedWithNoRejectError ( error , options ) ) ,
202
- resolve ,
203
- reject
204
- ) ,
205
- walletType : request . walletType
206
- } as Req )
207
- ) ;
165
+ ? this . #createInMemorySignRequest( commonRequestProps , account , sign , resolve , reject )
166
+ : this . #createHardwareSignRequest( commonRequestProps , account , sign , resolve , reject ) ;
167
+
168
+ emitter$ . next ( signRequest ) ;
169
+ } ) ;
170
+ }
171
+
172
+ #findAccount( request : { requestContext : RequestContext < WalletMetadata , AccountMetadata > } ) {
173
+ return request . requestContext . wallet . accounts . find (
174
+ ( { accountIndex, purpose = KeyPurpose . STANDARD } ) =>
175
+ accountIndex === request . requestContext . accountIndex && request . requestContext . purpose === purpose
176
+ ) ;
177
+ }
178
+
179
+ #createInMemorySignRequest< R , Req extends RequestBase < WalletMetadata , AccountMetadata > & SignRequest < R > > (
180
+ commonRequestProps : Omit < Req , 'reject' | 'sign' > ,
181
+ account : Bip32WalletAccount < AccountMetadata > ,
182
+ sign : ( keyAgent : KeyAgent ) => Promise < R > ,
183
+ resolve : ( result : R | Promise < R > ) => void ,
184
+ reject : ( error : unknown ) => void
185
+ ) : Req {
186
+ return {
187
+ ...commonRequestProps ,
188
+ sign : async ( passphrase : Uint8Array , options ?: SignOptions ) =>
189
+ bubbleResolveReject (
190
+ async ( ) => {
191
+ const wallet = commonRequestProps . requestContext . wallet as InMemoryWallet < WalletMetadata , AccountMetadata > ;
192
+ try {
193
+ const result = await sign (
194
+ await this . #keyAgentFactory. InMemory ( {
195
+ accountIndex : account . accountIndex ,
196
+ chainId : commonRequestProps . requestContext . chainId ,
197
+ encryptedRootPrivateKeyBytes : [ ...Buffer . from ( wallet . encryptedSecrets . rootPrivateKeyBytes , 'hex' ) ] ,
198
+ extendedAccountPublicKey : account . extendedAccountPublicKey ,
199
+ getPassphrase : async ( ) => passphrase ,
200
+ purpose : account . purpose || KeyPurpose . STANDARD
201
+ } )
202
+ ) ;
203
+ clearPassphrase ( passphrase ) ;
204
+ return result ;
205
+ } catch ( error ) {
206
+ clearPassphrase ( passphrase ) ;
207
+ return throwMaybeWrappedWithNoRejectError ( error , options ) ;
208
+ }
209
+ } ,
210
+ resolve ,
211
+ reject
212
+ ) ,
213
+ walletType : commonRequestProps . walletType
214
+ } as Req ;
215
+ }
216
+
217
+ #createHardwareSignRequest< R , Req extends RequestBase < WalletMetadata , AccountMetadata > & SignRequest < R > > (
218
+ commonRequestProps : Omit < Req , 'reject' | 'sign' > ,
219
+ account : Bip32WalletAccount < AccountMetadata > ,
220
+ sign : ( keyAgent : KeyAgent ) => Promise < R > ,
221
+ resolve : ( result : R | Promise < R > ) => void ,
222
+ reject : ( error : unknown ) => void
223
+ ) : Req {
224
+ return {
225
+ ...commonRequestProps ,
226
+ sign : async ( ) : Promise < R > =>
227
+ bubbleResolveReject (
228
+ async ( options ?: SignOptions ) => {
229
+ try {
230
+ const keyAgent = await this . #createHardwareKeyAgent( commonRequestProps , account ) ;
231
+ return await sign ( keyAgent ) ;
232
+ } catch ( error ) {
233
+ return throwMaybeWrappedWithNoRejectError ( error , options ) ;
234
+ }
235
+ } ,
236
+ resolve ,
237
+ reject
238
+ ) ,
239
+ walletType : commonRequestProps . walletType
240
+ } as Req ;
241
+ }
242
+
243
+ async #createHardwareKeyAgent(
244
+ request : { requestContext : RequestContext < WalletMetadata , AccountMetadata > ; walletType : WalletType } ,
245
+ account : Bip32WalletAccount < AccountMetadata >
246
+ ) : Promise < KeyAgent > {
247
+ if ( request . walletType === WalletType . Ledger ) {
248
+ return await this . #keyAgentFactory. Ledger ( {
249
+ accountIndex : request . requestContext . accountIndex ,
250
+ chainId : request . requestContext . chainId ,
251
+ communicationType : this . #hwOptions. communicationType ,
252
+ extendedAccountPublicKey : account . extendedAccountPublicKey ,
253
+ purpose : account . purpose || KeyPurpose . STANDARD
254
+ } ) ;
255
+ }
256
+
257
+ return await this . #keyAgentFactory. Trezor ( {
258
+ accountIndex : request . requestContext . accountIndex ,
259
+ chainId : request . requestContext . chainId ,
260
+ extendedAccountPublicKey : account . extendedAccountPublicKey ,
261
+ purpose : account . purpose || KeyPurpose . STANDARD ,
262
+ trezorConfig : this . #getTrezorConfig( request . requestContext . wallet )
208
263
} ) ;
209
264
}
210
265
}
0 commit comments