Skip to content

Commit 549706a

Browse files
authored
feat(keyring-sdk): add generateId to KeyringAccountRegistry (#503)
Add a new option to be able to customize the `KeyringAccountRegistry` ID generation. I'll follow-up with other PRs to make the `EthKeyringWrapper` class to use deterministic IDs similarly to what we do today for EVM account IDs. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk: change is additive and localized to `KeyringAccountRegistry` ID creation, with default behavior preserved; primary risk is downstream assumptions about UUID formatting if consumers supply non-UUID IDs. > > **Overview** > Adds a `KeyringAccountRegistryOptions.generateId` hook and wires `register(address)` to call it when creating new IDs, while keeping UUIDv4 as the default. > > Updates tests to cover default UUIDv4 behavior and custom generator usage, and notes the addition in the `keyring-sdk` changelog. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 19a95c6. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 62c919b commit 549706a

3 files changed

Lines changed: 57 additions & 2 deletions

File tree

packages/keyring-sdk/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Add `generateId` option to `KeyringAccountRegistry` ([#503](https://github.com/MetaMask/accounts/pull/503))
13+
1014
## [1.1.0]
1115

1216
### Added

packages/keyring-sdk/src/keyring-account-registry.test.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import type { KeyringAccount } from '@metamask/keyring-api';
22
import type { AccountId } from '@metamask/keyring-utils';
33

4-
import { KeyringAccountRegistry } from './keyring-account-registry';
4+
import {
5+
KeyringAccountRegistry,
6+
type KeyringAccountRegistryOptions,
7+
} from './keyring-account-registry';
58

69
/**
710
* Creates a mock KeyringAccount for testing.
@@ -22,6 +25,29 @@ function createMockAccount(id: AccountId, address: string): KeyringAccount {
2225
}
2326

2427
describe('KeyringAccountRegistry', () => {
28+
describe('constructor', () => {
29+
it('uses uuidv4 by default', () => {
30+
const registry = new KeyringAccountRegistry();
31+
const id = registry.register('0xaBc');
32+
33+
expect(id).toMatch(
34+
/^[\da-f]{8}-[\da-f]{4}-4[\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}$/u,
35+
);
36+
});
37+
38+
it('uses the provided generateId callback', () => {
39+
const generateId = jest
40+
.fn<AccountId, [string]>()
41+
.mockReturnValue('custom-id' as AccountId);
42+
const options: KeyringAccountRegistryOptions = { generateId };
43+
const registry = new KeyringAccountRegistry(options);
44+
const id = registry.register('0xaBc');
45+
46+
expect(generateId).toHaveBeenCalledWith('0xaBc');
47+
expect(id).toBe('custom-id');
48+
});
49+
});
50+
2551
describe('register', () => {
2652
it('registers an address and returns an account ID', () => {
2753
const registry = new KeyringAccountRegistry();

packages/keyring-sdk/src/keyring-account-registry.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@ import type { KeyringAccount } from '@metamask/keyring-api';
22
import type { AccountId } from '@metamask/keyring-utils';
33
import { v4 as uuidv4 } from 'uuid';
44

5+
export type KeyringAccountRegistryOptions = {
6+
/**
7+
* Custom ID generator invoked when registering a new address.
8+
*
9+
* Receives the address being registered and must return a unique account ID.
10+
* Defaults to a random UUID v4 when not provided.
11+
*
12+
* @param address - The address for which to generate an account ID.
13+
* @returns A unique account ID string.
14+
*/
15+
generateId?: (address: string) => AccountId;
16+
};
17+
518
/**
619
* In-memory registry for KeyringAccount objects.
720
*
@@ -15,6 +28,18 @@ export class KeyringAccountRegistry<
1528

1629
readonly #idByAddress = new Map<string, AccountId>();
1730

31+
readonly #generateId: (address: string) => AccountId;
32+
33+
/**
34+
* Create a new KeyringAccountRegistry.
35+
*
36+
* @param options - Optional configuration.
37+
* @param options.generateId - Custom ID generator. Defaults to `uuidv4`.
38+
*/
39+
constructor(options?: KeyringAccountRegistryOptions) {
40+
this.#generateId = options?.generateId ?? ((): AccountId => uuidv4());
41+
}
42+
1843
/**
1944
* Get an account by its account ID.
2045
*
@@ -57,7 +82,7 @@ export class KeyringAccountRegistry<
5782
if (existing) {
5883
return existing;
5984
}
60-
const id = uuidv4();
85+
const id = this.#generateId(address);
6186
this.#idByAddress.set(address, id);
6287
return id;
6388
}

0 commit comments

Comments
 (0)