1+ import { AnyBip32Wallet , Bip32WalletAccount , InMemoryWallet , WalletType } from '../types' ;
12import { Cardano , Serialization } from '@cardano-sdk/core' ;
23import { Cip30DataSignature } from '@cardano-sdk/dapp-connector' ;
34import { CustomError } from 'ts-custom-error' ;
4- import { InMemoryWallet , WalletType } from '../types' ;
55import { KeyAgent , KeyPurpose , SignDataContext , TrezorConfig , errors } from '@cardano-sdk/key-management' ;
66import { KeyAgentFactory } from './KeyAgentFactory' ;
77import { Logger } from 'ts-log' ;
@@ -83,6 +83,24 @@ export class SigningCoordinator<WalletMetadata extends {}, AccountMetadata exten
8383 this . #logger = contextLogger ( logger , 'SigningCoordinator' ) ;
8484 }
8585
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+
86104 async signTransaction (
87105 { tx, signContext, options } : SignTransactionProps ,
88106 requestContext : RequestContext < WalletMetadata , AccountMetadata >
@@ -127,11 +145,8 @@ export class SigningCoordinator<WalletMetadata extends {}, AccountMetadata exten
127145 if ( ! emitter$ . observed ) {
128146 return reject ( new WrongTargetError ( 'Not expecting sign requests at this time' ) ) ;
129147 }
130- const account = request . requestContext . wallet . accounts . find (
131- ( { accountIndex, purpose = KeyPurpose . STANDARD } ) =>
132- accountIndex === request . requestContext . accountIndex && request . requestContext . purpose === purpose
133- ) ;
134148
149+ const account = this . #findAccount( request ) ;
135150 if ( ! account ) {
136151 return reject (
137152 new errors . ProofGenerationError (
@@ -144,67 +159,107 @@ export class SigningCoordinator<WalletMetadata extends {}, AccountMetadata exten
144159 ...request ,
145160 reject : async ( reason : string ) => reject ( new errors . AuthenticationError ( reason ) )
146161 } ;
147- emitter$ . next (
162+
163+ const signRequest =
148164 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 )
208263 } ) ;
209264 }
210265}
0 commit comments