@talismn/keyring 1.0.11 → 2.0.0
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/LICENSE +253 -674
- package/dist/index.d.mts +130 -125
- package/dist/index.d.mts.map +1 -0
- package/dist/index.d.ts +130 -125
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +381 -469
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +362 -445
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,497 +1,409 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _talismn_crypto = require("@talismn/crypto");
|
|
3
|
+
//#region src/types/utils.ts
|
|
4
|
+
const isAccountOfType = (account, type) => {
|
|
5
|
+
return account?.type === type;
|
|
9
6
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
7
|
+
const isAccountInTypes = (account, types) => {
|
|
8
|
+
return !!account && types.includes(account.type);
|
|
17
9
|
};
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
getAccountGenesisHash: () => getAccountGenesisHash,
|
|
25
|
-
getAccountPlatform: () => getAccountPlatform,
|
|
26
|
-
getAccountSignetUrl: () => getAccountSignetUrl,
|
|
27
|
-
isAccountAddressEthereum: () => isAccountAddressEthereum,
|
|
28
|
-
isAccountAddressSs58: () => isAccountAddressSs58,
|
|
29
|
-
isAccountBitcoin: () => isAccountBitcoin,
|
|
30
|
-
isAccountExternal: () => isAccountExternal,
|
|
31
|
-
isAccountInTypes: () => isAccountInTypes,
|
|
32
|
-
isAccountLedgerPolkadot: () => isAccountLedgerPolkadot,
|
|
33
|
-
isAccountLedgerPolkadotGeneric: () => isAccountLedgerPolkadotGeneric,
|
|
34
|
-
isAccountLedgerPolkadotLegacy: () => isAccountLedgerPolkadotLegacy,
|
|
35
|
-
isAccountNotContact: () => isAccountNotContact,
|
|
36
|
-
isAccountOfType: () => isAccountOfType,
|
|
37
|
-
isAccountOwned: () => isAccountOwned,
|
|
38
|
-
isAccountPlatformEthereum: () => isAccountPlatformEthereum,
|
|
39
|
-
isAccountPlatformPolkadot: () => isAccountPlatformPolkadot,
|
|
40
|
-
isAccountPlatformSolana: () => isAccountPlatformSolana,
|
|
41
|
-
isAccountPortfolio: () => isAccountPortfolio
|
|
42
|
-
});
|
|
43
|
-
module.exports = __toCommonJS(index_exports);
|
|
44
|
-
|
|
45
|
-
// src/keyring/Keyring.ts
|
|
46
|
-
var import_crypto3 = require("@talismn/crypto");
|
|
47
|
-
|
|
48
|
-
// src/types/utils.ts
|
|
49
|
-
var import_crypto = require("@talismn/crypto");
|
|
50
|
-
var isAccountOfType = (account, type) => {
|
|
51
|
-
return account?.type === type;
|
|
52
|
-
};
|
|
53
|
-
var isAccountInTypes = (account, types) => {
|
|
54
|
-
return !!account && types.includes(account.type);
|
|
55
|
-
};
|
|
56
|
-
var ACCOUNT_TYPES_OWNED = [
|
|
57
|
-
"keypair",
|
|
58
|
-
"ledger-ethereum",
|
|
59
|
-
"ledger-polkadot",
|
|
60
|
-
"ledger-solana",
|
|
61
|
-
"polkadot-vault"
|
|
10
|
+
const ACCOUNT_TYPES_OWNED = [
|
|
11
|
+
"keypair",
|
|
12
|
+
"ledger-ethereum",
|
|
13
|
+
"ledger-polkadot",
|
|
14
|
+
"ledger-solana",
|
|
15
|
+
"polkadot-vault"
|
|
62
16
|
];
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
17
|
+
const ACCOUNT_TYPES_EXTERNAL = [
|
|
18
|
+
"contact",
|
|
19
|
+
"watch-only",
|
|
20
|
+
"ledger-ethereum",
|
|
21
|
+
"ledger-polkadot",
|
|
22
|
+
"ledger-solana",
|
|
23
|
+
"polkadot-vault",
|
|
24
|
+
"signet"
|
|
71
25
|
];
|
|
72
|
-
|
|
73
|
-
|
|
26
|
+
const isAccountExternal = (account) => {
|
|
27
|
+
return isAccountInTypes(account, ACCOUNT_TYPES_EXTERNAL);
|
|
74
28
|
};
|
|
75
|
-
|
|
76
|
-
|
|
29
|
+
const isAccountOwned = (account) => {
|
|
30
|
+
return isAccountInTypes(account, ACCOUNT_TYPES_OWNED);
|
|
77
31
|
};
|
|
78
|
-
|
|
79
|
-
|
|
32
|
+
const isAccountPortfolio = (account) => {
|
|
33
|
+
return isAccountOwned(account) || isAccountOfType(account, "watch-only") && account.isPortfolio;
|
|
80
34
|
};
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
35
|
+
const isAccountNotContact = (acc) => acc.type !== "contact";
|
|
36
|
+
const isAccountAddressEthereum = (account) => {
|
|
37
|
+
return !!account && (0, _talismn_crypto.isEthereumAddress)(account.address);
|
|
84
38
|
};
|
|
85
|
-
|
|
86
|
-
|
|
39
|
+
const isAccountPlatformEthereum = (account) => {
|
|
40
|
+
return !!account && account.type !== "ledger-polkadot" && (0, _talismn_crypto.isEthereumAddress)(account.address);
|
|
87
41
|
};
|
|
88
|
-
|
|
89
|
-
|
|
42
|
+
const isAccountPlatformSolana = (account) => {
|
|
43
|
+
return !!account && (0, _talismn_crypto.isSolanaAddress)(account.address);
|
|
90
44
|
};
|
|
91
|
-
|
|
92
|
-
|
|
45
|
+
const isAccountPlatformPolkadot = (account) => {
|
|
46
|
+
return !!account && account.type !== "ledger-ethereum" && (isAccountAddressEthereum(account) || isAccountAddressSs58(account));
|
|
93
47
|
};
|
|
94
|
-
|
|
95
|
-
|
|
48
|
+
const isAccountAddressSs58 = (account) => {
|
|
49
|
+
return !!account && (0, _talismn_crypto.detectAddressEncoding)(account.address) === "ss58";
|
|
96
50
|
};
|
|
97
|
-
|
|
98
|
-
|
|
51
|
+
const isAccountLedgerPolkadot = (account) => {
|
|
52
|
+
return isAccountOfType(account, "ledger-polkadot");
|
|
99
53
|
};
|
|
100
|
-
|
|
101
|
-
|
|
54
|
+
const isAccountLedgerPolkadotGeneric = (account) => {
|
|
55
|
+
return isAccountOfType(account, "ledger-polkadot") && !account.genesisHash;
|
|
102
56
|
};
|
|
103
|
-
|
|
104
|
-
|
|
57
|
+
const isAccountLedgerPolkadotLegacy = (account) => {
|
|
58
|
+
return isAccountOfType(account, "ledger-polkadot") && !!account.genesisHash;
|
|
105
59
|
};
|
|
106
|
-
|
|
107
|
-
|
|
60
|
+
const isAccountBitcoin = (account) => {
|
|
61
|
+
return !!account && (0, _talismn_crypto.isBitcoinAddress)(account.address);
|
|
108
62
|
};
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
63
|
+
const getAccountGenesisHash = (account) => {
|
|
64
|
+
if (!account) return void 0;
|
|
65
|
+
return "genesisHash" in account ? account.genesisHash || void 0 : void 0;
|
|
112
66
|
};
|
|
113
|
-
|
|
114
|
-
|
|
67
|
+
const getAccountSignetUrl = (account) => {
|
|
68
|
+
return isAccountOfType(account, "signet") ? account.url : void 0;
|
|
115
69
|
};
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
70
|
+
const getAccountPlatform = (account) => {
|
|
71
|
+
if (!account) return void 0;
|
|
72
|
+
return "curve" in account ? (0, _talismn_crypto.getAccountPlatformFromCurve)(account.curve) : (0, _talismn_crypto.getAccountPlatformFromAddress)(account.address);
|
|
119
73
|
};
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
144
|
-
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
145
|
-
const key = await deriveKey(password, salt);
|
|
146
|
-
const encryptedSeed = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, data);
|
|
147
|
-
const combined = new Uint8Array(salt.length + iv.length + encryptedSeed.byteLength);
|
|
148
|
-
combined.set(salt, 0);
|
|
149
|
-
combined.set(iv, salt.length);
|
|
150
|
-
combined.set(new Uint8Array(encryptedSeed), salt.length + iv.length);
|
|
151
|
-
return btoa(String.fromCharCode(...combined));
|
|
152
|
-
} catch (cause) {
|
|
153
|
-
throw new Error("Failed to encrypt data", { cause });
|
|
154
|
-
}
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/keyring/encryption.ts
|
|
76
|
+
const deriveKey = async (password, salt) => await crypto.subtle.importKey("raw", await (0, _talismn_crypto.pbkdf2)("SHA-256", new TextEncoder().encode(password), salt, 1e5, 32), {
|
|
77
|
+
name: "AES-GCM",
|
|
78
|
+
length: 256
|
|
79
|
+
}, false, ["encrypt", "decrypt"]);
|
|
80
|
+
const encryptData = async (data, password) => {
|
|
81
|
+
try {
|
|
82
|
+
const salt = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));
|
|
83
|
+
const iv = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(12));
|
|
84
|
+
const key = await deriveKey(password, salt);
|
|
85
|
+
const encryptedSeed = await crypto.subtle.encrypt({
|
|
86
|
+
name: "AES-GCM",
|
|
87
|
+
iv
|
|
88
|
+
}, key, data);
|
|
89
|
+
const combined = new Uint8Array(salt.length + iv.length + encryptedSeed.byteLength);
|
|
90
|
+
combined.set(salt, 0);
|
|
91
|
+
combined.set(iv, salt.length);
|
|
92
|
+
combined.set(new Uint8Array(encryptedSeed), salt.length + iv.length);
|
|
93
|
+
return btoa(String.fromCharCode(...combined));
|
|
94
|
+
} catch (cause) {
|
|
95
|
+
throw new Error("Failed to encrypt data", { cause });
|
|
96
|
+
}
|
|
155
97
|
};
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
98
|
+
const decryptData = async (encryptedData, password) => {
|
|
99
|
+
try {
|
|
100
|
+
const combined = Uint8Array.from(atob(encryptedData), (c) => c.charCodeAt(0));
|
|
101
|
+
const salt = combined.slice(0, 16);
|
|
102
|
+
const iv = combined.slice(16, 28);
|
|
103
|
+
const encryptedSeed = combined.slice(28);
|
|
104
|
+
const key = await deriveKey(password, salt);
|
|
105
|
+
const decryptedSeed = await crypto.subtle.decrypt({
|
|
106
|
+
name: "AES-GCM",
|
|
107
|
+
iv
|
|
108
|
+
}, key, encryptedSeed);
|
|
109
|
+
return new Uint8Array(decryptedSeed);
|
|
110
|
+
} catch (cause) {
|
|
111
|
+
throw new Error("Failed to decrypt data", { cause });
|
|
112
|
+
}
|
|
168
113
|
};
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
114
|
+
const changeEncryptedDataPassword = async (encryptedData, oldPassword, newPassword) => {
|
|
115
|
+
try {
|
|
116
|
+
const decrypted = await decryptData(encryptedData, oldPassword);
|
|
117
|
+
return await encryptData(decrypted, newPassword);
|
|
118
|
+
} catch (cause) {
|
|
119
|
+
throw new Error("Failed to change password on encrypted data", { cause });
|
|
120
|
+
}
|
|
176
121
|
};
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/keyring/utils.ts
|
|
124
|
+
const REGEX_HEX_STRING = /^0x[0-9a-fA-F]*$/;
|
|
125
|
+
const isHexString = (value) => {
|
|
126
|
+
return typeof value === "string" && REGEX_HEX_STRING.test(value);
|
|
182
127
|
};
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
var Keyring = class
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const encoding = (0, import_crypto3.addressEncodingFromCurve)(curve);
|
|
429
|
-
const address = (0, import_crypto3.addressFromPublicKey)(publicKey, encoding);
|
|
430
|
-
if (this.getAccount(address)) throw new Error("Account already exists");
|
|
431
|
-
const account = {
|
|
432
|
-
type: "keypair",
|
|
433
|
-
curve,
|
|
434
|
-
name,
|
|
435
|
-
address: (0, import_crypto3.normalizeAddress)(address),
|
|
436
|
-
secretKey: await encryptData(secretKey, password),
|
|
437
|
-
createdAt: Date.now()
|
|
438
|
-
};
|
|
439
|
-
this.#data.accounts.push(account);
|
|
440
|
-
return accountFromStorage(account);
|
|
441
|
-
}
|
|
442
|
-
getAccountSecretKey(address, password) {
|
|
443
|
-
if (typeof address !== "string" || !address) throw new Error("address is required");
|
|
444
|
-
if (typeof password !== "string" || !password) throw new Error("password is required");
|
|
445
|
-
const account = this.#data.accounts.find((a) => a.address === (0, import_crypto3.normalizeAddress)(address));
|
|
446
|
-
if (!account) throw new Error("Account not found");
|
|
447
|
-
if (account.type !== "keypair") throw new Error("Secret key unavailable");
|
|
448
|
-
return decryptData(account.secretKey, password);
|
|
449
|
-
}
|
|
450
|
-
async getDerivedAddress(mnemonicId, derivationPath, curve, password) {
|
|
451
|
-
if (typeof mnemonicId !== "string" || !mnemonicId) throw new Error("mnemonicId is required");
|
|
452
|
-
if (typeof password !== "string" || !password) throw new Error("password is required");
|
|
453
|
-
const mnemonic = this.#data.mnemonics.find((s) => s.id === mnemonicId);
|
|
454
|
-
if (!mnemonic) throw new Error("Mnemonic not found");
|
|
455
|
-
const entropy = await decryptData(mnemonic.entropy, password);
|
|
456
|
-
const seed = await (0, import_crypto3.entropyToSeed)(entropy, curve);
|
|
457
|
-
const pair = (0, import_crypto3.deriveKeypair)(seed, derivationPath, curve);
|
|
458
|
-
return pair.address;
|
|
459
|
-
}
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/keyring/Keyring.ts
|
|
130
|
+
var Keyring = class Keyring {
|
|
131
|
+
#data;
|
|
132
|
+
constructor(data) {
|
|
133
|
+
this.#data = structuredClone(data);
|
|
134
|
+
}
|
|
135
|
+
static create() {
|
|
136
|
+
return new Keyring({
|
|
137
|
+
passwordCheck: null,
|
|
138
|
+
mnemonics: [],
|
|
139
|
+
accounts: []
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
static load(data) {
|
|
143
|
+
if (!data.accounts || !data.mnemonics) throw new Error("Invalid data");
|
|
144
|
+
for (const account of data.accounts) if (account.type === "ledger-polkadot" && !account.curve) account.curve = "ed25519";
|
|
145
|
+
return new Keyring(data);
|
|
146
|
+
}
|
|
147
|
+
async checkPassword(password, reset = false) {
|
|
148
|
+
if (typeof password !== "string" || !password) throw new Error("password is required");
|
|
149
|
+
const passwordHash = oneWayHash(password);
|
|
150
|
+
const PASSWORD_CHECK_PHRASE = "PASSWORD_CHECK_PHRASE";
|
|
151
|
+
if (!this.#data.passwordCheck || reset) {
|
|
152
|
+
const bytes = _talismn_crypto.utf8.decode(PASSWORD_CHECK_PHRASE);
|
|
153
|
+
this.#data.passwordCheck = await encryptData(bytes, passwordHash);
|
|
154
|
+
} else try {
|
|
155
|
+
const bytes = await decryptData(this.#data.passwordCheck, passwordHash);
|
|
156
|
+
if (_talismn_crypto.utf8.encode(bytes) !== PASSWORD_CHECK_PHRASE) throw new Error("Invalid password");
|
|
157
|
+
} catch {
|
|
158
|
+
throw new Error("Invalid password");
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/** Returns true if a password has been set on this keyring. */
|
|
162
|
+
hasPassword() {
|
|
163
|
+
return this.#data.passwordCheck !== null;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Verify a password against the keyring's passwordCheck blob.
|
|
167
|
+
* Returns true on success, false on wrong password.
|
|
168
|
+
* Throws if no password has been set (caller should check hasPassword() first).
|
|
169
|
+
*/
|
|
170
|
+
async verifyPassword(password) {
|
|
171
|
+
if (!this.#data.passwordCheck) throw new Error("No password set — call hasPassword() before verifyPassword()");
|
|
172
|
+
try {
|
|
173
|
+
await this.checkPassword(password);
|
|
174
|
+
return true;
|
|
175
|
+
} catch {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Initialize the password on a fresh keyring that has no password set.
|
|
181
|
+
* Throws if a password is already set (use changePassword flow instead).
|
|
182
|
+
*/
|
|
183
|
+
async initializePassword(password) {
|
|
184
|
+
if (this.#data.passwordCheck !== null) throw new Error("Password already set — cannot re-initialize");
|
|
185
|
+
await this.checkPassword(password, true);
|
|
186
|
+
}
|
|
187
|
+
toJson() {
|
|
188
|
+
return structuredClone(this.#data);
|
|
189
|
+
}
|
|
190
|
+
async export(password, jsonPassword) {
|
|
191
|
+
const keyring = new Keyring(structuredClone(this.#data));
|
|
192
|
+
for (const mnemonic of keyring.#data.mnemonics) mnemonic.entropy = await changeEncryptedDataPassword(mnemonic.entropy, password, jsonPassword);
|
|
193
|
+
for (const account of keyring.#data.accounts) if (account.type === "keypair") account.secretKey = await changeEncryptedDataPassword(account.secretKey, password, jsonPassword);
|
|
194
|
+
await keyring.checkPassword(jsonPassword, true);
|
|
195
|
+
return keyring.toJson();
|
|
196
|
+
}
|
|
197
|
+
getMnemonics() {
|
|
198
|
+
return this.#data.mnemonics.map(mnemonicFromStorage);
|
|
199
|
+
}
|
|
200
|
+
async addMnemonic({ name, mnemonic, confirmed }, password) {
|
|
201
|
+
if (typeof name !== "string" || !name) throw new Error("name is required");
|
|
202
|
+
if (typeof mnemonic !== "string") throw new Error("mnemonic is required");
|
|
203
|
+
if (typeof confirmed !== "boolean") throw new Error("confirmed is required");
|
|
204
|
+
if (!(0, _talismn_crypto.isValidMnemonic)(mnemonic)) throw new Error("Invalid mnemonic");
|
|
205
|
+
await this.checkPassword(password);
|
|
206
|
+
const entropy = (0, _talismn_crypto.mnemonicToEntropy)(mnemonic);
|
|
207
|
+
const id = oneWayHash(entropy);
|
|
208
|
+
if (this.#data.mnemonics.find((s) => s.id === id)) throw new Error("Mnemonic already exists");
|
|
209
|
+
const storage = {
|
|
210
|
+
id,
|
|
211
|
+
name,
|
|
212
|
+
entropy: await encryptData(entropy, password),
|
|
213
|
+
confirmed,
|
|
214
|
+
createdAt: Date.now()
|
|
215
|
+
};
|
|
216
|
+
this.#data.mnemonics.push(storage);
|
|
217
|
+
return mnemonicFromStorage(storage);
|
|
218
|
+
}
|
|
219
|
+
getMnemonic(id) {
|
|
220
|
+
const mnemonic = this.#data.mnemonics.find((s) => s.id === id);
|
|
221
|
+
return mnemonic ? mnemonicFromStorage(mnemonic) : null;
|
|
222
|
+
}
|
|
223
|
+
updateMnemonic(id, { name, confirmed }) {
|
|
224
|
+
const mnemonic = this.#data.mnemonics.find((s) => s.id === id);
|
|
225
|
+
if (!mnemonic) throw new Error("Mnemonic not found");
|
|
226
|
+
if (name !== void 0) {
|
|
227
|
+
if (typeof name !== "string" || !name) throw new Error("name must be a string");
|
|
228
|
+
mnemonic.name = name;
|
|
229
|
+
}
|
|
230
|
+
if (confirmed !== void 0) {
|
|
231
|
+
if (typeof confirmed !== "boolean") throw new Error("confirmed must be a boolean");
|
|
232
|
+
mnemonic.confirmed = confirmed;
|
|
233
|
+
}
|
|
234
|
+
return mnemonicFromStorage(mnemonic);
|
|
235
|
+
}
|
|
236
|
+
removeMnemonic(id) {
|
|
237
|
+
const index = this.#data.mnemonics.findIndex((mnemonic) => mnemonic.id === id);
|
|
238
|
+
if (index === -1) throw new Error("Mnemonic not found");
|
|
239
|
+
this.#data.mnemonics.splice(index, 1);
|
|
240
|
+
}
|
|
241
|
+
async getMnemonicText(id, password) {
|
|
242
|
+
const mnemonic = this.#data.mnemonics.find((s) => s.id === id);
|
|
243
|
+
if (!mnemonic) throw new Error("Mnemonic not found");
|
|
244
|
+
return (0, _talismn_crypto.entropyToMnemonic)(await decryptData(mnemonic.entropy, password));
|
|
245
|
+
}
|
|
246
|
+
getExistingMnemonicId(mnemonic) {
|
|
247
|
+
const entropy = (0, _talismn_crypto.mnemonicToEntropy)(mnemonic);
|
|
248
|
+
const mnemonicId = oneWayHash(entropy);
|
|
249
|
+
return this.#data.mnemonics.some((s) => s.id === mnemonicId) ? mnemonicId : null;
|
|
250
|
+
}
|
|
251
|
+
getAccounts() {
|
|
252
|
+
return this.#data.accounts.map(accountFromStorage);
|
|
253
|
+
}
|
|
254
|
+
getAccount(address) {
|
|
255
|
+
const account = this.#data.accounts.find((s) => (0, _talismn_crypto.isAddressEqual)(s.address, address));
|
|
256
|
+
return account ? accountFromStorage(account) : null;
|
|
257
|
+
}
|
|
258
|
+
updateAccount(address, { name, isPortfolio, genesisHash }) {
|
|
259
|
+
const account = this.#data.accounts.find((s) => s.address === address);
|
|
260
|
+
if (!account) throw new Error("Account not found");
|
|
261
|
+
if (name) {
|
|
262
|
+
if (typeof name !== "string" || !name) throw new Error("name is required");
|
|
263
|
+
account.name = name;
|
|
264
|
+
}
|
|
265
|
+
if (account.type === "watch-only" && isPortfolio !== void 0) {
|
|
266
|
+
if (typeof isPortfolio !== "boolean") throw new Error("isPortfolio must be a boolean");
|
|
267
|
+
account.isPortfolio = isPortfolio;
|
|
268
|
+
}
|
|
269
|
+
if (account.type === "contact") if (genesisHash) {
|
|
270
|
+
if (!isHexString(genesisHash)) throw new Error("genesisHash must be a hex string");
|
|
271
|
+
account.genesisHash = genesisHash;
|
|
272
|
+
} else delete account.genesisHash;
|
|
273
|
+
return accountFromStorage(account);
|
|
274
|
+
}
|
|
275
|
+
removeAccount(address) {
|
|
276
|
+
const index = this.#data.accounts.findIndex((s) => (0, _talismn_crypto.isAddressEqual)(s.address, address));
|
|
277
|
+
if (index === -1) throw new Error("Account not found");
|
|
278
|
+
this.#data.accounts.splice(index, 1);
|
|
279
|
+
}
|
|
280
|
+
addAccountExternal(options) {
|
|
281
|
+
const address = (0, _talismn_crypto.normalizeAddress)(options.address);
|
|
282
|
+
if (this.getAccount(address)) throw new Error("Account already exists");
|
|
283
|
+
const account = {
|
|
284
|
+
...options,
|
|
285
|
+
address,
|
|
286
|
+
createdAt: Date.now()
|
|
287
|
+
};
|
|
288
|
+
if (!isAccountExternal(account)) throw new Error("Invalid account type");
|
|
289
|
+
this.#data.accounts.push(account);
|
|
290
|
+
return accountFromStorage(account);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Needs to be called before deriving an account from a mnemonic.
|
|
294
|
+
*
|
|
295
|
+
* This will ensure that it is present (or add it if possible) in the keyring before actually creating the account.
|
|
296
|
+
*
|
|
297
|
+
* @param options
|
|
298
|
+
* @param password
|
|
299
|
+
* @returns the id of the mnemonic
|
|
300
|
+
*/
|
|
301
|
+
async ensureMnemonic(options, password) {
|
|
302
|
+
await this.checkPassword(password);
|
|
303
|
+
switch (options.type) {
|
|
304
|
+
case "new-mnemonic": {
|
|
305
|
+
const { mnemonic, mnemonicName: name, confirmed } = options;
|
|
306
|
+
if (typeof name !== "string" || !name) throw new Error("mnemonicName is required");
|
|
307
|
+
if (typeof confirmed !== "boolean") throw new Error("confirmed is required");
|
|
308
|
+
const mnemonicId = this.getExistingMnemonicId(mnemonic);
|
|
309
|
+
if (mnemonicId) return mnemonicId;
|
|
310
|
+
const { id } = await this.addMnemonic({
|
|
311
|
+
name,
|
|
312
|
+
mnemonic,
|
|
313
|
+
confirmed
|
|
314
|
+
}, password);
|
|
315
|
+
return id;
|
|
316
|
+
}
|
|
317
|
+
case "existing-mnemonic":
|
|
318
|
+
if (typeof options.mnemonicId !== "string" || !options.mnemonicId) throw new Error("mnemonicId must be a string");
|
|
319
|
+
return options.mnemonicId;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
async addAccountDerive(options, password) {
|
|
323
|
+
await this.checkPassword(password);
|
|
324
|
+
const { curve, derivationPath, name } = options;
|
|
325
|
+
const mnemonicId = await this.ensureMnemonic(options, password);
|
|
326
|
+
const mnemonic = this.#data.mnemonics.find((s) => s.id === mnemonicId);
|
|
327
|
+
if (!mnemonic) throw new Error("Mnemonic not found");
|
|
328
|
+
const pair = (0, _talismn_crypto.deriveKeypair)(await (0, _talismn_crypto.entropyToSeed)(await decryptData(mnemonic.entropy, password), curve), derivationPath, curve);
|
|
329
|
+
if (this.getAccount(pair.address)) throw new Error("Account already exists");
|
|
330
|
+
const account = {
|
|
331
|
+
type: "keypair",
|
|
332
|
+
curve,
|
|
333
|
+
name,
|
|
334
|
+
address: (0, _talismn_crypto.normalizeAddress)(pair.address),
|
|
335
|
+
secretKey: await encryptData(pair.secretKey, password),
|
|
336
|
+
mnemonicId,
|
|
337
|
+
derivationPath,
|
|
338
|
+
createdAt: Date.now()
|
|
339
|
+
};
|
|
340
|
+
this.#data.accounts.push(account);
|
|
341
|
+
return accountFromStorage(account);
|
|
342
|
+
}
|
|
343
|
+
async addAccountKeypair({ curve, name, secretKey }, password) {
|
|
344
|
+
await this.checkPassword(password);
|
|
345
|
+
const address = (0, _talismn_crypto.addressFromPublicKey)((0, _talismn_crypto.getPublicKeyFromSecret)(secretKey, curve), (0, _talismn_crypto.addressEncodingFromCurve)(curve));
|
|
346
|
+
if (this.getAccount(address)) throw new Error("Account already exists");
|
|
347
|
+
const account = {
|
|
348
|
+
type: "keypair",
|
|
349
|
+
curve,
|
|
350
|
+
name,
|
|
351
|
+
address: (0, _talismn_crypto.normalizeAddress)(address),
|
|
352
|
+
secretKey: await encryptData(secretKey, password),
|
|
353
|
+
createdAt: Date.now()
|
|
354
|
+
};
|
|
355
|
+
this.#data.accounts.push(account);
|
|
356
|
+
return accountFromStorage(account);
|
|
357
|
+
}
|
|
358
|
+
getAccountSecretKey(address, password) {
|
|
359
|
+
if (typeof address !== "string" || !address) throw new Error("address is required");
|
|
360
|
+
if (typeof password !== "string" || !password) throw new Error("password is required");
|
|
361
|
+
const account = this.#data.accounts.find((a) => a.address === (0, _talismn_crypto.normalizeAddress)(address));
|
|
362
|
+
if (!account) throw new Error("Account not found");
|
|
363
|
+
if (account.type !== "keypair") throw new Error("Secret key unavailable");
|
|
364
|
+
return decryptData(account.secretKey, password);
|
|
365
|
+
}
|
|
366
|
+
async getDerivedAddress(mnemonicId, derivationPath, curve, password) {
|
|
367
|
+
if (typeof mnemonicId !== "string" || !mnemonicId) throw new Error("mnemonicId is required");
|
|
368
|
+
if (typeof password !== "string" || !password) throw new Error("password is required");
|
|
369
|
+
const mnemonic = this.#data.mnemonics.find((s) => s.id === mnemonicId);
|
|
370
|
+
if (!mnemonic) throw new Error("Mnemonic not found");
|
|
371
|
+
return (0, _talismn_crypto.deriveKeypair)(await (0, _talismn_crypto.entropyToSeed)(await decryptData(mnemonic.entropy, password), curve), derivationPath, curve).address;
|
|
372
|
+
}
|
|
460
373
|
};
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
374
|
+
const oneWayHash = (bytes) => {
|
|
375
|
+
if (typeof bytes === "string") bytes = _talismn_crypto.utf8.decode(bytes);
|
|
376
|
+
return _talismn_crypto.base58.encode((0, _talismn_crypto.blake3)(bytes));
|
|
464
377
|
};
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
378
|
+
const mnemonicFromStorage = (data) => {
|
|
379
|
+
const copy = structuredClone(data);
|
|
380
|
+
if ("entropy" in copy) delete copy.entropy;
|
|
381
|
+
return Object.freeze(copy);
|
|
469
382
|
};
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
383
|
+
const accountFromStorage = (data) => {
|
|
384
|
+
const copy = structuredClone(data);
|
|
385
|
+
if ("secretKey" in copy) delete copy.secretKey;
|
|
386
|
+
return Object.freeze(copy);
|
|
474
387
|
};
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
});
|
|
388
|
+
//#endregion
|
|
389
|
+
exports.Keyring = Keyring;
|
|
390
|
+
exports.getAccountGenesisHash = getAccountGenesisHash;
|
|
391
|
+
exports.getAccountPlatform = getAccountPlatform;
|
|
392
|
+
exports.getAccountSignetUrl = getAccountSignetUrl;
|
|
393
|
+
exports.isAccountAddressEthereum = isAccountAddressEthereum;
|
|
394
|
+
exports.isAccountAddressSs58 = isAccountAddressSs58;
|
|
395
|
+
exports.isAccountBitcoin = isAccountBitcoin;
|
|
396
|
+
exports.isAccountExternal = isAccountExternal;
|
|
397
|
+
exports.isAccountInTypes = isAccountInTypes;
|
|
398
|
+
exports.isAccountLedgerPolkadot = isAccountLedgerPolkadot;
|
|
399
|
+
exports.isAccountLedgerPolkadotGeneric = isAccountLedgerPolkadotGeneric;
|
|
400
|
+
exports.isAccountLedgerPolkadotLegacy = isAccountLedgerPolkadotLegacy;
|
|
401
|
+
exports.isAccountNotContact = isAccountNotContact;
|
|
402
|
+
exports.isAccountOfType = isAccountOfType;
|
|
403
|
+
exports.isAccountOwned = isAccountOwned;
|
|
404
|
+
exports.isAccountPlatformEthereum = isAccountPlatformEthereum;
|
|
405
|
+
exports.isAccountPlatformPolkadot = isAccountPlatformPolkadot;
|
|
406
|
+
exports.isAccountPlatformSolana = isAccountPlatformSolana;
|
|
407
|
+
exports.isAccountPortfolio = isAccountPortfolio;
|
|
408
|
+
|
|
497
409
|
//# sourceMappingURL=index.js.map
|