Skip to content

Commit 59347b0

Browse files
feat: add TrezorKeyringV2 (and OneKeyKeyringV2) (#412)
Related to: https://consensyssoftware.atlassian.net/browse/MUL-1321 ## Examples <!-- Are there any examples of this change being used in another repository? When considering changes to the MetaMask module template, it's strongly preferred that the change be experimented with in another repository first. This gives reviewers a better sense of how the change works, making it less likely the change will need to be reverted or adjusted later. --> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Implements V2 adapters over legacy hardware keyrings to expose accounts via the unified API. > > - Add `TrezorKeyringV2` with capabilities for `bip44:derive-index` and `bip44:derive-path`, derivation path parsing/validation (`m/44'/60'/0'/0`, legacy MEW, SLIP-0044 testnet), account registry caching, and graceful behavior when the device is locked > - Add `OneKeyKeyringV2` extending `TrezorKeyringV2` with `KeyringType.OneKey` > - Export new wrappers from `src/index.ts`; update package deps to `@metamask/keyring-api` and `@metamask/account-api`, tsconfig references, and README dependency graph > - Enhance legacy `trezor-keyring` by exposing `getIndexForAddress` and refactoring path lookup; keep serialization behavior intact > - Add comprehensive tests for account listing/creation, derivation paths, deletion, serialization, and locked-device scenarios; raise Jest coverage thresholds > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 3432867. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Cursor Agent <[email protected]>
1 parent 6c3b576 commit 59347b0

File tree

13 files changed

+1330
-8
lines changed

13 files changed

+1330
-8
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ linkStyle default opacity:0.5
6363
eth_qr_keyring --> keyring_utils;
6464
eth_simple_keyring --> keyring_api;
6565
eth_simple_keyring --> keyring_utils;
66+
eth_trezor_keyring --> keyring_api;
6667
eth_trezor_keyring --> keyring_utils;
68+
eth_trezor_keyring --> account_api;
6769
keyring_internal_api --> keyring_api;
6870
keyring_internal_api --> keyring_utils;
6971
keyring_internal_snap_client --> keyring_api;

packages/keyring-eth-trezor/CHANGELOG.md

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

88
## [Unreleased]
99

10+
### Added
11+
12+
- Add `TrezorKeyringV2` and `OneKeyKeyringV2` classes implementing `KeyringV2` interface ([#412](https://github.com/MetaMask/accounts/pull/412))
13+
- Wraps legacy `TrezorKeyring` and `OneKeyKeyring` to expose accounts via the unified `KeyringV2` API and the `KeyringAccount` type.
14+
- Extends `EthKeyringWrapper` for common Ethereum logic.
15+
1016
## [9.0.0]
1117

1218
### Changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ module.exports = merge(baseConfig, {
2323
// An object that configures minimum threshold enforcement for coverage results
2424
coverageThreshold: {
2525
global: {
26-
branches: 52.38,
27-
functions: 91.22,
28-
lines: 90.15,
29-
statements: 90.35,
26+
branches: 62.65,
27+
functions: 93.15,
28+
lines: 93.57,
29+
statements: 93.66,
3030
},
3131
},
3232
});

packages/keyring-eth-trezor/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
"@ethereumjs/tx": "^5.4.0",
5050
"@ethereumjs/util": "^9.1.0",
5151
"@metamask/eth-sig-util": "^8.2.0",
52+
"@metamask/keyring-api": "workspace:^",
53+
"@metamask/keyring-utils": "workspace:^",
5254
"@metamask/utils": "^11.1.0",
5355
"@trezor/connect-plugin-ethereum": "^9.0.5",
5456
"@trezor/connect-web": "^9.6.0",
@@ -59,8 +61,8 @@
5961
"@ethereumjs/common": "^4.4.0",
6062
"@lavamoat/allow-scripts": "^3.2.1",
6163
"@lavamoat/preinstall-always-fail": "^2.1.0",
64+
"@metamask/account-api": "workspace:^",
6265
"@metamask/auto-changelog": "^3.4.4",
63-
"@metamask/keyring-utils": "workspace:^",
6466
"@ts-bridge/cli": "^0.6.3",
6567
"@types/ethereumjs-tx": "^1.0.1",
6668
"@types/hdkey": "^2.0.1",
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export * from './trezor-keyring';
2+
export * from './trezor-keyring-v2';
23
export * from './onekey-keyring';
4+
export * from './onekey-keyring-v2';
35
export type * from './trezor-bridge';
46
export * from './trezor-connect-bridge';
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { KeyringType } from '@metamask/keyring-api';
2+
3+
import { OneKeyKeyring } from './onekey-keyring';
4+
import { OneKeyKeyringV2 } from './onekey-keyring-v2';
5+
import type { TrezorBridge } from './trezor-bridge';
6+
import { TrezorKeyringV2 } from './trezor-keyring-v2';
7+
8+
const entropySource = 'onekey-device-id-123';
9+
10+
/**
11+
* Get a mock bridge for the OneKeyKeyring.
12+
*
13+
* @returns A mock bridge.
14+
*/
15+
function getMockBridge(): TrezorBridge {
16+
return {
17+
init: jest.fn(),
18+
dispose: jest.fn(),
19+
getPublicKey: jest.fn(),
20+
ethereumSignTransaction: jest.fn(),
21+
ethereumSignMessage: jest.fn(),
22+
ethereumSignTypedData: jest.fn(),
23+
model: undefined,
24+
} as unknown as TrezorBridge;
25+
}
26+
27+
describe('OneKeyKeyringV2', () => {
28+
const createInnerKeyring = (): OneKeyKeyring => {
29+
return new OneKeyKeyring({ bridge: getMockBridge() });
30+
};
31+
32+
describe('constructor', () => {
33+
it('extends TrezorKeyringV2', () => {
34+
const inner = createInnerKeyring();
35+
const wrapper = new OneKeyKeyringV2({
36+
legacyKeyring: inner,
37+
entropySource,
38+
});
39+
40+
expect(wrapper).toBeInstanceOf(TrezorKeyringV2);
41+
});
42+
43+
it('sets the type to OneKey', () => {
44+
const inner = createInnerKeyring();
45+
const wrapper = new OneKeyKeyringV2({
46+
legacyKeyring: inner,
47+
entropySource,
48+
});
49+
50+
expect(wrapper.type).toBe(KeyringType.OneKey);
51+
});
52+
53+
it('stores the entropy source', () => {
54+
const inner = createInnerKeyring();
55+
const wrapper = new OneKeyKeyringV2({
56+
legacyKeyring: inner,
57+
entropySource,
58+
});
59+
60+
expect(wrapper.entropySource).toBe(entropySource);
61+
});
62+
});
63+
});
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { KeyringType, type EntropySourceId } from '@metamask/keyring-api';
2+
3+
import type { OneKeyKeyring } from './onekey-keyring';
4+
import { TrezorKeyringV2 } from './trezor-keyring-v2';
5+
6+
/**
7+
* Options for creating a OneKeyKeyringV2 instance.
8+
*/
9+
export type OneKeyKeyringV2Options = {
10+
legacyKeyring: OneKeyKeyring;
11+
entropySource: EntropySourceId;
12+
};
13+
14+
/**
15+
* Concrete {@link KeyringV2} adapter for {@link OneKeyKeyring}.
16+
*
17+
* This wrapper extends {@link TrezorKeyringV2} since OneKeyKeyring extends
18+
* TrezorKeyring. The only difference is the keyring type identifier.
19+
*/
20+
export class OneKeyKeyringV2 extends TrezorKeyringV2 {
21+
constructor(options: OneKeyKeyringV2Options) {
22+
super({
23+
legacyKeyring: options.legacyKeyring,
24+
entropySource: options.entropySource,
25+
type: KeyringType.OneKey,
26+
});
27+
}
28+
}

0 commit comments

Comments
 (0)