Skip to content
This repository was archived by the owner on Oct 7, 2024. It is now read-only.

Commit c111e49

Browse files
authored
Dispose or destroy keyrings on reference drop (#233)
* feat: dispose or destroy keyrings on removal * refactor: use for instead of while cycle * rollback refactor * test: refactor test case * refactor: make dispose async * test: add destroy method to keyring mock * ci: adjust jest coverage threshold
1 parent 5a16d21 commit c111e49

File tree

4 files changed

+62
-8
lines changed

4 files changed

+62
-8
lines changed

jest.config.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ module.exports = {
4141
// An object that configures minimum threshold enforcement for coverage results
4242
coverageThreshold: {
4343
global: {
44-
branches: 70.37,
45-
functions: 92.72,
46-
lines: 90.72,
47-
statements: 90.93,
44+
branches: 70.45,
45+
functions: 92.85,
46+
lines: 90.57,
47+
statements: 90.78,
4848
},
4949
},
5050
preset: 'ts-jest',

src/KeyringController.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,15 @@ describe('KeyringController', () => {
7878

7979
expect(lockSpy.calledOnce).toBe(true);
8080
});
81+
82+
it('calls keyring optional destroy function', async () => {
83+
const destroy = sinon.spy(KeyringMockWithInit.prototype, 'destroy');
84+
await keyringController.addNewKeyring('Keyring Mock With Init');
85+
86+
await keyringController.setLocked();
87+
88+
expect(destroy.calledOnce).toBe(true);
89+
});
8190
});
8291

8392
describe('submitPassword', () => {
@@ -544,6 +553,18 @@ describe('KeyringController', () => {
544553
expect(keyringController.keyrings).toHaveLength(1);
545554
});
546555

556+
it('calls keyring optional destroy function', async () => {
557+
const destroy = sinon.spy(KeyringMockWithInit.prototype, 'destroy');
558+
const keyring = await keyringController.addNewKeyring(
559+
'Keyring Mock With Init',
560+
);
561+
sinon.stub(keyringController, 'getKeyringForAccount').resolves(keyring);
562+
563+
await keyringController.removeAccount('0x0');
564+
565+
expect(destroy.calledOnce).toBe(true);
566+
});
567+
547568
it('does not remove the keyring if there are accounts remaining after removing one from the keyring', async () => {
548569
// Add a new keyring with two accounts
549570
await keyringController.addNewKeyring(KeyringType.HD, {

src/KeyringController.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,7 @@ class KeyringController extends EventEmitter {
175175
});
176176

177177
// remove keyrings
178-
this.keyrings = [];
179-
await this.updateMemStoreKeyrings();
178+
await this.#clearKeyrings();
180179
this.emit('lock');
181180
return this.fullUpdate();
182181
}
@@ -641,6 +640,8 @@ class KeyringController extends EventEmitter {
641640
const accounts = await keyring.getAccounts();
642641
if (accounts.length > 0) {
643642
validKeyrings.push(keyring);
643+
} else {
644+
await this.#disposeKeyring(keyring);
644645
}
645646
}),
646647
);
@@ -984,16 +985,40 @@ class KeyringController extends EventEmitter {
984985
* Clear Keyrings
985986
*
986987
* Deallocates all currently managed keyrings and accounts.
987-
* Used before initializing a new vault.
988+
* Used before initializing a new vault and after locking
989+
* MetaMask.
988990
*/
989-
async #clearKeyrings(): Promise<void> {
991+
async #clearKeyrings() {
990992
// clear keyrings from memory
993+
for (const keyring of this.keyrings) {
994+
await this.#disposeKeyring(keyring);
995+
}
991996
this.keyrings = [];
992997
this.memStore.updateState({
993998
keyrings: [],
994999
});
9951000
}
9961001

1002+
/**
1003+
* Dispose Keyring
1004+
*
1005+
* The Trezor Keyring has a method called `dispose` that removes the
1006+
* iframe from the DOM. The Ledger Keyring has a method called `destroy`
1007+
* that clears the keyring event listeners. This method checks if the provided
1008+
* keyring has either of these methods and calls them if they exist.
1009+
*
1010+
* @param keyring - The keyring to dispose or destroy.
1011+
*/
1012+
async #disposeKeyring(keyring: Keyring<Json>) {
1013+
if ('dispose' in keyring && typeof keyring.dispose === 'function') {
1014+
await keyring.dispose();
1015+
}
1016+
1017+
if ('destroy' in keyring && typeof keyring.destroy === 'function') {
1018+
await keyring.destroy();
1019+
}
1020+
}
1021+
9971022
/**
9981023
* Unlock Keyrings
9991024
*

src/test/keyring.mock.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ class KeyringMockWithInit implements Keyring<Json> {
3333
async deserialize(_: any) {
3434
return Promise.resolve();
3535
}
36+
37+
async removeAccount(_: any) {
38+
return Promise.resolve();
39+
}
40+
41+
async destroy() {
42+
return Promise.resolve();
43+
}
3644
}
3745

3846
export default KeyringMockWithInit;

0 commit comments

Comments
 (0)