Skip to content

Commit f9c3f0d

Browse files
committed
fix: skip generic accounts if not allowed
1 parent e7cb4d8 commit f9c3f0d

2 files changed

Lines changed: 70 additions & 12 deletions

File tree

packages/keyring-snap-bridge/src/SnapKeyring.test.ts

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2739,10 +2739,12 @@ describe('SnapKeyring', () => {
27392739
}
27402740
});
27412741

2742-
it('throws an error when creating generic accounts if not allowed', async () => {
2742+
it('skips and cleans up unsupported generic accounts when not allowed', async () => {
27432743
mockCallbacks.addAccount.mockClear();
27442744
mockCallbacks.saveState.mockClear();
27452745

2746+
mockMessenger.get.mockReturnValue(snapMetadata);
2747+
27462748
// Create a keyring with isAnyAccountTypeAllowed = false
27472749
const restrictedKeyring = new SnapKeyring({
27482750
messenger: mockSnapKeyringMessenger,
@@ -2752,11 +2754,26 @@ describe('SnapKeyring', () => {
27522754

27532755
const genericAccount = {
27542756
...newAccount1,
2757+
id: 'aa11bb22-cc33-4d44-8e55-ff6677889900',
2758+
address: '0xaabbccddee00112233445566778899aabbccddee',
27552759
type: AnyAccountType.Account,
27562760
};
27572761

2762+
const supportedAccount = {
2763+
...newAccount2,
2764+
id: 'bb11bb22-cc33-4d44-9e55-ff6677889900',
2765+
address: '0xbbccddee00112233445566778899aabbccddeeff',
2766+
type: EthAccountType.Eoa,
2767+
};
2768+
2769+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
2770+
27582771
mockMessengerHandleRequest({
2759-
[KeyringRpcMethod.CreateAccounts]: async () => [genericAccount],
2772+
[KeyringRpcMethod.CreateAccounts]: async () => [
2773+
genericAccount,
2774+
supportedAccount,
2775+
],
2776+
[KeyringRpcMethod.DeleteAccount]: async () => null,
27602777
});
27612778

27622779
const options: CreateAccountOptions = {
@@ -2765,12 +2782,31 @@ describe('SnapKeyring', () => {
27652782
groupIndex: 0,
27662783
};
27672784

2768-
await expect(
2769-
restrictedKeyring.createAccounts(snapId, options),
2770-
).rejects.toThrow(`Cannot create generic account '${genericAccount.id}'`);
2785+
const result = await restrictedKeyring.createAccounts(snapId, options);
27712786

2772-
// State should not be saved if validation fails
2773-
expect(mockCallbacks.saveState).not.toHaveBeenCalled();
2787+
expect(result).toHaveLength(1);
2788+
expect(result).not.toContain(genericAccount);
2789+
expect(result).toContain(supportedAccount);
2790+
expect(
2791+
restrictedKeyring.getAccountByAddress(genericAccount.address),
2792+
).toBeUndefined();
2793+
expect(
2794+
restrictedKeyring.getAccountByAddress(supportedAccount.address),
2795+
).toBeDefined();
2796+
2797+
expect(consoleSpy).toHaveBeenCalledWith(
2798+
`SnapKeyring - Found an unsupported generic account: ${genericAccount.id}`,
2799+
);
2800+
2801+
expect(mockMessenger.handleRequest).toHaveBeenCalledWith(
2802+
mockKeyringRpcRequest(KeyringRpcMethod.DeleteAccount, {
2803+
id: genericAccount.id,
2804+
}),
2805+
);
2806+
2807+
expect(mockCallbacks.saveState).toHaveBeenCalled();
2808+
2809+
consoleSpy.mockRestore();
27742810
});
27752811
});
27762812

packages/keyring-snap-bridge/src/SnapKeyring.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,9 @@ export class SnapKeyring {
900900
* options should always return the same accounts, even if the accounts
901901
* already exist in the keyring.
902902
*
903+
* NOTE: If generic accounts are not allowed, this method will skip their
904+
* creation and ask the Snap to remove them from its state.
905+
*
903906
* @param snapId - Snap ID to create the account(s) for.
904907
* @param options - Options describing how to create the account(s).
905908
* @returns An array of the created account objects.
@@ -913,24 +916,43 @@ export class SnapKeyring {
913916
snapId,
914917
});
915918

916-
// Add each returned account to the internal accounts map.
919+
const unsupportedAccountIds = [];
920+
917921
// NOTE: This method DOES NOT rely on the `AccountCreated` event to add
918922
// accounts to the keyring, since those accounts are created in batch.
919-
const accounts = await client.createAccounts(options);
920-
for (const account of accounts) {
923+
const snapAccounts = await client.createAccounts(options);
924+
925+
// Add each returned account to the internal accounts map and maybe filter
926+
// unsupported generic accounts.
927+
const accounts = [];
928+
for (const account of snapAccounts) {
921929
// The `AnyAccountType.Account` generic account type is allowed only during
922930
// development, so we check whether it's allowed before continuing.
923931
if (
924932
!this.#isAnyAccountTypeAllowed &&
925933
account.type === AnyAccountType.Account
926934
) {
927-
throw new Error(`Cannot create generic account '${account.id}'`);
935+
console.warn(
936+
`SnapKeyring - Found an unsupported generic account: ${account.id}`,
937+
);
938+
unsupportedAccountIds.push(account.id);
939+
940+
continue; // Skip adding this account.
928941
}
929942

943+
// Update the internal accounts map since `createAccounts` does not register
944+
// them through the `AccountCreated` event.
930945
this.#accounts.set(account.id, { account, snapId });
946+
947+
accounts.push(account);
948+
}
949+
950+
// Cleanup if we found unsupported accounts to avoid avoid "dangling" accounts
951+
// on the Snap side.
952+
for (const accountId of unsupportedAccountIds) {
953+
await client.deleteAccount(accountId);
931954
}
932955

933-
// Save the state after adding all accounts.
934956
await this.#callbacks.saveState();
935957

936958
return accounts;

0 commit comments

Comments
 (0)