@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,238 @@
1
+ "use strict";
2
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
+ };
7
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
+ if (kind === "m") throw new TypeError("Private method is not writable");
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
+ 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");
11
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
+ };
13
+ var _QrKeyring_instances, _QrKeyring_signer, _QrKeyring_accounts, _QrKeyring_accountToUnlock, _QrKeyring_currentPage, _QrKeyring_scanAndInitialize;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.QrKeyring = exports.getDefaultSerializedQrKeyringState = exports.QR_KEYRING_TYPE = void 0;
16
+ const utils_1 = require("@metamask/utils");
17
+ const airgapped_signer_1 = require("./airgapped-signer.cjs");
18
+ exports.QR_KEYRING_TYPE = 'QR Hardware Wallet Device';
19
+ /**
20
+ * Returns the default serialized state of the QrKeyring.
21
+ *
22
+ * @returns The default serialized state.
23
+ */
24
+ const getDefaultSerializedQrKeyringState = () => ({
25
+ initialized: false,
26
+ accounts: [],
27
+ });
28
+ exports.getDefaultSerializedQrKeyringState = getDefaultSerializedQrKeyringState;
29
+ /**
30
+ * Normalizes an address to a 0x-prefixed checksum address.
31
+ *
32
+ * @param address - The address to normalize.
33
+ * @returns The normalized address as a Hex string.
34
+ */
35
+ function normalizeAddress(address) {
36
+ return (0, utils_1.getChecksumAddress)((0, utils_1.add0x)(address));
37
+ }
38
+ class QrKeyring {
39
+ constructor(options) {
40
+ _QrKeyring_instances.add(this);
41
+ this.type = exports.QR_KEYRING_TYPE;
42
+ _QrKeyring_signer.set(this, new airgapped_signer_1.AirgappedSigner());
43
+ _QrKeyring_accounts.set(this, []);
44
+ _QrKeyring_accountToUnlock.set(this, void 0);
45
+ _QrKeyring_currentPage.set(this, 0);
46
+ this.bridge = options.bridge;
47
+ if (options?.ur) {
48
+ this.submitUR(options.ur);
49
+ }
50
+ }
51
+ /**
52
+ * Serializes the QrKeyring state
53
+ *
54
+ * @returns The serialized state
55
+ */
56
+ async serialize() {
57
+ const source = __classPrivateFieldGet(this, _QrKeyring_signer, "f").getSourceDetails();
58
+ if (!source ||
59
+ ![airgapped_signer_1.KeyringMode.HD, airgapped_signer_1.KeyringMode.ACCOUNT].includes(source.keyringMode)) {
60
+ // the keyring has not initialized with a device source yet
61
+ return (0, exports.getDefaultSerializedQrKeyringState)();
62
+ }
63
+ const accounts = __classPrivateFieldGet(this, _QrKeyring_accounts, "f").slice();
64
+ if (source.keyringMode === airgapped_signer_1.KeyringMode.HD) {
65
+ // These properties are only relevant for HD Keys
66
+ return {
67
+ initialized: true,
68
+ name: source.name,
69
+ keyringMode: airgapped_signer_1.KeyringMode.HD,
70
+ keyringAccount: source.keyringAccount,
71
+ xfp: source.xfp,
72
+ xpub: source.xpub,
73
+ hdPath: source.hdPath,
74
+ childrenPath: source.childrenPath,
75
+ accounts,
76
+ indexes: source.indexes,
77
+ };
78
+ }
79
+ // These properties are only relevant for Account Keys
80
+ return {
81
+ initialized: true,
82
+ name: source.name,
83
+ keyringMode: airgapped_signer_1.KeyringMode.ACCOUNT,
84
+ keyringAccount: source.keyringAccount,
85
+ xfp: source.xfp,
86
+ paths: source.paths,
87
+ accounts,
88
+ indexes: source.indexes,
89
+ };
90
+ }
91
+ /**
92
+ * Deserializes the QrKeyring state
93
+ *
94
+ * @param state - The serialized state to deserialize
95
+ */
96
+ async deserialize(state) {
97
+ if (!state.initialized) {
98
+ __classPrivateFieldSet(this, _QrKeyring_accounts, [], "f");
99
+ __classPrivateFieldGet(this, _QrKeyring_signer, "f").clear();
100
+ return;
101
+ }
102
+ __classPrivateFieldGet(this, _QrKeyring_signer, "f").init(state);
103
+ __classPrivateFieldSet(this, _QrKeyring_accounts, (state.accounts ?? []).map(normalizeAddress), "f");
104
+ }
105
+ /**
106
+ * Adds accounts to the QrKeyring
107
+ *
108
+ * @param accountsToAdd - The number of accounts to add
109
+ * @returns The accounts added
110
+ */
111
+ async addAccounts(accountsToAdd) {
112
+ const lastAccount = __classPrivateFieldGet(this, _QrKeyring_accounts, "f")[__classPrivateFieldGet(this, _QrKeyring_accounts, "f").length - 1];
113
+ const startIndex = __classPrivateFieldGet(this, _QrKeyring_accountToUnlock, "f") ??
114
+ (lastAccount ? __classPrivateFieldGet(this, _QrKeyring_signer, "f").indexFromAddress(lastAccount) : 0);
115
+ const newAccounts = [];
116
+ for (let i = 0; i < accountsToAdd; i++) {
117
+ const index = startIndex + i;
118
+ const address = __classPrivateFieldGet(this, _QrKeyring_signer, "f").addressFromIndex(index);
119
+ if (__classPrivateFieldGet(this, _QrKeyring_accounts, "f").includes(address)) {
120
+ continue;
121
+ }
122
+ __classPrivateFieldGet(this, _QrKeyring_accounts, "f").push(address);
123
+ newAccounts.push(address);
124
+ }
125
+ __classPrivateFieldSet(this, _QrKeyring_accountToUnlock, startIndex + accountsToAdd, "f");
126
+ return newAccounts;
127
+ }
128
+ /**
129
+ * Gets the accounts in the QrKeyring
130
+ *
131
+ * @returns The accounts in the QrKeyring
132
+ */
133
+ async getAccounts() {
134
+ return Array.from(__classPrivateFieldGet(this, _QrKeyring_accounts, "f").values());
135
+ }
136
+ /**
137
+ * Remove an account from the keyring
138
+ *
139
+ * @param address - The address of the account to remove
140
+ */
141
+ removeAccount(address) {
142
+ const normalizedAddress = normalizeAddress(address);
143
+ __classPrivateFieldSet(this, _QrKeyring_accounts, __classPrivateFieldGet(this, _QrKeyring_accounts, "f").filter((account) => account !== normalizedAddress), "f");
144
+ }
145
+ /**
146
+ * Submits a CBOR encoded UR to the QrKeyring
147
+ *
148
+ * @param ur - The CBOR encoded UR
149
+ */
150
+ submitUR(ur) {
151
+ __classPrivateFieldGet(this, _QrKeyring_signer, "f").init(ur);
152
+ }
153
+ /**
154
+ * Sets the next account index to unlock
155
+ *
156
+ * @param index - The index of the account to unlock
157
+ */
158
+ setAccountToUnlock(index) {
159
+ __classPrivateFieldSet(this, _QrKeyring_accountToUnlock, index, "f");
160
+ }
161
+ /**
162
+ * Get the name of the paired device or the keyring type
163
+ * if unavailable.
164
+ *
165
+ * @returns The name of the paired device or the keyring type.
166
+ */
167
+ getName() {
168
+ const source = __classPrivateFieldGet(this, _QrKeyring_signer, "f").getSourceDetails();
169
+ return source?.name ?? exports.QR_KEYRING_TYPE;
170
+ }
171
+ /**
172
+ * Fetch the first page of accounts. If the keyring is not currently initialized,
173
+ * it will trigger a scan request to initialize it.
174
+ *
175
+ * @returns The first page of accounts as an array of Hex strings.
176
+ */
177
+ async getFirstPage() {
178
+ __classPrivateFieldSet(this, _QrKeyring_currentPage, 0, "f");
179
+ return this.getCurrentPage();
180
+ }
181
+ /**
182
+ * Fetch the next page of accounts. If the keyring is not currently initialized,
183
+ * it will trigger a scan request to initialize it.
184
+ *
185
+ * @returns The next page of accounts as an array of IndexedAddress objects.
186
+ */
187
+ async getNextPage() {
188
+ __classPrivateFieldSet(this, _QrKeyring_currentPage, __classPrivateFieldGet(this, _QrKeyring_currentPage, "f") + 1, "f");
189
+ return this.getCurrentPage();
190
+ }
191
+ /**
192
+ * Fetch the previous page of accounts. If the keyring is not currently initialized,
193
+ * it will trigger a scan request to initialize it.
194
+ *
195
+ * @returns The previous page of accounts as an array of IndexedAddress objects.
196
+ */
197
+ async getPreviousPage() {
198
+ if (__classPrivateFieldGet(this, _QrKeyring_currentPage, "f") > 0) {
199
+ __classPrivateFieldSet(this, _QrKeyring_currentPage, __classPrivateFieldGet(this, _QrKeyring_currentPage, "f") - 1, "f");
200
+ }
201
+ else {
202
+ __classPrivateFieldSet(this, _QrKeyring_currentPage, 0, "f");
203
+ }
204
+ return this.getCurrentPage();
205
+ }
206
+ /**
207
+ * Fetch the current page of accounts. If the keyring is not currently initialized,
208
+ * it will trigger a scan request to initialize it.
209
+ *
210
+ * @returns The current page of accounts as an array of IndexedAddress objects.
211
+ */
212
+ async getCurrentPage() {
213
+ if (!__classPrivateFieldGet(this, _QrKeyring_signer, "f").isInitialized()) {
214
+ await __classPrivateFieldGet(this, _QrKeyring_instances, "m", _QrKeyring_scanAndInitialize).call(this);
215
+ }
216
+ return __classPrivateFieldGet(this, _QrKeyring_signer, "f").getAddressesPage(__classPrivateFieldGet(this, _QrKeyring_currentPage, "f"));
217
+ }
218
+ /**
219
+ * Clear the keyring state and forget any paired device or accounts.
220
+ */
221
+ async forgetDevice() {
222
+ __classPrivateFieldGet(this, _QrKeyring_signer, "f").clear();
223
+ __classPrivateFieldSet(this, _QrKeyring_accounts, [], "f");
224
+ __classPrivateFieldSet(this, _QrKeyring_accountToUnlock, undefined, "f");
225
+ __classPrivateFieldSet(this, _QrKeyring_currentPage, 0, "f");
226
+ }
227
+ }
228
+ exports.QrKeyring = QrKeyring;
229
+ _QrKeyring_signer = new WeakMap(), _QrKeyring_accounts = new WeakMap(), _QrKeyring_accountToUnlock = new WeakMap(), _QrKeyring_currentPage = new WeakMap(), _QrKeyring_instances = new WeakSet(), _QrKeyring_scanAndInitialize =
230
+ /**
231
+ * Scan for a QR code and initialize the keyring with the
232
+ * scanned UR.
233
+ */
234
+ async function _QrKeyring_scanAndInitialize() {
235
+ this.submitUR(await this.bridge.requestScan());
236
+ };
237
+ QrKeyring.type = exports.QR_KEYRING_TYPE;
238
+ //# sourceMappingURL=qr-keyring.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr-keyring.cjs","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"]}
@@ -0,0 +1,121 @@
1
+ import type { Keyring } from "@metamask/keyring-utils";
2
+ import type { Hex } from "@metamask/utils";
3
+ import { type AirgappedSignerDetails, type IndexedAddress } from "./airgapped-signer.cjs";
4
+ export declare const QR_KEYRING_TYPE = "QR Hardware Wallet Device";
5
+ export type QrKeyringBridge = {
6
+ requestScan: () => Promise<string>;
7
+ };
8
+ export type QrKeyringOptions = {
9
+ ur?: string;
10
+ bridge: QrKeyringBridge;
11
+ };
12
+ /**
13
+ * The state of the QrKeyring
14
+ *
15
+ * @property accounts - The accounts in the QrKeyring
16
+ */
17
+ export type SerializedQrKeyringState = {
18
+ version?: number;
19
+ accounts?: string[];
20
+ currentAccount?: number;
21
+ } & ({
22
+ initialized?: false;
23
+ } | ({
24
+ initialized: true;
25
+ } & AirgappedSignerDetails));
26
+ /**
27
+ * Returns the default serialized state of the QrKeyring.
28
+ *
29
+ * @returns The default serialized state.
30
+ */
31
+ export declare const getDefaultSerializedQrKeyringState: () => SerializedQrKeyringState;
32
+ export declare class QrKeyring implements Keyring {
33
+ #private;
34
+ static type: string;
35
+ readonly type = "QR Hardware Wallet Device";
36
+ readonly bridge: QrKeyringBridge;
37
+ constructor(options: QrKeyringOptions);
38
+ /**
39
+ * Serializes the QrKeyring state
40
+ *
41
+ * @returns The serialized state
42
+ */
43
+ serialize(): Promise<SerializedQrKeyringState>;
44
+ /**
45
+ * Deserializes the QrKeyring state
46
+ *
47
+ * @param state - The serialized state to deserialize
48
+ */
49
+ deserialize(state: SerializedQrKeyringState): Promise<void>;
50
+ /**
51
+ * Adds accounts to the QrKeyring
52
+ *
53
+ * @param accountsToAdd - The number of accounts to add
54
+ * @returns The accounts added
55
+ */
56
+ addAccounts(accountsToAdd: number): Promise<Hex[]>;
57
+ /**
58
+ * Gets the accounts in the QrKeyring
59
+ *
60
+ * @returns The accounts in the QrKeyring
61
+ */
62
+ getAccounts(): Promise<Hex[]>;
63
+ /**
64
+ * Remove an account from the keyring
65
+ *
66
+ * @param address - The address of the account to remove
67
+ */
68
+ removeAccount(address: Hex): void;
69
+ /**
70
+ * Submits a CBOR encoded UR to the QrKeyring
71
+ *
72
+ * @param ur - The CBOR encoded UR
73
+ */
74
+ submitUR(ur: string): void;
75
+ /**
76
+ * Sets the next account index to unlock
77
+ *
78
+ * @param index - The index of the account to unlock
79
+ */
80
+ setAccountToUnlock(index: number): void;
81
+ /**
82
+ * Get the name of the paired device or the keyring type
83
+ * if unavailable.
84
+ *
85
+ * @returns The name of the paired device or the keyring type.
86
+ */
87
+ getName(): string;
88
+ /**
89
+ * Fetch the first page of accounts. If the keyring is not currently initialized,
90
+ * it will trigger a scan request to initialize it.
91
+ *
92
+ * @returns The first page of accounts as an array of Hex strings.
93
+ */
94
+ getFirstPage(): Promise<IndexedAddress[]>;
95
+ /**
96
+ * Fetch the next page of accounts. If the keyring is not currently initialized,
97
+ * it will trigger a scan request to initialize it.
98
+ *
99
+ * @returns The next page of accounts as an array of IndexedAddress objects.
100
+ */
101
+ getNextPage(): Promise<IndexedAddress[]>;
102
+ /**
103
+ * Fetch the previous page of accounts. If the keyring is not currently initialized,
104
+ * it will trigger a scan request to initialize it.
105
+ *
106
+ * @returns The previous page of accounts as an array of IndexedAddress objects.
107
+ */
108
+ getPreviousPage(): Promise<IndexedAddress[]>;
109
+ /**
110
+ * Fetch the current page of accounts. If the keyring is not currently initialized,
111
+ * it will trigger a scan request to initialize it.
112
+ *
113
+ * @returns The current page of accounts as an array of IndexedAddress objects.
114
+ */
115
+ getCurrentPage(): Promise<IndexedAddress[]>;
116
+ /**
117
+ * Clear the keyring state and forget any paired device or accounts.
118
+ */
119
+ forgetDevice(): Promise<void>;
120
+ }
121
+ //# sourceMappingURL=qr-keyring.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr-keyring.d.cts","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,gCAAgC;AACvD,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAG3C,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EAGpB,+BAA2B;AAE5B,eAAO,MAAM,eAAe,8BAA8B,CAAC;AAE3D,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,eAAe,CAAC;CACzB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,CACA;IACE,WAAW,CAAC,EAAE,KAAK,CAAC;CACrB,GACD,CAAC;IACC,WAAW,EAAE,IAAI,CAAC;CACnB,GAAG,sBAAsB,CAAC,CAC9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,QACzC,wBAGF,CAAC;AAYL,qBAAa,SAAU,YAAW,OAAO;;IACvC,MAAM,CAAC,IAAI,SAAmB;IAE9B,QAAQ,CAAC,IAAI,+BAAmB;IAEhC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;gBAUrB,OAAO,EAAE,gBAAgB;IAQrC;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,wBAAwB,CAAC;IAyCpD;;;;OAIG;IACG,WAAW,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAWjE;;;;;OAKG;IACG,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAuBxD;;;;OAIG;IACG,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAInC;;;;OAIG;IACH,aAAa,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI;IAOjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAI1B;;;;OAIG;IACH,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIvC;;;;;OAKG;IACH,OAAO,IAAI,MAAM;IAKjB;;;;;OAKG;IACG,YAAY,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAK/C;;;;;OAKG;IACG,WAAW,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAK9C;;;;;OAKG;IACG,eAAe,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IASlD;;;;;OAKG;IACG,cAAc,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAQjD;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;CAcpC"}
@@ -0,0 +1,121 @@
1
+ import type { Keyring } from "@metamask/keyring-utils";
2
+ import type { Hex } from "@metamask/utils";
3
+ import { type AirgappedSignerDetails, type IndexedAddress } from "./airgapped-signer.mjs";
4
+ export declare const QR_KEYRING_TYPE = "QR Hardware Wallet Device";
5
+ export type QrKeyringBridge = {
6
+ requestScan: () => Promise<string>;
7
+ };
8
+ export type QrKeyringOptions = {
9
+ ur?: string;
10
+ bridge: QrKeyringBridge;
11
+ };
12
+ /**
13
+ * The state of the QrKeyring
14
+ *
15
+ * @property accounts - The accounts in the QrKeyring
16
+ */
17
+ export type SerializedQrKeyringState = {
18
+ version?: number;
19
+ accounts?: string[];
20
+ currentAccount?: number;
21
+ } & ({
22
+ initialized?: false;
23
+ } | ({
24
+ initialized: true;
25
+ } & AirgappedSignerDetails));
26
+ /**
27
+ * Returns the default serialized state of the QrKeyring.
28
+ *
29
+ * @returns The default serialized state.
30
+ */
31
+ export declare const getDefaultSerializedQrKeyringState: () => SerializedQrKeyringState;
32
+ export declare class QrKeyring implements Keyring {
33
+ #private;
34
+ static type: string;
35
+ readonly type = "QR Hardware Wallet Device";
36
+ readonly bridge: QrKeyringBridge;
37
+ constructor(options: QrKeyringOptions);
38
+ /**
39
+ * Serializes the QrKeyring state
40
+ *
41
+ * @returns The serialized state
42
+ */
43
+ serialize(): Promise<SerializedQrKeyringState>;
44
+ /**
45
+ * Deserializes the QrKeyring state
46
+ *
47
+ * @param state - The serialized state to deserialize
48
+ */
49
+ deserialize(state: SerializedQrKeyringState): Promise<void>;
50
+ /**
51
+ * Adds accounts to the QrKeyring
52
+ *
53
+ * @param accountsToAdd - The number of accounts to add
54
+ * @returns The accounts added
55
+ */
56
+ addAccounts(accountsToAdd: number): Promise<Hex[]>;
57
+ /**
58
+ * Gets the accounts in the QrKeyring
59
+ *
60
+ * @returns The accounts in the QrKeyring
61
+ */
62
+ getAccounts(): Promise<Hex[]>;
63
+ /**
64
+ * Remove an account from the keyring
65
+ *
66
+ * @param address - The address of the account to remove
67
+ */
68
+ removeAccount(address: Hex): void;
69
+ /**
70
+ * Submits a CBOR encoded UR to the QrKeyring
71
+ *
72
+ * @param ur - The CBOR encoded UR
73
+ */
74
+ submitUR(ur: string): void;
75
+ /**
76
+ * Sets the next account index to unlock
77
+ *
78
+ * @param index - The index of the account to unlock
79
+ */
80
+ setAccountToUnlock(index: number): void;
81
+ /**
82
+ * Get the name of the paired device or the keyring type
83
+ * if unavailable.
84
+ *
85
+ * @returns The name of the paired device or the keyring type.
86
+ */
87
+ getName(): string;
88
+ /**
89
+ * Fetch the first page of accounts. If the keyring is not currently initialized,
90
+ * it will trigger a scan request to initialize it.
91
+ *
92
+ * @returns The first page of accounts as an array of Hex strings.
93
+ */
94
+ getFirstPage(): Promise<IndexedAddress[]>;
95
+ /**
96
+ * Fetch the next page of accounts. If the keyring is not currently initialized,
97
+ * it will trigger a scan request to initialize it.
98
+ *
99
+ * @returns The next page of accounts as an array of IndexedAddress objects.
100
+ */
101
+ getNextPage(): Promise<IndexedAddress[]>;
102
+ /**
103
+ * Fetch the previous page of accounts. If the keyring is not currently initialized,
104
+ * it will trigger a scan request to initialize it.
105
+ *
106
+ * @returns The previous page of accounts as an array of IndexedAddress objects.
107
+ */
108
+ getPreviousPage(): Promise<IndexedAddress[]>;
109
+ /**
110
+ * Fetch the current page of accounts. If the keyring is not currently initialized,
111
+ * it will trigger a scan request to initialize it.
112
+ *
113
+ * @returns The current page of accounts as an array of IndexedAddress objects.
114
+ */
115
+ getCurrentPage(): Promise<IndexedAddress[]>;
116
+ /**
117
+ * Clear the keyring state and forget any paired device or accounts.
118
+ */
119
+ forgetDevice(): Promise<void>;
120
+ }
121
+ //# sourceMappingURL=qr-keyring.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qr-keyring.d.mts","sourceRoot":"","sources":["../src/qr-keyring.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,gCAAgC;AACvD,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAG3C,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EAGpB,+BAA2B;AAE5B,eAAO,MAAM,eAAe,8BAA8B,CAAC;AAE3D,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,eAAe,CAAC;CACzB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,CACA;IACE,WAAW,CAAC,EAAE,KAAK,CAAC;CACrB,GACD,CAAC;IACC,WAAW,EAAE,IAAI,CAAC;CACnB,GAAG,sBAAsB,CAAC,CAC9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,QACzC,wBAGF,CAAC;AAYL,qBAAa,SAAU,YAAW,OAAO;;IACvC,MAAM,CAAC,IAAI,SAAmB;IAE9B,QAAQ,CAAC,IAAI,+BAAmB;IAEhC,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;gBAUrB,OAAO,EAAE,gBAAgB;IAQrC;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,wBAAwB,CAAC;IAyCpD;;;;OAIG;IACG,WAAW,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAWjE;;;;;OAKG;IACG,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAuBxD;;;;OAIG;IACG,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAInC;;;;OAIG;IACH,aAAa,CAAC,OAAO,EAAE,GAAG,GAAG,IAAI;IAOjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAI1B;;;;OAIG;IACH,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIvC;;;;;OAKG;IACH,OAAO,IAAI,MAAM;IAKjB;;;;;OAKG;IACG,YAAY,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAK/C;;;;;OAKG;IACG,WAAW,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAK9C;;;;;OAKG;IACG,eAAe,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IASlD;;;;;OAKG;IACG,cAAc,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAQjD;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;CAcpC"}