@metamask-previews/eth-qr-keyring 1.0.0-065a890

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.
@@ -0,0 +1,233 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
7
+ if (kind === "m") throw new TypeError("Private method is not writable");
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
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
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
+ };
12
+ var _QrKeyring_instances, _QrKeyring_signer, _QrKeyring_accounts, _QrKeyring_accountToUnlock, _QrKeyring_currentPage, _QrKeyring_scanAndInitialize;
13
+ import { add0x, getChecksumAddress } from "@metamask/utils";
14
+ import { AirgappedSigner, KeyringMode } from "./airgapped-signer.mjs";
15
+ export const QR_KEYRING_TYPE = 'QR Hardware Wallet Device';
16
+ /**
17
+ * Returns the default serialized state of the QrKeyring.
18
+ *
19
+ * @returns The default serialized state.
20
+ */
21
+ export const getDefaultSerializedQrKeyringState = () => ({
22
+ initialized: false,
23
+ accounts: [],
24
+ });
25
+ /**
26
+ * Normalizes an address to a 0x-prefixed checksum address.
27
+ *
28
+ * @param address - The address to normalize.
29
+ * @returns The normalized address as a Hex string.
30
+ */
31
+ function normalizeAddress(address) {
32
+ return getChecksumAddress(add0x(address));
33
+ }
34
+ export class QrKeyring {
35
+ constructor(options) {
36
+ _QrKeyring_instances.add(this);
37
+ this.type = QR_KEYRING_TYPE;
38
+ _QrKeyring_signer.set(this, new AirgappedSigner());
39
+ _QrKeyring_accounts.set(this, []);
40
+ _QrKeyring_accountToUnlock.set(this, void 0);
41
+ _QrKeyring_currentPage.set(this, 0);
42
+ this.bridge = options.bridge;
43
+ if (options?.ur) {
44
+ this.submitUR(options.ur);
45
+ }
46
+ }
47
+ /**
48
+ * Serializes the QrKeyring state
49
+ *
50
+ * @returns The serialized state
51
+ */
52
+ async serialize() {
53
+ const source = __classPrivateFieldGet(this, _QrKeyring_signer, "f").getSourceDetails();
54
+ if (!source ||
55
+ ![KeyringMode.HD, KeyringMode.ACCOUNT].includes(source.keyringMode)) {
56
+ // the keyring has not initialized with a device source yet
57
+ return getDefaultSerializedQrKeyringState();
58
+ }
59
+ const accounts = __classPrivateFieldGet(this, _QrKeyring_accounts, "f").slice();
60
+ if (source.keyringMode === KeyringMode.HD) {
61
+ // These properties are only relevant for HD Keys
62
+ return {
63
+ initialized: true,
64
+ name: source.name,
65
+ keyringMode: KeyringMode.HD,
66
+ keyringAccount: source.keyringAccount,
67
+ xfp: source.xfp,
68
+ xpub: source.xpub,
69
+ hdPath: source.hdPath,
70
+ childrenPath: source.childrenPath,
71
+ accounts,
72
+ indexes: source.indexes,
73
+ };
74
+ }
75
+ // These properties are only relevant for Account Keys
76
+ return {
77
+ initialized: true,
78
+ name: source.name,
79
+ keyringMode: KeyringMode.ACCOUNT,
80
+ keyringAccount: source.keyringAccount,
81
+ xfp: source.xfp,
82
+ paths: source.paths,
83
+ accounts,
84
+ indexes: source.indexes,
85
+ };
86
+ }
87
+ /**
88
+ * Deserializes the QrKeyring state
89
+ *
90
+ * @param state - The serialized state to deserialize
91
+ */
92
+ async deserialize(state) {
93
+ if (!state.initialized) {
94
+ __classPrivateFieldSet(this, _QrKeyring_accounts, [], "f");
95
+ __classPrivateFieldGet(this, _QrKeyring_signer, "f").clear();
96
+ return;
97
+ }
98
+ __classPrivateFieldGet(this, _QrKeyring_signer, "f").init(state);
99
+ __classPrivateFieldSet(this, _QrKeyring_accounts, (state.accounts ?? []).map(normalizeAddress), "f");
100
+ }
101
+ /**
102
+ * Adds accounts to the QrKeyring
103
+ *
104
+ * @param accountsToAdd - The number of accounts to add
105
+ * @returns The accounts added
106
+ */
107
+ async addAccounts(accountsToAdd) {
108
+ const lastAccount = __classPrivateFieldGet(this, _QrKeyring_accounts, "f")[__classPrivateFieldGet(this, _QrKeyring_accounts, "f").length - 1];
109
+ const startIndex = __classPrivateFieldGet(this, _QrKeyring_accountToUnlock, "f") ??
110
+ (lastAccount ? __classPrivateFieldGet(this, _QrKeyring_signer, "f").indexFromAddress(lastAccount) : 0);
111
+ const newAccounts = [];
112
+ for (let i = 0; i < accountsToAdd; i++) {
113
+ const index = startIndex + i;
114
+ const address = __classPrivateFieldGet(this, _QrKeyring_signer, "f").addressFromIndex(index);
115
+ if (__classPrivateFieldGet(this, _QrKeyring_accounts, "f").includes(address)) {
116
+ continue;
117
+ }
118
+ __classPrivateFieldGet(this, _QrKeyring_accounts, "f").push(address);
119
+ newAccounts.push(address);
120
+ }
121
+ __classPrivateFieldSet(this, _QrKeyring_accountToUnlock, startIndex + accountsToAdd, "f");
122
+ return newAccounts;
123
+ }
124
+ /**
125
+ * Gets the accounts in the QrKeyring
126
+ *
127
+ * @returns The accounts in the QrKeyring
128
+ */
129
+ async getAccounts() {
130
+ return Array.from(__classPrivateFieldGet(this, _QrKeyring_accounts, "f").values());
131
+ }
132
+ /**
133
+ * Remove an account from the keyring
134
+ *
135
+ * @param address - The address of the account to remove
136
+ */
137
+ removeAccount(address) {
138
+ const normalizedAddress = normalizeAddress(address);
139
+ __classPrivateFieldSet(this, _QrKeyring_accounts, __classPrivateFieldGet(this, _QrKeyring_accounts, "f").filter((account) => account !== normalizedAddress), "f");
140
+ }
141
+ /**
142
+ * Submits a CBOR encoded UR to the QrKeyring
143
+ *
144
+ * @param ur - The CBOR encoded UR
145
+ */
146
+ submitUR(ur) {
147
+ __classPrivateFieldGet(this, _QrKeyring_signer, "f").init(ur);
148
+ }
149
+ /**
150
+ * Sets the next account index to unlock
151
+ *
152
+ * @param index - The index of the account to unlock
153
+ */
154
+ setAccountToUnlock(index) {
155
+ __classPrivateFieldSet(this, _QrKeyring_accountToUnlock, index, "f");
156
+ }
157
+ /**
158
+ * Get the name of the paired device or the keyring type
159
+ * if unavailable.
160
+ *
161
+ * @returns The name of the paired device or the keyring type.
162
+ */
163
+ getName() {
164
+ const source = __classPrivateFieldGet(this, _QrKeyring_signer, "f").getSourceDetails();
165
+ return source?.name ?? QR_KEYRING_TYPE;
166
+ }
167
+ /**
168
+ * Fetch the first page of accounts. If the keyring is not currently initialized,
169
+ * it will trigger a scan request to initialize it.
170
+ *
171
+ * @returns The first page of accounts as an array of Hex strings.
172
+ */
173
+ async getFirstPage() {
174
+ __classPrivateFieldSet(this, _QrKeyring_currentPage, 0, "f");
175
+ return this.getCurrentPage();
176
+ }
177
+ /**
178
+ * Fetch the next page of accounts. If the keyring is not currently initialized,
179
+ * it will trigger a scan request to initialize it.
180
+ *
181
+ * @returns The next page of accounts as an array of IndexedAddress objects.
182
+ */
183
+ async getNextPage() {
184
+ __classPrivateFieldSet(this, _QrKeyring_currentPage, __classPrivateFieldGet(this, _QrKeyring_currentPage, "f") + 1, "f");
185
+ return this.getCurrentPage();
186
+ }
187
+ /**
188
+ * Fetch the previous page of accounts. If the keyring is not currently initialized,
189
+ * it will trigger a scan request to initialize it.
190
+ *
191
+ * @returns The previous page of accounts as an array of IndexedAddress objects.
192
+ */
193
+ async getPreviousPage() {
194
+ if (__classPrivateFieldGet(this, _QrKeyring_currentPage, "f") > 0) {
195
+ __classPrivateFieldSet(this, _QrKeyring_currentPage, __classPrivateFieldGet(this, _QrKeyring_currentPage, "f") - 1, "f");
196
+ }
197
+ else {
198
+ __classPrivateFieldSet(this, _QrKeyring_currentPage, 0, "f");
199
+ }
200
+ return this.getCurrentPage();
201
+ }
202
+ /**
203
+ * Fetch the current page of accounts. If the keyring is not currently initialized,
204
+ * it will trigger a scan request to initialize it.
205
+ *
206
+ * @returns The current page of accounts as an array of IndexedAddress objects.
207
+ */
208
+ async getCurrentPage() {
209
+ if (!__classPrivateFieldGet(this, _QrKeyring_signer, "f").isInitialized()) {
210
+ await __classPrivateFieldGet(this, _QrKeyring_instances, "m", _QrKeyring_scanAndInitialize).call(this);
211
+ }
212
+ return __classPrivateFieldGet(this, _QrKeyring_signer, "f").getAddressesPage(__classPrivateFieldGet(this, _QrKeyring_currentPage, "f"));
213
+ }
214
+ /**
215
+ * Clear the keyring state and forget any paired device or accounts.
216
+ */
217
+ async forgetDevice() {
218
+ __classPrivateFieldGet(this, _QrKeyring_signer, "f").clear();
219
+ __classPrivateFieldSet(this, _QrKeyring_accounts, [], "f");
220
+ __classPrivateFieldSet(this, _QrKeyring_accountToUnlock, undefined, "f");
221
+ __classPrivateFieldSet(this, _QrKeyring_currentPage, 0, "f");
222
+ }
223
+ }
224
+ _QrKeyring_signer = new WeakMap(), _QrKeyring_accounts = new WeakMap(), _QrKeyring_accountToUnlock = new WeakMap(), _QrKeyring_currentPage = new WeakMap(), _QrKeyring_instances = new WeakSet(), _QrKeyring_scanAndInitialize =
225
+ /**
226
+ * Scan for a QR code and initialize the keyring with the
227
+ * scanned UR.
228
+ */
229
+ async function _QrKeyring_scanAndInitialize() {
230
+ this.submitUR(await this.bridge.requestScan());
231
+ };
232
+ QrKeyring.type = QR_KEYRING_TYPE;
233
+ //# sourceMappingURL=qr-keyring.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr-keyring.mjs","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Keyring } from '@metamask/keyring-utils';\nimport type { Hex } from '@metamask/utils';\nimport { add0x, getChecksumAddress } from '@metamask/utils';\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\nexport type QrKeyringBridge = {\n requestScan: () => Promise<string>;\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): 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 * Scan for a QR code and initialize the keyring with the\n * scanned UR.\n */\n async #scanAndInitialize(): Promise<void> {\n this.submitUR(await this.bridge.requestScan());\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "@metamask-previews/eth-qr-keyring",
3
+ "version": "1.0.0-065a890",
4
+ "description": "A simple standard interface for a series of Ethereum private keys.",
5
+ "keywords": [
6
+ "ethereum",
7
+ "keyring",
8
+ "qr",
9
+ "metamask"
10
+ ],
11
+ "homepage": "https://github.com/MetaMask/accounts/packages/keyring-eth-qr#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/MetaMask/accounts/issues"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/MetaMask/accounts.git"
18
+ },
19
+ "exports": {
20
+ ".": {
21
+ "import": {
22
+ "types": "./dist/index.d.mts",
23
+ "default": "./dist/index.mjs"
24
+ },
25
+ "require": {
26
+ "types": "./dist/index.d.cts",
27
+ "default": "./dist/index.cjs"
28
+ }
29
+ }
30
+ },
31
+ "main": "./dist/index.cjs",
32
+ "types": "./dist/index.d.cts",
33
+ "files": [
34
+ "dist/"
35
+ ],
36
+ "scripts": {
37
+ "build": "tsc --build tsconfig.build.json",
38
+ "build:clean": "rimraf dist && yarn build",
39
+ "build:docs": "typedoc",
40
+ "build:force": "tsc --build tsconfig.build.json --force",
41
+ "changelog:update": "../../scripts/update-changelog.sh @metamask/eth-simple-keyring",
42
+ "changelog:validate": "../../scripts/validate-changelog.sh @metamask/eth-simple-keyring",
43
+ "publish:preview": "yarn npm publish --tag preview",
44
+ "sample": "ts-node src/sample.ts",
45
+ "test": "jest",
46
+ "test:clean": "jest --clearCache",
47
+ "test:verbose": "jest --verbose",
48
+ "test:watch": "jest --watch"
49
+ },
50
+ "dependencies": {
51
+ "@ethereumjs/util": "^9.1.0",
52
+ "@keystonehq/bc-ur-registry-eth": "^0.19.1",
53
+ "@keystonehq/ur-decoder": "^0.12.2",
54
+ "@metamask/keyring-utils": "3.0.0",
55
+ "@metamask/utils": "^11.3.0",
56
+ "@ngraveio/bc-ur": "^1.1.13",
57
+ "hdkey": "^2.1.0"
58
+ },
59
+ "devDependencies": {
60
+ "@keystonehq/base-eth-keyring": "^0.15.1",
61
+ "@keystonehq/metamask-airgapped-keyring": "^0.15.2",
62
+ "@lavamoat/allow-scripts": "^3.2.1",
63
+ "@metamask/auto-changelog": "^3.4.4",
64
+ "@types/hdkey": "^2.0.1",
65
+ "@types/jest": "^29.5.12",
66
+ "@types/node": "^20.12.12",
67
+ "deepmerge": "^4.2.2",
68
+ "depcheck": "^1.4.7",
69
+ "jest": "^29.5.0",
70
+ "ts-jest": "^29.0.5",
71
+ "ts-node": "^10.9.2",
72
+ "typedoc": "^0.25.13",
73
+ "typescript": "~4.8.4"
74
+ },
75
+ "engines": {
76
+ "node": "^18.18 || >=20"
77
+ },
78
+ "publishConfig": {
79
+ "access": "public",
80
+ "registry": "https://registry.npmjs.org/"
81
+ },
82
+ "lavamoat": {
83
+ "allowScripts": {
84
+ "keccak": true,
85
+ "secp256k1": true,
86
+ "@lavamoat/preinstall-always-fail": false,
87
+ "ethereumjs-tx>ethereumjs-util>ethereum-cryptography>keccak": false,
88
+ "ethereumjs-tx>ethereumjs-util>ethereum-cryptography>secp256k1": false,
89
+ "@keystonehq/ur-decoder>@keystonehq/bc-ur-registry-eth>ethereumjs-util>ethereum-cryptography>keccak": false,
90
+ "@lavamoat/allow-scripts>@lavamoat/preinstall-always-fail": false,
91
+ "hdkey>secp256k1": false
92
+ }
93
+ }
94
+ }