@metamask-previews/eth-qr-keyring 1.1.0-e40d1ad → 1.1.0-fd40efd

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+
12
+ - Add `QrKeyringV2` class implementing `KeyringV2` interface ([#411](https://github.com/MetaMask/accounts/pull/411)), ([#447](https://github.com/MetaMask/accounts/pull/447)), ([#451](https://github.com/MetaMask/accounts/pull/451)), ([#453](https://github.com/MetaMask/accounts/pull/453))
13
+ - Wraps legacy `QrKeyring` to expose accounts via the unified `KeyringV2` API and the `KeyringAccount` type.
14
+ - Extends `EthKeyringWrapper` for common Ethereum logic.
15
+
10
16
  ## [1.1.0]
11
17
 
12
18
  ### Added
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.QrScanRequestType = exports.QR_KEYRING_TYPE = exports.QrKeyring = exports.QrKeyringDeferredPromiseBridge = exports.QrKeyringScannerBridge = void 0;
3
+ exports.QrKeyringV2 = exports.QrScanRequestType = exports.QR_KEYRING_TYPE = exports.QrKeyring = exports.QrKeyringDeferredPromiseBridge = exports.QrKeyringScannerBridge = void 0;
4
4
  var qr_keyring_scanner_bridge_1 = require("./qr-keyring-scanner-bridge.cjs");
5
5
  Object.defineProperty(exports, "QrKeyringScannerBridge", { enumerable: true, get: function () { return qr_keyring_scanner_bridge_1.QrKeyringScannerBridge; } });
6
6
  var qr_keyring_deferred_promise_bridge_1 = require("./qr-keyring-deferred-promise-bridge.cjs");
@@ -9,4 +9,6 @@ var qr_keyring_1 = require("./qr-keyring.cjs");
9
9
  Object.defineProperty(exports, "QrKeyring", { enumerable: true, get: function () { return qr_keyring_1.QrKeyring; } });
10
10
  Object.defineProperty(exports, "QR_KEYRING_TYPE", { enumerable: true, get: function () { return qr_keyring_1.QR_KEYRING_TYPE; } });
11
11
  Object.defineProperty(exports, "QrScanRequestType", { enumerable: true, get: function () { return qr_keyring_1.QrScanRequestType; } });
12
+ var qr_keyring_v2_1 = require("./qr-keyring-v2.cjs");
13
+ Object.defineProperty(exports, "QrKeyringV2", { enumerable: true, get: function () { return qr_keyring_v2_1.QrKeyringV2; } });
12
14
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,6EAGqC;AAFnC,mIAAA,sBAAsB,OAAA;AAGxB,+FAG8C;AAF5C,oJAAA,8BAA8B,OAAA;AAGhC,+CAUsB;AATpB,uGAAA,SAAS,OAAA;AACT,6GAAA,eAAe,OAAA;AACf,+GAAA,iBAAiB,OAAA","sourcesContent":["export {\n QrKeyringScannerBridge,\n type QrKeyringScannerBridgeOptions,\n} from './qr-keyring-scanner-bridge';\nexport {\n QrKeyringDeferredPromiseBridge,\n type QrKeyringDeferredPromiseBridgeOptions,\n} from './qr-keyring-deferred-promise-bridge';\nexport {\n QrKeyring,\n QR_KEYRING_TYPE,\n QrScanRequestType,\n type QrScanRequest,\n type QrKeyringBridge,\n type QrKeyringOptions,\n type QrSignatureRequest,\n type SerializedQrKeyringState,\n type SerializedUR,\n} from './qr-keyring';\n"]}
1
+ {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,6EAGqC;AAFnC,mIAAA,sBAAsB,OAAA;AAGxB,+FAG8C;AAF5C,oJAAA,8BAA8B,OAAA;AAGhC,+CAUsB;AATpB,uGAAA,SAAS,OAAA;AACT,6GAAA,eAAe,OAAA;AACf,+GAAA,iBAAiB,OAAA;AAQnB,qDAIyB;AAHvB,4GAAA,WAAW,OAAA","sourcesContent":["export {\n QrKeyringScannerBridge,\n type QrKeyringScannerBridgeOptions,\n} from './qr-keyring-scanner-bridge';\nexport {\n QrKeyringDeferredPromiseBridge,\n type QrKeyringDeferredPromiseBridgeOptions,\n} from './qr-keyring-deferred-promise-bridge';\nexport {\n QrKeyring,\n QR_KEYRING_TYPE,\n QrScanRequestType,\n type QrScanRequest,\n type QrKeyringBridge,\n type QrKeyringOptions,\n type QrSignatureRequest,\n type SerializedQrKeyringState,\n type SerializedUR,\n} from './qr-keyring';\nexport {\n QrKeyringV2,\n type QrKeyringV2Options,\n type QrAccountModeCreateOptions,\n} from './qr-keyring-v2';\n"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { QrKeyringScannerBridge, type QrKeyringScannerBridgeOptions, } from "./qr-keyring-scanner-bridge.cjs";
2
2
  export { QrKeyringDeferredPromiseBridge, type QrKeyringDeferredPromiseBridgeOptions, } from "./qr-keyring-deferred-promise-bridge.cjs";
3
3
  export { QrKeyring, QR_KEYRING_TYPE, QrScanRequestType, type QrScanRequest, type QrKeyringBridge, type QrKeyringOptions, type QrSignatureRequest, type SerializedQrKeyringState, type SerializedUR, } from "./qr-keyring.cjs";
4
+ export { QrKeyringV2, type QrKeyringV2Options, type QrAccountModeCreateOptions, } from "./qr-keyring-v2.cjs";
4
5
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,6BAA6B,GACnC,wCAAoC;AACrC,OAAO,EACL,8BAA8B,EAC9B,KAAK,qCAAqC,GAC3C,iDAA6C;AAC9C,OAAO,EACL,SAAS,EACT,eAAe,EACf,iBAAiB,EACjB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,YAAY,GAClB,yBAAqB"}
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,6BAA6B,GACnC,wCAAoC;AACrC,OAAO,EACL,8BAA8B,EAC9B,KAAK,qCAAqC,GAC3C,iDAA6C;AAC9C,OAAO,EACL,SAAS,EACT,eAAe,EACf,iBAAiB,EACjB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,YAAY,GAClB,yBAAqB;AACtB,OAAO,EACL,WAAW,EACX,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,GAChC,4BAAwB"}
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { QrKeyringScannerBridge, type QrKeyringScannerBridgeOptions, } from "./qr-keyring-scanner-bridge.mjs";
2
2
  export { QrKeyringDeferredPromiseBridge, type QrKeyringDeferredPromiseBridgeOptions, } from "./qr-keyring-deferred-promise-bridge.mjs";
3
3
  export { QrKeyring, QR_KEYRING_TYPE, QrScanRequestType, type QrScanRequest, type QrKeyringBridge, type QrKeyringOptions, type QrSignatureRequest, type SerializedQrKeyringState, type SerializedUR, } from "./qr-keyring.mjs";
4
+ export { QrKeyringV2, type QrKeyringV2Options, type QrAccountModeCreateOptions, } from "./qr-keyring-v2.mjs";
4
5
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,6BAA6B,GACnC,wCAAoC;AACrC,OAAO,EACL,8BAA8B,EAC9B,KAAK,qCAAqC,GAC3C,iDAA6C;AAC9C,OAAO,EACL,SAAS,EACT,eAAe,EACf,iBAAiB,EACjB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,YAAY,GAClB,yBAAqB"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,6BAA6B,GACnC,wCAAoC;AACrC,OAAO,EACL,8BAA8B,EAC9B,KAAK,qCAAqC,GAC3C,iDAA6C;AAC9C,OAAO,EACL,SAAS,EACT,eAAe,EACf,iBAAiB,EACjB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,YAAY,GAClB,yBAAqB;AACtB,OAAO,EACL,WAAW,EACX,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,GAChC,4BAAwB"}
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  export { QrKeyringScannerBridge } from "./qr-keyring-scanner-bridge.mjs";
2
2
  export { QrKeyringDeferredPromiseBridge } from "./qr-keyring-deferred-promise-bridge.mjs";
3
3
  export { QrKeyring, QR_KEYRING_TYPE, QrScanRequestType } from "./qr-keyring.mjs";
4
+ export { QrKeyringV2 } from "./qr-keyring-v2.mjs";
4
5
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EAEvB,wCAAoC;AACrC,OAAO,EACL,8BAA8B,EAE/B,iDAA6C;AAC9C,OAAO,EACL,SAAS,EACT,eAAe,EACf,iBAAiB,EAOlB,yBAAqB","sourcesContent":["export {\n QrKeyringScannerBridge,\n type QrKeyringScannerBridgeOptions,\n} from './qr-keyring-scanner-bridge';\nexport {\n QrKeyringDeferredPromiseBridge,\n type QrKeyringDeferredPromiseBridgeOptions,\n} from './qr-keyring-deferred-promise-bridge';\nexport {\n QrKeyring,\n QR_KEYRING_TYPE,\n QrScanRequestType,\n type QrScanRequest,\n type QrKeyringBridge,\n type QrKeyringOptions,\n type QrSignatureRequest,\n type SerializedQrKeyringState,\n type SerializedUR,\n} from './qr-keyring';\n"]}
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EAEvB,wCAAoC;AACrC,OAAO,EACL,8BAA8B,EAE/B,iDAA6C;AAC9C,OAAO,EACL,SAAS,EACT,eAAe,EACf,iBAAiB,EAOlB,yBAAqB;AACtB,OAAO,EACL,WAAW,EAGZ,4BAAwB","sourcesContent":["export {\n QrKeyringScannerBridge,\n type QrKeyringScannerBridgeOptions,\n} from './qr-keyring-scanner-bridge';\nexport {\n QrKeyringDeferredPromiseBridge,\n type QrKeyringDeferredPromiseBridgeOptions,\n} from './qr-keyring-deferred-promise-bridge';\nexport {\n QrKeyring,\n QR_KEYRING_TYPE,\n QrScanRequestType,\n type QrScanRequest,\n type QrKeyringBridge,\n type QrKeyringOptions,\n type QrSignatureRequest,\n type SerializedQrKeyringState,\n type SerializedUR,\n} from './qr-keyring';\nexport {\n QrKeyringV2,\n type QrKeyringV2Options,\n type QrAccountModeCreateOptions,\n} from './qr-keyring-v2';\n"]}
@@ -0,0 +1,266 @@
1
+ "use strict";
2
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
+ };
7
+ var _QrKeyringV2_instances, _QrKeyringV2_getDeviceState, _QrKeyringV2_createHdModeAccount, _QrKeyringV2_createAccountModeAccount, _QrKeyringV2_createAccountModeAccounts, _QrKeyringV2_createHdModeAccounts;
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.QrKeyringV2 = void 0;
10
+ const keyring_api_1 = require("@metamask/keyring-api");
11
+ const device_1 = require("./device.cjs");
12
+ /**
13
+ * Methods supported by QR keyring EOA accounts.
14
+ * QR keyrings support a subset of signing methods (no encryption, app keys, or EIP-7702).
15
+ */
16
+ const QR_KEYRING_METHODS = [
17
+ keyring_api_1.EthMethod.SignTransaction,
18
+ keyring_api_1.EthMethod.PersonalSign,
19
+ keyring_api_1.EthMethod.SignTypedDataV4,
20
+ ];
21
+ /**
22
+ * Capabilities for HD mode devices (index-based derivation supported).
23
+ */
24
+ const HD_MODE_CAPABILITIES = {
25
+ scopes: [keyring_api_1.EthScope.Eoa],
26
+ bip44: {
27
+ deriveIndex: true,
28
+ },
29
+ };
30
+ /**
31
+ * Capabilities for Account mode devices (custom account selection).
32
+ */
33
+ const ACCOUNT_MODE_CAPABILITIES = {
34
+ scopes: [keyring_api_1.EthScope.Eoa],
35
+ custom: {
36
+ createAccounts: true,
37
+ },
38
+ };
39
+ class QrKeyringV2 extends keyring_api_1.EthKeyringWrapper {
40
+ constructor(options) {
41
+ super({
42
+ type: keyring_api_1.KeyringType.Qr,
43
+ inner: options.legacyKeyring,
44
+ // Default capabilities - overridden by getter below
45
+ capabilities: HD_MODE_CAPABILITIES,
46
+ });
47
+ _QrKeyringV2_instances.add(this);
48
+ this.entropySource = options.entropySource;
49
+ }
50
+ /**
51
+ * Get the capabilities of this keyring.
52
+ *
53
+ * Overrides the base class getter to return capabilities dynamically
54
+ * based on the current device mode.
55
+ *
56
+ * @returns The keyring's capabilities.
57
+ */
58
+ get capabilities() {
59
+ return this.inner.getMode() === device_1.DeviceMode.ACCOUNT
60
+ ? ACCOUNT_MODE_CAPABILITIES
61
+ : HD_MODE_CAPABILITIES;
62
+ }
63
+ async getAccounts() {
64
+ const addresses = await this.inner.getAccounts();
65
+ const deviceState = await __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_getDeviceState).call(this);
66
+ if (!deviceState) {
67
+ // No device paired yet, return empty
68
+ return [];
69
+ }
70
+ const { mode, indexes } = deviceState;
71
+ return addresses.map((address) => {
72
+ // Check if we already have this account in the registry
73
+ const existingId = this.registry.getAccountId(address);
74
+ if (existingId) {
75
+ const cached = this.registry.get(existingId);
76
+ if (cached) {
77
+ return cached;
78
+ }
79
+ }
80
+ if (mode === device_1.DeviceMode.HD) {
81
+ // HD mode: index must be in the map
82
+ const addressIndex = indexes[address];
83
+ if (addressIndex === undefined) {
84
+ throw new Error(`Address ${address} not found in device indexes. This indicates an inconsistent keyring state.`);
85
+ }
86
+ return __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_createHdModeAccount).call(this, address, addressIndex);
87
+ }
88
+ // Account mode: validate address exists in paths map
89
+ const { paths } = deviceState;
90
+ if (paths[address] === undefined) {
91
+ throw new Error(`Address ${address} not found in device paths. This indicates an inconsistent keyring state.`);
92
+ }
93
+ return __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_createAccountModeAccount).call(this, address);
94
+ });
95
+ }
96
+ async createAccounts(options) {
97
+ return this.withLock(async () => {
98
+ const deviceState = await __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_getDeviceState).call(this);
99
+ if (!deviceState) {
100
+ throw new Error('No device paired. Cannot create accounts.');
101
+ }
102
+ // Validate entropy source for all account creation types
103
+ if (options.entropySource !== this.entropySource) {
104
+ throw new Error(`Entropy source mismatch: expected '${this.entropySource}', got '${options.entropySource}'`);
105
+ }
106
+ // Handle Account mode with custom account creation
107
+ if (deviceState.mode === device_1.DeviceMode.ACCOUNT) {
108
+ if (options.type !== 'custom') {
109
+ throw new Error(`Account mode devices only support 'custom' account creation type, got '${options.type}'. ` +
110
+ `Use { type: 'custom', entropySource, addressIndex } to select a pre-defined address.`);
111
+ }
112
+ return __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_createAccountModeAccounts).call(this, options, deviceState);
113
+ }
114
+ // HD mode: support derive-index
115
+ return __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_createHdModeAccounts).call(this, options, deviceState);
116
+ });
117
+ }
118
+ /**
119
+ * Delete an account from the keyring.
120
+ *
121
+ * @param accountId - The account ID to delete.
122
+ */
123
+ async deleteAccount(accountId) {
124
+ await this.withLock(async () => {
125
+ const { address } = await this.getAccount(accountId);
126
+ const hexAddress = this.toHexAddress(address);
127
+ // Remove from the legacy keyring
128
+ this.inner.removeAccount(hexAddress);
129
+ // Remove from the registry
130
+ this.registry.delete(accountId);
131
+ });
132
+ }
133
+ }
134
+ exports.QrKeyringV2 = QrKeyringV2;
135
+ _QrKeyringV2_instances = new WeakSet(), _QrKeyringV2_getDeviceState =
136
+ /**
137
+ * Get the device state from the inner keyring.
138
+ *
139
+ * @returns The device state, or undefined if no device is paired.
140
+ */
141
+ async function _QrKeyringV2_getDeviceState() {
142
+ const state = await this.inner.serialize();
143
+ if (!state?.initialized) {
144
+ return undefined;
145
+ }
146
+ if (state.keyringMode === device_1.DeviceMode.ACCOUNT) {
147
+ return {
148
+ mode: device_1.DeviceMode.ACCOUNT,
149
+ indexes: state.indexes,
150
+ paths: state.paths,
151
+ };
152
+ }
153
+ return {
154
+ mode: device_1.DeviceMode.HD,
155
+ indexes: state.indexes,
156
+ };
157
+ }, _QrKeyringV2_createHdModeAccount = function _QrKeyringV2_createHdModeAccount(address, addressIndex) {
158
+ const id = this.registry.register(address);
159
+ const derivationPath = this.inner.getPathFromAddress(address);
160
+ if (!derivationPath) {
161
+ throw new Error(`Cannot create account for address ${address}: derivation path not found in keyring.`);
162
+ }
163
+ const account = {
164
+ id,
165
+ type: keyring_api_1.EthAccountType.Eoa,
166
+ address,
167
+ scopes: [...this.capabilities.scopes],
168
+ methods: [...QR_KEYRING_METHODS],
169
+ options: {
170
+ entropy: {
171
+ type: keyring_api_1.KeyringAccountEntropyTypeOption.Mnemonic,
172
+ id: this.entropySource,
173
+ groupIndex: addressIndex,
174
+ derivationPath,
175
+ },
176
+ },
177
+ };
178
+ this.registry.set(account);
179
+ return account;
180
+ }, _QrKeyringV2_createAccountModeAccount = function _QrKeyringV2_createAccountModeAccount(address) {
181
+ const id = this.registry.register(address);
182
+ const account = {
183
+ id,
184
+ type: keyring_api_1.EthAccountType.Eoa,
185
+ address,
186
+ scopes: [...this.capabilities.scopes],
187
+ methods: [...QR_KEYRING_METHODS],
188
+ options: {
189
+ entropy: {
190
+ type: keyring_api_1.KeyringAccountEntropyTypeOption.Custom,
191
+ },
192
+ },
193
+ };
194
+ this.registry.set(account);
195
+ return account;
196
+ }, _QrKeyringV2_createAccountModeAccounts =
197
+ /**
198
+ * Creates accounts for Account mode devices using custom options.
199
+ *
200
+ * @param options - The account creation options.
201
+ * @param deviceState - The current device state.
202
+ * @param deviceState.mode - The device mode (ACCOUNT).
203
+ * @param deviceState.paths - Map of addresses to derivation paths.
204
+ * @param deviceState.indexes - Map of addresses to their indexes.
205
+ * @returns The created accounts.
206
+ */
207
+ async function _QrKeyringV2_createAccountModeAccounts(options, deviceState) {
208
+ const { addressIndex } = options;
209
+ if (!Number.isInteger(addressIndex) || addressIndex < 0) {
210
+ throw new Error(`Invalid addressIndex: ${String(addressIndex)}. Must be a non-negative integer.`);
211
+ }
212
+ // Get available addresses from paths
213
+ const availableAddresses = Object.keys(deviceState.paths);
214
+ if (addressIndex >= availableAddresses.length) {
215
+ throw new Error(`Address index ${addressIndex} is out of bounds. Device has ${availableAddresses.length} pre-defined address(es).`);
216
+ }
217
+ const address = availableAddresses[addressIndex];
218
+ // Check if already exists
219
+ const currentAccounts = await this.getAccounts();
220
+ const existingAccount = currentAccounts.find((account) => account.address.toLowerCase() === address?.toLowerCase());
221
+ if (existingAccount) {
222
+ return [existingAccount];
223
+ }
224
+ // Add the account via the inner keyring
225
+ this.inner.setAccountToUnlock(addressIndex);
226
+ const [newAddress] = await this.inner.addAccounts(1);
227
+ if (!newAddress) {
228
+ throw new Error('Failed to create new account');
229
+ }
230
+ const newAccount = __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_createAccountModeAccount).call(this, newAddress);
231
+ return [newAccount];
232
+ }, _QrKeyringV2_createHdModeAccounts =
233
+ /**
234
+ * Creates accounts for HD mode devices using index-based derivation.
235
+ *
236
+ * @param options - The account creation options.
237
+ * @param _deviceState - The current device state (reserved for future use).
238
+ * @param _deviceState.indexes - Map of addresses to their indexes.
239
+ * @returns The created accounts.
240
+ */
241
+ async function _QrKeyringV2_createHdModeAccounts(options, _deviceState) {
242
+ if (options.type !== 'bip44:derive-index') {
243
+ throw new Error(`Unsupported account creation type for HD mode QrKeyring: ${String(options.type)}. Supported type: 'bip44:derive-index'.`);
244
+ }
245
+ if (!Number.isInteger(options.groupIndex) || options.groupIndex < 0) {
246
+ throw new Error(`Invalid groupIndex: ${options.groupIndex}. Must be a non-negative integer.`);
247
+ }
248
+ const targetIndex = options.groupIndex;
249
+ // Check if an account at this index already exists
250
+ const currentAccounts = await this.getAccounts();
251
+ const existingAccount = currentAccounts.find((account) => account.options.entropy?.type ===
252
+ keyring_api_1.KeyringAccountEntropyTypeOption.Mnemonic &&
253
+ account.options.entropy.groupIndex === targetIndex);
254
+ if (existingAccount) {
255
+ return [existingAccount];
256
+ }
257
+ // Derive the account at the specified index
258
+ this.inner.setAccountToUnlock(targetIndex);
259
+ const [newAddress] = await this.inner.addAccounts(1);
260
+ if (!newAddress) {
261
+ throw new Error('Failed to create new account');
262
+ }
263
+ const newAccount = __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_createHdModeAccount).call(this, newAddress, targetIndex);
264
+ return [newAccount];
265
+ };
266
+ //# sourceMappingURL=qr-keyring-v2.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr-keyring-v2.cjs","sourceRoot":"","sources":["../src/qr-keyring-v2.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Bip44Account } from '@metamask/account-api';\nimport {\n type CreateAccountOptions,\n EthAccountType,\n EthKeyringWrapper,\n EthMethod,\n EthScope,\n type KeyringAccount,\n KeyringAccountEntropyTypeOption,\n type KeyringCapabilities,\n type KeyringV2,\n KeyringType,\n type EntropySourceId,\n type CreateAccountBip44DeriveIndexOptions,\n} from '@metamask/keyring-api';\nimport type { AccountId } from '@metamask/keyring-utils';\nimport type { Hex } from '@metamask/utils';\n\nimport { DeviceMode } from './device';\nimport type { QrKeyring } from './qr-keyring';\n\n/**\n * Methods supported by QR keyring EOA accounts.\n * QR keyrings support a subset of signing methods (no encryption, app keys, or EIP-7702).\n */\nconst QR_KEYRING_METHODS = [\n EthMethod.SignTransaction,\n EthMethod.PersonalSign,\n EthMethod.SignTypedDataV4,\n];\n\n/**\n * Capabilities for HD mode devices (index-based derivation supported).\n */\nconst HD_MODE_CAPABILITIES: KeyringCapabilities = {\n scopes: [EthScope.Eoa],\n bip44: {\n deriveIndex: true,\n },\n};\n\n/**\n * Capabilities for Account mode devices (custom account selection).\n */\nconst ACCOUNT_MODE_CAPABILITIES: KeyringCapabilities = {\n scopes: [EthScope.Eoa],\n custom: {\n createAccounts: true,\n },\n};\n\n/**\n * Custom options for creating accounts on Account mode QR devices.\n * Account mode devices provide pre-defined addresses that must be selected by index.\n */\nexport type QrAccountModeCreateOptions = {\n type: 'custom';\n /**\n * The entropy source ID (device fingerprint) to verify we're targeting the correct device.\n */\n entropySource: EntropySourceId;\n /**\n * The index of the pre-defined address to add from the device.\n * This refers to the position in the device's address list, not a BIP-44 derivation index.\n */\n addressIndex: number;\n};\n\n/**\n * Account creation options for QR keyring.\n * Excludes unsupported types (custom, private-key:import) and adds our specific QrAccountModeCreateOptions.\n */\nexport type QrKeyringCreateAccountOptions =\n | CreateAccountBip44DeriveIndexOptions\n | QrAccountModeCreateOptions;\n\n/**\n * Concrete {@link KeyringV2} adapter for {@link QrKeyring}.\n *\n * This wrapper exposes the accounts and signing capabilities of the legacy\n * QR keyring via the unified V2 interface.\n *\n * Account handling differs by device mode:\n * - **HD mode**: Accounts are derived by index with `groupIndex` values.\n * Supports `bip44:derive-index` for index-based derivation.\n * - **Account mode**: Accounts are treated as private key imports since the\n * device provides pre-defined addresses with arbitrary paths. Uses `custom`\n * account creation type to select addresses by their position in the device.\n */\nexport type QrKeyringV2Options = {\n legacyKeyring: QrKeyring;\n entropySource: EntropySourceId;\n};\n\nexport class QrKeyringV2\n extends EthKeyringWrapper<QrKeyring>\n implements KeyringV2\n{\n readonly entropySource: EntropySourceId;\n\n constructor(options: QrKeyringV2Options) {\n super({\n type: KeyringType.Qr,\n inner: options.legacyKeyring,\n // Default capabilities - overridden by getter below\n capabilities: HD_MODE_CAPABILITIES,\n });\n this.entropySource = options.entropySource;\n }\n\n /**\n * Get the capabilities of this keyring.\n *\n * Overrides the base class getter to return capabilities dynamically\n * based on the current device mode.\n *\n * @returns The keyring's capabilities.\n */\n override get capabilities(): KeyringCapabilities {\n return this.inner.getMode() === DeviceMode.ACCOUNT\n ? ACCOUNT_MODE_CAPABILITIES\n : HD_MODE_CAPABILITIES;\n }\n\n /**\n * Get the device state from the inner keyring.\n *\n * @returns The device state, or undefined if no device is paired.\n */\n async #getDeviceState(): Promise<\n | {\n mode: DeviceMode.HD;\n indexes: Record<Hex, number>;\n }\n | {\n mode: DeviceMode.ACCOUNT;\n indexes: Record<Hex, number>;\n paths: Record<Hex, string>;\n }\n | undefined\n > {\n const state = await this.inner.serialize();\n\n if (!state?.initialized) {\n return undefined;\n }\n\n if (state.keyringMode === DeviceMode.ACCOUNT) {\n return {\n mode: DeviceMode.ACCOUNT,\n indexes: state.indexes,\n paths: state.paths,\n };\n }\n\n return {\n mode: DeviceMode.HD,\n indexes: state.indexes,\n };\n }\n\n /**\n * Creates a Bip44Account for HD mode devices.\n *\n * @param address - The account address.\n * @param addressIndex - The account index in the derivation path.\n * @returns The created Bip44Account.\n */\n #createHdModeAccount(\n address: Hex,\n addressIndex: number,\n ): Bip44Account<KeyringAccount> {\n const id = this.registry.register(address);\n\n const derivationPath = this.inner.getPathFromAddress(address);\n\n if (!derivationPath) {\n throw new Error(\n `Cannot create account for address ${address}: derivation path not found in keyring.`,\n );\n }\n\n const account: Bip44Account<KeyringAccount> = {\n id,\n type: EthAccountType.Eoa,\n address,\n scopes: [...this.capabilities.scopes],\n methods: [...QR_KEYRING_METHODS],\n options: {\n entropy: {\n type: KeyringAccountEntropyTypeOption.Mnemonic,\n id: this.entropySource,\n groupIndex: addressIndex,\n derivationPath,\n },\n },\n };\n\n this.registry.set(account);\n return account;\n }\n\n /**\n * Creates a KeyringAccount for Account mode devices.\n *\n * Account mode devices provide pre-defined addresses with arbitrary derivation\n * paths, so we treat them as private key imports rather than BIP-44 accounts.\n *\n * @param address - The account address.\n * @returns The created KeyringAccount.\n */\n #createAccountModeAccount(address: Hex): KeyringAccount {\n const id = this.registry.register(address);\n\n const account: KeyringAccount = {\n id,\n type: EthAccountType.Eoa,\n address,\n scopes: [...this.capabilities.scopes],\n methods: [...QR_KEYRING_METHODS],\n options: {\n entropy: {\n type: KeyringAccountEntropyTypeOption.Custom,\n },\n },\n };\n\n this.registry.set(account);\n return account;\n }\n\n async getAccounts(): Promise<KeyringAccount[]> {\n const addresses = await this.inner.getAccounts();\n const deviceState = await this.#getDeviceState();\n\n if (!deviceState) {\n // No device paired yet, return empty\n return [];\n }\n\n const { mode, indexes } = deviceState;\n\n return addresses.map((address) => {\n // Check if we already have this account in the registry\n const existingId = this.registry.getAccountId(address);\n if (existingId) {\n const cached = this.registry.get(existingId);\n if (cached) {\n return cached;\n }\n }\n\n if (mode === DeviceMode.HD) {\n // HD mode: index must be in the map\n const addressIndex = indexes[address];\n if (addressIndex === undefined) {\n throw new Error(\n `Address ${address} not found in device indexes. This indicates an inconsistent keyring state.`,\n );\n }\n return this.#createHdModeAccount(address, addressIndex);\n }\n\n // Account mode: validate address exists in paths map\n const { paths } = deviceState;\n if (paths[address] === undefined) {\n throw new Error(\n `Address ${address} not found in device paths. This indicates an inconsistent keyring state.`,\n );\n }\n return this.#createAccountModeAccount(address);\n });\n }\n\n async createAccounts(\n options: QrKeyringCreateAccountOptions,\n ): Promise<KeyringAccount[]> {\n return this.withLock(async () => {\n const deviceState = await this.#getDeviceState();\n\n if (!deviceState) {\n throw new Error('No device paired. Cannot create accounts.');\n }\n\n // Validate entropy source for all account creation types\n if (options.entropySource !== this.entropySource) {\n throw new Error(\n `Entropy source mismatch: expected '${this.entropySource}', got '${options.entropySource}'`,\n );\n }\n\n // Handle Account mode with custom account creation\n if (deviceState.mode === DeviceMode.ACCOUNT) {\n if (options.type !== 'custom') {\n throw new Error(\n `Account mode devices only support 'custom' account creation type, got '${options.type}'. ` +\n `Use { type: 'custom', entropySource, addressIndex } to select a pre-defined address.`,\n );\n }\n return this.#createAccountModeAccounts(options, deviceState);\n }\n\n // HD mode: support derive-index\n return this.#createHdModeAccounts(options, deviceState);\n });\n }\n\n /**\n * Creates accounts for Account mode devices using custom options.\n *\n * @param options - The account creation options.\n * @param deviceState - The current device state.\n * @param deviceState.mode - The device mode (ACCOUNT).\n * @param deviceState.paths - Map of addresses to derivation paths.\n * @param deviceState.indexes - Map of addresses to their indexes.\n * @returns The created accounts.\n */\n async #createAccountModeAccounts(\n options: QrAccountModeCreateOptions,\n deviceState: {\n mode: DeviceMode.ACCOUNT;\n paths: Record<Hex, string>;\n indexes: Record<Hex, number>;\n },\n ): Promise<KeyringAccount[]> {\n const { addressIndex } = options;\n\n if (!Number.isInteger(addressIndex) || addressIndex < 0) {\n throw new Error(\n `Invalid addressIndex: ${String(\n addressIndex,\n )}. Must be a non-negative integer.`,\n );\n }\n\n // Get available addresses from paths\n const availableAddresses = Object.keys(deviceState.paths) as Hex[];\n if (addressIndex >= availableAddresses.length) {\n throw new Error(\n `Address index ${addressIndex} is out of bounds. Device has ${availableAddresses.length} pre-defined address(es).`,\n );\n }\n\n const address = availableAddresses[addressIndex];\n\n // Check if already exists\n const currentAccounts = await this.getAccounts();\n const existingAccount = currentAccounts.find(\n (account) => account.address.toLowerCase() === address?.toLowerCase(),\n );\n if (existingAccount) {\n return [existingAccount];\n }\n\n // Add the account via the inner keyring\n this.inner.setAccountToUnlock(addressIndex);\n const [newAddress] = await this.inner.addAccounts(1);\n\n if (!newAddress) {\n throw new Error('Failed to create new account');\n }\n\n const newAccount = this.#createAccountModeAccount(newAddress);\n return [newAccount];\n }\n\n /**\n * Creates accounts for HD mode devices using index-based derivation.\n *\n * @param options - The account creation options.\n * @param _deviceState - The current device state (reserved for future use).\n * @param _deviceState.indexes - Map of addresses to their indexes.\n * @returns The created accounts.\n */\n async #createHdModeAccounts(\n options: CreateAccountOptions,\n _deviceState: { indexes: Record<Hex, number> },\n ): Promise<Bip44Account<KeyringAccount>[]> {\n if (options.type !== 'bip44:derive-index') {\n throw new Error(\n `Unsupported account creation type for HD mode QrKeyring: ${String(\n options.type,\n )}. Supported type: 'bip44:derive-index'.`,\n );\n }\n\n if (!Number.isInteger(options.groupIndex) || options.groupIndex < 0) {\n throw new Error(\n `Invalid groupIndex: ${options.groupIndex}. Must be a non-negative integer.`,\n );\n }\n\n const targetIndex = options.groupIndex;\n\n // Check if an account at this index already exists\n const currentAccounts = await this.getAccounts();\n const existingAccount = currentAccounts.find(\n (account) =>\n account.options.entropy?.type ===\n KeyringAccountEntropyTypeOption.Mnemonic &&\n account.options.entropy.groupIndex === targetIndex,\n );\n\n if (existingAccount) {\n return [existingAccount as Bip44Account<KeyringAccount>];\n }\n\n // Derive the account at the specified index\n this.inner.setAccountToUnlock(targetIndex);\n const [newAddress] = await this.inner.addAccounts(1);\n\n if (!newAddress) {\n throw new Error('Failed to create new account');\n }\n\n const newAccount = this.#createHdModeAccount(newAddress, targetIndex);\n return [newAccount];\n }\n\n /**\n * Delete an account from the keyring.\n *\n * @param accountId - The account ID to delete.\n */\n async deleteAccount(accountId: AccountId): Promise<void> {\n await this.withLock(async () => {\n const { address } = await this.getAccount(accountId);\n const hexAddress = this.toHexAddress(address);\n\n // Remove from the legacy keyring\n this.inner.removeAccount(hexAddress);\n\n // Remove from the registry\n this.registry.delete(accountId);\n });\n }\n}\n"]}
@@ -0,0 +1,64 @@
1
+ import { EthKeyringWrapper, type KeyringAccount, type KeyringCapabilities, type KeyringV2, type EntropySourceId, type CreateAccountBip44DeriveIndexOptions } from "@metamask/keyring-api";
2
+ import type { AccountId } from "@metamask/keyring-utils";
3
+ import type { QrKeyring } from "./qr-keyring.cjs";
4
+ /**
5
+ * Custom options for creating accounts on Account mode QR devices.
6
+ * Account mode devices provide pre-defined addresses that must be selected by index.
7
+ */
8
+ export type QrAccountModeCreateOptions = {
9
+ type: 'custom';
10
+ /**
11
+ * The entropy source ID (device fingerprint) to verify we're targeting the correct device.
12
+ */
13
+ entropySource: EntropySourceId;
14
+ /**
15
+ * The index of the pre-defined address to add from the device.
16
+ * This refers to the position in the device's address list, not a BIP-44 derivation index.
17
+ */
18
+ addressIndex: number;
19
+ };
20
+ /**
21
+ * Account creation options for QR keyring.
22
+ * Excludes unsupported types (custom, private-key:import) and adds our specific QrAccountModeCreateOptions.
23
+ */
24
+ export type QrKeyringCreateAccountOptions = CreateAccountBip44DeriveIndexOptions | QrAccountModeCreateOptions;
25
+ /**
26
+ * Concrete {@link KeyringV2} adapter for {@link QrKeyring}.
27
+ *
28
+ * This wrapper exposes the accounts and signing capabilities of the legacy
29
+ * QR keyring via the unified V2 interface.
30
+ *
31
+ * Account handling differs by device mode:
32
+ * - **HD mode**: Accounts are derived by index with `groupIndex` values.
33
+ * Supports `bip44:derive-index` for index-based derivation.
34
+ * - **Account mode**: Accounts are treated as private key imports since the
35
+ * device provides pre-defined addresses with arbitrary paths. Uses `custom`
36
+ * account creation type to select addresses by their position in the device.
37
+ */
38
+ export type QrKeyringV2Options = {
39
+ legacyKeyring: QrKeyring;
40
+ entropySource: EntropySourceId;
41
+ };
42
+ export declare class QrKeyringV2 extends EthKeyringWrapper<QrKeyring> implements KeyringV2 {
43
+ #private;
44
+ readonly entropySource: EntropySourceId;
45
+ constructor(options: QrKeyringV2Options);
46
+ /**
47
+ * Get the capabilities of this keyring.
48
+ *
49
+ * Overrides the base class getter to return capabilities dynamically
50
+ * based on the current device mode.
51
+ *
52
+ * @returns The keyring's capabilities.
53
+ */
54
+ get capabilities(): KeyringCapabilities;
55
+ getAccounts(): Promise<KeyringAccount[]>;
56
+ createAccounts(options: QrKeyringCreateAccountOptions): Promise<KeyringAccount[]>;
57
+ /**
58
+ * Delete an account from the keyring.
59
+ *
60
+ * @param accountId - The account ID to delete.
61
+ */
62
+ deleteAccount(accountId: AccountId): Promise<void>;
63
+ }
64
+ //# sourceMappingURL=qr-keyring-v2.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr-keyring-v2.d.cts","sourceRoot":"","sources":["../src/qr-keyring-v2.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,iBAAiB,EAGjB,KAAK,cAAc,EAEnB,KAAK,mBAAmB,EACxB,KAAK,SAAS,EAEd,KAAK,eAAe,EACpB,KAAK,oCAAoC,EAC1C,8BAA8B;AAC/B,OAAO,KAAK,EAAE,SAAS,EAAE,gCAAgC;AAIzD,OAAO,KAAK,EAAE,SAAS,EAAE,yBAAqB;AAgC9C;;;GAGG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,QAAQ,CAAC;IACf;;OAEG;IACH,aAAa,EAAE,eAAe,CAAC;IAC/B;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,6BAA6B,GACrC,oCAAoC,GACpC,0BAA0B,CAAC;AAE/B;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,aAAa,EAAE,SAAS,CAAC;IACzB,aAAa,EAAE,eAAe,CAAC;CAChC,CAAC;AAEF,qBAAa,WACX,SAAQ,iBAAiB,CAAC,SAAS,CACnC,YAAW,SAAS;;IAEpB,QAAQ,CAAC,aAAa,EAAE,eAAe,CAAC;gBAE5B,OAAO,EAAE,kBAAkB;IAUvC;;;;;;;OAOG;IACH,IAAa,YAAY,IAAI,mBAAmB,CAI/C;IA6GK,WAAW,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IA2CxC,cAAc,CAClB,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,cAAc,EAAE,CAAC;IA+I5B;;;;OAIG;IACG,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAYzD"}
@@ -0,0 +1,64 @@
1
+ import { EthKeyringWrapper, type KeyringAccount, type KeyringCapabilities, type KeyringV2, type EntropySourceId, type CreateAccountBip44DeriveIndexOptions } from "@metamask/keyring-api";
2
+ import type { AccountId } from "@metamask/keyring-utils";
3
+ import type { QrKeyring } from "./qr-keyring.mjs";
4
+ /**
5
+ * Custom options for creating accounts on Account mode QR devices.
6
+ * Account mode devices provide pre-defined addresses that must be selected by index.
7
+ */
8
+ export type QrAccountModeCreateOptions = {
9
+ type: 'custom';
10
+ /**
11
+ * The entropy source ID (device fingerprint) to verify we're targeting the correct device.
12
+ */
13
+ entropySource: EntropySourceId;
14
+ /**
15
+ * The index of the pre-defined address to add from the device.
16
+ * This refers to the position in the device's address list, not a BIP-44 derivation index.
17
+ */
18
+ addressIndex: number;
19
+ };
20
+ /**
21
+ * Account creation options for QR keyring.
22
+ * Excludes unsupported types (custom, private-key:import) and adds our specific QrAccountModeCreateOptions.
23
+ */
24
+ export type QrKeyringCreateAccountOptions = CreateAccountBip44DeriveIndexOptions | QrAccountModeCreateOptions;
25
+ /**
26
+ * Concrete {@link KeyringV2} adapter for {@link QrKeyring}.
27
+ *
28
+ * This wrapper exposes the accounts and signing capabilities of the legacy
29
+ * QR keyring via the unified V2 interface.
30
+ *
31
+ * Account handling differs by device mode:
32
+ * - **HD mode**: Accounts are derived by index with `groupIndex` values.
33
+ * Supports `bip44:derive-index` for index-based derivation.
34
+ * - **Account mode**: Accounts are treated as private key imports since the
35
+ * device provides pre-defined addresses with arbitrary paths. Uses `custom`
36
+ * account creation type to select addresses by their position in the device.
37
+ */
38
+ export type QrKeyringV2Options = {
39
+ legacyKeyring: QrKeyring;
40
+ entropySource: EntropySourceId;
41
+ };
42
+ export declare class QrKeyringV2 extends EthKeyringWrapper<QrKeyring> implements KeyringV2 {
43
+ #private;
44
+ readonly entropySource: EntropySourceId;
45
+ constructor(options: QrKeyringV2Options);
46
+ /**
47
+ * Get the capabilities of this keyring.
48
+ *
49
+ * Overrides the base class getter to return capabilities dynamically
50
+ * based on the current device mode.
51
+ *
52
+ * @returns The keyring's capabilities.
53
+ */
54
+ get capabilities(): KeyringCapabilities;
55
+ getAccounts(): Promise<KeyringAccount[]>;
56
+ createAccounts(options: QrKeyringCreateAccountOptions): Promise<KeyringAccount[]>;
57
+ /**
58
+ * Delete an account from the keyring.
59
+ *
60
+ * @param accountId - The account ID to delete.
61
+ */
62
+ deleteAccount(accountId: AccountId): Promise<void>;
63
+ }
64
+ //# sourceMappingURL=qr-keyring-v2.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr-keyring-v2.d.mts","sourceRoot":"","sources":["../src/qr-keyring-v2.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,iBAAiB,EAGjB,KAAK,cAAc,EAEnB,KAAK,mBAAmB,EACxB,KAAK,SAAS,EAEd,KAAK,eAAe,EACpB,KAAK,oCAAoC,EAC1C,8BAA8B;AAC/B,OAAO,KAAK,EAAE,SAAS,EAAE,gCAAgC;AAIzD,OAAO,KAAK,EAAE,SAAS,EAAE,yBAAqB;AAgC9C;;;GAGG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,QAAQ,CAAC;IACf;;OAEG;IACH,aAAa,EAAE,eAAe,CAAC;IAC/B;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,6BAA6B,GACrC,oCAAoC,GACpC,0BAA0B,CAAC;AAE/B;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,aAAa,EAAE,SAAS,CAAC;IACzB,aAAa,EAAE,eAAe,CAAC;CAChC,CAAC;AAEF,qBAAa,WACX,SAAQ,iBAAiB,CAAC,SAAS,CACnC,YAAW,SAAS;;IAEpB,QAAQ,CAAC,aAAa,EAAE,eAAe,CAAC;gBAE5B,OAAO,EAAE,kBAAkB;IAUvC;;;;;;;OAOG;IACH,IAAa,YAAY,IAAI,mBAAmB,CAI/C;IA6GK,WAAW,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IA2CxC,cAAc,CAClB,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,cAAc,EAAE,CAAC;IA+I5B;;;;OAIG;IACG,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAYzD"}
@@ -0,0 +1,262 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var _QrKeyringV2_instances, _QrKeyringV2_getDeviceState, _QrKeyringV2_createHdModeAccount, _QrKeyringV2_createAccountModeAccount, _QrKeyringV2_createAccountModeAccounts, _QrKeyringV2_createHdModeAccounts;
7
+ import { EthAccountType, EthKeyringWrapper, EthMethod, EthScope, KeyringAccountEntropyTypeOption, KeyringType } from "@metamask/keyring-api";
8
+ import { DeviceMode } from "./device.mjs";
9
+ /**
10
+ * Methods supported by QR keyring EOA accounts.
11
+ * QR keyrings support a subset of signing methods (no encryption, app keys, or EIP-7702).
12
+ */
13
+ const QR_KEYRING_METHODS = [
14
+ EthMethod.SignTransaction,
15
+ EthMethod.PersonalSign,
16
+ EthMethod.SignTypedDataV4,
17
+ ];
18
+ /**
19
+ * Capabilities for HD mode devices (index-based derivation supported).
20
+ */
21
+ const HD_MODE_CAPABILITIES = {
22
+ scopes: [EthScope.Eoa],
23
+ bip44: {
24
+ deriveIndex: true,
25
+ },
26
+ };
27
+ /**
28
+ * Capabilities for Account mode devices (custom account selection).
29
+ */
30
+ const ACCOUNT_MODE_CAPABILITIES = {
31
+ scopes: [EthScope.Eoa],
32
+ custom: {
33
+ createAccounts: true,
34
+ },
35
+ };
36
+ export class QrKeyringV2 extends EthKeyringWrapper {
37
+ constructor(options) {
38
+ super({
39
+ type: KeyringType.Qr,
40
+ inner: options.legacyKeyring,
41
+ // Default capabilities - overridden by getter below
42
+ capabilities: HD_MODE_CAPABILITIES,
43
+ });
44
+ _QrKeyringV2_instances.add(this);
45
+ this.entropySource = options.entropySource;
46
+ }
47
+ /**
48
+ * Get the capabilities of this keyring.
49
+ *
50
+ * Overrides the base class getter to return capabilities dynamically
51
+ * based on the current device mode.
52
+ *
53
+ * @returns The keyring's capabilities.
54
+ */
55
+ get capabilities() {
56
+ return this.inner.getMode() === DeviceMode.ACCOUNT
57
+ ? ACCOUNT_MODE_CAPABILITIES
58
+ : HD_MODE_CAPABILITIES;
59
+ }
60
+ async getAccounts() {
61
+ const addresses = await this.inner.getAccounts();
62
+ const deviceState = await __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_getDeviceState).call(this);
63
+ if (!deviceState) {
64
+ // No device paired yet, return empty
65
+ return [];
66
+ }
67
+ const { mode, indexes } = deviceState;
68
+ return addresses.map((address) => {
69
+ // Check if we already have this account in the registry
70
+ const existingId = this.registry.getAccountId(address);
71
+ if (existingId) {
72
+ const cached = this.registry.get(existingId);
73
+ if (cached) {
74
+ return cached;
75
+ }
76
+ }
77
+ if (mode === DeviceMode.HD) {
78
+ // HD mode: index must be in the map
79
+ const addressIndex = indexes[address];
80
+ if (addressIndex === undefined) {
81
+ throw new Error(`Address ${address} not found in device indexes. This indicates an inconsistent keyring state.`);
82
+ }
83
+ return __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_createHdModeAccount).call(this, address, addressIndex);
84
+ }
85
+ // Account mode: validate address exists in paths map
86
+ const { paths } = deviceState;
87
+ if (paths[address] === undefined) {
88
+ throw new Error(`Address ${address} not found in device paths. This indicates an inconsistent keyring state.`);
89
+ }
90
+ return __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_createAccountModeAccount).call(this, address);
91
+ });
92
+ }
93
+ async createAccounts(options) {
94
+ return this.withLock(async () => {
95
+ const deviceState = await __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_getDeviceState).call(this);
96
+ if (!deviceState) {
97
+ throw new Error('No device paired. Cannot create accounts.');
98
+ }
99
+ // Validate entropy source for all account creation types
100
+ if (options.entropySource !== this.entropySource) {
101
+ throw new Error(`Entropy source mismatch: expected '${this.entropySource}', got '${options.entropySource}'`);
102
+ }
103
+ // Handle Account mode with custom account creation
104
+ if (deviceState.mode === DeviceMode.ACCOUNT) {
105
+ if (options.type !== 'custom') {
106
+ throw new Error(`Account mode devices only support 'custom' account creation type, got '${options.type}'. ` +
107
+ `Use { type: 'custom', entropySource, addressIndex } to select a pre-defined address.`);
108
+ }
109
+ return __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_createAccountModeAccounts).call(this, options, deviceState);
110
+ }
111
+ // HD mode: support derive-index
112
+ return __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_createHdModeAccounts).call(this, options, deviceState);
113
+ });
114
+ }
115
+ /**
116
+ * Delete an account from the keyring.
117
+ *
118
+ * @param accountId - The account ID to delete.
119
+ */
120
+ async deleteAccount(accountId) {
121
+ await this.withLock(async () => {
122
+ const { address } = await this.getAccount(accountId);
123
+ const hexAddress = this.toHexAddress(address);
124
+ // Remove from the legacy keyring
125
+ this.inner.removeAccount(hexAddress);
126
+ // Remove from the registry
127
+ this.registry.delete(accountId);
128
+ });
129
+ }
130
+ }
131
+ _QrKeyringV2_instances = new WeakSet(), _QrKeyringV2_getDeviceState =
132
+ /**
133
+ * Get the device state from the inner keyring.
134
+ *
135
+ * @returns The device state, or undefined if no device is paired.
136
+ */
137
+ async function _QrKeyringV2_getDeviceState() {
138
+ const state = await this.inner.serialize();
139
+ if (!state?.initialized) {
140
+ return undefined;
141
+ }
142
+ if (state.keyringMode === DeviceMode.ACCOUNT) {
143
+ return {
144
+ mode: DeviceMode.ACCOUNT,
145
+ indexes: state.indexes,
146
+ paths: state.paths,
147
+ };
148
+ }
149
+ return {
150
+ mode: DeviceMode.HD,
151
+ indexes: state.indexes,
152
+ };
153
+ }, _QrKeyringV2_createHdModeAccount = function _QrKeyringV2_createHdModeAccount(address, addressIndex) {
154
+ const id = this.registry.register(address);
155
+ const derivationPath = this.inner.getPathFromAddress(address);
156
+ if (!derivationPath) {
157
+ throw new Error(`Cannot create account for address ${address}: derivation path not found in keyring.`);
158
+ }
159
+ const account = {
160
+ id,
161
+ type: EthAccountType.Eoa,
162
+ address,
163
+ scopes: [...this.capabilities.scopes],
164
+ methods: [...QR_KEYRING_METHODS],
165
+ options: {
166
+ entropy: {
167
+ type: KeyringAccountEntropyTypeOption.Mnemonic,
168
+ id: this.entropySource,
169
+ groupIndex: addressIndex,
170
+ derivationPath,
171
+ },
172
+ },
173
+ };
174
+ this.registry.set(account);
175
+ return account;
176
+ }, _QrKeyringV2_createAccountModeAccount = function _QrKeyringV2_createAccountModeAccount(address) {
177
+ const id = this.registry.register(address);
178
+ const account = {
179
+ id,
180
+ type: EthAccountType.Eoa,
181
+ address,
182
+ scopes: [...this.capabilities.scopes],
183
+ methods: [...QR_KEYRING_METHODS],
184
+ options: {
185
+ entropy: {
186
+ type: KeyringAccountEntropyTypeOption.Custom,
187
+ },
188
+ },
189
+ };
190
+ this.registry.set(account);
191
+ return account;
192
+ }, _QrKeyringV2_createAccountModeAccounts =
193
+ /**
194
+ * Creates accounts for Account mode devices using custom options.
195
+ *
196
+ * @param options - The account creation options.
197
+ * @param deviceState - The current device state.
198
+ * @param deviceState.mode - The device mode (ACCOUNT).
199
+ * @param deviceState.paths - Map of addresses to derivation paths.
200
+ * @param deviceState.indexes - Map of addresses to their indexes.
201
+ * @returns The created accounts.
202
+ */
203
+ async function _QrKeyringV2_createAccountModeAccounts(options, deviceState) {
204
+ const { addressIndex } = options;
205
+ if (!Number.isInteger(addressIndex) || addressIndex < 0) {
206
+ throw new Error(`Invalid addressIndex: ${String(addressIndex)}. Must be a non-negative integer.`);
207
+ }
208
+ // Get available addresses from paths
209
+ const availableAddresses = Object.keys(deviceState.paths);
210
+ if (addressIndex >= availableAddresses.length) {
211
+ throw new Error(`Address index ${addressIndex} is out of bounds. Device has ${availableAddresses.length} pre-defined address(es).`);
212
+ }
213
+ const address = availableAddresses[addressIndex];
214
+ // Check if already exists
215
+ const currentAccounts = await this.getAccounts();
216
+ const existingAccount = currentAccounts.find((account) => account.address.toLowerCase() === address?.toLowerCase());
217
+ if (existingAccount) {
218
+ return [existingAccount];
219
+ }
220
+ // Add the account via the inner keyring
221
+ this.inner.setAccountToUnlock(addressIndex);
222
+ const [newAddress] = await this.inner.addAccounts(1);
223
+ if (!newAddress) {
224
+ throw new Error('Failed to create new account');
225
+ }
226
+ const newAccount = __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_createAccountModeAccount).call(this, newAddress);
227
+ return [newAccount];
228
+ }, _QrKeyringV2_createHdModeAccounts =
229
+ /**
230
+ * Creates accounts for HD mode devices using index-based derivation.
231
+ *
232
+ * @param options - The account creation options.
233
+ * @param _deviceState - The current device state (reserved for future use).
234
+ * @param _deviceState.indexes - Map of addresses to their indexes.
235
+ * @returns The created accounts.
236
+ */
237
+ async function _QrKeyringV2_createHdModeAccounts(options, _deviceState) {
238
+ if (options.type !== 'bip44:derive-index') {
239
+ throw new Error(`Unsupported account creation type for HD mode QrKeyring: ${String(options.type)}. Supported type: 'bip44:derive-index'.`);
240
+ }
241
+ if (!Number.isInteger(options.groupIndex) || options.groupIndex < 0) {
242
+ throw new Error(`Invalid groupIndex: ${options.groupIndex}. Must be a non-negative integer.`);
243
+ }
244
+ const targetIndex = options.groupIndex;
245
+ // Check if an account at this index already exists
246
+ const currentAccounts = await this.getAccounts();
247
+ const existingAccount = currentAccounts.find((account) => account.options.entropy?.type ===
248
+ KeyringAccountEntropyTypeOption.Mnemonic &&
249
+ account.options.entropy.groupIndex === targetIndex);
250
+ if (existingAccount) {
251
+ return [existingAccount];
252
+ }
253
+ // Derive the account at the specified index
254
+ this.inner.setAccountToUnlock(targetIndex);
255
+ const [newAddress] = await this.inner.addAccounts(1);
256
+ if (!newAddress) {
257
+ throw new Error('Failed to create new account');
258
+ }
259
+ const newAccount = __classPrivateFieldGet(this, _QrKeyringV2_instances, "m", _QrKeyringV2_createHdModeAccount).call(this, newAddress, targetIndex);
260
+ return [newAccount];
261
+ };
262
+ //# sourceMappingURL=qr-keyring-v2.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr-keyring-v2.mjs","sourceRoot":"","sources":["../src/qr-keyring-v2.ts"],"names":[],"mappings":";;;;;;AACA,OAAO,EAEL,cAAc,EACd,iBAAiB,EACjB,SAAS,EACT,QAAQ,EAER,+BAA+B,EAG/B,WAAW,EAGZ,8BAA8B;AAI/B,OAAO,EAAE,UAAU,EAAE,qBAAiB;AAGtC;;;GAGG;AACH,MAAM,kBAAkB,GAAG;IACzB,SAAS,CAAC,eAAe;IACzB,SAAS,CAAC,YAAY;IACtB,SAAS,CAAC,eAAe;CAC1B,CAAC;AAEF;;GAEG;AACH,MAAM,oBAAoB,GAAwB;IAChD,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;IACtB,KAAK,EAAE;QACL,WAAW,EAAE,IAAI;KAClB;CACF,CAAC;AAEF;;GAEG;AACH,MAAM,yBAAyB,GAAwB;IACrD,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;IACtB,MAAM,EAAE;QACN,cAAc,EAAE,IAAI;KACrB;CACF,CAAC;AA6CF,MAAM,OAAO,WACX,SAAQ,iBAA4B;IAKpC,YAAY,OAA2B;QACrC,KAAK,CAAC;YACJ,IAAI,EAAE,WAAW,CAAC,EAAE;YACpB,KAAK,EAAE,OAAO,CAAC,aAAa;YAC5B,oDAAoD;YACpD,YAAY,EAAE,oBAAoB;SACnC,CAAC,CAAC;;QACH,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAC7C,CAAC;IAED;;;;;;;OAOG;IACH,IAAa,YAAY;QACvB,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,UAAU,CAAC,OAAO;YAChD,CAAC,CAAC,yBAAyB;YAC3B,CAAC,CAAC,oBAAoB,CAAC;IAC3B,CAAC;IA6GD,KAAK,CAAC,WAAW;QACf,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QACjD,MAAM,WAAW,GAAG,MAAM,uBAAA,IAAI,2DAAgB,MAApB,IAAI,CAAkB,CAAC;QAEjD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,qCAAqC;YACrC,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,WAAW,CAAC;QAEtC,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;YAC/B,wDAAwD;YACxD,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;YACvD,IAAI,UAAU,EAAE,CAAC;gBACf,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBAC7C,IAAI,MAAM,EAAE,CAAC;oBACX,OAAO,MAAM,CAAC;gBAChB,CAAC;YACH,CAAC;YAED,IAAI,IAAI,KAAK,UAAU,CAAC,EAAE,EAAE,CAAC;gBAC3B,oCAAoC;gBACpC,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;gBACtC,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;oBAC/B,MAAM,IAAI,KAAK,CACb,WAAW,OAAO,6EAA6E,CAChG,CAAC;gBACJ,CAAC;gBACD,OAAO,uBAAA,IAAI,gEAAqB,MAAzB,IAAI,EAAsB,OAAO,EAAE,YAAY,CAAC,CAAC;YAC1D,CAAC;YAED,qDAAqD;YACrD,MAAM,EAAE,KAAK,EAAE,GAAG,WAAW,CAAC;YAC9B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;gBACjC,MAAM,IAAI,KAAK,CACb,WAAW,OAAO,2EAA2E,CAC9F,CAAC;YACJ,CAAC;YACD,OAAO,uBAAA,IAAI,qEAA0B,MAA9B,IAAI,EAA2B,OAAO,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,cAAc,CAClB,OAAsC;QAEtC,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE;YAC9B,MAAM,WAAW,GAAG,MAAM,uBAAA,IAAI,2DAAgB,MAApB,IAAI,CAAkB,CAAC;YAEjD,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;YAC/D,CAAC;YAED,yDAAyD;YACzD,IAAI,OAAO,CAAC,aAAa,KAAK,IAAI,CAAC,aAAa,EAAE,CAAC;gBACjD,MAAM,IAAI,KAAK,CACb,sCAAsC,IAAI,CAAC,aAAa,WAAW,OAAO,CAAC,aAAa,GAAG,CAC5F,CAAC;YACJ,CAAC;YAED,mDAAmD;YACnD,IAAI,WAAW,CAAC,IAAI,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;gBAC5C,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBAC9B,MAAM,IAAI,KAAK,CACb,0EAA0E,OAAO,CAAC,IAAI,KAAK;wBACzF,sFAAsF,CACzF,CAAC;gBACJ,CAAC;gBACD,OAAO,uBAAA,IAAI,sEAA2B,MAA/B,IAAI,EAA4B,OAAO,EAAE,WAAW,CAAC,CAAC;YAC/D,CAAC;YAED,gCAAgC;YAChC,OAAO,uBAAA,IAAI,iEAAsB,MAA1B,IAAI,EAAuB,OAAO,EAAE,WAAW,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;IACL,CAAC;IAkHD;;;;OAIG;IACH,KAAK,CAAC,aAAa,CAAC,SAAoB;QACtC,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE;YAC7B,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;YACrD,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;YAE9C,iCAAiC;YACjC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAErC,2BAA2B;YAC3B,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;IACL,CAAC;CACF;;AAxTC;;;;GAIG;AACH,KAAK;IAYH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;IAE3C,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,KAAK,CAAC,WAAW,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;QAC7C,OAAO;YACL,IAAI,EAAE,UAAU,CAAC,OAAO;YACxB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,KAAK,EAAE,KAAK,CAAC,KAAK;SACnB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,UAAU,CAAC,EAAE;QACnB,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC;AACJ,CAAC,+EAUC,OAAY,EACZ,YAAoB;IAEpB,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAE3C,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAE9D,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACb,qCAAqC,OAAO,yCAAyC,CACtF,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAiC;QAC5C,EAAE;QACF,IAAI,EAAE,cAAc,CAAC,GAAG;QACxB,OAAO;QACP,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;QACrC,OAAO,EAAE,CAAC,GAAG,kBAAkB,CAAC;QAChC,OAAO,EAAE;YACP,OAAO,EAAE;gBACP,IAAI,EAAE,+BAA+B,CAAC,QAAQ;gBAC9C,EAAE,EAAE,IAAI,CAAC,aAAa;gBACtB,UAAU,EAAE,YAAY;gBACxB,cAAc;aACf;SACF;KACF,CAAC;IAEF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3B,OAAO,OAAO,CAAC;AACjB,CAAC,yFAWyB,OAAY;IACpC,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAE3C,MAAM,OAAO,GAAmB;QAC9B,EAAE;QACF,IAAI,EAAE,cAAc,CAAC,GAAG;QACxB,OAAO;QACP,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;QACrC,OAAO,EAAE,CAAC,GAAG,kBAAkB,CAAC;QAChC,OAAO,EAAE;YACP,OAAO,EAAE;gBACP,IAAI,EAAE,+BAA+B,CAAC,MAAM;aAC7C;SACF;KACF,CAAC;IAEF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3B,OAAO,OAAO,CAAC;AACjB,CAAC;AA8ED;;;;;;;;;GASG;AACH,KAAK,iDACH,OAAmC,EACnC,WAIC;IAED,MAAM,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;IAEjC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CACb,yBAAyB,MAAM,CAC7B,YAAY,CACb,mCAAmC,CACrC,CAAC;IACJ,CAAC;IAED,qCAAqC;IACrC,MAAM,kBAAkB,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAU,CAAC;IACnE,IAAI,YAAY,IAAI,kBAAkB,CAAC,MAAM,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CACb,iBAAiB,YAAY,iCAAiC,kBAAkB,CAAC,MAAM,2BAA2B,CACnH,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAEjD,0BAA0B;IAC1B,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;IACjD,MAAM,eAAe,GAAG,eAAe,CAAC,IAAI,CAC1C,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,OAAO,EAAE,WAAW,EAAE,CACtE,CAAC;IACF,IAAI,eAAe,EAAE,CAAC;QACpB,OAAO,CAAC,eAAe,CAAC,CAAC;IAC3B,CAAC;IAED,wCAAwC;IACxC,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAC5C,MAAM,CAAC,UAAU,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAErD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,UAAU,GAAG,uBAAA,IAAI,qEAA0B,MAA9B,IAAI,EAA2B,UAAU,CAAC,CAAC;IAC9D,OAAO,CAAC,UAAU,CAAC,CAAC;AACtB,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,4CACH,OAA6B,EAC7B,YAA8C;IAE9C,IAAI,OAAO,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,4DAA4D,MAAM,CAChE,OAAO,CAAC,IAAI,CACb,yCAAyC,CAC3C,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CACb,uBAAuB,OAAO,CAAC,UAAU,mCAAmC,CAC7E,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAEvC,mDAAmD;IACnD,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;IACjD,MAAM,eAAe,GAAG,eAAe,CAAC,IAAI,CAC1C,CAAC,OAAO,EAAE,EAAE,CACV,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI;QAC3B,+BAA+B,CAAC,QAAQ;QAC1C,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,KAAK,WAAW,CACrD,CAAC;IAEF,IAAI,eAAe,EAAE,CAAC;QACpB,OAAO,CAAC,eAA+C,CAAC,CAAC;IAC3D,CAAC;IAED,4CAA4C;IAC5C,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC;IAC3C,MAAM,CAAC,UAAU,CAAC,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAErD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,UAAU,GAAG,uBAAA,IAAI,gEAAqB,MAAzB,IAAI,EAAsB,UAAU,EAAE,WAAW,CAAC,CAAC;IACtE,OAAO,CAAC,UAAU,CAAC,CAAC;AACtB,CAAC","sourcesContent":["import type { Bip44Account } from '@metamask/account-api';\nimport {\n type CreateAccountOptions,\n EthAccountType,\n EthKeyringWrapper,\n EthMethod,\n EthScope,\n type KeyringAccount,\n KeyringAccountEntropyTypeOption,\n type KeyringCapabilities,\n type KeyringV2,\n KeyringType,\n type EntropySourceId,\n type CreateAccountBip44DeriveIndexOptions,\n} from '@metamask/keyring-api';\nimport type { AccountId } from '@metamask/keyring-utils';\nimport type { Hex } from '@metamask/utils';\n\nimport { DeviceMode } from './device';\nimport type { QrKeyring } from './qr-keyring';\n\n/**\n * Methods supported by QR keyring EOA accounts.\n * QR keyrings support a subset of signing methods (no encryption, app keys, or EIP-7702).\n */\nconst QR_KEYRING_METHODS = [\n EthMethod.SignTransaction,\n EthMethod.PersonalSign,\n EthMethod.SignTypedDataV4,\n];\n\n/**\n * Capabilities for HD mode devices (index-based derivation supported).\n */\nconst HD_MODE_CAPABILITIES: KeyringCapabilities = {\n scopes: [EthScope.Eoa],\n bip44: {\n deriveIndex: true,\n },\n};\n\n/**\n * Capabilities for Account mode devices (custom account selection).\n */\nconst ACCOUNT_MODE_CAPABILITIES: KeyringCapabilities = {\n scopes: [EthScope.Eoa],\n custom: {\n createAccounts: true,\n },\n};\n\n/**\n * Custom options for creating accounts on Account mode QR devices.\n * Account mode devices provide pre-defined addresses that must be selected by index.\n */\nexport type QrAccountModeCreateOptions = {\n type: 'custom';\n /**\n * The entropy source ID (device fingerprint) to verify we're targeting the correct device.\n */\n entropySource: EntropySourceId;\n /**\n * The index of the pre-defined address to add from the device.\n * This refers to the position in the device's address list, not a BIP-44 derivation index.\n */\n addressIndex: number;\n};\n\n/**\n * Account creation options for QR keyring.\n * Excludes unsupported types (custom, private-key:import) and adds our specific QrAccountModeCreateOptions.\n */\nexport type QrKeyringCreateAccountOptions =\n | CreateAccountBip44DeriveIndexOptions\n | QrAccountModeCreateOptions;\n\n/**\n * Concrete {@link KeyringV2} adapter for {@link QrKeyring}.\n *\n * This wrapper exposes the accounts and signing capabilities of the legacy\n * QR keyring via the unified V2 interface.\n *\n * Account handling differs by device mode:\n * - **HD mode**: Accounts are derived by index with `groupIndex` values.\n * Supports `bip44:derive-index` for index-based derivation.\n * - **Account mode**: Accounts are treated as private key imports since the\n * device provides pre-defined addresses with arbitrary paths. Uses `custom`\n * account creation type to select addresses by their position in the device.\n */\nexport type QrKeyringV2Options = {\n legacyKeyring: QrKeyring;\n entropySource: EntropySourceId;\n};\n\nexport class QrKeyringV2\n extends EthKeyringWrapper<QrKeyring>\n implements KeyringV2\n{\n readonly entropySource: EntropySourceId;\n\n constructor(options: QrKeyringV2Options) {\n super({\n type: KeyringType.Qr,\n inner: options.legacyKeyring,\n // Default capabilities - overridden by getter below\n capabilities: HD_MODE_CAPABILITIES,\n });\n this.entropySource = options.entropySource;\n }\n\n /**\n * Get the capabilities of this keyring.\n *\n * Overrides the base class getter to return capabilities dynamically\n * based on the current device mode.\n *\n * @returns The keyring's capabilities.\n */\n override get capabilities(): KeyringCapabilities {\n return this.inner.getMode() === DeviceMode.ACCOUNT\n ? ACCOUNT_MODE_CAPABILITIES\n : HD_MODE_CAPABILITIES;\n }\n\n /**\n * Get the device state from the inner keyring.\n *\n * @returns The device state, or undefined if no device is paired.\n */\n async #getDeviceState(): Promise<\n | {\n mode: DeviceMode.HD;\n indexes: Record<Hex, number>;\n }\n | {\n mode: DeviceMode.ACCOUNT;\n indexes: Record<Hex, number>;\n paths: Record<Hex, string>;\n }\n | undefined\n > {\n const state = await this.inner.serialize();\n\n if (!state?.initialized) {\n return undefined;\n }\n\n if (state.keyringMode === DeviceMode.ACCOUNT) {\n return {\n mode: DeviceMode.ACCOUNT,\n indexes: state.indexes,\n paths: state.paths,\n };\n }\n\n return {\n mode: DeviceMode.HD,\n indexes: state.indexes,\n };\n }\n\n /**\n * Creates a Bip44Account for HD mode devices.\n *\n * @param address - The account address.\n * @param addressIndex - The account index in the derivation path.\n * @returns The created Bip44Account.\n */\n #createHdModeAccount(\n address: Hex,\n addressIndex: number,\n ): Bip44Account<KeyringAccount> {\n const id = this.registry.register(address);\n\n const derivationPath = this.inner.getPathFromAddress(address);\n\n if (!derivationPath) {\n throw new Error(\n `Cannot create account for address ${address}: derivation path not found in keyring.`,\n );\n }\n\n const account: Bip44Account<KeyringAccount> = {\n id,\n type: EthAccountType.Eoa,\n address,\n scopes: [...this.capabilities.scopes],\n methods: [...QR_KEYRING_METHODS],\n options: {\n entropy: {\n type: KeyringAccountEntropyTypeOption.Mnemonic,\n id: this.entropySource,\n groupIndex: addressIndex,\n derivationPath,\n },\n },\n };\n\n this.registry.set(account);\n return account;\n }\n\n /**\n * Creates a KeyringAccount for Account mode devices.\n *\n * Account mode devices provide pre-defined addresses with arbitrary derivation\n * paths, so we treat them as private key imports rather than BIP-44 accounts.\n *\n * @param address - The account address.\n * @returns The created KeyringAccount.\n */\n #createAccountModeAccount(address: Hex): KeyringAccount {\n const id = this.registry.register(address);\n\n const account: KeyringAccount = {\n id,\n type: EthAccountType.Eoa,\n address,\n scopes: [...this.capabilities.scopes],\n methods: [...QR_KEYRING_METHODS],\n options: {\n entropy: {\n type: KeyringAccountEntropyTypeOption.Custom,\n },\n },\n };\n\n this.registry.set(account);\n return account;\n }\n\n async getAccounts(): Promise<KeyringAccount[]> {\n const addresses = await this.inner.getAccounts();\n const deviceState = await this.#getDeviceState();\n\n if (!deviceState) {\n // No device paired yet, return empty\n return [];\n }\n\n const { mode, indexes } = deviceState;\n\n return addresses.map((address) => {\n // Check if we already have this account in the registry\n const existingId = this.registry.getAccountId(address);\n if (existingId) {\n const cached = this.registry.get(existingId);\n if (cached) {\n return cached;\n }\n }\n\n if (mode === DeviceMode.HD) {\n // HD mode: index must be in the map\n const addressIndex = indexes[address];\n if (addressIndex === undefined) {\n throw new Error(\n `Address ${address} not found in device indexes. This indicates an inconsistent keyring state.`,\n );\n }\n return this.#createHdModeAccount(address, addressIndex);\n }\n\n // Account mode: validate address exists in paths map\n const { paths } = deviceState;\n if (paths[address] === undefined) {\n throw new Error(\n `Address ${address} not found in device paths. This indicates an inconsistent keyring state.`,\n );\n }\n return this.#createAccountModeAccount(address);\n });\n }\n\n async createAccounts(\n options: QrKeyringCreateAccountOptions,\n ): Promise<KeyringAccount[]> {\n return this.withLock(async () => {\n const deviceState = await this.#getDeviceState();\n\n if (!deviceState) {\n throw new Error('No device paired. Cannot create accounts.');\n }\n\n // Validate entropy source for all account creation types\n if (options.entropySource !== this.entropySource) {\n throw new Error(\n `Entropy source mismatch: expected '${this.entropySource}', got '${options.entropySource}'`,\n );\n }\n\n // Handle Account mode with custom account creation\n if (deviceState.mode === DeviceMode.ACCOUNT) {\n if (options.type !== 'custom') {\n throw new Error(\n `Account mode devices only support 'custom' account creation type, got '${options.type}'. ` +\n `Use { type: 'custom', entropySource, addressIndex } to select a pre-defined address.`,\n );\n }\n return this.#createAccountModeAccounts(options, deviceState);\n }\n\n // HD mode: support derive-index\n return this.#createHdModeAccounts(options, deviceState);\n });\n }\n\n /**\n * Creates accounts for Account mode devices using custom options.\n *\n * @param options - The account creation options.\n * @param deviceState - The current device state.\n * @param deviceState.mode - The device mode (ACCOUNT).\n * @param deviceState.paths - Map of addresses to derivation paths.\n * @param deviceState.indexes - Map of addresses to their indexes.\n * @returns The created accounts.\n */\n async #createAccountModeAccounts(\n options: QrAccountModeCreateOptions,\n deviceState: {\n mode: DeviceMode.ACCOUNT;\n paths: Record<Hex, string>;\n indexes: Record<Hex, number>;\n },\n ): Promise<KeyringAccount[]> {\n const { addressIndex } = options;\n\n if (!Number.isInteger(addressIndex) || addressIndex < 0) {\n throw new Error(\n `Invalid addressIndex: ${String(\n addressIndex,\n )}. Must be a non-negative integer.`,\n );\n }\n\n // Get available addresses from paths\n const availableAddresses = Object.keys(deviceState.paths) as Hex[];\n if (addressIndex >= availableAddresses.length) {\n throw new Error(\n `Address index ${addressIndex} is out of bounds. Device has ${availableAddresses.length} pre-defined address(es).`,\n );\n }\n\n const address = availableAddresses[addressIndex];\n\n // Check if already exists\n const currentAccounts = await this.getAccounts();\n const existingAccount = currentAccounts.find(\n (account) => account.address.toLowerCase() === address?.toLowerCase(),\n );\n if (existingAccount) {\n return [existingAccount];\n }\n\n // Add the account via the inner keyring\n this.inner.setAccountToUnlock(addressIndex);\n const [newAddress] = await this.inner.addAccounts(1);\n\n if (!newAddress) {\n throw new Error('Failed to create new account');\n }\n\n const newAccount = this.#createAccountModeAccount(newAddress);\n return [newAccount];\n }\n\n /**\n * Creates accounts for HD mode devices using index-based derivation.\n *\n * @param options - The account creation options.\n * @param _deviceState - The current device state (reserved for future use).\n * @param _deviceState.indexes - Map of addresses to their indexes.\n * @returns The created accounts.\n */\n async #createHdModeAccounts(\n options: CreateAccountOptions,\n _deviceState: { indexes: Record<Hex, number> },\n ): Promise<Bip44Account<KeyringAccount>[]> {\n if (options.type !== 'bip44:derive-index') {\n throw new Error(\n `Unsupported account creation type for HD mode QrKeyring: ${String(\n options.type,\n )}. Supported type: 'bip44:derive-index'.`,\n );\n }\n\n if (!Number.isInteger(options.groupIndex) || options.groupIndex < 0) {\n throw new Error(\n `Invalid groupIndex: ${options.groupIndex}. Must be a non-negative integer.`,\n );\n }\n\n const targetIndex = options.groupIndex;\n\n // Check if an account at this index already exists\n const currentAccounts = await this.getAccounts();\n const existingAccount = currentAccounts.find(\n (account) =>\n account.options.entropy?.type ===\n KeyringAccountEntropyTypeOption.Mnemonic &&\n account.options.entropy.groupIndex === targetIndex,\n );\n\n if (existingAccount) {\n return [existingAccount as Bip44Account<KeyringAccount>];\n }\n\n // Derive the account at the specified index\n this.inner.setAccountToUnlock(targetIndex);\n const [newAddress] = await this.inner.addAccounts(1);\n\n if (!newAddress) {\n throw new Error('Failed to create new account');\n }\n\n const newAccount = this.#createHdModeAccount(newAddress, targetIndex);\n return [newAccount];\n }\n\n /**\n * Delete an account from the keyring.\n *\n * @param accountId - The account ID to delete.\n */\n async deleteAccount(accountId: AccountId): Promise<void> {\n await this.withLock(async () => {\n const { address } = await this.getAccount(accountId);\n const hexAddress = this.toHexAddress(address);\n\n // Remove from the legacy keyring\n this.inner.removeAccount(hexAddress);\n\n // Remove from the registry\n this.registry.delete(accountId);\n });\n }\n}\n"]}
@@ -98,6 +98,16 @@ class QrKeyring {
98
98
  }), "f");
