Skip to content

Commit 6e2aa22

Browse files
committed
fix: address PR feedbacks
1 parent 92a41fa commit 6e2aa22

4 files changed

Lines changed: 58 additions & 30 deletions

File tree

packages/keyring-eth-trezor/jest.config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ module.exports = merge(baseConfig, {
2525
global: {
2626
branches: 60.92,
2727
functions: 93.15,
28-
lines: 93.5,
29-
statements: 93.59,
28+
lines: 93.4,
29+
statements: 93.5,
3030
},
3131
},
3232
});

packages/keyring-eth-trezor/src/trezor-keyring-v2.test.ts

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -627,9 +627,9 @@ describe('TrezorKeyringV2', () => {
627627
expect(account.options.entropy.derivationPath).toBe(`m/44'/60'/0'/0`);
628628
});
629629

630-
it('clears registry when switching HD paths', async () => {
631-
// Create wrapper with account on BIP44 path
632-
const inner = createInnerKeyring();
630+
it('clears registry when switching HD paths via createAccounts', async () => {
631+
// Create wrapper with account on BIP44 standard path
632+
const inner = createInnerKeyring(); // Uses m/44'/60'/0'/0 by default
633633
inner.setAccountToUnlock(0);
634634
await inner.addAccounts(1);
635635

@@ -643,21 +643,60 @@ describe('TrezorKeyringV2', () => {
643643
expect(initialAccounts).toHaveLength(1);
644644
const initialAccountId = getFirstAccount(initialAccounts).id;
645645

646-
// Switch to legacy MEW path and add account
647-
// This simulates what createAccounts does when path changes
648-
inner.setHdPath(`m/44'/60'/0'`);
649-
inner.hdk = fakeHdKey; // Reset HDKey after setHdPath clears it
650-
inner.setAccountToUnlock(0);
651-
await inner.addAccounts(1);
646+
// Spy on registry.clear to verify it's called when path changes
647+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
648+
const clearSpy = jest.spyOn((wrapper as any).registry, 'clear');
649+
650+
// inner.hdPath is m/44'/60'/0'/0
651+
// Call createAccounts with legacy MEW path (m/44'/60'/0') - this should trigger path change
652+
// We need to reset the HDKey after setHdPath clears it inside createAccounts
653+
const originalSetHdPath = inner.setHdPath.bind(inner);
654+
jest.spyOn(inner, 'setHdPath').mockImplementation((path) => {
655+
originalSetHdPath(path);
656+
inner.hdk = fakeHdKey;
657+
});
658+
659+
await wrapper.createAccounts(derivePathOptions(`m/44'/60'/0'/0`));
652660

653-
// Call createAccounts with the new path - this should clear registry
654-
await wrapper.createAccounts(derivePathOptions(`m/44'/60'/0'/1`));
661+
// Verify registry.clear was called
662+
expect(clearSpy).toHaveBeenCalled();
655663

656-
// The old account should no longer be accessible
664+
// The old account should no longer be accessible because registry was cleared
657665
await expect(wrapper.getAccount(initialAccountId)).rejects.toThrow(
658666
/Account not found/u,
659667
);
660668
});
669+
670+
it('does not clear registry when using same HD path', async () => {
671+
// Create wrapper with account on BIP44 standard path
672+
const inner = createInnerKeyring();
673+
inner.setAccountToUnlock(0);
674+
await inner.addAccounts(1);
675+
676+
const wrapper = new TrezorKeyringV2({
677+
legacyKeyring: inner,
678+
entropySource,
679+
});
680+
681+
// Get initial accounts - this populates the registry
682+
const initialAccounts = await wrapper.getAccounts();
683+
expect(initialAccounts).toHaveLength(1);
684+
const initialAccountId = getFirstAccount(initialAccounts).id;
685+
686+
// Spy on registry.clear
687+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
688+
const clearSpy = jest.spyOn((wrapper as any).registry, 'clear');
689+
690+
// Call createAccounts with same HD path base (m/44'/60'/0'/0) but different index
691+
await wrapper.createAccounts(derivePathOptions(`m/44'/60'/0'/0/1`));
692+
693+
// Verify registry.clear was NOT called
694+
expect(clearSpy).not.toHaveBeenCalled();
695+
696+
// The original account should still be accessible
697+
const account = await wrapper.getAccount(initialAccountId);
698+
expect(account).toBeDefined();
699+
});
661700
});
662701

663702
describe('locked device handling', () => {

packages/keyring-eth-trezor/src/trezor-keyring-v2.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -88,20 +88,13 @@ 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-
9791
constructor(options: TrezorKeyringV2Options) {
9892
super({
9993
type: options.type ?? KeyringType.Trezor,
10094
inner: options.legacyKeyring as TrezorKeyringAsEthKeyring,
10195
capabilities: trezorKeyringV2Capabilities,
10296
});
10397
this.entropySource = options.entropySource;
104-
this.#currentHdPath = this.inner.hdPath as AllowedHdPath;
10598
}
10699

107100
/**
@@ -130,9 +123,6 @@ export class TrezorKeyringV2
130123
// initialized HDKey. The registry will be populated lazily when
131124
// getAccounts() is called after the device is unlocked.
132125
await this.inner.deserialize(state);
133-
134-
// Sync the tracked HD path with the deserialized state
135-
this.#currentHdPath = this.inner.hdPath as AllowedHdPath;
136126
});
137127
}
138128

@@ -304,9 +294,8 @@ export class TrezorKeyringV2
304294
// If the HD path is changing, clear the registry to avoid stale accounts.
305295
// The TrezorKeyring operates on a single path at a time - accounts from
306296
// different paths cannot coexist in the inner keyring.
307-
if (basePath !== this.#currentHdPath) {
297+
if (basePath !== this.inner.hdPath) {
308298
this.registry.clear();
309-
this.#currentHdPath = basePath;
310299
}
311300

312301
this.inner.setHdPath(basePath);

packages/keyring-eth-trezor/src/trezor-keyring.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ export class TrezorKeyring implements Keyring {
203203
const newAccounts: Hex[] = [];
204204

205205
for (let i = from; i < to; i++) {
206-
const address = this.addressFromIndex(pathBase, i);
206+
const address = this.#addressFromIndex(pathBase, i);
207207
if (!this.accounts.includes(address)) {
208208
this.accounts = [...this.accounts, address];
209209
newAccounts.push(address);
@@ -247,7 +247,7 @@ export class TrezorKeyring implements Keyring {
247247
const accounts = [];
248248

249249
for (let i = from; i < to; i++) {
250-
const address = this.addressFromIndex(pathBase, i);
250+
const address = this.#addressFromIndex(pathBase, i);
251251
accounts.push({
252252
address,
253253
balance: null,
@@ -565,7 +565,7 @@ export class TrezorKeyring implements Keyring {
565565
* @param i - The derivation index.
566566
* @returns The checksummed address.
567567
*/
568-
protected addressFromIndex(basePath: string, i: number): Hex {
568+
#addressFromIndex(basePath: string, i: number): Hex {
569569
const dkey = this.hdk.derive(`${basePath}/${i}`);
570570
const address = bytesToHex(publicToAddress(dkey.publicKey, true));
571571
return toChecksumAddress(address);
@@ -587,7 +587,7 @@ export class TrezorKeyring implements Keyring {
587587

588588
if (typeof index === 'undefined') {
589589
for (let i = 0; i < MAX_INDEX; i++) {
590-
if (checksummedAddress === this.addressFromIndex(pathBase, i)) {
590+
if (checksummedAddress === this.#addressFromIndex(pathBase, i)) {
591591
index = i;
592592
break;
593593
}

0 commit comments

Comments
 (0)