@@ -88,13 +88,20 @@ export class TrezorKeyringV2
8888{
8989 readonly entropySource : EntropySourceId ;
9090
91+ /**
92+ * Tracks the current HD path to detect path changes.
93+ * When the path changes, the registry must be cleared.
94+ */
95+ #currentHdPath: AllowedHdPath ;
96+
9197 constructor ( options : TrezorKeyringV2Options ) {
9298 super ( {
9399 type : options . type ?? KeyringType . Trezor ,
94100 inner : options . legacyKeyring as TrezorKeyringAsEthKeyring ,
95101 capabilities : trezorKeyringV2Capabilities ,
96102 } ) ;
97103 this . entropySource = options . entropySource ;
104+ this . #currentHdPath = this . inner . hdPath as AllowedHdPath ;
98105 }
99106
100107 /**
@@ -123,6 +130,9 @@ export class TrezorKeyringV2
123130 // initialized HDKey. The registry will be populated lazily when
124131 // getAccounts() is called after the device is unlocked.
125132 await this . inner . deserialize ( state ) ;
133+
134+ // Sync the tracked HD path with the deserialized state
135+ this . #currentHdPath = this . inner . hdPath as AllowedHdPath ;
126136 } ) ;
127137 }
128138
@@ -193,6 +203,30 @@ export class TrezorKeyringV2
193203 return [ ] ;
194204 }
195205
206+ // If the device is locked, we cannot derive addresses to find indices.
207+ // Return cached accounts if available, otherwise throw a clear error.
208+ if ( ! this . inner . isUnlocked ( ) ) {
209+ const cachedAccounts = addresses
210+ . map ( ( address ) => {
211+ const existingId = this . registry . getAccountId ( address ) ;
212+ return existingId ? this . registry . get ( existingId ) : undefined ;
213+ } )
214+ . filter (
215+ ( account ) : account is Bip44Account < KeyringAccount > =>
216+ account !== undefined ,
217+ ) ;
218+
219+ // If we have all accounts cached, return them
220+ if ( cachedAccounts . length === addresses . length ) {
221+ return cachedAccounts ;
222+ }
223+
224+ // Some accounts are not cached and device is locked
225+ throw new Error (
226+ 'Trezor device is locked. Please unlock the device to access accounts.' ,
227+ ) ;
228+ }
229+
196230 return addresses . map ( ( address ) => {
197231 // Check if we already have this account in the registry
198232 const existingId = this . registry . getAccountId ( address ) ;
@@ -267,12 +301,14 @@ export class TrezorKeyringV2
267301 }
268302
269303 // Derive the account at the specified index.
270- // Note: setHdPath resets the inner keyring's accounts array when the path changes.
271- // This mirrors the production behavior where switching HD paths (via the dropdown)
272- // resets the entire account list. The TrezorKeyring operates on a single path at
273- // a time - accounts from different paths cannot coexist. The V2 registry tracks
274- // accounts independently, but getAccounts() relies on the inner keyring's account
275- // list, so accounts from a previous path would become inaccessible after switching.
304+ // If the HD path is changing, clear the registry to avoid stale accounts.
305+ // The TrezorKeyring operates on a single path at a time - accounts from
306+ // different paths cannot coexist in the inner keyring.
307+ if ( basePath !== this . #currentHdPath) {
308+ this . registry . clear ( ) ;
309+ this . #currentHdPath = basePath ;
310+ }
311+
276312 this . inner . setHdPath ( basePath ) ;
277313 this . inner . setAccountToUnlock ( targetIndex ) ;
278314 const [ newAddress ] = await this . inner . addAccounts ( 1 ) ;
0 commit comments