99
99
  __classPrivateFieldSet(this, _QrKeyring_accounts, (state.accounts ?? []).map(normalizeAddress), "f");
100
100
  }
101
+ /**
102
+ * Get the derivation path for a given address.
103
+ *
104
+ * @param address - The address to get the derivation path for.
105
+ * @returns The derivation path for the address, or undefined if the device
106
+ * is not paired or the address is not found.
107
+ */
108
+ getPathFromAddress(address) {
109
+ return __classPrivateFieldGet(this, _QrKeyring_device, "f")?.pathFromAddress(address);
110
+ }
101
111
  /**
102
112
  * Adds accounts to the QrKeyring
103
113
  *
@@ -170,6 +180,14 @@ class QrKeyring {
170
180
  const source = __classPrivateFieldGet(this, _QrKeyring_device, "f").getDeviceDetails();
171
181
  return source.name;
172
182
  }
183
+ /**
184
+ * Get the mode of the paired device.
185
+ *
186
+ * @returns The device mode, or undefined if no device is paired.
187
+ */
188
+ getMode() {
189
+ return __classPrivateFieldGet(this, _QrKeyring_device, "f")?.getDeviceDetails().keyringMode;
190
+ }
173
191
  /**
174
192
  * Fetch the first page of accounts. If the keyring is not currently initialized,
175
193
  * it will trigger a scan request to initialize it.
@@ -1 +1 @@
1
- {"version":3,"file":"qr-keyring.cjs","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"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","sourcesContent":["import { type TypedTransaction, type TypedTxData } from '@ethereumjs/tx';\nimport type { TypedMessage, MessageTypes } from '@metamask/eth-sig-util';\nimport type { Keyring } from '@metamask/keyring-utils';\nimport { type Hex, add0x, assert, getChecksumAddress } from '@metamask/utils';\n\nimport {\n type DeviceDetails,\n type IndexedAddress,\n Device,\n DeviceMode,\n} from './device';\n\nexport const QR_KEYRING_TYPE = 'QR Hardware Wallet Device';\n\nexport enum QrScanRequestType {\n /**\n * Request a scan for a QR code containing a UR\n * with information related to a hardware wallet.\n */\n PAIR = 'pair',\n /**\n * Request a scan for a QR code containing a\n * UR-encoded transaction signature.\n */\n SIGN = 'sign',\n}\n\nexport type QrSignatureRequest = {\n requestId: string;\n payload: SerializedUR;\n requestTitle?: string;\n requestDescription?: string;\n};\n\nexport type QrScanRequest = {\n type: QrScanRequestType;\n request?: QrSignatureRequest;\n};\n\nexport type SerializedUR = {\n type: string;\n cbor: string;\n};\n\nexport type QrKeyringBridge = {\n requestScan: (request: QrScanRequest) => Promise<SerializedUR>;\n};\n\nexport type QrKeyringOptions = {\n ur?: string;\n bridge: QrKeyringBridge;\n};\n\n/**\n * The state of the QrKeyring\n *\n * @property accounts - The accounts in the QrKeyring\n */\nexport type SerializedQrKeyringState = {\n version?: number;\n accounts?: string[];\n currentAccount?: number;\n} & (\n | {\n initialized?: false;\n }\n | ({\n initialized: true;\n } & DeviceDetails)\n);\n\n/**\n * Returns the default serialized state of the QrKeyring.\n *\n * @returns The default serialized state.\n */\nexport const getDefaultSerializedQrKeyringState =\n (): SerializedQrKeyringState => ({\n initialized: false,\n accounts: [],\n });\n\n/**\n * Normalizes an address to a 0x-prefixed checksum address.\n *\n * @param address - The address to normalize.\n * @returns The normalized address as a Hex string.\n */\nfunction normalizeAddress(address: string): Hex {\n return getChecksumAddress(add0x(address));\n}\n\nexport class QrKeyring implements Keyring {\n static type = QR_KEYRING_TYPE;\n\n readonly type = QR_KEYRING_TYPE;\n\n readonly bridge: QrKeyringBridge;\n\n #device?: Device | undefined;\n\n #accounts: Hex[] = [];\n\n #accountToUnlock?: number | undefined;\n\n #currentPage: number = 0;\n\n constructor(options: QrKeyringOptions) {\n this.bridge = options.bridge;\n\n if (options?.ur) {\n this.pairDevice(options.ur);\n }\n }\n\n /**\n * Serializes the QrKeyring state\n *\n * @returns The serialized state\n */\n async serialize(): Promise<SerializedQrKeyringState> {\n const deviceDetails = this.#device\n ? this.#device.getDeviceDetails()\n : undefined;\n\n if (\n !deviceDetails ||\n ![DeviceMode.HD, DeviceMode.ACCOUNT].includes(deviceDetails.keyringMode)\n ) {\n // the keyring has not initialized with a deviceDetails device yet\n return getDefaultSerializedQrKeyringState();\n }\n\n return {\n ...deviceDetails,\n initialized: true,\n accounts: this.#accounts.slice(),\n };\n }\n\n /**\n * Deserializes the QrKeyring state\n *\n * @param state - The serialized state to deserialize\n */\n async deserialize(state: SerializedQrKeyringState): Promise<void> {\n if (!state.initialized) {\n this.#accounts = [];\n this.#device = undefined;\n return;\n }\n\n this.#device = new Device({\n requestScan: this.bridge.requestScan.bind(this.bridge),\n source: state,\n });\n this.#accounts = (state.accounts ?? []).map(normalizeAddress);\n }\n\n /**\n * Adds accounts to the QrKeyring\n *\n * @param accountsToAdd - The number of accounts to add\n * @returns The accounts added\n */\n async addAccounts(accountsToAdd: number): Promise<Hex[]> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n\n const newAccounts: Hex[] = [];\n\n for (let i = 0; i < accountsToAdd; i++) {\n const index = this.#accountToUnlock ?? this.#accounts.length + i;\n const address = this.#device.addressFromIndex(index);\n this.setAccountToUnlock(index + 1);\n\n if (this.#accounts.includes(address)) {\n continue;\n }\n\n this.#accounts.push(address);\n newAccounts.push(address);\n }\n\n return newAccounts;\n }\n\n /**\n * Gets the accounts in the QrKeyring\n *\n * @returns The accounts in the QrKeyring\n */\n async getAccounts(): Promise<Hex[]> {\n return this.#accounts.slice();\n }\n\n /**\n * Remove an account from the keyring\n *\n * @param address - The address of the account to remove\n */\n removeAccount(address: Hex): void {\n const normalizedAddress = normalizeAddress(address);\n this.#accounts = this.#accounts.filter(\n (account) => account !== normalizedAddress,\n );\n }\n\n /**\n * Pair a QR-based Hardware Device from a CBOR encoded UR to the QrKeyring\n *\n * @param ur - The CBOR encoded UR\n */\n pairDevice(ur: string | SerializedUR): void {\n this.#device = new Device({\n requestScan: this.bridge.requestScan.bind(this.bridge),\n source: ur,\n });\n }\n\n /**\n * Sets the next account index to unlock\n *\n * @param index - The index of the account to unlock\n */\n setAccountToUnlock(index: number): void {\n this.#accountToUnlock = index;\n }\n\n /**\n * Get the name of the paired device or the keyring type\n * if unavailable.\n *\n * @returns The name of the paired device or the keyring type.\n */\n getName(): string {\n if (!this.#device) {\n return QR_KEYRING_TYPE;\n }\n const source = this.#device.getDeviceDetails();\n return source.name;\n }\n\n /**\n * Fetch the first page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The first page of accounts as an array of Hex strings.\n */\n async getFirstPage(): Promise<IndexedAddress[]> {\n this.#currentPage = 0;\n return this.getCurrentPage();\n }\n\n /**\n * Fetch the next page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The next page of accounts as an array of IndexedAddress objects.\n */\n async getNextPage(): Promise<IndexedAddress[]> {\n this.#currentPage += 1;\n return this.getCurrentPage();\n }\n\n /**\n * Fetch the previous page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The previous page of accounts as an array of IndexedAddress objects.\n */\n async getPreviousPage(): Promise<IndexedAddress[]> {\n if (this.#currentPage > 0) {\n this.#currentPage -= 1;\n } else {\n this.#currentPage = 0;\n }\n return this.getCurrentPage();\n }\n\n /**\n * Fetch the current page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The current page of accounts as an array of IndexedAddress objects.\n */\n async getCurrentPage(): Promise<IndexedAddress[]> {\n if (!this.#device) {\n await this.#scanAndInitialize();\n }\n assert(\n this.#device,\n 'A device is expected to be paired before fetching accounts.',\n );\n return this.#device.getAddressesPage(this.#currentPage);\n }\n\n /**\n * Clear the keyring state and forget any paired device or accounts.\n */\n async forgetDevice(): Promise<void> {\n this.#device = undefined;\n this.#accounts = [];\n this.#accountToUnlock = undefined;\n this.#currentPage = 0;\n }\n\n /**\n * Sign a transaction. This is equivalent to the `eth_signTransaction`\n * Ethereum JSON-RPC method. See the Ethereum JSON-RPC API documentation for\n * more details.\n *\n * @param address - The address of the account to use for signing.\n * @param transaction - The transaction to sign.\n * @returns The signed transaction.\n */\n async signTransaction(\n address: Hex,\n transaction: TypedTransaction,\n ): Promise<TypedTxData> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n return this.#device.signTransaction(address, transaction);\n }\n\n /**\n * Sign a message. This is equivalent to the `eth_signTypedData` v4 Ethereum\n * JSON-RPC method.\n *\n * @param address - The address of the account to use for signing.\n * @param data - The data to sign.\n * @returns The signed message.\n */\n async signTypedData<Types extends MessageTypes>(\n address: Hex,\n data: TypedMessage<Types>,\n ): Promise<string> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n return this.#device.signTypedData(address, data);\n }\n\n /**\n * Sign a message. This is equivalent to the `eth_sign` Ethereum JSON-RPC\n * method, which is exposed by MetaMask as the method `personal_sign`. See\n * the Ethereum JSON-RPC API documentation for more details.\n *\n * For more information about this method and why we call it `personal_sign`,\n * see the {@link https://docs.metamask.io/guide/signing-data.html|MetaMask Docs}.\n *\n * @param address - The address of the account to use for signing.\n * @param message - The message to sign.\n * @returns The signed message.\n */\n async signPersonalMessage(address: Hex, message: Hex): Promise<string> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n return this.#device.signPersonalMessage(address, message);\n }\n\n /**\n * Scan for a QR code and initialize the keyring with the\n * scanned UR.\n */\n async #scanAndInitialize(): Promise<void> {\n this.pairDevice(\n await this.bridge.requestScan({ type: QrScanRequestType.PAIR }),\n );\n }\n}\n"]}
1
+ {"version":3,"file":"qr-keyring.cjs","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"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","sourcesContent":["import { type TypedTransaction, type TypedTxData } from '@ethereumjs/tx';\nimport type { TypedMessage, MessageTypes } from '@metamask/eth-sig-util';\nimport type { Keyring } from '@metamask/keyring-utils';\nimport { type Hex, add0x, assert, getChecksumAddress } from '@metamask/utils';\n\nimport {\n type DeviceDetails,\n type IndexedAddress,\n Device,\n DeviceMode,\n} from './device';\n\nexport const QR_KEYRING_TYPE = 'QR Hardware Wallet Device';\n\nexport enum QrScanRequestType {\n /**\n * Request a scan for a QR code containing a UR\n * with information related to a hardware wallet.\n */\n PAIR = 'pair',\n /**\n * Request a scan for a QR code containing a\n * UR-encoded transaction signature.\n */\n SIGN = 'sign',\n}\n\nexport type QrSignatureRequest = {\n requestId: string;\n payload: SerializedUR;\n requestTitle?: string;\n requestDescription?: string;\n};\n\nexport type QrScanRequest = {\n type: QrScanRequestType;\n request?: QrSignatureRequest;\n};\n\nexport type SerializedUR = {\n type: string;\n cbor: string;\n};\n\nexport type QrKeyringBridge = {\n requestScan: (request: QrScanRequest) => Promise<SerializedUR>;\n};\n\nexport type QrKeyringOptions = {\n ur?: string;\n bridge: QrKeyringBridge;\n};\n\n/**\n * The state of the QrKeyring\n *\n * @property accounts - The accounts in the QrKeyring\n */\nexport type SerializedQrKeyringState = {\n version?: number;\n accounts?: string[];\n currentAccount?: number;\n} & (\n | {\n initialized?: false;\n }\n | ({\n initialized: true;\n } & DeviceDetails)\n);\n\n/**\n * Returns the default serialized state of the QrKeyring.\n *\n * @returns The default serialized state.\n */\nexport const getDefaultSerializedQrKeyringState =\n (): SerializedQrKeyringState => ({\n initialized: false,\n accounts: [],\n });\n\n/**\n * Normalizes an address to a 0x-prefixed checksum address.\n *\n * @param address - The address to normalize.\n * @returns The normalized address as a Hex string.\n */\nfunction normalizeAddress(address: string): Hex {\n return getChecksumAddress(add0x(address));\n}\n\nexport class QrKeyring implements Keyring {\n static type = QR_KEYRING_TYPE;\n\n readonly type = QR_KEYRING_TYPE;\n\n readonly bridge: QrKeyringBridge;\n\n #device?: Device | undefined;\n\n #accounts: Hex[] = [];\n\n #accountToUnlock?: number | undefined;\n\n #currentPage: number = 0;\n\n constructor(options: QrKeyringOptions) {\n this.bridge = options.bridge;\n\n if (options?.ur) {\n this.pairDevice(options.ur);\n }\n }\n\n /**\n * Serializes the QrKeyring state\n *\n * @returns The serialized state\n */\n async serialize(): Promise<SerializedQrKeyringState> {\n const deviceDetails = this.#device\n ? this.#device.getDeviceDetails()\n : undefined;\n\n if (\n !deviceDetails ||\n ![DeviceMode.HD, DeviceMode.ACCOUNT].includes(deviceDetails.keyringMode)\n ) {\n // the keyring has not initialized with a deviceDetails device yet\n return getDefaultSerializedQrKeyringState();\n }\n\n return {\n ...deviceDetails,\n initialized: true,\n accounts: this.#accounts.slice(),\n };\n }\n\n /**\n * Deserializes the QrKeyring state\n *\n * @param state - The serialized state to deserialize\n */\n async deserialize(state: SerializedQrKeyringState): Promise<void> {\n if (!state.initialized) {\n this.#accounts = [];\n this.#device = undefined;\n return;\n }\n\n this.#device = new Device({\n requestScan: this.bridge.requestScan.bind(this.bridge),\n source: state,\n });\n this.#accounts = (state.accounts ?? []).map(normalizeAddress);\n }\n\n /**\n * Get the derivation path for a given address.\n *\n * @param address - The address to get the derivation path for.\n * @returns The derivation path for the address, or undefined if the device\n * is not paired or the address is not found.\n */\n getPathFromAddress(address: Hex): string | undefined {\n return this.#device?.pathFromAddress(address);\n }\n\n /**\n * Adds accounts to the QrKeyring\n *\n * @param accountsToAdd - The number of accounts to add\n * @returns The accounts added\n */\n async addAccounts(accountsToAdd: number): Promise<Hex[]> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n\n const newAccounts: Hex[] = [];\n\n for (let i = 0; i < accountsToAdd; i++) {\n const index = this.#accountToUnlock ?? this.#accounts.length + i;\n const address = this.#device.addressFromIndex(index);\n this.setAccountToUnlock(index + 1);\n\n if (this.#accounts.includes(address)) {\n continue;\n }\n\n this.#accounts.push(address);\n newAccounts.push(address);\n }\n\n return newAccounts;\n }\n\n /**\n * Gets the accounts in the QrKeyring\n *\n * @returns The accounts in the QrKeyring\n */\n async getAccounts(): Promise<Hex[]> {\n return this.#accounts.slice();\n }\n\n /**\n * Remove an account from the keyring\n *\n * @param address - The address of the account to remove\n */\n removeAccount(address: Hex): void {\n const normalizedAddress = normalizeAddress(address);\n this.#accounts = this.#accounts.filter(\n (account) => account !== normalizedAddress,\n );\n }\n\n /**\n * Pair a QR-based Hardware Device from a CBOR encoded UR to the QrKeyring\n *\n * @param ur - The CBOR encoded UR\n */\n pairDevice(ur: string | SerializedUR): void {\n this.#device = new Device({\n requestScan: this.bridge.requestScan.bind(this.bridge),\n source: ur,\n });\n }\n\n /**\n * Sets the next account index to unlock\n *\n * @param index - The index of the account to unlock\n */\n setAccountToUnlock(index: number): void {\n this.#accountToUnlock = index;\n }\n\n /**\n * Get the name of the paired device or the keyring type\n * if unavailable.\n *\n * @returns The name of the paired device or the keyring type.\n */\n getName(): string {\n if (!this.#device) {\n return QR_KEYRING_TYPE;\n }\n const source = this.#device.getDeviceDetails();\n return source.name;\n }\n\n /**\n * Get the mode of the paired device.\n *\n * @returns The device mode, or undefined if no device is paired.\n */\n getMode(): DeviceMode | undefined {\n return this.#device?.getDeviceDetails().keyringMode;\n }\n\n /**\n * Fetch the first page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The first page of accounts as an array of Hex strings.\n */\n async getFirstPage(): Promise<IndexedAddress[]> {\n this.#currentPage = 0;\n return this.getCurrentPage();\n }\n\n /**\n * Fetch the next page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The next page of accounts as an array of IndexedAddress objects.\n */\n async getNextPage(): Promise<IndexedAddress[]> {\n this.#currentPage += 1;\n return this.getCurrentPage();\n }\n\n /**\n * Fetch the previous page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The previous page of accounts as an array of IndexedAddress objects.\n */\n async getPreviousPage(): Promise<IndexedAddress[]> {\n if (this.#currentPage > 0) {\n this.#currentPage -= 1;\n } else {\n this.#currentPage = 0;\n }\n return this.getCurrentPage();\n }\n\n /**\n * Fetch the current page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The current page of accounts as an array of IndexedAddress objects.\n */\n async getCurrentPage(): Promise<IndexedAddress[]> {\n if (!this.#device) {\n await this.#scanAndInitialize();\n }\n assert(\n this.#device,\n 'A device is expected to be paired before fetching accounts.',\n );\n return this.#device.getAddressesPage(this.#currentPage);\n }\n\n /**\n * Clear the keyring state and forget any paired device or accounts.\n */\n async forgetDevice(): Promise<void> {\n this.#device = undefined;\n this.#accounts = [];\n this.#accountToUnlock = undefined;\n this.#currentPage = 0;\n }\n\n /**\n * Sign a transaction. This is equivalent to the `eth_signTransaction`\n * Ethereum JSON-RPC method. See the Ethereum JSON-RPC API documentation for\n * more details.\n *\n * @param address - The address of the account to use for signing.\n * @param transaction - The transaction to sign.\n * @returns The signed transaction.\n */\n async signTransaction(\n address: Hex,\n transaction: TypedTransaction,\n ): Promise<TypedTxData> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n return this.#device.signTransaction(address, transaction);\n }\n\n /**\n * Sign a message. This is equivalent to the `eth_signTypedData` v4 Ethereum\n * JSON-RPC method.\n *\n * @param address - The address of the account to use for signing.\n * @param data - The data to sign.\n * @returns The signed message.\n */\n async signTypedData<Types extends MessageTypes>(\n address: Hex,\n data: TypedMessage<Types>,\n ): Promise<string> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n return this.#device.signTypedData(address, data);\n }\n\n /**\n * Sign a message. This is equivalent to the `eth_sign` Ethereum JSON-RPC\n * method, which is exposed by MetaMask as the method `personal_sign`. See\n * the Ethereum JSON-RPC API documentation for more details.\n *\n * For more information about this method and why we call it `personal_sign`,\n * see the {@link https://docs.metamask.io/guide/signing-data.html|MetaMask Docs}.\n *\n * @param address - The address of the account to use for signing.\n * @param message - The message to sign.\n * @returns The signed message.\n */\n async signPersonalMessage(address: Hex, message: Hex): Promise<string> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n return this.#device.signPersonalMessage(address, message);\n }\n\n /**\n * Scan for a QR code and initialize the keyring with the\n * scanned UR.\n */\n async #scanAndInitialize(): Promise<void> {\n this.pairDevice(\n await this.bridge.requestScan({ type: QrScanRequestType.PAIR }),\n );\n }\n}\n"]}
@@ -2,7 +2,7 @@ import { type TypedTransaction, type TypedTxData } from "@ethereumjs/tx";
2
2
  import type { TypedMessage, MessageTypes } from "@metamask/eth-sig-util";
3
3
  import type { Keyring } from "@metamask/keyring-utils";
4
4
  import { type Hex } from "@metamask/utils";
5
- import { type DeviceDetails, type IndexedAddress } from "./device.cjs";
5
+ import { type DeviceDetails, type IndexedAddress, DeviceMode } from "./device.cjs";
6
6
  export declare const QR_KEYRING_TYPE = "QR Hardware Wallet Device";
7
7
  export declare enum QrScanRequestType {
8
8
  /**
@@ -75,6 +75,14 @@ export declare class QrKeyring implements Keyring {
75
75
  * @param state - The serialized state to deserialize
76
76
  */
77
77
  deserialize(state: SerializedQrKeyringState): Promise<void>;
78
+ /**
79
+ * Get the derivation path for a given address.
80
+ *
81
+ * @param address - The address to get the derivation path for.
82
+ * @returns The derivation path for the address, or undefined if the device
83
+ * is not paired or the address is not found.
84
+ */
85
+ getPathFromAddress(address: Hex): string | undefined;
78
86
  /**
79
87
  * Adds accounts to the QrKeyring
80
88
  *
@@ -113,6 +121,12 @@ export declare class QrKeyring implements Keyring {
113
121
  * @returns The name of the paired device or the keyring type.
114
122
  */
115
123
  getName(): string;
124
+ /**
125
+ * Get the mode of the paired device.
126
+ *
127
+ * @returns The device mode, or undefined if no device is paired.
128
+ */
129
+ getMode(): DeviceMode | undefined;
116
130
  /**
117
131
  * Fetch the first page of accounts. If the keyring is not currently initialized,
118
132
  * it will trigger a scan request to initialize it.
@@ -1 +1 @@
1
- {"version":3,"file":"qr-keyring.d.cts","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"qr-keyring.d.cts","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { type TypedTransaction, type TypedTxData } from "@ethereumjs/tx";
2
2
  import type { TypedMessage, MessageTypes } from "@metamask/eth-sig-util";
3
3
  import type { Keyring } from "@metamask/keyring-utils";
4
4
  import { type Hex } from "@metamask/utils";
5
- import { type DeviceDetails, type IndexedAddress } from "./device.mjs";
5
+ import { type DeviceDetails, type IndexedAddress, DeviceMode } from "./device.mjs";
6
6
  export declare const QR_KEYRING_TYPE = "QR Hardware Wallet Device";
7
7
  export declare enum QrScanRequestType {
8
8
  /**
@@ -75,6 +75,14 @@ export declare class QrKeyring implements Keyring {
75
75
  * @param state - The serialized state to deserialize
76
76
  */
77
77
  deserialize(state: SerializedQrKeyringState): Promise<void>;
78
+ /**
79
+ * Get the derivation path for a given address.
80
+ *
81
+ * @param address - The address to get the derivation path for.
82
+ * @returns The derivation path for the address, or undefined if the device
83
+ * is not paired or the address is not found.
84
+ */
85
+ getPathFromAddress(address: Hex): string | undefined;
78
86
  /**
79
87
  * Adds accounts to the QrKeyring
80
88
  *
@@ -113,6 +121,12 @@ export declare class QrKeyring implements Keyring {
113
121
  * @returns The name of the paired device or the keyring type.
114
122
  */
115
123
  getName(): string;
124
+ /**
125
+ * Get the mode of the paired device.
126
+ *
127
+ * @returns The device mode, or undefined if no device is paired.
128
+ */
129
+ getMode(): DeviceMode | undefined;
116
130
  /**
117
131
  * Fetch the first page of accounts. If the keyring is not currently initialized,
118
132
  * it will trigger a scan request to initialize it.
@@ -1 +1 @@
1
- {"version":3,"file":"qr-keyring.d.mts","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"qr-keyring.d.mts","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"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"}
@@ -94,6 +94,16 @@ export class QrKeyring {
94
94
  }), "f");
95
95
  __classPrivateFieldSet(this, _QrKeyring_accounts, (state.accounts ?? []).map(normalizeAddress), "f");
96
96
  }
97
+ /**
98
+ * Get the derivation path for a given address.
99
+ *
100
+ * @param address - The address to get the derivation path for.
101
+ * @returns The derivation path for the address, or undefined if the device
102
+ * is not paired or the address is not found.
103
+ */
104
+ getPathFromAddress(address) {
105
+ return __classPrivateFieldGet(this, _QrKeyring_device, "f")?.pathFromAddress(address);
106
+ }
97
107
  /**
98
108
  * Adds accounts to the QrKeyring
99
109
  *
@@ -166,6 +176,14 @@ export class QrKeyring {
166
176
  const source = __classPrivateFieldGet(this, _QrKeyring_device, "f").getDeviceDetails();
167
177
  return source.name;
168
178
  }
179
+ /**
180
+ * Get the mode of the paired device.
181
+ *
182
+ * @returns The device mode, or undefined if no device is paired.
183
+ */
184
+ getMode() {
185
+ return __classPrivateFieldGet(this, _QrKeyring_device, "f")?.getDeviceDetails().keyringMode;
186
+ }
169
187
  /**
170
188
  * Fetch the first page of accounts. If the keyring is not currently initialized,
171
189
  * it will trigger a scan request to initialize it.
@@ -1 +1 @@
1
- {"version":3,"file":"qr-keyring.mjs","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"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","sourcesContent":["import { type TypedTransaction, type TypedTxData } from '@ethereumjs/tx';\nimport type { TypedMessage, MessageTypes } from '@metamask/eth-sig-util';\nimport type { Keyring } from '@metamask/keyring-utils';\nimport { type Hex, add0x, assert, getChecksumAddress } from '@metamask/utils';\n\nimport {\n type DeviceDetails,\n type IndexedAddress,\n Device,\n DeviceMode,\n} from './device';\n\nexport const QR_KEYRING_TYPE = 'QR Hardware Wallet Device';\n\nexport enum QrScanRequestType {\n /**\n * Request a scan for a QR code containing a UR\n * with information related to a hardware wallet.\n */\n PAIR = 'pair',\n /**\n * Request a scan for a QR code containing a\n * UR-encoded transaction signature.\n */\n SIGN = 'sign',\n}\n\nexport type QrSignatureRequest = {\n requestId: string;\n payload: SerializedUR;\n requestTitle?: string;\n requestDescription?: string;\n};\n\nexport type QrScanRequest = {\n type: QrScanRequestType;\n request?: QrSignatureRequest;\n};\n\nexport type SerializedUR = {\n type: string;\n cbor: string;\n};\n\nexport type QrKeyringBridge = {\n requestScan: (request: QrScanRequest) => Promise<SerializedUR>;\n};\n\nexport type QrKeyringOptions = {\n ur?: string;\n bridge: QrKeyringBridge;\n};\n\n/**\n * The state of the QrKeyring\n *\n * @property accounts - The accounts in the QrKeyring\n */\nexport type SerializedQrKeyringState = {\n version?: number;\n accounts?: string[];\n currentAccount?: number;\n} & (\n | {\n initialized?: false;\n }\n | ({\n initialized: true;\n } & DeviceDetails)\n);\n\n/**\n * Returns the default serialized state of the QrKeyring.\n *\n * @returns The default serialized state.\n */\nexport const getDefaultSerializedQrKeyringState =\n (): SerializedQrKeyringState => ({\n initialized: false,\n accounts: [],\n });\n\n/**\n * Normalizes an address to a 0x-prefixed checksum address.\n *\n * @param address - The address to normalize.\n * @returns The normalized address as a Hex string.\n */\nfunction normalizeAddress(address: string): Hex {\n return getChecksumAddress(add0x(address));\n}\n\nexport class QrKeyring implements Keyring {\n static type = QR_KEYRING_TYPE;\n\n readonly type = QR_KEYRING_TYPE;\n\n readonly bridge: QrKeyringBridge;\n\n #device?: Device | undefined;\n\n #accounts: Hex[] = [];\n\n #accountToUnlock?: number | undefined;\n\n #currentPage: number = 0;\n\n constructor(options: QrKeyringOptions) {\n this.bridge = options.bridge;\n\n if (options?.ur) {\n this.pairDevice(options.ur);\n }\n }\n\n /**\n * Serializes the QrKeyring state\n *\n * @returns The serialized state\n */\n async serialize(): Promise<SerializedQrKeyringState> {\n const deviceDetails = this.#device\n ? this.#device.getDeviceDetails()\n : undefined;\n\n if (\n !deviceDetails ||\n ![DeviceMode.HD, DeviceMode.ACCOUNT].includes(deviceDetails.keyringMode)\n ) {\n // the keyring has not initialized with a deviceDetails device yet\n return getDefaultSerializedQrKeyringState();\n }\n\n return {\n ...deviceDetails,\n initialized: true,\n accounts: this.#accounts.slice(),\n };\n }\n\n /**\n * Deserializes the QrKeyring state\n *\n * @param state - The serialized state to deserialize\n */\n async deserialize(state: SerializedQrKeyringState): Promise<void> {\n if (!state.initialized) {\n this.#accounts = [];\n this.#device = undefined;\n return;\n }\n\n this.#device = new Device({\n requestScan: this.bridge.requestScan.bind(this.bridge),\n source: state,\n });\n this.#accounts = (state.accounts ?? []).map(normalizeAddress);\n }\n\n /**\n * Adds accounts to the QrKeyring\n *\n * @param accountsToAdd - The number of accounts to add\n * @returns The accounts added\n */\n async addAccounts(accountsToAdd: number): Promise<Hex[]> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n\n const newAccounts: Hex[] = [];\n\n for (let i = 0; i < accountsToAdd; i++) {\n const index = this.#accountToUnlock ?? this.#accounts.length + i;\n const address = this.#device.addressFromIndex(index);\n this.setAccountToUnlock(index + 1);\n\n if (this.#accounts.includes(address)) {\n continue;\n }\n\n this.#accounts.push(address);\n newAccounts.push(address);\n }\n\n return newAccounts;\n }\n\n /**\n * Gets the accounts in the QrKeyring\n *\n * @returns The accounts in the QrKeyring\n */\n async getAccounts(): Promise<Hex[]> {\n return this.#accounts.slice();\n }\n\n /**\n * Remove an account from the keyring\n *\n * @param address - The address of the account to remove\n */\n removeAccount(address: Hex): void {\n const normalizedAddress = normalizeAddress(address);\n this.#accounts = this.#accounts.filter(\n (account) => account !== normalizedAddress,\n );\n }\n\n /**\n * Pair a QR-based Hardware Device from a CBOR encoded UR to the QrKeyring\n *\n * @param ur - The CBOR encoded UR\n */\n pairDevice(ur: string | SerializedUR): void {\n this.#device = new Device({\n requestScan: this.bridge.requestScan.bind(this.bridge),\n source: ur,\n });\n }\n\n /**\n * Sets the next account index to unlock\n *\n * @param index - The index of the account to unlock\n */\n setAccountToUnlock(index: number): void {\n this.#accountToUnlock = index;\n }\n\n /**\n * Get the name of the paired device or the keyring type\n * if unavailable.\n *\n * @returns The name of the paired device or the keyring type.\n */\n getName(): string {\n if (!this.#device) {\n return QR_KEYRING_TYPE;\n }\n const source = this.#device.getDeviceDetails();\n return source.name;\n }\n\n /**\n * Fetch the first page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The first page of accounts as an array of Hex strings.\n */\n async getFirstPage(): Promise<IndexedAddress[]> {\n this.#currentPage = 0;\n return this.getCurrentPage();\n }\n\n /**\n * Fetch the next page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The next page of accounts as an array of IndexedAddress objects.\n */\n async getNextPage(): Promise<IndexedAddress[]> {\n this.#currentPage += 1;\n return this.getCurrentPage();\n }\n\n /**\n * Fetch the previous page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The previous page of accounts as an array of IndexedAddress objects.\n */\n async getPreviousPage(): Promise<IndexedAddress[]> {\n if (this.#currentPage > 0) {\n this.#currentPage -= 1;\n } else {\n this.#currentPage = 0;\n }\n return this.getCurrentPage();\n }\n\n /**\n * Fetch the current page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The current page of accounts as an array of IndexedAddress objects.\n */\n async getCurrentPage(): Promise<IndexedAddress[]> {\n if (!this.#device) {\n await this.#scanAndInitialize();\n }\n assert(\n this.#device,\n 'A device is expected to be paired before fetching accounts.',\n );\n return this.#device.getAddressesPage(this.#currentPage);\n }\n\n /**\n * Clear the keyring state and forget any paired device or accounts.\n */\n async forgetDevice(): Promise<void> {\n this.#device = undefined;\n this.#accounts = [];\n this.#accountToUnlock = undefined;\n this.#currentPage = 0;\n }\n\n /**\n * Sign a transaction. This is equivalent to the `eth_signTransaction`\n * Ethereum JSON-RPC method. See the Ethereum JSON-RPC API documentation for\n * more details.\n *\n * @param address - The address of the account to use for signing.\n * @param transaction - The transaction to sign.\n * @returns The signed transaction.\n */\n async signTransaction(\n address: Hex,\n transaction: TypedTransaction,\n ): Promise<TypedTxData> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n return this.#device.signTransaction(address, transaction);\n }\n\n /**\n * Sign a message. This is equivalent to the `eth_signTypedData` v4 Ethereum\n * JSON-RPC method.\n *\n * @param address - The address of the account to use for signing.\n * @param data - The data to sign.\n * @returns The signed message.\n */\n async signTypedData<Types extends MessageTypes>(\n address: Hex,\n data: TypedMessage<Types>,\n ): Promise<string> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n return this.#device.signTypedData(address, data);\n }\n\n /**\n * Sign a message. This is equivalent to the `eth_sign` Ethereum JSON-RPC\n * method, which is exposed by MetaMask as the method `personal_sign`. See\n * the Ethereum JSON-RPC API documentation for more details.\n *\n * For more information about this method and why we call it `personal_sign`,\n * see the {@link https://docs.metamask.io/guide/signing-data.html|MetaMask Docs}.\n *\n * @param address - The address of the account to use for signing.\n * @param message - The message to sign.\n * @returns The signed message.\n */\n async signPersonalMessage(address: Hex, message: Hex): Promise<string> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n return this.#device.signPersonalMessage(address, message);\n }\n\n /**\n * Scan for a QR code and initialize the keyring with the\n * scanned UR.\n */\n async #scanAndInitialize(): Promise<void> {\n this.pairDevice(\n await this.bridge.requestScan({ type: QrScanRequestType.PAIR }),\n );\n }\n}\n"]}
1
+ {"version":3,"file":"qr-keyring.mjs","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"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","sourcesContent":["import { type TypedTransaction, type TypedTxData } from '@ethereumjs/tx';\nimport type { TypedMessage, MessageTypes } from '@metamask/eth-sig-util';\nimport type { Keyring } from '@metamask/keyring-utils';\nimport { type Hex, add0x, assert, getChecksumAddress } from '@metamask/utils';\n\nimport {\n type DeviceDetails,\n type IndexedAddress,\n Device,\n DeviceMode,\n} from './device';\n\nexport const QR_KEYRING_TYPE = 'QR Hardware Wallet Device';\n\nexport enum QrScanRequestType {\n /**\n * Request a scan for a QR code containing a UR\n * with information related to a hardware wallet.\n */\n PAIR = 'pair',\n /**\n * Request a scan for a QR code containing a\n * UR-encoded transaction signature.\n */\n SIGN = 'sign',\n}\n\nexport type QrSignatureRequest = {\n requestId: string;\n payload: SerializedUR;\n requestTitle?: string;\n requestDescription?: string;\n};\n\nexport type QrScanRequest = {\n type: QrScanRequestType;\n request?: QrSignatureRequest;\n};\n\nexport type SerializedUR = {\n type: string;\n cbor: string;\n};\n\nexport type QrKeyringBridge = {\n requestScan: (request: QrScanRequest) => Promise<SerializedUR>;\n};\n\nexport type QrKeyringOptions = {\n ur?: string;\n bridge: QrKeyringBridge;\n};\n\n/**\n * The state of the QrKeyring\n *\n * @property accounts - The accounts in the QrKeyring\n */\nexport type SerializedQrKeyringState = {\n version?: number;\n accounts?: string[];\n currentAccount?: number;\n} & (\n | {\n initialized?: false;\n }\n | ({\n initialized: true;\n } & DeviceDetails)\n);\n\n/**\n * Returns the default serialized state of the QrKeyring.\n *\n * @returns The default serialized state.\n */\nexport const getDefaultSerializedQrKeyringState =\n (): SerializedQrKeyringState => ({\n initialized: false,\n accounts: [],\n });\n\n/**\n * Normalizes an address to a 0x-prefixed checksum address.\n *\n * @param address - The address to normalize.\n * @returns The normalized address as a Hex string.\n */\nfunction normalizeAddress(address: string): Hex {\n return getChecksumAddress(add0x(address));\n}\n\nexport class QrKeyring implements Keyring {\n static type = QR_KEYRING_TYPE;\n\n readonly type = QR_KEYRING_TYPE;\n\n readonly bridge: QrKeyringBridge;\n\n #device?: Device | undefined;\n\n #accounts: Hex[] = [];\n\n #accountToUnlock?: number | undefined;\n\n #currentPage: number = 0;\n\n constructor(options: QrKeyringOptions) {\n this.bridge = options.bridge;\n\n if (options?.ur) {\n this.pairDevice(options.ur);\n }\n }\n\n /**\n * Serializes the QrKeyring state\n *\n * @returns The serialized state\n */\n async serialize(): Promise<SerializedQrKeyringState> {\n const deviceDetails = this.#device\n ? this.#device.getDeviceDetails()\n : undefined;\n\n if (\n !deviceDetails ||\n ![DeviceMode.HD, DeviceMode.ACCOUNT].includes(deviceDetails.keyringMode)\n ) {\n // the keyring has not initialized with a deviceDetails device yet\n return getDefaultSerializedQrKeyringState();\n }\n\n return {\n ...deviceDetails,\n initialized: true,\n accounts: this.#accounts.slice(),\n };\n }\n\n /**\n * Deserializes the QrKeyring state\n *\n * @param state - The serialized state to deserialize\n */\n async deserialize(state: SerializedQrKeyringState): Promise<void> {\n if (!state.initialized) {\n this.#accounts = [];\n this.#device = undefined;\n return;\n }\n\n this.#device = new Device({\n requestScan: this.bridge.requestScan.bind(this.bridge),\n source: state,\n });\n this.#accounts = (state.accounts ?? []).map(normalizeAddress);\n }\n\n /**\n * Get the derivation path for a given address.\n *\n * @param address - The address to get the derivation path for.\n * @returns The derivation path for the address, or undefined if the device\n * is not paired or the address is not found.\n */\n getPathFromAddress(address: Hex): string | undefined {\n return this.#device?.pathFromAddress(address);\n }\n\n /**\n * Adds accounts to the QrKeyring\n *\n * @param accountsToAdd - The number of accounts to add\n * @returns The accounts added\n */\n async addAccounts(accountsToAdd: number): Promise<Hex[]> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n\n const newAccounts: Hex[] = [];\n\n for (let i = 0; i < accountsToAdd; i++) {\n const index = this.#accountToUnlock ?? this.#accounts.length + i;\n const address = this.#device.addressFromIndex(index);\n this.setAccountToUnlock(index + 1);\n\n if (this.#accounts.includes(address)) {\n continue;\n }\n\n this.#accounts.push(address);\n newAccounts.push(address);\n }\n\n return newAccounts;\n }\n\n /**\n * Gets the accounts in the QrKeyring\n *\n * @returns The accounts in the QrKeyring\n */\n async getAccounts(): Promise<Hex[]> {\n return this.#accounts.slice();\n }\n\n /**\n * Remove an account from the keyring\n *\n * @param address - The address of the account to remove\n */\n removeAccount(address: Hex): void {\n const normalizedAddress = normalizeAddress(address);\n this.#accounts = this.#accounts.filter(\n (account) => account !== normalizedAddress,\n );\n }\n\n /**\n * Pair a QR-based Hardware Device from a CBOR encoded UR to the QrKeyring\n *\n * @param ur - The CBOR encoded UR\n */\n pairDevice(ur: string | SerializedUR): void {\n this.#device = new Device({\n requestScan: this.bridge.requestScan.bind(this.bridge),\n source: ur,\n });\n }\n\n /**\n * Sets the next account index to unlock\n *\n * @param index - The index of the account to unlock\n */\n setAccountToUnlock(index: number): void {\n this.#accountToUnlock = index;\n }\n\n /**\n * Get the name of the paired device or the keyring type\n * if unavailable.\n *\n * @returns The name of the paired device or the keyring type.\n */\n getName(): string {\n if (!this.#device) {\n return QR_KEYRING_TYPE;\n }\n const source = this.#device.getDeviceDetails();\n return source.name;\n }\n\n /**\n * Get the mode of the paired device.\n *\n * @returns The device mode, or undefined if no device is paired.\n */\n getMode(): DeviceMode | undefined {\n return this.#device?.getDeviceDetails().keyringMode;\n }\n\n /**\n * Fetch the first page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The first page of accounts as an array of Hex strings.\n */\n async getFirstPage(): Promise<IndexedAddress[]> {\n this.#currentPage = 0;\n return this.getCurrentPage();\n }\n\n /**\n * Fetch the next page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The next page of accounts as an array of IndexedAddress objects.\n */\n async getNextPage(): Promise<IndexedAddress[]> {\n this.#currentPage += 1;\n return this.getCurrentPage();\n }\n\n /**\n * Fetch the previous page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The previous page of accounts as an array of IndexedAddress objects.\n */\n async getPreviousPage(): Promise<IndexedAddress[]> {\n if (this.#currentPage > 0) {\n this.#currentPage -= 1;\n } else {\n this.#currentPage = 0;\n }\n return this.getCurrentPage();\n }\n\n /**\n * Fetch the current page of accounts. If the keyring is not currently initialized,\n * it will trigger a scan request to initialize it.\n *\n * @returns The current page of accounts as an array of IndexedAddress objects.\n */\n async getCurrentPage(): Promise<IndexedAddress[]> {\n if (!this.#device) {\n await this.#scanAndInitialize();\n }\n assert(\n this.#device,\n 'A device is expected to be paired before fetching accounts.',\n );\n return this.#device.getAddressesPage(this.#currentPage);\n }\n\n /**\n * Clear the keyring state and forget any paired device or accounts.\n */\n async forgetDevice(): Promise<void> {\n this.#device = undefined;\n this.#accounts = [];\n this.#accountToUnlock = undefined;\n this.#currentPage = 0;\n }\n\n /**\n * Sign a transaction. This is equivalent to the `eth_signTransaction`\n * Ethereum JSON-RPC method. See the Ethereum JSON-RPC API documentation for\n * more details.\n *\n * @param address - The address of the account to use for signing.\n * @param transaction - The transaction to sign.\n * @returns The signed transaction.\n */\n async signTransaction(\n address: Hex,\n transaction: TypedTransaction,\n ): Promise<TypedTxData> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n return this.#device.signTransaction(address, transaction);\n }\n\n /**\n * Sign a message. This is equivalent to the `eth_signTypedData` v4 Ethereum\n * JSON-RPC method.\n *\n * @param address - The address of the account to use for signing.\n * @param data - The data to sign.\n * @returns The signed message.\n */\n async signTypedData<Types extends MessageTypes>(\n address: Hex,\n data: TypedMessage<Types>,\n ): Promise<string> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n return this.#device.signTypedData(address, data);\n }\n\n /**\n * Sign a message. This is equivalent to the `eth_sign` Ethereum JSON-RPC\n * method, which is exposed by MetaMask as the method `personal_sign`. See\n * the Ethereum JSON-RPC API documentation for more details.\n *\n * For more information about this method and why we call it `personal_sign`,\n * see the {@link https://docs.metamask.io/guide/signing-data.html|MetaMask Docs}.\n *\n * @param address - The address of the account to use for signing.\n * @param message - The message to sign.\n * @returns The signed message.\n */\n async signPersonalMessage(address: Hex, message: Hex): Promise<string> {\n if (!this.#device) {\n throw new Error('No device paired.');\n }\n return this.#device.signPersonalMessage(address, message);\n }\n\n /**\n * Scan for a QR code and initialize the keyring with the\n * scanned UR.\n */\n async #scanAndInitialize(): Promise<void> {\n this.pairDevice(\n await this.bridge.requestScan({ type: QrScanRequestType.PAIR }),\n );\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/eth-qr-keyring",
3
- "version": "1.1.0-e40d1ad",
3
+ "version": "1.1.0-fd40efd",
4
4
  "description": "A simple standard interface for a series of Ethereum private keys.",
5
5
  "keywords": [
6
6
  "ethereum",
@@ -53,7 +53,8 @@
53
53
  "@ethereumjs/util": "^9.1.0",
54
54
  "@keystonehq/bc-ur-registry-eth": "^0.19.1",
55
55
  "@metamask/eth-sig-util": "^8.2.0",
56
- "@metamask/keyring-utils": "3.1.0",
56
+ "@metamask/keyring-api": "21.5.0",
57
+ "@metamask/keyring-utils": "3.2.0",
57
58
  "@metamask/utils": "^11.1.0",
58
59
  "async-mutex": "^0.5.0",
59
60
  "hdkey": "^2.1.0",
@@ -63,6 +64,7 @@
63
64
  "@ethereumjs/common": "^4.4.0",
64
65
  "@keystonehq/metamask-airgapped-keyring": "^0.15.2",
65
66
  "@lavamoat/allow-scripts": "^3.2.1",
67
+ "@metamask/account-api": "1.0.0",
66
68
  "@metamask/auto-changelog": "^3.4.4",
67
69
  "@types/hdkey": "^2.0.1",
68
70
  "@types/jest": "^29.5.12",