@metamask-previews/eth-qr-keyring 1.0.0-a63b603 → 1.0.0-e4f6caa
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/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 +16 -41
- package/dist/qr-keyring.cjs.map +1 -1
- package/dist/qr-keyring.d.cts +0 -13
- package/dist/qr-keyring.d.cts.map +1 -1
- package/dist/qr-keyring.d.mts +0 -13
- package/dist/qr-keyring.d.mts.map +1 -1
- package/dist/qr-keyring.mjs +17 -42
- package/dist/qr-keyring.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -14,8 +14,8 @@ var _QrKeyringScannerBridge_requestScan;
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.QrKeyringScannerBridge = void 0;
|
|
16
16
|
/**
|
|
17
|
-
* A
|
|
18
|
-
*
|
|
17
|
+
* A bridge that allows the QrKeyring to request a QR code scan
|
|
18
|
+
* while keeping the implementation defined by the QrKeyring consumer.
|
|
19
19
|
*/
|
|
20
20
|
class QrKeyringScannerBridge {
|
|
21
21
|
constructor({ requestScan }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qr-keyring-scanner-bridge.cjs","sourceRoot":"","sources":["../src/qr-keyring-scanner-bridge.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAgBA;;;GAGG;AACH,MAAa,sBAAsB;IAGjC,YAAY,EAAE,WAAW,EAAiC;QAFjD,sDAA2D;QAGlE,uBAAA,IAAI,uCAAgB,WAAW,MAAA,CAAC;IAClC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,WAAW,CAAC,OAAsB;QACtC,OAAO,uBAAA,IAAI,2CAAa,MAAjB,IAAI,EAAc,OAAO,CAAC,CAAC;IACpC,CAAC;CACF;AAhBD,wDAgBC","sourcesContent":["import type {\n QrKeyringBridge,\n QrScanRequest,\n SerializedUR,\n} from './qr-keyring';\n\n/**\n * Options for the QrKeyringScannerBridge.\n */\nexport type QrKeyringScannerBridgeOptions = {\n /**\n *
|
|
1
|
+
{"version":3,"file":"qr-keyring-scanner-bridge.cjs","sourceRoot":"","sources":["../src/qr-keyring-scanner-bridge.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAgBA;;;GAGG;AACH,MAAa,sBAAsB;IAGjC,YAAY,EAAE,WAAW,EAAiC;QAFjD,sDAA2D;QAGlE,uBAAA,IAAI,uCAAgB,WAAW,MAAA,CAAC;IAClC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,WAAW,CAAC,OAAsB;QACtC,OAAO,uBAAA,IAAI,2CAAa,MAAjB,IAAI,EAAc,OAAO,CAAC,CAAC;IACpC,CAAC;CACF;AAhBD,wDAgBC","sourcesContent":["import type {\n QrKeyringBridge,\n QrScanRequest,\n SerializedUR,\n} from './qr-keyring';\n\n/**\n * Options for the QrKeyringScannerBridge.\n */\nexport type QrKeyringScannerBridgeOptions = {\n /**\n * An injected function that the bridge uses to request a QR code scan.\n */\n requestScan: (request: QrScanRequest) => Promise<SerializedUR>;\n};\n\n/**\n * A bridge that allows the QrKeyring to request a QR code scan\n * while keeping the implementation defined by the QrKeyring consumer.\n */\nexport class QrKeyringScannerBridge implements QrKeyringBridge {\n readonly #requestScan: QrKeyringScannerBridgeOptions['requestScan'];\n\n constructor({ requestScan }: QrKeyringScannerBridgeOptions) {\n this.#requestScan = requestScan;\n }\n\n /**\n * Request a QR code scan, obtaining a CBOR and a type as response.\n *\n * @param request - The type of QR scan request.\n * @returns The scanned data as a serialized UR.\n */\n async requestScan(request: QrScanRequest): Promise<SerializedUR> {\n return this.#requestScan(request);\n }\n}\n"]}
|
|
@@ -4,13 +4,13 @@ import type { QrKeyringBridge, QrScanRequest, SerializedUR } from "./qr-keyring.
|
|
|
4
4
|
*/
|
|
5
5
|
export type QrKeyringScannerBridgeOptions = {
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* An injected function that the bridge uses to request a QR code scan.
|
|
8
8
|
*/
|
|
9
9
|
requestScan: (request: QrScanRequest) => Promise<SerializedUR>;
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
12
|
-
* A
|
|
13
|
-
*
|
|
12
|
+
* A bridge that allows the QrKeyring to request a QR code scan
|
|
13
|
+
* while keeping the implementation defined by the QrKeyring consumer.
|
|
14
14
|
*/
|
|
15
15
|
export declare class QrKeyringScannerBridge implements QrKeyringBridge {
|
|
16
16
|
#private;
|
|
@@ -4,13 +4,13 @@ import type { QrKeyringBridge, QrScanRequest, SerializedUR } from "./qr-keyring.
|
|
|
4
4
|
*/
|
|
5
5
|
export type QrKeyringScannerBridgeOptions = {
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* An injected function that the bridge uses to request a QR code scan.
|
|
8
8
|
*/
|
|
9
9
|
requestScan: (request: QrScanRequest) => Promise<SerializedUR>;
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
12
|
-
* A
|
|
13
|
-
*
|
|
12
|
+
* A bridge that allows the QrKeyring to request a QR code scan
|
|
13
|
+
* while keeping the implementation defined by the QrKeyring consumer.
|
|
14
14
|
*/
|
|
15
15
|
export declare class QrKeyringScannerBridge implements QrKeyringBridge {
|
|
16
16
|
#private;
|
|
@@ -11,8 +11,8 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
11
11
|
};
|
|
12
12
|
var _QrKeyringScannerBridge_requestScan;
|
|
13
13
|
/**
|
|
14
|
-
* A
|
|
15
|
-
*
|
|
14
|
+
* A bridge that allows the QrKeyring to request a QR code scan
|
|
15
|
+
* while keeping the implementation defined by the QrKeyring consumer.
|
|
16
16
|
*/
|
|
17
17
|
export class QrKeyringScannerBridge {
|
|
18
18
|
constructor({ requestScan }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qr-keyring-scanner-bridge.mjs","sourceRoot":"","sources":["../src/qr-keyring-scanner-bridge.ts"],"names":[],"mappings":";;;;;;;;;;;;AAgBA;;;GAGG;AACH,MAAM,OAAO,sBAAsB;IAGjC,YAAY,EAAE,WAAW,EAAiC;QAFjD,sDAA2D;QAGlE,uBAAA,IAAI,uCAAgB,WAAW,MAAA,CAAC;IAClC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,WAAW,CAAC,OAAsB;QACtC,OAAO,uBAAA,IAAI,2CAAa,MAAjB,IAAI,EAAc,OAAO,CAAC,CAAC;IACpC,CAAC;CACF","sourcesContent":["import type {\n QrKeyringBridge,\n QrScanRequest,\n SerializedUR,\n} from './qr-keyring';\n\n/**\n * Options for the QrKeyringScannerBridge.\n */\nexport type QrKeyringScannerBridgeOptions = {\n /**\n *
|
|
1
|
+
{"version":3,"file":"qr-keyring-scanner-bridge.mjs","sourceRoot":"","sources":["../src/qr-keyring-scanner-bridge.ts"],"names":[],"mappings":";;;;;;;;;;;;AAgBA;;;GAGG;AACH,MAAM,OAAO,sBAAsB;IAGjC,YAAY,EAAE,WAAW,EAAiC;QAFjD,sDAA2D;QAGlE,uBAAA,IAAI,uCAAgB,WAAW,MAAA,CAAC;IAClC,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,WAAW,CAAC,OAAsB;QACtC,OAAO,uBAAA,IAAI,2CAAa,MAAjB,IAAI,EAAc,OAAO,CAAC,CAAC;IACpC,CAAC;CACF","sourcesContent":["import type {\n QrKeyringBridge,\n QrScanRequest,\n SerializedUR,\n} from './qr-keyring';\n\n/**\n * Options for the QrKeyringScannerBridge.\n */\nexport type QrKeyringScannerBridgeOptions = {\n /**\n * An injected function that the bridge uses to request a QR code scan.\n */\n requestScan: (request: QrScanRequest) => Promise<SerializedUR>;\n};\n\n/**\n * A bridge that allows the QrKeyring to request a QR code scan\n * while keeping the implementation defined by the QrKeyring consumer.\n */\nexport class QrKeyringScannerBridge implements QrKeyringBridge {\n readonly #requestScan: QrKeyringScannerBridgeOptions['requestScan'];\n\n constructor({ requestScan }: QrKeyringScannerBridgeOptions) {\n this.#requestScan = requestScan;\n }\n\n /**\n * Request a QR code scan, obtaining a CBOR and a type as response.\n *\n * @param request - The type of QR scan request.\n * @returns The scanned data as a serialized UR.\n */\n async requestScan(request: QrScanRequest): Promise<SerializedUR> {\n return this.#requestScan(request);\n }\n}\n"]}
|
package/dist/qr-keyring.cjs
CHANGED
|
@@ -253,18 +253,24 @@ class QrKeyring {
|
|
|
253
253
|
* @returns The signed transaction.
|
|
254
254
|
*/
|
|
255
255
|
async signTransaction(address, transaction) {
|
|
256
|
-
const
|
|
257
|
-
if (!
|
|
256
|
+
const signer = __classPrivateFieldGet(this, _QrKeyring_signer, "f").getSourceDetails();
|
|
257
|
+
if (!signer?.xfp) {
|
|
258
258
|
throw new Error('Keyring is not initialized. Please scan a QR code.');
|
|
259
259
|
}
|
|
260
260
|
const dataType = transaction.type === tx_1.TransactionType.Legacy
|
|
261
261
|
? bc_ur_registry_eth_1.DataType.transaction
|
|
262
262
|
: bc_ur_registry_eth_1.DataType.typedTransaction;
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
263
|
+
let messageToSign;
|
|
264
|
+
if (transaction.type === tx_1.TransactionType.Legacy) {
|
|
265
|
+
messageToSign = Buffer.from(rlp_1.RLP.encode(transaction.getMessageToSign()));
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
messageToSign = Buffer.from(transaction.getMessageToSign());
|
|
269
|
+
}
|
|
270
|
+
const hdPath = __classPrivateFieldGet(this, _QrKeyring_signer, "f").pathFromAddress(address);
|
|
271
|
+
const chainId = Number(transaction.common.chainId());
|
|
266
272
|
const requestId = (0, uuid_1.v4)();
|
|
267
|
-
const ethSignRequestUR = bc_ur_registry_eth_1.EthSignRequest.constructETHRequest(messageToSign, dataType,
|
|
273
|
+
const ethSignRequestUR = bc_ur_registry_eth_1.EthSignRequest.constructETHRequest(messageToSign, dataType, hdPath, signer.xfp, requestId, chainId).toUR();
|
|
268
274
|
const { r, s, v } = await __classPrivateFieldGet(this, _QrKeyring_instances, "m", _QrKeyring_requestSignature).call(this, {
|
|
269
275
|
requestId,
|
|
270
276
|
payload: {
|
|
@@ -292,12 +298,13 @@ class QrKeyring {
|
|
|
292
298
|
* @returns The signed message.
|
|
293
299
|
*/
|
|
294
300
|
async signTypedData(address, data) {
|
|
295
|
-
const
|
|
296
|
-
if (!
|
|
301
|
+
const signer = __classPrivateFieldGet(this, _QrKeyring_signer, "f").getSourceDetails();
|
|
302
|
+
if (!signer?.xfp) {
|
|
297
303
|
throw new Error('Keyring is not initialized. Please scan a QR code.');
|
|
298
304
|
}
|
|
305
|
+
const hdPath = __classPrivateFieldGet(this, _QrKeyring_signer, "f").pathFromAddress(address);
|
|
299
306
|
const requestId = (0, uuid_1.v4)();
|
|
300
|
-
const ethSignRequestUR = bc_ur_registry_eth_1.EthSignRequest.constructETHRequest(Buffer.from(JSON.stringify(data), 'utf8'), bc_ur_registry_eth_1.DataType.typedData,
|
|
307
|
+
const ethSignRequestUR = bc_ur_registry_eth_1.EthSignRequest.constructETHRequest(Buffer.from(JSON.stringify(data), 'utf8'), bc_ur_registry_eth_1.DataType.typedData, hdPath, signer.xfp, requestId, undefined, address).toUR();
|
|
301
308
|
const { r, s, v } = await __classPrivateFieldGet(this, _QrKeyring_instances, "m", _QrKeyring_requestSignature).call(this, {
|
|
302
309
|
requestId,
|
|
303
310
|
payload: {
|
|
@@ -313,38 +320,6 @@ class QrKeyring {
|
|
|
313
320
|
Uint8Array.from(v),
|
|
314
321
|
]).toString('hex'));
|
|
315
322
|
}
|
|
316
|
-
/**
|
|
317
|
-
* Sign a message. This is equivalent to the `eth_sign` Ethereum JSON-RPC
|
|
318
|
-
* method, which is exposed by MetaMask as the method `personal_sign`. See
|
|
319
|
-
* the Ethereum JSON-RPC API documentation for more details.
|
|
320
|
-
*
|
|
321
|
-
* For more information about this method and why we call it `personal_sign`,
|
|
322
|
-
* see the {@link https://docs.metamask.io/guide/signing-data.html|MetaMask Docs}.
|
|
323
|
-
*
|
|
324
|
-
* @param address - The address of the account to use for signing.
|
|
325
|
-
* @param message - The message to sign.
|
|
326
|
-
* @returns The signed message.
|
|
327
|
-
*/
|
|
328
|
-
async signPersonalMessage(address, message) {
|
|
329
|
-
const signerSource = __classPrivateFieldGet(this, _QrKeyring_signer, "f").getSourceDetails();
|
|
330
|
-
if (!signerSource?.xfp) {
|
|
331
|
-
throw new Error('Keyring is not initialized. Please scan a QR code.');
|
|
332
|
-
}
|
|
333
|
-
const requestId = (0, uuid_1.v4)();
|
|
334
|
-
const ethSignRequestUR = bc_ur_registry_eth_1.EthSignRequest.constructETHRequest(Buffer.from((0, utils_1.remove0x)(message), 'hex'), bc_ur_registry_eth_1.DataType.personalMessage, __classPrivateFieldGet(this, _QrKeyring_signer, "f").pathFromAddress(address), signerSource.xfp, requestId, undefined, address).toUR();
|
|
335
|
-
const { r, s, v } = await __classPrivateFieldGet(this, _QrKeyring_instances, "m", _QrKeyring_requestSignature).call(this, {
|
|
336
|
-
requestId,
|
|
337
|
-
payload: {
|
|
338
|
-
type: ethSignRequestUR.type,
|
|
339
|
-
cbor: ethSignRequestUR.cbor.toString('hex'),
|
|
340
|
-
},
|
|
341
|
-
});
|
|
342
|
-
return (0, utils_1.add0x)(Buffer.concat([
|
|
343
|
-
Uint8Array.from(r),
|
|
344
|
-
Uint8Array.from(s),
|
|
345
|
-
Uint8Array.from(v),
|
|
346
|
-
]).toString('hex'));
|
|
347
|
-
}
|
|
348
323
|
}
|
|
349
324
|
exports.QrKeyring = QrKeyring;
|
|
350
325
|
_QrKeyring_signer = new WeakMap(), _QrKeyring_accounts = new WeakMap(), _QrKeyring_accountToUnlock = new WeakMap(), _QrKeyring_currentPage = new WeakMap(), _QrKeyring_instances = new WeakSet(), _QrKeyring_scanAndInitialize =
|
package/dist/qr-keyring.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qr-keyring.cjs","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"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","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, remove0x } 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 signerSource = this.#signer.getSourceDetails();\n if (!signerSource?.xfp) {\n throw new Error('Keyring is not initialized. Please scan a QR code.');\n }\n\n const dataType =\n transaction.type === TransactionType.Legacy\n ? DataType.transaction\n : DataType.typedTransaction;\n\n const messageToSign = Buffer.from(\n transaction.type === TransactionType.Legacy\n ? RLP.encode(transaction.getMessageToSign())\n : (transaction as FeeMarketEIP1559Transaction).getMessageToSign(),\n );\n\n const requestId = uuidv4();\n const ethSignRequestUR = EthSignRequest.constructETHRequest(\n messageToSign,\n dataType,\n this.#signer.pathFromAddress(address),\n signerSource.xfp,\n requestId,\n Number(transaction.common.chainId()),\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: '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 signerSource = this.#signer.getSourceDetails();\n if (!signerSource?.xfp) {\n throw new Error('Keyring is not initialized. Please scan a QR code.');\n }\n\n const requestId = uuidv4();\n const ethSignRequestUR = EthSignRequest.constructETHRequest(\n Buffer.from(JSON.stringify(data), 'utf8'),\n DataType.typedData,\n this.#signer.pathFromAddress(address),\n signerSource.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 * 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 const signerSource = this.#signer.getSourceDetails();\n if (!signerSource?.xfp) {\n throw new Error('Keyring is not initialized. Please scan a QR code.');\n }\n\n const requestId = uuidv4();\n const ethSignRequestUR = EthSignRequest.constructETHRequest(\n Buffer.from(remove0x(message), 'hex'),\n DataType.personalMessage,\n this.#signer.pathFromAddress(address),\n signerSource.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 });\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.cjs","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"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","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"]}
|
package/dist/qr-keyring.d.cts
CHANGED
|
@@ -164,18 +164,5 @@ export declare class QrKeyring implements Keyring {
|
|
|
164
164
|
* @returns The signed message.
|
|
165
165
|
*/
|
|
166
166
|
signTypedData<Types extends MessageTypes>(address: Hex, data: TypedMessage<Types>): Promise<string>;
|
|
167
|
-
/**
|
|
168
|
-
* Sign a message. This is equivalent to the `eth_sign` Ethereum JSON-RPC
|
|
169
|
-
* method, which is exposed by MetaMask as the method `personal_sign`. See
|
|
170
|
-
* the Ethereum JSON-RPC API documentation for more details.
|
|
171
|
-
*
|
|
172
|
-
* For more information about this method and why we call it `personal_sign`,
|
|
173
|
-
* see the {@link https://docs.metamask.io/guide/signing-data.html|MetaMask Docs}.
|
|
174
|
-
*
|
|
175
|
-
* @param address - The address of the account to use for signing.
|
|
176
|
-
* @param message - The message to sign.
|
|
177
|
-
* @returns The signed message.
|
|
178
|
-
*/
|
|
179
|
-
signPersonalMessage(address: Hex, message: Hex): Promise<string>;
|
|
180
167
|
}
|
|
181
168
|
//# sourceMappingURL=qr-keyring.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qr-keyring.d.cts","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,uBAAuB;AAMxB,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,+BAA+B;AACzE,OAAO,KAAK,EAAE,OAAO,EAAE,gCAAgC;AACvD,OAAO,EAAE,KAAK,GAAG,
|
|
1
|
+
{"version":3,"file":"qr-keyring.d.cts","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"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"}
|
package/dist/qr-keyring.d.mts
CHANGED
|
@@ -164,18 +164,5 @@ export declare class QrKeyring implements Keyring {
|
|
|
164
164
|
* @returns The signed message.
|
|
165
165
|
*/
|
|
166
166
|
signTypedData<Types extends MessageTypes>(address: Hex, data: TypedMessage<Types>): Promise<string>;
|
|
167
|
-
/**
|
|
168
|
-
* Sign a message. This is equivalent to the `eth_sign` Ethereum JSON-RPC
|
|
169
|
-
* method, which is exposed by MetaMask as the method `personal_sign`. See
|
|
170
|
-
* the Ethereum JSON-RPC API documentation for more details.
|
|
171
|
-
*
|
|
172
|
-
* For more information about this method and why we call it `personal_sign`,
|
|
173
|
-
* see the {@link https://docs.metamask.io/guide/signing-data.html|MetaMask Docs}.
|
|
174
|
-
*
|
|
175
|
-
* @param address - The address of the account to use for signing.
|
|
176
|
-
* @param message - The message to sign.
|
|
177
|
-
* @returns The signed message.
|
|
178
|
-
*/
|
|
179
|
-
signPersonalMessage(address: Hex, message: Hex): Promise<string>;
|
|
180
167
|
}
|
|
181
168
|
//# sourceMappingURL=qr-keyring.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qr-keyring.d.mts","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,uBAAuB;AAMxB,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,+BAA+B;AACzE,OAAO,KAAK,EAAE,OAAO,EAAE,gCAAgC;AACvD,OAAO,EAAE,KAAK,GAAG,
|
|
1
|
+
{"version":3,"file":"qr-keyring.d.mts","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"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"}
|
package/dist/qr-keyring.mjs
CHANGED
|
@@ -13,7 +13,7 @@ var _QrKeyring_instances, _QrKeyring_signer, _QrKeyring_accounts, _QrKeyring_acc
|
|
|
13
13
|
import { RLP } from "@ethereumjs/rlp";
|
|
14
14
|
import { TransactionFactory, TransactionType } from "@ethereumjs/tx";
|
|
15
15
|
import { DataType, ETHSignature, EthSignRequest } from "@keystonehq/bc-ur-registry-eth";
|
|
16
|
-
import { add0x, getChecksumAddress
|
|
16
|
+
import { add0x, getChecksumAddress } from "@metamask/utils";
|
|
17
17
|
import { stringify, v4 as uuidv4 } from "uuid";
|
|
18
18
|
import { AirgappedSigner, KeyringMode } from "./airgapped-signer.mjs";
|
|
19
19
|
export const QR_KEYRING_TYPE = 'QR Hardware Wallet Device';
|
|
@@ -249,18 +249,24 @@ export class QrKeyring {
|
|
|
249
249
|
* @returns The signed transaction.
|
|
250
250
|
*/
|
|
251
251
|
async signTransaction(address, transaction) {
|
|
252
|
-
const
|
|
253
|
-
if (!
|
|
252
|
+
const signer = __classPrivateFieldGet(this, _QrKeyring_signer, "f").getSourceDetails();
|
|
253
|
+
if (!signer?.xfp) {
|
|
254
254
|
throw new Error('Keyring is not initialized. Please scan a QR code.');
|
|
255
255
|
}
|
|
256
256
|
const dataType = transaction.type === TransactionType.Legacy
|
|
257
257
|
? DataType.transaction
|
|
258
258
|
: DataType.typedTransaction;
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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());
|
|
265
|
+
}
|
|
266
|
+
const hdPath = __classPrivateFieldGet(this, _QrKeyring_signer, "f").pathFromAddress(address);
|
|
267
|
+
const chainId = Number(transaction.common.chainId());
|
|
262
268
|
const requestId = uuidv4();
|
|
263
|
-
const ethSignRequestUR = EthSignRequest.constructETHRequest(messageToSign, dataType,
|
|
269
|
+
const ethSignRequestUR = EthSignRequest.constructETHRequest(messageToSign, dataType, hdPath, signer.xfp, requestId, chainId).toUR();
|
|
264
270
|
const { r, s, v } = await __classPrivateFieldGet(this, _QrKeyring_instances, "m", _QrKeyring_requestSignature).call(this, {
|
|
265
271
|
requestId,
|
|
266
272
|
payload: {
|
|
@@ -288,12 +294,13 @@ export class QrKeyring {
|
|
|
288
294
|
* @returns The signed message.
|
|
289
295
|
*/
|
|
290
296
|
async signTypedData(address, data) {
|
|
291
|
-
const
|
|
292
|
-
if (!
|
|
297
|
+
const signer = __classPrivateFieldGet(this, _QrKeyring_signer, "f").getSourceDetails();
|
|
298
|
+
if (!signer?.xfp) {
|
|
293
299
|
throw new Error('Keyring is not initialized. Please scan a QR code.');
|
|
294
300
|
}
|
|
301
|
+
const hdPath = __classPrivateFieldGet(this, _QrKeyring_signer, "f").pathFromAddress(address);
|
|
295
302
|
const requestId = uuidv4();
|
|
296
|
-
const ethSignRequestUR = EthSignRequest.constructETHRequest(Buffer.from(JSON.stringify(data), 'utf8'), DataType.typedData,
|
|
303
|
+
const ethSignRequestUR = EthSignRequest.constructETHRequest(Buffer.from(JSON.stringify(data), 'utf8'), DataType.typedData, hdPath, signer.xfp, requestId, undefined, address).toUR();
|
|
297
304
|
const { r, s, v } = await __classPrivateFieldGet(this, _QrKeyring_instances, "m", _QrKeyring_requestSignature).call(this, {
|
|
298
305
|
requestId,
|
|
299
306
|
payload: {
|
|
@@ -309,38 +316,6 @@ export class QrKeyring {
|
|
|
309
316
|
Uint8Array.from(v),
|
|
310
317
|
]).toString('hex'));
|
|
311
318
|
}
|
|
312
|
-
/**
|
|
313
|
-
* Sign a message. This is equivalent to the `eth_sign` Ethereum JSON-RPC
|
|
314
|
-
* method, which is exposed by MetaMask as the method `personal_sign`. See
|
|
315
|
-
* the Ethereum JSON-RPC API documentation for more details.
|
|
316
|
-
*
|
|
317
|
-
* For more information about this method and why we call it `personal_sign`,
|
|
318
|
-
* see the {@link https://docs.metamask.io/guide/signing-data.html|MetaMask Docs}.
|
|
319
|
-
*
|
|
320
|
-
* @param address - The address of the account to use for signing.
|
|
321
|
-
* @param message - The message to sign.
|
|
322
|
-
* @returns The signed message.
|
|
323
|
-
*/
|
|
324
|
-
async signPersonalMessage(address, message) {
|
|
325
|
-
const signerSource = __classPrivateFieldGet(this, _QrKeyring_signer, "f").getSourceDetails();
|
|
326
|
-
if (!signerSource?.xfp) {
|
|
327
|
-
throw new Error('Keyring is not initialized. Please scan a QR code.');
|
|
328
|
-
}
|
|
329
|
-
const requestId = uuidv4();
|
|
330
|
-
const ethSignRequestUR = EthSignRequest.constructETHRequest(Buffer.from(remove0x(message), 'hex'), DataType.personalMessage, __classPrivateFieldGet(this, _QrKeyring_signer, "f").pathFromAddress(address), signerSource.xfp, requestId, undefined, address).toUR();
|
|
331
|
-
const { r, s, v } = await __classPrivateFieldGet(this, _QrKeyring_instances, "m", _QrKeyring_requestSignature).call(this, {
|
|
332
|
-
requestId,
|
|
333
|
-
payload: {
|
|
334
|
-
type: ethSignRequestUR.type,
|
|
335
|
-
cbor: ethSignRequestUR.cbor.toString('hex'),
|
|
336
|
-
},
|
|
337
|
-
});
|
|
338
|
-
return add0x(Buffer.concat([
|
|
339
|
-
Uint8Array.from(r),
|
|
340
|
-
Uint8Array.from(s),
|
|
341
|
-
Uint8Array.from(v),
|
|
342
|
-
]).toString('hex'));
|
|
343
|
-
}
|
|
344
319
|
}
|
|
345
320
|
_QrKeyring_signer = new WeakMap(), _QrKeyring_accounts = new WeakMap(), _QrKeyring_accountToUnlock = new WeakMap(), _QrKeyring_currentPage = new WeakMap(), _QrKeyring_instances = new WeakSet(), _QrKeyring_scanAndInitialize =
|
|
346
321
|
/**
|
package/dist/qr-keyring.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qr-keyring.mjs","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"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","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, remove0x } 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 signerSource = this.#signer.getSourceDetails();\n if (!signerSource?.xfp) {\n throw new Error('Keyring is not initialized. Please scan a QR code.');\n }\n\n const dataType =\n transaction.type === TransactionType.Legacy\n ? DataType.transaction\n : DataType.typedTransaction;\n\n const messageToSign = Buffer.from(\n transaction.type === TransactionType.Legacy\n ? RLP.encode(transaction.getMessageToSign())\n : (transaction as FeeMarketEIP1559Transaction).getMessageToSign(),\n );\n\n const requestId = uuidv4();\n const ethSignRequestUR = EthSignRequest.constructETHRequest(\n messageToSign,\n dataType,\n this.#signer.pathFromAddress(address),\n signerSource.xfp,\n requestId,\n Number(transaction.common.chainId()),\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: '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 signerSource = this.#signer.getSourceDetails();\n if (!signerSource?.xfp) {\n throw new Error('Keyring is not initialized. Please scan a QR code.');\n }\n\n const requestId = uuidv4();\n const ethSignRequestUR = EthSignRequest.constructETHRequest(\n Buffer.from(JSON.stringify(data), 'utf8'),\n DataType.typedData,\n this.#signer.pathFromAddress(address),\n signerSource.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 * 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 const signerSource = this.#signer.getSourceDetails();\n if (!signerSource?.xfp) {\n throw new Error('Keyring is not initialized. Please scan a QR code.');\n }\n\n const requestId = uuidv4();\n const ethSignRequestUR = EthSignRequest.constructETHRequest(\n Buffer.from(remove0x(message), 'hex'),\n DataType.personalMessage,\n this.#signer.pathFromAddress(address),\n signerSource.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 });\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 { 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"]}
|