@metamask-previews/eth-qr-keyring 1.0.0-e4f6caa → 1.1.0-9fbf2ff
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -1
- package/dist/device.cjs +345 -0
- package/dist/device.cjs.map +1 -0
- package/dist/device.d.cts +175 -0
- package/dist/device.d.cts.map +1 -0
- package/dist/device.d.mts +175 -0
- package/dist/device.d.mts.map +1 -0
- package/dist/device.mjs +345 -0
- package/dist/device.mjs.map +1 -0
- package/dist/index.cjs +9 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +3 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3 -2
- package/dist/index.mjs.map +1 -1
- package/dist/qr-keyring-deferred-promise-bridge.cjs +76 -0
- package/dist/qr-keyring-deferred-promise-bridge.cjs.map +1 -0
- package/dist/qr-keyring-deferred-promise-bridge.d.cts +46 -0
- package/dist/qr-keyring-deferred-promise-bridge.d.cts.map +1 -0
- package/dist/qr-keyring-deferred-promise-bridge.d.mts +46 -0
- package/dist/qr-keyring-deferred-promise-bridge.d.mts.map +1 -0
- package/dist/qr-keyring-deferred-promise-bridge.mjs +72 -0
- package/dist/qr-keyring-deferred-promise-bridge.mjs.map +1 -0
- package/dist/qr-keyring-scanner-bridge.cjs +2 -2
- package/dist/qr-keyring-scanner-bridge.cjs.map +1 -1
- package/dist/qr-keyring-scanner-bridge.d.cts +3 -3
- package/dist/qr-keyring-scanner-bridge.d.mts +3 -3
- package/dist/qr-keyring-scanner-bridge.mjs +2 -2
- package/dist/qr-keyring-scanner-bridge.mjs.map +1 -1
- package/dist/qr-keyring.cjs +65 -137
- package/dist/qr-keyring.cjs.map +1 -1
- package/dist/qr-keyring.d.cts +17 -4
- package/dist/qr-keyring.d.cts.map +1 -1
- package/dist/qr-keyring.d.mts +17 -4
- package/dist/qr-keyring.d.mts.map +1 -1
- package/dist/qr-keyring.mjs +66 -138
- package/dist/qr-keyring.mjs.map +1 -1
- package/package.json +6 -5
- package/dist/airgapped-signer.cjs +0 -270
- package/dist/airgapped-signer.cjs.map +0 -1
- package/dist/airgapped-signer.d.cts +0 -140
- package/dist/airgapped-signer.d.cts.map +0 -1
- package/dist/airgapped-signer.d.mts +0 -140
- package/dist/airgapped-signer.d.mts.map +0 -1
- package/dist/airgapped-signer.mjs +0 -270
- package/dist/airgapped-signer.mjs.map +0 -1
|
@@ -1,270 +0,0 @@
|
|
|
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 _AirgappedSigner_instances, _AirgappedSigner_source, _AirgappedSigner_initFromUR, _AirgappedSigner_decodeUR;
|
|
13
|
-
function $importDefault(module) {
|
|
14
|
-
if (module?.__esModule) {
|
|
15
|
-
return module.default;
|
|
16
|
-
}
|
|
17
|
-
return module;
|
|
18
|
-
}
|
|
19
|
-
import { publicToAddress } from "@ethereumjs/util";
|
|
20
|
-
import { CryptoAccount, CryptoHDKey, URRegistryDecoder } from "@keystonehq/bc-ur-registry-eth";
|
|
21
|
-
import { add0x, getChecksumAddress } from "@metamask/utils";
|
|
22
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
23
|
-
import $HdKey from "hdkey";
|
|
24
|
-
const HdKey = $importDefault($HdKey);
|
|
25
|
-
export const SUPPORTED_UR_TYPE = {
|
|
26
|
-
CRYPTO_HDKEY: 'crypto-hdkey',
|
|
27
|
-
CRYPTO_ACCOUNT: 'crypto-account',
|
|
28
|
-
ETH_SIGNATURE: 'eth-signature',
|
|
29
|
-
};
|
|
30
|
-
export var KeyringMode;
|
|
31
|
-
(function (KeyringMode) {
|
|
32
|
-
KeyringMode["HD"] = "hd";
|
|
33
|
-
KeyringMode["ACCOUNT"] = "account";
|
|
34
|
-
})(KeyringMode || (KeyringMode = {}));
|
|
35
|
-
const DEFAULT_CHILDREN_PATH = '0/*';
|
|
36
|
-
const MAX_INDEX = 1000;
|
|
37
|
-
/**
|
|
38
|
-
* Get the fingerprint of the source CryptoAccount or CryptoHDKey
|
|
39
|
-
*
|
|
40
|
-
* @param source - The source CryptoAccount or CryptoHDKey
|
|
41
|
-
* @returns The fingerprint of the source
|
|
42
|
-
*/
|
|
43
|
-
function getFingerprintFromSource(source) {
|
|
44
|
-
return source instanceof CryptoAccount
|
|
45
|
-
? source.getMasterFingerprint()?.toString('hex')
|
|
46
|
-
: source.getParentFingerprint()?.toString('hex');
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Get fingerprint, account paths and names from the a CryptoAccount
|
|
50
|
-
*
|
|
51
|
-
* Note: This function emulates the behavior of the `@keystonehq/base-eth-keyring`
|
|
52
|
-
* library when dealing with CryptoAccount objects (for backwards compatibility
|
|
53
|
-
* reasons). Though, the way it retrieves `name` and `keyringAccount` is questionable,
|
|
54
|
-
* as `name` and `keyringAccount` are updated after each descriptor discovery, effectively
|
|
55
|
-
* returning the last descriptor's `name` and `keyringAccount`.
|
|
56
|
-
*
|
|
57
|
-
* @param source - The source CryptoAccount
|
|
58
|
-
* @returns The paths
|
|
59
|
-
*/
|
|
60
|
-
function readCryptoAccountOutputDescriptors(source) {
|
|
61
|
-
const descriptors = source.getOutputDescriptors();
|
|
62
|
-
if (!descriptors || descriptors.length === 0) {
|
|
63
|
-
throw new Error('No output descriptors found in CryptoAccount');
|
|
64
|
-
}
|
|
65
|
-
let name = '';
|
|
66
|
-
let keyringAccount = '';
|
|
67
|
-
const paths = descriptors.reduce((descriptorsPaths, current) => {
|
|
68
|
-
const hdKey = current.getHDKey();
|
|
69
|
-
if (hdKey) {
|
|
70
|
-
const path = `M/${hdKey.getOrigin().getPath()}`;
|
|
71
|
-
const address = getChecksumAddress(add0x(Buffer.from(publicToAddress(hdKey.getKey(), true)).toString('hex')));
|
|
72
|
-
descriptorsPaths[address] = path;
|
|
73
|
-
name = hdKey.getName();
|
|
74
|
-
keyringAccount = hdKey.getNote();
|
|
75
|
-
}
|
|
76
|
-
return descriptorsPaths;
|
|
77
|
-
}, {});
|
|
78
|
-
return {
|
|
79
|
-
paths,
|
|
80
|
-
name,
|
|
81
|
-
keyringAccount,
|
|
82
|
-
xfp: getFingerprintFromSource(source),
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
export class AirgappedSigner {
|
|
86
|
-
constructor() {
|
|
87
|
-
_AirgappedSigner_instances.add(this);
|
|
88
|
-
_AirgappedSigner_source.set(this, void 0);
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Initialize the AirgappedSigner with a source.
|
|
92
|
-
*
|
|
93
|
-
* @param source - The signer source, in the form of details object, or a UR string
|
|
94
|
-
*/
|
|
95
|
-
init(source) {
|
|
96
|
-
if (typeof source === 'string' || 'cbor' in source) {
|
|
97
|
-
__classPrivateFieldGet(this, _AirgappedSigner_instances, "m", _AirgappedSigner_initFromUR).call(this, source);
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
__classPrivateFieldSet(this, _AirgappedSigner_source, source, "f");
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Check if the AirgappedSigner is initialized
|
|
105
|
-
*
|
|
106
|
-
* @returns True if the AirgappedSigner is initialized, false otherwise
|
|
107
|
-
*/
|
|
108
|
-
isInitialized() {
|
|
109
|
-
return __classPrivateFieldGet(this, _AirgappedSigner_source, "f") !== undefined;
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Derive an address from the source at a given index
|
|
113
|
-
*
|
|
114
|
-
* @param index - The index to derive the address from
|
|
115
|
-
* @returns The derived address in hex format
|
|
116
|
-
* @throws Will throw an error if the source is not initialized
|
|
117
|
-
*/
|
|
118
|
-
addressFromIndex(index) {
|
|
119
|
-
if (!__classPrivateFieldGet(this, _AirgappedSigner_source, "f")) {
|
|
120
|
-
throw new Error('UR not initialized');
|
|
121
|
-
}
|
|
122
|
-
if (__classPrivateFieldGet(this, _AirgappedSigner_source, "f").keyringMode === KeyringMode.ACCOUNT) {
|
|
123
|
-
const address = Object.keys(__classPrivateFieldGet(this, _AirgappedSigner_source, "f").paths)[index];
|
|
124
|
-
if (!address) {
|
|
125
|
-
throw new Error(`Address not found for index ${index}`);
|
|
126
|
-
}
|
|
127
|
-
return add0x(address);
|
|
128
|
-
}
|
|
129
|
-
const childPath = `m/${__classPrivateFieldGet(this, _AirgappedSigner_source, "f").childrenPath.replace('*', index.toString())}`;
|
|
130
|
-
const hdKey = HdKey.fromExtendedKey(__classPrivateFieldGet(this, _AirgappedSigner_source, "f").xpub);
|
|
131
|
-
const childKey = hdKey.derive(childPath);
|
|
132
|
-
const address = Buffer.from(publicToAddress(childKey.publicKey, true)).toString('hex');
|
|
133
|
-
const normalizedAddress = getChecksumAddress(add0x(address));
|
|
134
|
-
__classPrivateFieldGet(this, _AirgappedSigner_source, "f").indexes[normalizedAddress] = index;
|
|
135
|
-
return normalizedAddress;
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Retrieve the path of an address derived from the source.
|
|
139
|
-
*
|
|
140
|
-
* @param address - The address to retrieve the path for
|
|
141
|
-
* @returns The path of the address
|
|
142
|
-
*/
|
|
143
|
-
pathFromAddress(address) {
|
|
144
|
-
if (!__classPrivateFieldGet(this, _AirgappedSigner_source, "f")) {
|
|
145
|
-
throw new Error('UR not initialized');
|
|
146
|
-
}
|
|
147
|
-
const normalizedAddress = getChecksumAddress(add0x(address));
|
|
148
|
-
if (__classPrivateFieldGet(this, _AirgappedSigner_source, "f").keyringMode === KeyringMode.ACCOUNT) {
|
|
149
|
-
const path = __classPrivateFieldGet(this, _AirgappedSigner_source, "f").paths[normalizedAddress];
|
|
150
|
-
if (path === undefined) {
|
|
151
|
-
throw new Error(`Unknown address ${normalizedAddress}`);
|
|
152
|
-
}
|
|
153
|
-
return path;
|
|
154
|
-
}
|
|
155
|
-
const index = this.indexFromAddress(normalizedAddress);
|
|
156
|
-
return `${__classPrivateFieldGet(this, _AirgappedSigner_source, "f").hdPath}/${__classPrivateFieldGet(this, _AirgappedSigner_source, "f").childrenPath
|
|
157
|
-
.replace('*', index.toString())
|
|
158
|
-
.replace(/\*/gu, '0')}`;
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Retrieve the index of an address from the source
|
|
162
|
-
*
|
|
163
|
-
* @param address - The normalized address to retrieve the index for
|
|
164
|
-
* @returns The index of the address
|
|
165
|
-
*/
|
|
166
|
-
indexFromAddress(address) {
|
|
167
|
-
if (!__classPrivateFieldGet(this, _AirgappedSigner_source, "f")) {
|
|
168
|
-
throw new Error('UR not initialized');
|
|
169
|
-
}
|
|
170
|
-
const cachedIndex = __classPrivateFieldGet(this, _AirgappedSigner_source, "f").indexes[address];
|
|
171
|
-
if (cachedIndex !== undefined) {
|
|
172
|
-
return Number(cachedIndex);
|
|
173
|
-
}
|
|
174
|
-
if (__classPrivateFieldGet(this, _AirgappedSigner_source, "f").keyringMode === KeyringMode.ACCOUNT) {
|
|
175
|
-
const path = __classPrivateFieldGet(this, _AirgappedSigner_source, "f").paths[address];
|
|
176
|
-
if (path === undefined) {
|
|
177
|
-
throw new Error(`Unknown address`);
|
|
178
|
-
}
|
|
179
|
-
const index = path.split('/').pop();
|
|
180
|
-
if (index === undefined) {
|
|
181
|
-
throw new Error(`Invalid path for address ${address}`);
|
|
182
|
-
}
|
|
183
|
-
return Number(index);
|
|
184
|
-
}
|
|
185
|
-
for (let i = 0; i < MAX_INDEX; i++) {
|
|
186
|
-
const derivedAddress = this.addressFromIndex(i);
|
|
187
|
-
if (derivedAddress === address) {
|
|
188
|
-
return i;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
throw new Error(`Address ${address} not found`);
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Get a page of addresses derived from the source.
|
|
195
|
-
*
|
|
196
|
-
* @param page - The page number to retrieve
|
|
197
|
-
* @param pageSize - The number of addresses per page
|
|
198
|
-
* @returns An array of IndexedAddress objects, each containing the address and its index
|
|
199
|
-
* @throws Will throw an error if the source is not initialized
|
|
200
|
-
*/
|
|
201
|
-
getAddressesPage(page, pageSize = 5) {
|
|
202
|
-
const startIndex = page * pageSize;
|
|
203
|
-
const endIndex = startIndex + pageSize;
|
|
204
|
-
const addresses = [];
|
|
205
|
-
for (let i = startIndex; i < endIndex; i++) {
|
|
206
|
-
const address = this.addressFromIndex(i);
|
|
207
|
-
addresses.push({ address, index: i });
|
|
208
|
-
}
|
|
209
|
-
return addresses;
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Gets the source details of the AirgappedSigner.
|
|
213
|
-
*
|
|
214
|
-
* @returns The source details, or undefined if not initialized
|
|
215
|
-
*/
|
|
216
|
-
getSourceDetails() {
|
|
217
|
-
return __classPrivateFieldGet(this, _AirgappedSigner_source, "f");
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* Clear the source details.
|
|
221
|
-
*/
|
|
222
|
-
clear() {
|
|
223
|
-
__classPrivateFieldSet(this, _AirgappedSigner_source, undefined, "f");
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
_AirgappedSigner_source = new WeakMap(), _AirgappedSigner_instances = new WeakSet(), _AirgappedSigner_initFromUR = function _AirgappedSigner_initFromUR(ur) {
|
|
227
|
-
const source = __classPrivateFieldGet(this, _AirgappedSigner_instances, "m", _AirgappedSigner_decodeUR).call(this, ur);
|
|
228
|
-
const fingerprint = getFingerprintFromSource(source);
|
|
229
|
-
if (source instanceof CryptoAccount) {
|
|
230
|
-
const { name, xfp, paths, keyringAccount } = readCryptoAccountOutputDescriptors(source);
|
|
231
|
-
__classPrivateFieldSet(this, _AirgappedSigner_source, {
|
|
232
|
-
keyringMode: KeyringMode.ACCOUNT,
|
|
233
|
-
keyringAccount,
|
|
234
|
-
name,
|
|
235
|
-
xfp,
|
|
236
|
-
paths,
|
|
237
|
-
indexes: {},
|
|
238
|
-
}, "f");
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
const { getBip32Key, getOrigin, getChildren, getName, getNote } = source;
|
|
242
|
-
__classPrivateFieldSet(this, _AirgappedSigner_source, {
|
|
243
|
-
keyringMode: KeyringMode.HD,
|
|
244
|
-
keyringAccount: getNote(),
|
|
245
|
-
name: getName(),
|
|
246
|
-
xfp: fingerprint,
|
|
247
|
-
hdPath: `m/${getOrigin().getPath()}`,
|
|
248
|
-
childrenPath: getChildren()?.getPath() || DEFAULT_CHILDREN_PATH,
|
|
249
|
-
xpub: getBip32Key(),
|
|
250
|
-
indexes: {},
|
|
251
|
-
}, "f");
|
|
252
|
-
}
|
|
253
|
-
}, _AirgappedSigner_decodeUR = function _AirgappedSigner_decodeUR(ur) {
|
|
254
|
-
const decodedUR = typeof ur === 'string'
|
|
255
|
-
? URRegistryDecoder.decode(ur)
|
|
256
|
-
: {
|
|
257
|
-
type: ur.type,
|
|
258
|
-
cbor: Buffer.from(ur.cbor, 'hex'),
|
|
259
|
-
};
|
|
260
|
-
const { type, cbor } = decodedUR;
|
|
261
|
-
switch (type) {
|
|
262
|
-
case SUPPORTED_UR_TYPE.CRYPTO_HDKEY:
|
|
263
|
-
return CryptoHDKey.fromCBOR(cbor);
|
|
264
|
-
case SUPPORTED_UR_TYPE.CRYPTO_ACCOUNT:
|
|
265
|
-
return CryptoAccount.fromCBOR(cbor);
|
|
266
|
-
default:
|
|
267
|
-
throw new Error('Unsupported UR type');
|
|
268
|
-
}
|
|
269
|
-
};
|
|
270
|
-
//# sourceMappingURL=airgapped-signer.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"airgapped-signer.mjs","sourceRoot":"","sources":["../src/airgapped-signer.ts"],"names":[],"mappings":"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","sourcesContent":["import { publicToAddress } from '@ethereumjs/util';\nimport {\n CryptoAccount,\n CryptoHDKey,\n URRegistryDecoder,\n} from '@keystonehq/bc-ur-registry-eth';\nimport { add0x, getChecksumAddress, type Hex } from '@metamask/utils';\n// eslint-disable-next-line @typescript-eslint/naming-convention\nimport HdKey from 'hdkey';\n\nimport type { SerializedUR } from './qr-keyring';\n\nexport const SUPPORTED_UR_TYPE = {\n CRYPTO_HDKEY: 'crypto-hdkey',\n CRYPTO_ACCOUNT: 'crypto-account',\n ETH_SIGNATURE: 'eth-signature',\n};\n\nexport enum KeyringMode {\n HD = 'hd',\n ACCOUNT = 'account',\n}\n\nconst DEFAULT_CHILDREN_PATH = '0/*';\n\nconst MAX_INDEX = 1_000;\n\n/**\n * Common details for the signer source, which can be either a CryptoAccount or CryptoHDKey.\n */\nexport type CommonSignerDetails = {\n /**\n * Value take out from the device note field, if available.\n */\n keyringAccount: string;\n /**\n * The name of the device\n */\n name: string;\n /**\n * The device fingerprint, hex-encoded\n */\n xfp: string;\n /**\n * Indexes of the accounts derived from the device\n * in the form of a map from address to index\n */\n indexes: Record<Hex, number>;\n};\n\n/**\n * Details for the HD mode of the AirgappedSigner. This mode derives\n * accounts from a root public key (xpub) and a derivation path.\n */\nexport type HDModeSignerDetails = {\n /**\n * The keyring mode is HD, indicating that it derives accounts from a\n * root public key (xpub) and a derivation path.\n */\n keyringMode: KeyringMode.HD;\n /**\n * The xpub of the HD key\n */\n xpub: string;\n /**\n * The derivation path of the HD key\n */\n hdPath: string;\n /**\n * The path used to derive child accounts\n */\n childrenPath: string;\n};\n\n/**\n * Details for the Account mode of the AirgappedSigner. This mode derives\n * accounts from a set of addresses and their corresponding paths.\n */\nexport type AccountModeSignerDetails = {\n /**\n * The keyring mode is ACCOUNT, indicating that it derives accounts from\n * a set of addresses and their corresponding paths.\n */\n keyringMode: KeyringMode.ACCOUNT;\n /**\n * The derivation paths for each hex-encoded address in the device\n */\n paths: Record<Hex, string>;\n};\n\n/**\n * An address with its corresponding index.\n */\nexport type IndexedAddress = {\n address: Hex;\n index: number;\n};\n\n/**\n * The details of the source CryptoAccount or CryptoHDKey\n * that the AirgappedSigner uses to derive accounts.\n */\nexport type AirgappedSignerDetails = CommonSignerDetails &\n (HDModeSignerDetails | AccountModeSignerDetails);\n\n/**\n * Get the fingerprint of the source CryptoAccount or CryptoHDKey\n *\n * @param source - The source CryptoAccount or CryptoHDKey\n * @returns The fingerprint of the source\n */\nfunction getFingerprintFromSource(source: CryptoAccount | CryptoHDKey): string {\n return source instanceof CryptoAccount\n ? source.getMasterFingerprint()?.toString('hex')\n : source.getParentFingerprint()?.toString('hex');\n}\n\n/**\n * Get fingerprint, account paths and names from the a CryptoAccount\n *\n * Note: This function emulates the behavior of the `@keystonehq/base-eth-keyring`\n * library when dealing with CryptoAccount objects (for backwards compatibility\n * reasons). Though, the way it retrieves `name` and `keyringAccount` is questionable,\n * as `name` and `keyringAccount` are updated after each descriptor discovery, effectively\n * returning the last descriptor's `name` and `keyringAccount`.\n *\n * @param source - The source CryptoAccount\n * @returns The paths\n */\nfunction readCryptoAccountOutputDescriptors(source: CryptoAccount): {\n xfp: string;\n paths: Record<Hex, string>;\n name: string;\n keyringAccount: string;\n} {\n const descriptors = source.getOutputDescriptors();\n\n if (!descriptors || descriptors.length === 0) {\n throw new Error('No output descriptors found in CryptoAccount');\n }\n\n let name = '';\n let keyringAccount = '';\n const paths = descriptors.reduce(\n (descriptorsPaths: Record<Hex, string>, current) => {\n const hdKey = current.getHDKey();\n if (hdKey) {\n const path = `M/${hdKey.getOrigin().getPath()}`;\n const address = getChecksumAddress(\n add0x(\n Buffer.from(publicToAddress(hdKey.getKey(), true)).toString('hex'),\n ),\n );\n descriptorsPaths[address] = path;\n name = hdKey.getName();\n keyringAccount = hdKey.getNote();\n }\n return descriptorsPaths;\n },\n {},\n );\n\n return {\n paths,\n name,\n keyringAccount,\n xfp: getFingerprintFromSource(source),\n };\n}\n\nexport class AirgappedSigner {\n #source?: AirgappedSignerDetails | undefined;\n\n /**\n * Initialize the AirgappedSigner with a source.\n *\n * @param source - The signer source, in the form of details object, or a UR string\n */\n init(source: AirgappedSignerDetails | string | SerializedUR): void {\n if (typeof source === 'string' || 'cbor' in source) {\n this.#initFromUR(source);\n } else {\n this.#source = source;\n }\n }\n\n /**\n * Check if the AirgappedSigner is initialized\n *\n * @returns True if the AirgappedSigner is initialized, false otherwise\n */\n isInitialized(): boolean {\n return this.#source !== undefined;\n }\n\n /**\n * Derive an address from the source at a given index\n *\n * @param index - The index to derive the address from\n * @returns The derived address in hex format\n * @throws Will throw an error if the source is not initialized\n */\n addressFromIndex(index: number): Hex {\n if (!this.#source) {\n throw new Error('UR not initialized');\n }\n\n if (this.#source.keyringMode === KeyringMode.ACCOUNT) {\n const address = Object.keys(this.#source.paths)[index];\n if (!address) {\n throw new Error(`Address not found for index ${index}`);\n }\n return add0x(address);\n }\n const childPath = `m/${this.#source.childrenPath.replace(\n '*',\n index.toString(),\n )}`;\n\n const hdKey = HdKey.fromExtendedKey(this.#source.xpub);\n const childKey = hdKey.derive(childPath);\n\n const address = Buffer.from(\n publicToAddress(childKey.publicKey, true),\n ).toString('hex');\n\n const normalizedAddress = getChecksumAddress(add0x(address));\n\n this.#source.indexes[normalizedAddress] = index;\n\n return normalizedAddress;\n }\n\n /**\n * Retrieve the path of an address derived from the source.\n *\n * @param address - The address to retrieve the path for\n * @returns The path of the address\n */\n pathFromAddress(address: Hex): string {\n if (!this.#source) {\n throw new Error('UR not initialized');\n }\n\n const normalizedAddress = getChecksumAddress(add0x(address));\n\n if (this.#source.keyringMode === KeyringMode.ACCOUNT) {\n const path = this.#source.paths[normalizedAddress];\n if (path === undefined) {\n throw new Error(`Unknown address ${normalizedAddress}`);\n }\n return path;\n }\n\n const index = this.indexFromAddress(normalizedAddress);\n return `${this.#source.hdPath}/${this.#source.childrenPath\n .replace('*', index.toString())\n .replace(/\\*/gu, '0')}`;\n }\n\n /**\n * Retrieve the index of an address from the source\n *\n * @param address - The normalized address to retrieve the index for\n * @returns The index of the address\n */\n indexFromAddress(address: Hex): number {\n if (!this.#source) {\n throw new Error('UR not initialized');\n }\n\n const cachedIndex = this.#source.indexes[address];\n if (cachedIndex !== undefined) {\n return Number(cachedIndex);\n }\n\n if (this.#source.keyringMode === KeyringMode.ACCOUNT) {\n const path = this.#source.paths[address];\n if (path === undefined) {\n throw new Error(`Unknown address`);\n }\n\n const index = path.split('/').pop();\n if (index === undefined) {\n throw new Error(`Invalid path for address ${address}`);\n }\n\n return Number(index);\n }\n\n for (let i = 0; i < MAX_INDEX; i++) {\n const derivedAddress = this.addressFromIndex(i);\n if (derivedAddress === address) {\n return i;\n }\n }\n\n throw new Error(`Address ${address} not found`);\n }\n\n /**\n * Get a page of addresses derived from the source.\n *\n * @param page - The page number to retrieve\n * @param pageSize - The number of addresses per page\n * @returns An array of IndexedAddress objects, each containing the address and its index\n * @throws Will throw an error if the source is not initialized\n */\n getAddressesPage(page: number, pageSize = 5): IndexedAddress[] {\n const startIndex = page * pageSize;\n const endIndex = startIndex + pageSize;\n const addresses: IndexedAddress[] = [];\n\n for (let i = startIndex; i < endIndex; i++) {\n const address = this.addressFromIndex(i);\n addresses.push({ address, index: i });\n }\n\n return addresses;\n }\n\n /**\n * Gets the source details of the AirgappedSigner.\n *\n * @returns The source details, or undefined if not initialized\n */\n getSourceDetails(): AirgappedSignerDetails | undefined {\n return this.#source;\n }\n\n /**\n * Clear the source details.\n */\n clear(): void {\n this.#source = undefined;\n }\n\n /**\n * Set the root account from a UR string\n *\n * @param ur - The UR string to set the root account from\n */\n #initFromUR(ur: string | SerializedUR): void {\n const source = this.#decodeUR(ur);\n const fingerprint = getFingerprintFromSource(source);\n\n if (source instanceof CryptoAccount) {\n const { name, xfp, paths, keyringAccount } =\n readCryptoAccountOutputDescriptors(source);\n this.#source = {\n keyringMode: KeyringMode.ACCOUNT,\n keyringAccount,\n name,\n xfp,\n paths,\n indexes: {},\n };\n } else {\n const { getBip32Key, getOrigin, getChildren, getName, getNote } = source;\n this.#source = {\n keyringMode: KeyringMode.HD,\n keyringAccount: getNote(),\n name: getName(),\n xfp: fingerprint,\n hdPath: `m/${getOrigin().getPath()}`,\n childrenPath: getChildren()?.getPath() || DEFAULT_CHILDREN_PATH,\n xpub: getBip32Key(),\n indexes: {},\n };\n }\n }\n\n /**\n * Decodes a UR\n *\n * @param ur - The UR to decode\n * @returns The decoded CryptoAccount or CryptoHDKey\n * @throws Will throw an error if the UR type is not supported\n */\n #decodeUR(ur: string | SerializedUR): CryptoAccount | CryptoHDKey {\n const decodedUR =\n typeof ur === 'string'\n ? URRegistryDecoder.decode(ur)\n : {\n type: ur.type,\n cbor: Buffer.from(ur.cbor, 'hex'),\n };\n\n const { type, cbor } = decodedUR;\n\n switch (type) {\n case SUPPORTED_UR_TYPE.CRYPTO_HDKEY:\n return CryptoHDKey.fromCBOR(cbor);\n case SUPPORTED_UR_TYPE.CRYPTO_ACCOUNT:\n return CryptoAccount.fromCBOR(cbor);\n default:\n throw new Error('Unsupported UR type');\n }\n }\n}\n"]}
|