@metamask-previews/eth-qr-keyring 1.0.0-e4f6caa → 1.1.0-9fbf2ff
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 +19 -1
- package/dist/device.cjs +345 -0
- package/dist/device.cjs.map +1 -0
- package/dist/device.d.cts +175 -0
- package/dist/device.d.cts.map +1 -0
- package/dist/device.d.mts +175 -0
- package/dist/device.d.mts.map +1 -0
- package/dist/device.mjs +345 -0
- package/dist/device.mjs.map +1 -0
- package/dist/index.cjs +9 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +3 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3 -2
- package/dist/index.mjs.map +1 -1
- package/dist/qr-keyring-deferred-promise-bridge.cjs +76 -0
- package/dist/qr-keyring-deferred-promise-bridge.cjs.map +1 -0
- package/dist/qr-keyring-deferred-promise-bridge.d.cts +46 -0
- package/dist/qr-keyring-deferred-promise-bridge.d.cts.map +1 -0
- package/dist/qr-keyring-deferred-promise-bridge.d.mts +46 -0
- package/dist/qr-keyring-deferred-promise-bridge.d.mts.map +1 -0
- package/dist/qr-keyring-deferred-promise-bridge.mjs +72 -0
- package/dist/qr-keyring-deferred-promise-bridge.mjs.map +1 -0
- package/dist/qr-keyring-scanner-bridge.cjs +2 -2
- package/dist/qr-keyring-scanner-bridge.cjs.map +1 -1
- package/dist/qr-keyring-scanner-bridge.d.cts +3 -3
- package/dist/qr-keyring-scanner-bridge.d.mts +3 -3
- package/dist/qr-keyring-scanner-bridge.mjs +2 -2
- package/dist/qr-keyring-scanner-bridge.mjs.map +1 -1
- package/dist/qr-keyring.cjs +65 -137
- package/dist/qr-keyring.cjs.map +1 -1
- package/dist/qr-keyring.d.cts +17 -4
- package/dist/qr-keyring.d.cts.map +1 -1
- package/dist/qr-keyring.d.mts +17 -4
- package/dist/qr-keyring.d.mts.map +1 -1
- package/dist/qr-keyring.mjs +66 -138
- package/dist/qr-keyring.mjs.map +1 -1
- package/package.json +6 -5
- package/dist/airgapped-signer.cjs +0 -270
- package/dist/airgapped-signer.cjs.map +0 -1
- package/dist/airgapped-signer.d.cts +0 -140
- package/dist/airgapped-signer.d.cts.map +0 -1
- package/dist/airgapped-signer.d.mts +0 -140
- package/dist/airgapped-signer.d.mts.map +0 -1
- package/dist/airgapped-signer.mjs +0 -270
- package/dist/airgapped-signer.mjs.map +0 -1
package/dist/qr-keyring.mjs
CHANGED
|
@@ -9,16 +9,10 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
10
|
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
11
|
};
|
|
12
|
-
var _QrKeyring_instances,
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { DataType, ETHSignature, EthSignRequest } from "@keystonehq/bc-ur-registry-eth";
|
|
16
|
-
import { add0x, getChecksumAddress } from "@metamask/utils";
|
|
17
|
-
import { stringify, v4 as uuidv4 } from "uuid";
|
|
18
|
-
import { AirgappedSigner, KeyringMode } from "./airgapped-signer.mjs";
|
|
12
|
+
var _QrKeyring_instances, _QrKeyring_device, _QrKeyring_accounts, _QrKeyring_accountToUnlock, _QrKeyring_currentPage, _QrKeyring_scanAndInitialize;
|
|
13
|
+
import { add0x, assert, getChecksumAddress } from "@metamask/utils";
|
|
14
|
+
import { Device, DeviceMode } from "./device.mjs";
|
|
19
15
|
export const QR_KEYRING_TYPE = 'QR Hardware Wallet Device';
|
|
20
|
-
const DEFAULT_SCAN_REQUEST_TITLE = 'Scan with your hardware wallet';
|
|
21
|
-
const DEFAULT_SCAN_REQUEST_DESCRIPTION = 'After your device has scanned this QR code, click on "Scan" to receive the information.';
|
|
22
16
|
export var QrScanRequestType;
|
|
23
17
|
(function (QrScanRequestType) {
|
|
24
18
|
/**
|
|
@@ -54,13 +48,13 @@ export class QrKeyring {
|
|
|
54
48
|
constructor(options) {
|
|
55
49
|
_QrKeyring_instances.add(this);
|
|
56
50
|
this.type = QR_KEYRING_TYPE;
|
|
57
|
-
|
|
51
|
+
_QrKeyring_device.set(this, void 0);
|
|
58
52
|
_QrKeyring_accounts.set(this, []);
|
|
59
53
|
_QrKeyring_accountToUnlock.set(this, void 0);
|
|
60
54
|
_QrKeyring_currentPage.set(this, 0);
|
|
61
55
|
this.bridge = options.bridge;
|
|
62
56
|
if (options?.ur) {
|
|
63
|
-
this.
|
|
57
|
+
this.pairDevice(options.ur);
|
|
64
58
|
}
|
|
65
59
|
}
|
|
66
60
|
/**
|
|
@@ -69,38 +63,18 @@ export class QrKeyring {
|
|
|
69
63
|
* @returns The serialized state
|
|
70
64
|
*/
|
|
71
65
|
async serialize() {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
66
|
+
const deviceDetails = __classPrivateFieldGet(this, _QrKeyring_device, "f")
|
|
67
|
+
? __classPrivateFieldGet(this, _QrKeyring_device, "f").getDeviceDetails()
|
|
68
|
+
: undefined;
|
|
69
|
+
if (!deviceDetails ||
|
|
70
|
+
![DeviceMode.HD, DeviceMode.ACCOUNT].includes(deviceDetails.keyringMode)) {
|
|
71
|
+
// the keyring has not initialized with a deviceDetails device yet
|
|
76
72
|
return getDefaultSerializedQrKeyringState();
|
|
77
73
|
}
|
|
78
|
-
const accounts = __classPrivateFieldGet(this, _QrKeyring_accounts, "f").slice();
|
|
79
|
-
if (source.keyringMode === KeyringMode.HD) {
|
|
80
|
-
// These properties are only relevant for HD Keys
|
|
81
|
-
return {
|
|
82
|
-
initialized: true,
|
|
83
|
-
name: source.name,
|
|
84
|
-
keyringMode: KeyringMode.HD,
|
|
85
|
-
keyringAccount: source.keyringAccount,
|
|
86
|
-
xfp: source.xfp,
|
|
87
|
-
xpub: source.xpub,
|
|
88
|
-
hdPath: source.hdPath,
|
|
89
|
-
childrenPath: source.childrenPath,
|
|
90
|
-
accounts,
|
|
91
|
-
indexes: source.indexes,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
// These properties are only relevant for Account Keys
|
|
95
74
|
return {
|
|
75
|
+
...deviceDetails,
|
|
96
76
|
initialized: true,
|
|
97
|
-
|
|
98
|
-
keyringMode: KeyringMode.ACCOUNT,
|
|
99
|
-
keyringAccount: source.keyringAccount,
|
|
100
|
-
xfp: source.xfp,
|
|
101
|
-
paths: source.paths,
|
|
102
|
-
accounts,
|
|
103
|
-
indexes: source.indexes,
|
|
77
|
+
accounts: __classPrivateFieldGet(this, _QrKeyring_accounts, "f").slice(),
|
|
104
78
|
};
|
|
105
79
|
}
|
|
106
80
|
/**
|
|
@@ -111,10 +85,13 @@ export class QrKeyring {
|
|
|
111
85
|
async deserialize(state) {
|
|
112
86
|
if (!state.initialized) {
|
|
113
87
|
__classPrivateFieldSet(this, _QrKeyring_accounts, [], "f");
|
|
114
|
-
|
|
88
|
+
__classPrivateFieldSet(this, _QrKeyring_device, undefined, "f");
|
|
115
89
|
return;
|
|
116
90
|
}
|
|
117
|
-
|
|
91
|
+
__classPrivateFieldSet(this, _QrKeyring_device, new Device({
|
|
92
|
+
requestScan: this.bridge.requestScan.bind(this.bridge),
|
|
93
|
+
source: state,
|
|
94
|
+
}), "f");
|
|
118
95
|
__classPrivateFieldSet(this, _QrKeyring_accounts, (state.accounts ?? []).map(normalizeAddress), "f");
|
|
119
96
|
}
|
|
120
97
|
/**
|
|
@@ -124,20 +101,20 @@ export class QrKeyring {
|
|
|
124
101
|
* @returns The accounts added
|
|
125
102
|
*/
|
|
126
103
|
async addAccounts(accountsToAdd) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
104
|
+
if (!__classPrivateFieldGet(this, _QrKeyring_device, "f")) {
|
|
105
|
+
throw new Error('No device paired.');
|
|
106
|
+
}
|
|
130
107
|
const newAccounts = [];
|
|
131
108
|
for (let i = 0; i < accountsToAdd; i++) {
|
|
132
|
-
const index =
|
|
133
|
-
const address = __classPrivateFieldGet(this,
|
|
109
|
+
const index = __classPrivateFieldGet(this, _QrKeyring_accountToUnlock, "f") ?? __classPrivateFieldGet(this, _QrKeyring_accounts, "f").length + i;
|
|
110
|
+
const address = __classPrivateFieldGet(this, _QrKeyring_device, "f").addressFromIndex(index);
|
|
111
|
+
this.setAccountToUnlock(index + 1);
|
|
134
112
|
if (__classPrivateFieldGet(this, _QrKeyring_accounts, "f").includes(address)) {
|
|
135
113
|
continue;
|
|
136
114
|
}
|
|
137
115
|
__classPrivateFieldGet(this, _QrKeyring_accounts, "f").push(address);
|
|
138
116
|
newAccounts.push(address);
|
|
139
117
|
}
|
|
140
|
-
__classPrivateFieldSet(this, _QrKeyring_accountToUnlock, startIndex + accountsToAdd, "f");
|
|
141
118
|
return newAccounts;
|
|
142
119
|
}
|
|
143
120
|
/**
|
|
@@ -146,7 +123,7 @@ export class QrKeyring {
|
|
|
146
123
|
* @returns The accounts in the QrKeyring
|
|
147
124
|
*/
|
|
148
125
|
async getAccounts() {
|
|
149
|
-
return
|
|
126
|
+
return __classPrivateFieldGet(this, _QrKeyring_accounts, "f").slice();
|
|
150
127
|
}
|
|
151
128
|
/**
|
|
152
129
|
* Remove an account from the keyring
|
|
@@ -158,12 +135,15 @@ export class QrKeyring {
|
|
|
158
135
|
__classPrivateFieldSet(this, _QrKeyring_accounts, __classPrivateFieldGet(this, _QrKeyring_accounts, "f").filter((account) => account !== normalizedAddress), "f");
|
|
159
136
|
}
|
|
160
137
|
/**
|
|
161
|
-
*
|
|
138
|
+
* Pair a QR-based Hardware Device from a CBOR encoded UR to the QrKeyring
|
|
162
139
|
*
|
|
163
140
|
* @param ur - The CBOR encoded UR
|
|
164
141
|
*/
|
|
165
|
-
|
|
166
|
-
|
|
142
|
+
pairDevice(ur) {
|
|
143
|
+
__classPrivateFieldSet(this, _QrKeyring_device, new Device({
|
|
144
|
+
requestScan: this.bridge.requestScan.bind(this.bridge),
|
|
145
|
+
source: ur,
|
|
146
|
+
}), "f");
|
|
167
147
|
}
|
|
168
148
|
/**
|
|
169
149
|
* Sets the next account index to unlock
|
|
@@ -180,8 +160,11 @@ export class QrKeyring {
|
|
|
180
160
|
* @returns The name of the paired device or the keyring type.
|
|
181
161
|
*/
|
|
182
162
|
getName() {
|
|
183
|
-
|
|
184
|
-
|
|
163
|
+
if (!__classPrivateFieldGet(this, _QrKeyring_device, "f")) {
|
|
164
|
+
return QR_KEYRING_TYPE;
|
|
165
|
+
}
|
|
166
|
+
const source = __classPrivateFieldGet(this, _QrKeyring_device, "f").getDeviceDetails();
|
|
167
|
+
return source.name;
|
|
185
168
|
}
|
|
186
169
|
/**
|
|
187
170
|
* Fetch the first page of accounts. If the keyring is not currently initialized,
|
|
@@ -225,16 +208,17 @@ export class QrKeyring {
|
|
|
225
208
|
* @returns The current page of accounts as an array of IndexedAddress objects.
|
|
226
209
|
*/
|
|
227
210
|
async getCurrentPage() {
|
|
228
|
-
if (!__classPrivateFieldGet(this,
|
|
211
|
+
if (!__classPrivateFieldGet(this, _QrKeyring_device, "f")) {
|
|
229
212
|
await __classPrivateFieldGet(this, _QrKeyring_instances, "m", _QrKeyring_scanAndInitialize).call(this);
|
|
230
213
|
}
|
|
231
|
-
|
|
214
|
+
assert(__classPrivateFieldGet(this, _QrKeyring_device, "f"), 'A device is expected to be paired before fetching accounts.');
|
|
215
|
+
return __classPrivateFieldGet(this, _QrKeyring_device, "f").getAddressesPage(__classPrivateFieldGet(this, _QrKeyring_currentPage, "f"));
|
|
232
216
|
}
|
|
233
217
|
/**
|
|
234
218
|
* Clear the keyring state and forget any paired device or accounts.
|
|
235
219
|
*/
|
|
236
220
|
async forgetDevice() {
|
|
237
|
-
|
|
221
|
+
__classPrivateFieldSet(this, _QrKeyring_device, undefined, "f");
|
|
238
222
|
__classPrivateFieldSet(this, _QrKeyring_accounts, [], "f");
|
|
239
223
|
__classPrivateFieldSet(this, _QrKeyring_accountToUnlock, undefined, "f");
|
|
240
224
|
__classPrivateFieldSet(this, _QrKeyring_currentPage, 0, "f");
|
|
@@ -249,41 +233,10 @@ export class QrKeyring {
|
|
|
249
233
|
* @returns The signed transaction.
|
|
250
234
|
*/
|
|
251
235
|
async signTransaction(address, transaction) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
throw new Error('Keyring is not initialized. Please scan a QR code.');
|
|
255
|
-
}
|
|
256
|
-
const dataType = transaction.type === TransactionType.Legacy
|
|
257
|
-
? DataType.transaction
|
|
258
|
-
: DataType.typedTransaction;
|
|
259
|
-
let messageToSign;
|
|
260
|
-
if (transaction.type === TransactionType.Legacy) {
|
|
261
|
-
messageToSign = Buffer.from(RLP.encode(transaction.getMessageToSign()));
|
|
262
|
-
}
|
|
263
|
-
else {
|
|
264
|
-
messageToSign = Buffer.from(transaction.getMessageToSign());
|
|
236
|
+
if (!__classPrivateFieldGet(this, _QrKeyring_device, "f")) {
|
|
237
|
+
throw new Error('No device paired.');
|
|
265
238
|
}
|
|
266
|
-
|
|
267
|
-
const chainId = Number(transaction.common.chainId());
|
|
268
|
-
const requestId = uuidv4();
|
|
269
|
-
const ethSignRequestUR = EthSignRequest.constructETHRequest(messageToSign, dataType, hdPath, signer.xfp, requestId, chainId).toUR();
|
|
270
|
-
const { r, s, v } = await __classPrivateFieldGet(this, _QrKeyring_instances, "m", _QrKeyring_requestSignature).call(this, {
|
|
271
|
-
requestId,
|
|
272
|
-
payload: {
|
|
273
|
-
type: ethSignRequestUR.type,
|
|
274
|
-
cbor: ethSignRequestUR.cbor.toString('hex'),
|
|
275
|
-
},
|
|
276
|
-
requestTitle: 'Scan with your hardware wallet',
|
|
277
|
-
requestDescription: 'After your device has signed this message, click on "Scan" to receive the signature',
|
|
278
|
-
});
|
|
279
|
-
return TransactionFactory.fromTxData({
|
|
280
|
-
...transaction,
|
|
281
|
-
r,
|
|
282
|
-
s,
|
|
283
|
-
v,
|
|
284
|
-
}, {
|
|
285
|
-
common: transaction.common,
|
|
286
|
-
});
|
|
239
|
+
return __classPrivateFieldGet(this, _QrKeyring_device, "f").signTransaction(address, transaction);
|
|
287
240
|
}
|
|
288
241
|
/**
|
|
289
242
|
* Sign a message. This is equivalent to the `eth_signTypedData` v4 Ethereum
|
|
@@ -294,62 +247,37 @@ export class QrKeyring {
|
|
|
294
247
|
* @returns The signed message.
|
|
295
248
|
*/
|
|
296
249
|
async signTypedData(address, data) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
throw new Error('Keyring is not initialized. Please scan a QR code.');
|
|
250
|
+
if (!__classPrivateFieldGet(this, _QrKeyring_device, "f")) {
|
|
251
|
+
throw new Error('No device paired.');
|
|
300
252
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
253
|
+
return __classPrivateFieldGet(this, _QrKeyring_device, "f").signTypedData(address, data);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Sign a message. This is equivalent to the `eth_sign` Ethereum JSON-RPC
|
|
257
|
+
* method, which is exposed by MetaMask as the method `personal_sign`. See
|
|
258
|
+
* the Ethereum JSON-RPC API documentation for more details.
|
|
259
|
+
*
|
|
260
|
+
* For more information about this method and why we call it `personal_sign`,
|
|
261
|
+
* see the {@link https://docs.metamask.io/guide/signing-data.html|MetaMask Docs}.
|
|
262
|
+
*
|
|
263
|
+
* @param address - The address of the account to use for signing.
|
|
264
|
+
* @param message - The message to sign.
|
|
265
|
+
* @returns The signed message.
|
|
266
|
+
*/
|
|
267
|
+
async signPersonalMessage(address, message) {
|
|
268
|
+
if (!__classPrivateFieldGet(this, _QrKeyring_device, "f")) {
|
|
269
|
+
throw new Error('No device paired.');
|
|
270
|
+
}
|
|
271
|
+
return __classPrivateFieldGet(this, _QrKeyring_device, "f").signPersonalMessage(address, message);
|
|
318
272
|
}
|
|
319
273
|
}
|
|
320
|
-
|
|
274
|
+
_QrKeyring_device = new WeakMap(), _QrKeyring_accounts = new WeakMap(), _QrKeyring_accountToUnlock = new WeakMap(), _QrKeyring_currentPage = new WeakMap(), _QrKeyring_instances = new WeakSet(), _QrKeyring_scanAndInitialize =
|
|
321
275
|
/**
|
|
322
276
|
* Scan for a QR code and initialize the keyring with the
|
|
323
277
|
* scanned UR.
|
|
324
278
|
*/
|
|
325
279
|
async function _QrKeyring_scanAndInitialize() {
|
|
326
|
-
this.
|
|
327
|
-
}, _QrKeyring_requestSignature =
|
|
328
|
-
/**
|
|
329
|
-
* Request a signature for a transaction or message.
|
|
330
|
-
*
|
|
331
|
-
* @param request - The signature request containing the data to sign.
|
|
332
|
-
* @returns The signature as an object containing r, s, and v values.
|
|
333
|
-
*/
|
|
334
|
-
async function _QrKeyring_requestSignature(request) {
|
|
335
|
-
const response = await this.bridge.requestScan({
|
|
336
|
-
type: QrScanRequestType.SIGN,
|
|
337
|
-
request,
|
|
338
|
-
});
|
|
339
|
-
const signatureEnvelope = ETHSignature.fromCBOR(Buffer.from(response.cbor, 'hex'));
|
|
340
|
-
const signature = signatureEnvelope.getSignature();
|
|
341
|
-
const requestId = signatureEnvelope.getRequestId();
|
|
342
|
-
if (!requestId) {
|
|
343
|
-
throw new Error('Signature request ID is missing.');
|
|
344
|
-
}
|
|
345
|
-
if (request.requestId !== stringify(requestId)) {
|
|
346
|
-
throw new Error(`Signature request ID mismatch. Expected: ${request.requestId}, received: ${requestId.toString('hex')}`);
|
|
347
|
-
}
|
|
348
|
-
return {
|
|
349
|
-
r: signature.subarray(0, 32),
|
|
350
|
-
s: signature.subarray(32, 64),
|
|
351
|
-
v: signature.subarray(64),
|
|
352
|
-
};
|
|
280
|
+
this.pairDevice(await this.bridge.requestScan({ type: QrScanRequestType.PAIR }));
|
|
353
281
|
};
|
|
354
282
|
QrKeyring.type = QR_KEYRING_TYPE;
|
|
355
283
|
//# sourceMappingURL=qr-keyring.mjs.map
|
package/dist/qr-keyring.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qr-keyring.mjs","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,GAAG,EAAE,wBAAwB;AACtC,OAAO,EAEL,kBAAkB,EAClB,eAAe,EAGhB,uBAAuB;AACxB,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,cAAc,EACf,uCAAuC;AAGxC,OAAO,EAAY,KAAK,EAAE,kBAAkB,EAAE,wBAAwB;AACtE,OAAO,EAAE,SAAS,EAAE,EAAE,IAAI,MAAM,EAAE,aAAa;AAE/C,OAAO,EAGL,eAAe,EACf,WAAW,EACZ,+BAA2B;AAE5B,MAAM,CAAC,MAAM,eAAe,GAAG,2BAA2B,CAAC;AAE3D,MAAM,0BAA0B,GAAG,gCAAgC,CAAC;AAEpE,MAAM,gCAAgC,GACpC,yFAAyF,CAAC;AAE5F,MAAM,CAAN,IAAY,iBAWX;AAXD,WAAY,iBAAiB;IAC3B;;;OAGG;IACH,kCAAa,CAAA;IACb;;;OAGG;IACH,kCAAa,CAAA;AACf,CAAC,EAXW,iBAAiB,KAAjB,iBAAiB,QAW5B;AA8CD;;;;GAIG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAC7C,GAA6B,EAAE,CAAC,CAAC;IAC/B,WAAW,EAAE,KAAK;IAClB,QAAQ,EAAE,EAAE;CACb,CAAC,CAAC;AAEL;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,OAAe;IACvC,OAAO,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,OAAO,SAAS;IAepB,YAAY,OAAyB;;QAZ5B,SAAI,GAAG,eAAe,CAAC;QAIvB,4BAA2B,IAAI,eAAe,EAAE,EAAC;QAE1D,8BAAmB,EAAE,EAAC;QAEtB,6CAAsC;QAEtC,iCAAuB,CAAC,EAAC;QAGvB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAE7B,IAAI,OAAO,EAAE,EAAE,EAAE,CAAC;YAChB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,SAAS;QACb,MAAM,MAAM,GAAG,uBAAA,IAAI,yBAAQ,CAAC,gBAAgB,EAAE,CAAC;QAE/C,IACE,CAAC,MAAM;YACP,CAAC,CAAC,WAAW,CAAC,EAAE,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,EACnE,CAAC;YACD,2DAA2D;YAC3D,OAAO,kCAAkC,EAAE,CAAC;QAC9C,CAAC;QAED,MAAM,QAAQ,GAAG,uBAAA,IAAI,2BAAU,CAAC,KAAK,EAAE,CAAC;QAExC,IAAI,MAAM,CAAC,WAAW,KAAK,WAAW,CAAC,EAAE,EAAE,CAAC;YAC1C,iDAAiD;YACjD,OAAO;gBACL,WAAW,EAAE,IAAI;gBACjB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,WAAW,EAAE,WAAW,CAAC,EAAE;gBAC3B,cAAc,EAAE,MAAM,CAAC,cAAc;gBACrC,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,QAAQ;gBACR,OAAO,EAAE,MAAM,CAAC,OAAO;aACxB,CAAC;QACJ,CAAC;QACD,sDAAsD;QACtD,OAAO;YACL,WAAW,EAAE,IAAI;YACjB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,WAAW,EAAE,WAAW,CAAC,OAAO;YAChC,cAAc,EAAE,MAAM,CAAC,cAAc;YACrC,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,QAAQ;YACR,OAAO,EAAE,MAAM,CAAC,OAAO;SACxB,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CAAC,KAA+B;QAC/C,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YACvB,uBAAA,IAAI,uBAAa,EAAE,MAAA,CAAC;YACpB,uBAAA,IAAI,yBAAQ,CAAC,KAAK,EAAE,CAAC;YACrB,OAAO;QACT,CAAC;QAED,uBAAA,IAAI,yBAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,uBAAA,IAAI,uBAAa,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,MAAA,CAAC;IAChE,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,WAAW,CAAC,aAAqB;QACrC,MAAM,WAAW,GAAG,uBAAA,IAAI,2BAAU,CAAC,uBAAA,IAAI,2BAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC9D,MAAM,UAAU,GACd,uBAAA,IAAI,kCAAiB;YACrB,CAAC,WAAW,CAAC,CAAC,CAAC,uBAAA,IAAI,yBAAQ,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACjE,MAAM,WAAW,GAAU,EAAE,CAAC;QAE9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,MAAM,KAAK,GAAG,UAAU,GAAG,CAAC,CAAC;YAC7B,MAAM,OAAO,GAAG,uBAAA,IAAI,yBAAQ,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;YAErD,IAAI,uBAAA,IAAI,2BAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBACrC,SAAS;YACX,CAAC;YAED,uBAAA,IAAI,2BAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC7B,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;QAED,uBAAA,IAAI,8BAAoB,UAAU,GAAG,aAAa,MAAA,CAAC;QACnD,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW;QACf,OAAO,KAAK,CAAC,IAAI,CAAC,uBAAA,IAAI,2BAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED;;;;OAIG;IACH,aAAa,CAAC,OAAY;QACxB,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;QACpD,uBAAA,IAAI,uBAAa,uBAAA,IAAI,2BAAU,CAAC,MAAM,CACpC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,iBAAiB,CAC3C,MAAA,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,QAAQ,CAAC,EAAyB;QAChC,uBAAA,IAAI,yBAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACxB,CAAC;IAED;;;;OAIG;IACH,kBAAkB,CAAC,KAAa;QAC9B,uBAAA,IAAI,8BAAoB,KAAK,MAAA,CAAC;IAChC,CAAC;IAED;;;;;OAKG;IACH,OAAO;QACL,MAAM,MAAM,GAAG,uBAAA,IAAI,yBAAQ,CAAC,gBAAgB,EAAE,CAAC;QAC/C,OAAO,MAAM,EAAE,IAAI,IAAI,eAAe,CAAC;IACzC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,YAAY;QAChB,uBAAA,IAAI,0BAAgB,CAAC,MAAA,CAAC;QACtB,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,WAAW;QACf,iHAAqB,CAAC,MAAA,CAAC;QACvB,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,eAAe;QACnB,IAAI,uBAAA,IAAI,8BAAa,GAAG,CAAC,EAAE,CAAC;YAC1B,iHAAqB,CAAC,MAAA,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,uBAAA,IAAI,0BAAgB,CAAC,MAAA,CAAC;QACxB,CAAC;QACD,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,cAAc;QAClB,IAAI,CAAC,uBAAA,IAAI,yBAAQ,CAAC,aAAa,EAAE,EAAE,CAAC;YAClC,MAAM,uBAAA,IAAI,0DAAmB,MAAvB,IAAI,CAAqB,CAAC;QAClC,CAAC;QAED,OAAO,uBAAA,IAAI,yBAAQ,CAAC,gBAAgB,CAAC,uBAAA,IAAI,8BAAa,CAAC,CAAC;IAC1D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY;QAChB,uBAAA,IAAI,yBAAQ,CAAC,KAAK,EAAE,CAAC;QACrB,uBAAA,IAAI,uBAAa,EAAE,MAAA,CAAC;QACpB,uBAAA,IAAI,8BAAoB,SAAS,MAAA,CAAC;QAClC,uBAAA,IAAI,0BAAgB,CAAC,MAAA,CAAC;IACxB,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,eAAe,CACnB,OAAY,EACZ,WAA6B;QAE7B,MAAM,MAAM,GAAG,uBAAA,IAAI,yBAAQ,CAAC,gBAAgB,EAAE,CAAC;QAC/C,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;QACxE,CAAC;QACD,MAAM,QAAQ,GACZ,WAAW,CAAC,IAAI,KAAK,eAAe,CAAC,MAAM;YACzC,CAAC,CAAC,QAAQ,CAAC,WAAW;YACtB,CAAC,CAAC,QAAQ,CAAC,gBAAgB,CAAC;QAChC,IAAI,aAAqB,CAAC;QAC1B,IAAI,WAAW,CAAC,IAAI,KAAK,eAAe,CAAC,MAAM,EAAE,CAAC;YAChD,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC;QAC1E,CAAC;aAAM,CAAC;YACN,aAAa,GAAG,MAAM,CAAC,IAAI,CACxB,WAA2C,CAAC,gBAAgB,EAAE,CAChE,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,uBAAA,IAAI,yBAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QACrD,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QACrD,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC;QAC3B,MAAM,gBAAgB,GAAG,cAAc,CAAC,mBAAmB,CACzD,aAAa,EACb,QAAQ,EACR,MAAM,EACN,MAAM,CAAC,GAAG,EACV,SAAS,EACT,OAAO,CACR,CAAC,IAAI,EAAE,CAAC;QACT,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,MAAM,uBAAA,IAAI,yDAAkB,MAAtB,IAAI,EAAmB;YAC/C,SAAS;YACT,OAAO,EAAE;gBACP,IAAI,EAAE,gBAAgB,CAAC,IAAI;gBAC3B,IAAI,EAAE,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;aAC5C;YACD,YAAY,EAAE,gCAAgC;YAC9C,kBAAkB,EAChB,qFAAqF;SACxF,CAAC,CAAC;QAEH,OAAO,kBAAkB,CAAC,UAAU,CAClC;YACE,GAAG,WAAW;YACd,CAAC;YACD,CAAC;YACD,CAAC;SACF,EACD;YACE,MAAM,EAAE,WAAW,CAAC,MAAM;SAC3B,CACF,CAAC;IACJ,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,aAAa,CACjB,OAAY,EACZ,IAAyB;QAEzB,MAAM,MAAM,GAAG,uBAAA,IAAI,yBAAQ,CAAC,gBAAgB,EAAE,CAAC;QAC/C,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;QACxE,CAAC;QAED,MAAM,MAAM,GAAG,uBAAA,IAAI,yBAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QACrD,MAAM,SAAS,GAAG,MAAM,EAAE,CAAC;QAC3B,MAAM,gBAAgB,GAAG,cAAc,CAAC,mBAAmB,CACzD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,EACzC,QAAQ,CAAC,SAAS,EAClB,MAAM,EACN,MAAM,CAAC,GAAG,EACV,SAAS,EACT,SAAS,EACT,OAAO,CACR,CAAC,IAAI,EAAE,CAAC;QAET,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,MAAM,uBAAA,IAAI,yDAAkB,MAAtB,IAAI,EAAmB;YAC/C,SAAS;YACT,OAAO,EAAE;gBACP,IAAI,EAAE,gBAAgB,CAAC,IAAI;gBAC3B,IAAI,EAAE,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;aAC5C;YACD,YAAY,EAAE,0BAA0B;YACxC,kBAAkB,EAAE,gCAAgC;SACrD,CAAC,CAAC;QAEH,OAAO,KAAK,CACV,MAAM,CAAC,MAAM,CAAC;YACZ,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;YAClB,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;YAClB,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;SACnB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CACnB,CAAC;IACJ,CAAC;;;AAED;;;GAGG;AACH,KAAK;IACH,IAAI,CAAC,QAAQ,CACX,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,IAAI,EAAE,CAAC,CAChE,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,KAAK,sCACH,OAA2B;IAE3B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;QAC7C,IAAI,EAAE,iBAAiB,CAAC,IAAI;QAC5B,OAAO;KACR,CAAC,CAAC;IACH,MAAM,iBAAiB,GAAG,YAAY,CAAC,QAAQ,CAC7C,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAClC,CAAC;IACF,MAAM,SAAS,GAAG,iBAAiB,CAAC,YAAY,EAAE,CAAC;IACnD,MAAM,SAAS,GAAG,iBAAiB,CAAC,YAAY,EAAE,CAAC;IAEnD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CACb,4CACE,OAAO,CAAC,SACV,eAAe,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAC3C,CAAC;IACJ,CAAC;IAED,OAAO;QACL,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC;QAC5B,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;QAC7B,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;KAC1B,CAAC;AACJ,CAAC;AA/XM,cAAI,GAAG,eAAe,AAAlB,CAAmB","sourcesContent":["import { RLP } from '@ethereumjs/rlp';\nimport {\n type FeeMarketEIP1559Transaction,\n TransactionFactory,\n TransactionType,\n type TypedTransaction,\n type TypedTxData,\n} from '@ethereumjs/tx';\nimport {\n DataType,\n ETHSignature,\n EthSignRequest,\n} from '@keystonehq/bc-ur-registry-eth';\nimport type { TypedMessage, MessageTypes } from '@metamask/eth-sig-util';\nimport type { Keyring } from '@metamask/keyring-utils';\nimport { type Hex, add0x, getChecksumAddress } from '@metamask/utils';\nimport { stringify, v4 as uuidv4 } from 'uuid';\n\nimport {\n type AirgappedSignerDetails,\n type IndexedAddress,\n AirgappedSigner,\n KeyringMode,\n} from './airgapped-signer';\n\nexport const QR_KEYRING_TYPE = 'QR Hardware Wallet Device';\n\nconst DEFAULT_SCAN_REQUEST_TITLE = 'Scan with your hardware wallet';\n\nconst DEFAULT_SCAN_REQUEST_DESCRIPTION =\n 'After your device has scanned this QR code, click on \"Scan\" to receive the information.';\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 } & AirgappedSignerDetails)\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 readonly #signer: AirgappedSigner = new AirgappedSigner();\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.submitUR(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 source = this.#signer.getSourceDetails();\n\n if (\n !source ||\n ![KeyringMode.HD, KeyringMode.ACCOUNT].includes(source.keyringMode)\n ) {\n // the keyring has not initialized with a device source yet\n return getDefaultSerializedQrKeyringState();\n }\n\n const accounts = this.#accounts.slice();\n\n if (source.keyringMode === KeyringMode.HD) {\n // These properties are only relevant for HD Keys\n return {\n initialized: true,\n name: source.name,\n keyringMode: KeyringMode.HD,\n keyringAccount: source.keyringAccount,\n xfp: source.xfp,\n xpub: source.xpub,\n hdPath: source.hdPath,\n childrenPath: source.childrenPath,\n accounts,\n indexes: source.indexes,\n };\n }\n // These properties are only relevant for Account Keys\n return {\n initialized: true,\n name: source.name,\n keyringMode: KeyringMode.ACCOUNT,\n keyringAccount: source.keyringAccount,\n xfp: source.xfp,\n paths: source.paths,\n accounts,\n indexes: source.indexes,\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.#signer.clear();\n return;\n }\n\n this.#signer.init(state);\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 const lastAccount = this.#accounts[this.#accounts.length - 1];\n const startIndex =\n this.#accountToUnlock ??\n (lastAccount ? this.#signer.indexFromAddress(lastAccount) : 0);\n const newAccounts: Hex[] = [];\n\n for (let i = 0; i < accountsToAdd; i++) {\n const index = startIndex + i;\n const address = this.#signer.addressFromIndex(index);\n\n if (this.#accounts.includes(address)) {\n continue;\n }\n\n this.#accounts.push(address);\n newAccounts.push(address);\n }\n\n this.#accountToUnlock = startIndex + accountsToAdd;\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 Array.from(this.#accounts.values());\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 * Submits a CBOR encoded UR to the QrKeyring\n *\n * @param ur - The CBOR encoded UR\n */\n submitUR(ur: string | SerializedUR): void {\n this.#signer.init(ur);\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 const source = this.#signer.getSourceDetails();\n return source?.name ?? QR_KEYRING_TYPE;\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.#signer.isInitialized()) {\n await this.#scanAndInitialize();\n }\n\n return this.#signer.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.#signer.clear();\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 const signer = this.#signer.getSourceDetails();\n if (!signer?.xfp) {\n throw new Error('Keyring is not initialized. Please scan a QR code.');\n }\n const dataType =\n transaction.type === TransactionType.Legacy\n ? DataType.transaction\n : DataType.typedTransaction;\n let messageToSign: Buffer;\n if (transaction.type === TransactionType.Legacy) {\n messageToSign = Buffer.from(RLP.encode(transaction.getMessageToSign()));\n } else {\n messageToSign = Buffer.from(\n (transaction as FeeMarketEIP1559Transaction).getMessageToSign(),\n );\n }\n\n const hdPath = this.#signer.pathFromAddress(address);\n const chainId = Number(transaction.common.chainId());\n const requestId = uuidv4();\n const ethSignRequestUR = EthSignRequest.constructETHRequest(\n messageToSign,\n dataType,\n hdPath,\n signer.xfp,\n requestId,\n chainId,\n ).toUR();\n const { r, s, v } = await this.#requestSignature({\n requestId,\n payload: {\n type: ethSignRequestUR.type,\n cbor: ethSignRequestUR.cbor.toString('hex'),\n },\n requestTitle: 'Scan with your hardware wallet',\n requestDescription:\n 'After your device has signed this message, click on \"Scan\" to receive the signature',\n });\n\n return TransactionFactory.fromTxData(\n {\n ...transaction,\n r,\n s,\n v,\n },\n {\n common: transaction.common,\n },\n );\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 const signer = this.#signer.getSourceDetails();\n if (!signer?.xfp) {\n throw new Error('Keyring is not initialized. Please scan a QR code.');\n }\n\n const hdPath = this.#signer.pathFromAddress(address);\n const requestId = uuidv4();\n const ethSignRequestUR = EthSignRequest.constructETHRequest(\n Buffer.from(JSON.stringify(data), 'utf8'),\n DataType.typedData,\n hdPath,\n signer.xfp,\n requestId,\n undefined,\n address,\n ).toUR();\n\n const { r, s, v } = await this.#requestSignature({\n requestId,\n payload: {\n type: ethSignRequestUR.type,\n cbor: ethSignRequestUR.cbor.toString('hex'),\n },\n requestTitle: DEFAULT_SCAN_REQUEST_TITLE,\n requestDescription: DEFAULT_SCAN_REQUEST_DESCRIPTION,\n });\n\n return add0x(\n Buffer.concat([\n Uint8Array.from(r),\n Uint8Array.from(s),\n Uint8Array.from(v),\n ]).toString('hex'),\n );\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.submitUR(\n await this.bridge.requestScan({ type: QrScanRequestType.PAIR }),\n );\n }\n\n /**\n * Request a signature for a transaction or message.\n *\n * @param request - The signature request containing the data to sign.\n * @returns The signature as an object containing r, s, and v values.\n */\n async #requestSignature(\n request: QrSignatureRequest,\n ): Promise<{ r: Buffer; s: Buffer; v: Buffer }> {\n const response = await this.bridge.requestScan({\n type: QrScanRequestType.SIGN,\n request,\n });\n const signatureEnvelope = ETHSignature.fromCBOR(\n Buffer.from(response.cbor, 'hex'),\n );\n const signature = signatureEnvelope.getSignature();\n const requestId = signatureEnvelope.getRequestId();\n\n if (!requestId) {\n throw new Error('Signature request ID is missing.');\n }\n\n if (request.requestId !== stringify(requestId)) {\n throw new Error(\n `Signature request ID mismatch. Expected: ${\n request.requestId\n }, received: ${requestId.toString('hex')}`,\n );\n }\n\n return {\n r: signature.subarray(0, 32),\n s: signature.subarray(32, 64),\n v: signature.subarray(64),\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 * 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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/eth-qr-keyring",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0-9fbf2ff",
|
|
4
4
|
"description": "A simple standard interface for a series of Ethereum private keys.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ethereum",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"build:clean": "rimraf dist && yarn build",
|
|
39
39
|
"build:docs": "typedoc",
|
|
40
40
|
"build:force": "tsc --build tsconfig.build.json --force",
|
|
41
|
-
"changelog:update": "../../scripts/update-changelog.sh @metamask/eth-
|
|
42
|
-
"changelog:validate": "../../scripts/validate-changelog.sh @metamask/eth-
|
|
41
|
+
"changelog:update": "../../scripts/update-changelog.sh @metamask/eth-qr-keyring",
|
|
42
|
+
"changelog:validate": "../../scripts/validate-changelog.sh @metamask/eth-qr-keyring",
|
|
43
43
|
"publish:preview": "yarn npm publish --tag preview",
|
|
44
44
|
"sample": "ts-node src/sample.ts",
|
|
45
45
|
"test": "jest",
|
|
@@ -53,13 +53,14 @@
|
|
|
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.
|
|
56
|
+
"@metamask/keyring-utils": "3.1.0",
|
|
57
57
|
"@metamask/utils": "^11.1.0",
|
|
58
|
+
"async-mutex": "^0.5.0",
|
|
58
59
|
"hdkey": "^2.1.0",
|
|
59
60
|
"uuid": "^9.0.1"
|
|
60
61
|
},
|
|
61
62
|
"devDependencies": {
|
|
62
|
-
"@
|
|
63
|
+
"@ethereumjs/common": "^4.4.0",
|
|
63
64
|
"@keystonehq/metamask-airgapped-keyring": "^0.15.2",
|
|
64
65
|
"@lavamoat/allow-scripts": "^3.2.1",
|
|
65
66
|
"@metamask/auto-changelog": "^3.4.4",
|