@unicitylabs/sphere-sdk 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.cjs +855 -417
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +117 -4
- package/dist/core/index.d.ts +117 -4
- package/dist/core/index.js +744 -303
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +31 -15
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +31 -15
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +12 -12
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +5 -3
- package/dist/impl/nodejs/index.d.ts +5 -3
- package/dist/impl/nodejs/index.js +12 -12
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +994 -596
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +106 -4
- package/dist/index.d.ts +106 -4
- package/dist/index.js +904 -506
- package/dist/index.js.map +1 -1
- package/dist/l1/index.cjs +18 -0
- package/dist/l1/index.cjs.map +1 -1
- package/dist/l1/index.d.cts +4 -0
- package/dist/l1/index.d.ts +4 -0
- package/dist/l1/index.js +18 -0
- package/dist/l1/index.js.map +1 -1
- package/package.json +1 -1
package/dist/core/index.cjs
CHANGED
|
@@ -5,6 +5,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
8
11
|
var __export = (target, all) => {
|
|
9
12
|
for (var name in all)
|
|
10
13
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -27,82 +30,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
30
|
));
|
|
28
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
32
|
|
|
30
|
-
// core/index.ts
|
|
31
|
-
var core_exports = {};
|
|
32
|
-
__export(core_exports, {
|
|
33
|
-
CHARSET: () => CHARSET,
|
|
34
|
-
CurrencyUtils: () => CurrencyUtils,
|
|
35
|
-
DEFAULT_DERIVATION_PATH: () => DEFAULT_DERIVATION_PATH,
|
|
36
|
-
DEFAULT_TOKEN_DECIMALS: () => DEFAULT_TOKEN_DECIMALS,
|
|
37
|
-
Sphere: () => Sphere,
|
|
38
|
-
base58Decode: () => base58Decode,
|
|
39
|
-
base58Encode: () => base58Encode,
|
|
40
|
-
bytesToHex: () => bytesToHex2,
|
|
41
|
-
computeHash160: () => computeHash160,
|
|
42
|
-
convertBits: () => convertBits,
|
|
43
|
-
createAddress: () => createAddress,
|
|
44
|
-
createBech32: () => createBech32,
|
|
45
|
-
createKeyPair: () => createKeyPair,
|
|
46
|
-
createSphere: () => createSphere,
|
|
47
|
-
decodeBech32: () => decodeBech32,
|
|
48
|
-
decrypt: () => decrypt2,
|
|
49
|
-
decryptJson: () => decryptJson,
|
|
50
|
-
decryptMnemonic: () => decryptMnemonic,
|
|
51
|
-
decryptSimple: () => decryptSimple,
|
|
52
|
-
deriveAddressInfo: () => deriveAddressInfo,
|
|
53
|
-
deriveChildKey: () => deriveChildKey,
|
|
54
|
-
deriveKeyAtPath: () => deriveKeyAtPath,
|
|
55
|
-
deserializeEncrypted: () => deserializeEncrypted,
|
|
56
|
-
doubleSha256: () => doubleSha256,
|
|
57
|
-
ec: () => ec,
|
|
58
|
-
encodeBech32: () => encodeBech32,
|
|
59
|
-
encrypt: () => encrypt2,
|
|
60
|
-
encryptMnemonic: () => encryptMnemonic,
|
|
61
|
-
encryptSimple: () => encryptSimple,
|
|
62
|
-
entropyToMnemonic: () => entropyToMnemonic2,
|
|
63
|
-
extractFromText: () => extractFromText,
|
|
64
|
-
findPattern: () => findPattern,
|
|
65
|
-
formatAmount: () => formatAmount,
|
|
66
|
-
generateAddressInfo: () => generateAddressInfo,
|
|
67
|
-
generateMasterKey: () => generateMasterKey,
|
|
68
|
-
generateMnemonic: () => generateMnemonic2,
|
|
69
|
-
generateRandomKey: () => generateRandomKey,
|
|
70
|
-
getAddressHrp: () => getAddressHrp,
|
|
71
|
-
getPublicKey: () => getPublicKey,
|
|
72
|
-
getSphere: () => getSphere,
|
|
73
|
-
hash160: () => hash160,
|
|
74
|
-
hash160ToBytes: () => hash160ToBytes,
|
|
75
|
-
hexToBytes: () => hexToBytes,
|
|
76
|
-
identityFromMnemonic: () => identityFromMnemonic,
|
|
77
|
-
identityFromMnemonicSync: () => identityFromMnemonicSync,
|
|
78
|
-
importSphere: () => importSphere,
|
|
79
|
-
initSphere: () => initSphere,
|
|
80
|
-
isEncryptedData: () => isEncryptedData,
|
|
81
|
-
isValidBech32: () => isValidBech32,
|
|
82
|
-
isValidPrivateKey: () => isValidPrivateKey,
|
|
83
|
-
loadSphere: () => loadSphere,
|
|
84
|
-
mnemonicToEntropy: () => mnemonicToEntropy2,
|
|
85
|
-
mnemonicToSeed: () => mnemonicToSeed2,
|
|
86
|
-
mnemonicToSeedSync: () => mnemonicToSeedSync2,
|
|
87
|
-
privateKeyToAddressInfo: () => privateKeyToAddressInfo,
|
|
88
|
-
publicKeyToAddress: () => publicKeyToAddress,
|
|
89
|
-
randomBytes: () => randomBytes,
|
|
90
|
-
randomHex: () => randomHex,
|
|
91
|
-
randomUUID: () => randomUUID,
|
|
92
|
-
ripemd160: () => ripemd160,
|
|
93
|
-
serializeEncrypted: () => serializeEncrypted,
|
|
94
|
-
sha256: () => sha256,
|
|
95
|
-
sleep: () => sleep,
|
|
96
|
-
sphereExists: () => sphereExists,
|
|
97
|
-
toHumanReadable: () => toHumanReadable,
|
|
98
|
-
toSmallestUnit: () => toSmallestUnit,
|
|
99
|
-
validateMnemonic: () => validateMnemonic2
|
|
100
|
-
});
|
|
101
|
-
module.exports = __toCommonJS(core_exports);
|
|
102
|
-
|
|
103
33
|
// core/bech32.ts
|
|
104
|
-
var CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
105
|
-
var GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
|
|
106
34
|
function convertBits(data, fromBits, toBits, pad) {
|
|
107
35
|
let acc = 0;
|
|
108
36
|
let bits = 0;
|
|
@@ -209,10 +137,17 @@ function getAddressHrp(addr) {
|
|
|
209
137
|
const result = decodeBech32(addr);
|
|
210
138
|
return result?.hrp ?? null;
|
|
211
139
|
}
|
|
212
|
-
var
|
|
140
|
+
var CHARSET, GENERATOR, createBech32;
|
|
141
|
+
var init_bech32 = __esm({
|
|
142
|
+
"core/bech32.ts"() {
|
|
143
|
+
"use strict";
|
|
144
|
+
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
145
|
+
GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
|
|
146
|
+
createBech32 = encodeBech32;
|
|
147
|
+
}
|
|
148
|
+
});
|
|
213
149
|
|
|
214
150
|
// l1/addressToScriptHash.ts
|
|
215
|
-
var import_crypto_js = __toESM(require("crypto-js"), 1);
|
|
216
151
|
function bytesToHex(buf) {
|
|
217
152
|
return Array.from(buf).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
218
153
|
}
|
|
@@ -223,267 +158,75 @@ function addressToScriptHash(address) {
|
|
|
223
158
|
const sha = import_crypto_js.default.SHA256(import_crypto_js.default.enc.Hex.parse(scriptHex)).toString();
|
|
224
159
|
return sha.match(/../g).reverse().join("");
|
|
225
160
|
}
|
|
161
|
+
var import_crypto_js;
|
|
162
|
+
var init_addressToScriptHash = __esm({
|
|
163
|
+
"l1/addressToScriptHash.ts"() {
|
|
164
|
+
"use strict";
|
|
165
|
+
init_bech32();
|
|
166
|
+
import_crypto_js = __toESM(require("crypto-js"), 1);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
226
169
|
|
|
227
|
-
//
|
|
228
|
-
var
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
function mnemonicToSeedSync2(mnemonic, passphrase = "") {
|
|
247
|
-
const seedBuffer = bip39.mnemonicToSeedSync(mnemonic, passphrase);
|
|
248
|
-
return Buffer.from(seedBuffer).toString("hex");
|
|
249
|
-
}
|
|
250
|
-
function mnemonicToEntropy2(mnemonic) {
|
|
251
|
-
return bip39.mnemonicToEntropy(mnemonic);
|
|
252
|
-
}
|
|
253
|
-
function entropyToMnemonic2(entropy) {
|
|
254
|
-
return bip39.entropyToMnemonic(entropy);
|
|
170
|
+
// l1/network.ts
|
|
171
|
+
var network_exports = {};
|
|
172
|
+
__export(network_exports, {
|
|
173
|
+
broadcast: () => broadcast,
|
|
174
|
+
connect: () => connect,
|
|
175
|
+
disconnect: () => disconnect,
|
|
176
|
+
getBalance: () => getBalance,
|
|
177
|
+
getBlockHeader: () => getBlockHeader,
|
|
178
|
+
getCurrentBlockHeight: () => getCurrentBlockHeight,
|
|
179
|
+
getTransaction: () => getTransaction,
|
|
180
|
+
getTransactionHistory: () => getTransactionHistory,
|
|
181
|
+
getUtxo: () => getUtxo,
|
|
182
|
+
isWebSocketConnected: () => isWebSocketConnected,
|
|
183
|
+
rpc: () => rpc,
|
|
184
|
+
subscribeBlocks: () => subscribeBlocks,
|
|
185
|
+
waitForConnection: () => waitForConnection
|
|
186
|
+
});
|
|
187
|
+
function isWebSocketConnected() {
|
|
188
|
+
return isConnected && ws !== null && ws.readyState === WebSocket.OPEN;
|
|
255
189
|
}
|
|
256
|
-
function
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
import_crypto_js2.default.enc.Utf8.parse("Bitcoin seed")
|
|
260
|
-
).toString();
|
|
261
|
-
const IL = I.substring(0, 64);
|
|
262
|
-
const IR = I.substring(64);
|
|
263
|
-
const masterKeyBigInt = BigInt("0x" + IL);
|
|
264
|
-
if (masterKeyBigInt === 0n || masterKeyBigInt >= CURVE_ORDER) {
|
|
265
|
-
throw new Error("Invalid master key generated");
|
|
190
|
+
function waitForConnection() {
|
|
191
|
+
if (isWebSocketConnected()) {
|
|
192
|
+
return Promise.resolve();
|
|
266
193
|
}
|
|
267
|
-
return {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
194
|
+
return new Promise((resolve, reject) => {
|
|
195
|
+
const callback = {
|
|
196
|
+
resolve: () => {
|
|
197
|
+
if (callback.timeoutId) clearTimeout(callback.timeoutId);
|
|
198
|
+
resolve();
|
|
199
|
+
},
|
|
200
|
+
reject: (err) => {
|
|
201
|
+
if (callback.timeoutId) clearTimeout(callback.timeoutId);
|
|
202
|
+
reject(err);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
callback.timeoutId = setTimeout(() => {
|
|
206
|
+
const idx = connectionCallbacks.indexOf(callback);
|
|
207
|
+
if (idx > -1) connectionCallbacks.splice(idx, 1);
|
|
208
|
+
reject(new Error("Connection timeout"));
|
|
209
|
+
}, CONNECTION_TIMEOUT);
|
|
210
|
+
connectionCallbacks.push(callback);
|
|
211
|
+
});
|
|
271
212
|
}
|
|
272
|
-
function
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
if (isHardened) {
|
|
276
|
-
const indexHex = index.toString(16).padStart(8, "0");
|
|
277
|
-
data = "00" + parentPrivKey + indexHex;
|
|
278
|
-
} else {
|
|
279
|
-
const keyPair = ec.keyFromPrivate(parentPrivKey, "hex");
|
|
280
|
-
const compressedPubKey = keyPair.getPublic(true, "hex");
|
|
281
|
-
const indexHex = index.toString(16).padStart(8, "0");
|
|
282
|
-
data = compressedPubKey + indexHex;
|
|
283
|
-
}
|
|
284
|
-
const I = import_crypto_js2.default.HmacSHA512(
|
|
285
|
-
import_crypto_js2.default.enc.Hex.parse(data),
|
|
286
|
-
import_crypto_js2.default.enc.Hex.parse(parentChainCode)
|
|
287
|
-
).toString();
|
|
288
|
-
const IL = I.substring(0, 64);
|
|
289
|
-
const IR = I.substring(64);
|
|
290
|
-
const ilBigInt = BigInt("0x" + IL);
|
|
291
|
-
const parentKeyBigInt = BigInt("0x" + parentPrivKey);
|
|
292
|
-
if (ilBigInt >= CURVE_ORDER) {
|
|
293
|
-
throw new Error("Invalid key: IL >= curve order");
|
|
213
|
+
function connect(endpoint = DEFAULT_ENDPOINT) {
|
|
214
|
+
if (isConnected) {
|
|
215
|
+
return Promise.resolve();
|
|
294
216
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
throw new Error("Invalid key: child key is zero");
|
|
217
|
+
if (isConnecting) {
|
|
218
|
+
return waitForConnection();
|
|
298
219
|
}
|
|
299
|
-
|
|
300
|
-
return {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
for (const part of pathParts) {
|
|
310
|
-
const isHardened = part.endsWith("'") || part.endsWith("h");
|
|
311
|
-
const indexStr = part.replace(/['h]$/, "");
|
|
312
|
-
let index = parseInt(indexStr, 10);
|
|
313
|
-
if (isHardened) {
|
|
314
|
-
index += 2147483648;
|
|
315
|
-
}
|
|
316
|
-
const derived = deriveChildKey(currentKey, currentChainCode, index);
|
|
317
|
-
currentKey = derived.privateKey;
|
|
318
|
-
currentChainCode = derived.chainCode;
|
|
319
|
-
}
|
|
320
|
-
return {
|
|
321
|
-
privateKey: currentKey,
|
|
322
|
-
chainCode: currentChainCode
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
function getPublicKey(privateKey, compressed = true) {
|
|
326
|
-
const keyPair = ec.keyFromPrivate(privateKey, "hex");
|
|
327
|
-
return keyPair.getPublic(compressed, "hex");
|
|
328
|
-
}
|
|
329
|
-
function createKeyPair(privateKey) {
|
|
330
|
-
return {
|
|
331
|
-
privateKey,
|
|
332
|
-
publicKey: getPublicKey(privateKey)
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
function sha256(data, inputEncoding = "hex") {
|
|
336
|
-
const parsed = inputEncoding === "hex" ? import_crypto_js2.default.enc.Hex.parse(data) : import_crypto_js2.default.enc.Utf8.parse(data);
|
|
337
|
-
return import_crypto_js2.default.SHA256(parsed).toString();
|
|
338
|
-
}
|
|
339
|
-
function ripemd160(data, inputEncoding = "hex") {
|
|
340
|
-
const parsed = inputEncoding === "hex" ? import_crypto_js2.default.enc.Hex.parse(data) : import_crypto_js2.default.enc.Utf8.parse(data);
|
|
341
|
-
return import_crypto_js2.default.RIPEMD160(parsed).toString();
|
|
342
|
-
}
|
|
343
|
-
function hash160(data) {
|
|
344
|
-
const sha = sha256(data, "hex");
|
|
345
|
-
return ripemd160(sha, "hex");
|
|
346
|
-
}
|
|
347
|
-
function doubleSha256(data, inputEncoding = "hex") {
|
|
348
|
-
const first = sha256(data, inputEncoding);
|
|
349
|
-
return sha256(first, "hex");
|
|
350
|
-
}
|
|
351
|
-
var computeHash160 = hash160;
|
|
352
|
-
function hash160ToBytes(hash160Hex) {
|
|
353
|
-
const matches = hash160Hex.match(/../g);
|
|
354
|
-
if (!matches) return new Uint8Array(0);
|
|
355
|
-
return Uint8Array.from(matches.map((x) => parseInt(x, 16)));
|
|
356
|
-
}
|
|
357
|
-
function publicKeyToAddress(publicKey, prefix = "alpha", witnessVersion = 0) {
|
|
358
|
-
const pubKeyHash = hash160(publicKey);
|
|
359
|
-
const programBytes = hash160ToBytes(pubKeyHash);
|
|
360
|
-
return encodeBech32(prefix, witnessVersion, programBytes);
|
|
361
|
-
}
|
|
362
|
-
function privateKeyToAddressInfo(privateKey, prefix = "alpha") {
|
|
363
|
-
const publicKey = getPublicKey(privateKey);
|
|
364
|
-
const address = publicKeyToAddress(publicKey, prefix);
|
|
365
|
-
return { address, publicKey };
|
|
366
|
-
}
|
|
367
|
-
function hexToBytes(hex) {
|
|
368
|
-
const matches = hex.match(/../g);
|
|
369
|
-
if (!matches) {
|
|
370
|
-
return new Uint8Array(0);
|
|
371
|
-
}
|
|
372
|
-
return Uint8Array.from(matches.map((x) => parseInt(x, 16)));
|
|
373
|
-
}
|
|
374
|
-
function bytesToHex2(bytes) {
|
|
375
|
-
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
376
|
-
}
|
|
377
|
-
function randomBytes(length) {
|
|
378
|
-
const words = import_crypto_js2.default.lib.WordArray.random(length);
|
|
379
|
-
return words.toString(import_crypto_js2.default.enc.Hex);
|
|
380
|
-
}
|
|
381
|
-
async function identityFromMnemonic(mnemonic, passphrase = "") {
|
|
382
|
-
if (!validateMnemonic2(mnemonic)) {
|
|
383
|
-
throw new Error("Invalid mnemonic phrase");
|
|
384
|
-
}
|
|
385
|
-
const seedHex = await mnemonicToSeed2(mnemonic, passphrase);
|
|
386
|
-
return generateMasterKey(seedHex);
|
|
387
|
-
}
|
|
388
|
-
function identityFromMnemonicSync(mnemonic, passphrase = "") {
|
|
389
|
-
if (!validateMnemonic2(mnemonic)) {
|
|
390
|
-
throw new Error("Invalid mnemonic phrase");
|
|
391
|
-
}
|
|
392
|
-
const seedHex = mnemonicToSeedSync2(mnemonic, passphrase);
|
|
393
|
-
return generateMasterKey(seedHex);
|
|
394
|
-
}
|
|
395
|
-
function deriveAddressInfo(masterKey, basePath, index, isChange = false, prefix = "alpha") {
|
|
396
|
-
const chain = isChange ? 1 : 0;
|
|
397
|
-
const fullPath = `${basePath}/${chain}/${index}`;
|
|
398
|
-
const derived = deriveKeyAtPath(masterKey.privateKey, masterKey.chainCode, fullPath);
|
|
399
|
-
const publicKey = getPublicKey(derived.privateKey);
|
|
400
|
-
const address = publicKeyToAddress(publicKey, prefix);
|
|
401
|
-
return {
|
|
402
|
-
privateKey: derived.privateKey,
|
|
403
|
-
publicKey,
|
|
404
|
-
address,
|
|
405
|
-
path: fullPath,
|
|
406
|
-
index
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
function generateAddressInfo(privateKey, index, path, prefix = "alpha") {
|
|
410
|
-
const { address, publicKey } = privateKeyToAddressInfo(privateKey, prefix);
|
|
411
|
-
return {
|
|
412
|
-
privateKey,
|
|
413
|
-
publicKey,
|
|
414
|
-
address,
|
|
415
|
-
path,
|
|
416
|
-
index
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// l1/crypto.ts
|
|
421
|
-
var import_crypto_js3 = __toESM(require("crypto-js"), 1);
|
|
422
|
-
|
|
423
|
-
// l1/address.ts
|
|
424
|
-
var import_crypto_js4 = __toESM(require("crypto-js"), 1);
|
|
425
|
-
|
|
426
|
-
// l1/network.ts
|
|
427
|
-
var DEFAULT_ENDPOINT = "wss://fulcrum.unicity.network:50004";
|
|
428
|
-
var ws = null;
|
|
429
|
-
var isConnected = false;
|
|
430
|
-
var isConnecting = false;
|
|
431
|
-
var requestId = 0;
|
|
432
|
-
var intentionalClose = false;
|
|
433
|
-
var reconnectAttempts = 0;
|
|
434
|
-
var isBlockSubscribed = false;
|
|
435
|
-
var lastBlockHeader = null;
|
|
436
|
-
var pending = {};
|
|
437
|
-
var blockSubscribers = [];
|
|
438
|
-
var connectionCallbacks = [];
|
|
439
|
-
var MAX_RECONNECT_ATTEMPTS = 10;
|
|
440
|
-
var BASE_DELAY = 2e3;
|
|
441
|
-
var MAX_DELAY = 6e4;
|
|
442
|
-
var RPC_TIMEOUT = 3e4;
|
|
443
|
-
var CONNECTION_TIMEOUT = 3e4;
|
|
444
|
-
function isWebSocketConnected() {
|
|
445
|
-
return isConnected && ws !== null && ws.readyState === WebSocket.OPEN;
|
|
446
|
-
}
|
|
447
|
-
function waitForConnection() {
|
|
448
|
-
if (isWebSocketConnected()) {
|
|
449
|
-
return Promise.resolve();
|
|
450
|
-
}
|
|
451
|
-
return new Promise((resolve, reject) => {
|
|
452
|
-
const callback = {
|
|
453
|
-
resolve: () => {
|
|
454
|
-
if (callback.timeoutId) clearTimeout(callback.timeoutId);
|
|
455
|
-
resolve();
|
|
456
|
-
},
|
|
457
|
-
reject: (err) => {
|
|
458
|
-
if (callback.timeoutId) clearTimeout(callback.timeoutId);
|
|
459
|
-
reject(err);
|
|
460
|
-
}
|
|
461
|
-
};
|
|
462
|
-
callback.timeoutId = setTimeout(() => {
|
|
463
|
-
const idx = connectionCallbacks.indexOf(callback);
|
|
464
|
-
if (idx > -1) connectionCallbacks.splice(idx, 1);
|
|
465
|
-
reject(new Error("Connection timeout"));
|
|
466
|
-
}, CONNECTION_TIMEOUT);
|
|
467
|
-
connectionCallbacks.push(callback);
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
function connect(endpoint = DEFAULT_ENDPOINT) {
|
|
471
|
-
if (isConnected) {
|
|
472
|
-
return Promise.resolve();
|
|
473
|
-
}
|
|
474
|
-
if (isConnecting) {
|
|
475
|
-
return waitForConnection();
|
|
476
|
-
}
|
|
477
|
-
isConnecting = true;
|
|
478
|
-
return new Promise((resolve, reject) => {
|
|
479
|
-
let hasResolved = false;
|
|
480
|
-
try {
|
|
481
|
-
ws = new WebSocket(endpoint);
|
|
482
|
-
} catch (err) {
|
|
483
|
-
console.error("[L1] WebSocket constructor threw exception:", err);
|
|
484
|
-
isConnecting = false;
|
|
485
|
-
reject(err);
|
|
486
|
-
return;
|
|
220
|
+
isConnecting = true;
|
|
221
|
+
return new Promise((resolve, reject) => {
|
|
222
|
+
let hasResolved = false;
|
|
223
|
+
try {
|
|
224
|
+
ws = new WebSocket(endpoint);
|
|
225
|
+
} catch (err) {
|
|
226
|
+
console.error("[L1] WebSocket constructor threw exception:", err);
|
|
227
|
+
isConnecting = false;
|
|
228
|
+
reject(err);
|
|
229
|
+
return;
|
|
487
230
|
}
|
|
488
231
|
ws.onopen = () => {
|
|
489
232
|
isConnected = true;
|
|
@@ -631,52 +374,397 @@ async function getBalance(address) {
|
|
|
631
374
|
async function broadcast(rawHex) {
|
|
632
375
|
return await rpc("blockchain.transaction.broadcast", [rawHex]);
|
|
633
376
|
}
|
|
634
|
-
async function
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
377
|
+
async function subscribeBlocks(cb) {
|
|
378
|
+
if (!isConnected && !isConnecting) {
|
|
379
|
+
await connect();
|
|
380
|
+
}
|
|
381
|
+
if (!isWebSocketConnected()) {
|
|
382
|
+
await waitForConnection();
|
|
383
|
+
}
|
|
384
|
+
blockSubscribers.push(cb);
|
|
385
|
+
if (!isBlockSubscribed) {
|
|
386
|
+
isBlockSubscribed = true;
|
|
387
|
+
const header = await rpc("blockchain.headers.subscribe", []);
|
|
388
|
+
if (header) {
|
|
389
|
+
lastBlockHeader = header;
|
|
390
|
+
blockSubscribers.forEach((subscriber) => subscriber(header));
|
|
391
|
+
}
|
|
392
|
+
} else if (lastBlockHeader) {
|
|
393
|
+
cb(lastBlockHeader);
|
|
394
|
+
}
|
|
395
|
+
return () => {
|
|
396
|
+
const index = blockSubscribers.indexOf(cb);
|
|
397
|
+
if (index > -1) {
|
|
398
|
+
blockSubscribers.splice(index, 1);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
async function getTransactionHistory(address) {
|
|
403
|
+
const scriptHash = addressToScriptHash(address);
|
|
404
|
+
const result = await rpc("blockchain.scripthash.get_history", [scriptHash]);
|
|
405
|
+
if (!Array.isArray(result)) {
|
|
406
|
+
console.warn("get_history returned non-array:", result);
|
|
407
|
+
return [];
|
|
408
|
+
}
|
|
409
|
+
return result;
|
|
410
|
+
}
|
|
411
|
+
async function getTransaction(txid) {
|
|
412
|
+
return await rpc("blockchain.transaction.get", [txid, true]);
|
|
413
|
+
}
|
|
414
|
+
async function getBlockHeader(height) {
|
|
415
|
+
return await rpc("blockchain.block.header", [height, height]);
|
|
416
|
+
}
|
|
417
|
+
async function getCurrentBlockHeight() {
|
|
418
|
+
try {
|
|
419
|
+
const header = await rpc("blockchain.headers.subscribe", []);
|
|
420
|
+
return header?.height || 0;
|
|
421
|
+
} catch (err) {
|
|
422
|
+
console.error("Error getting current block height:", err);
|
|
423
|
+
return 0;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
function disconnect() {
|
|
427
|
+
if (ws) {
|
|
428
|
+
intentionalClose = true;
|
|
429
|
+
ws.close();
|
|
430
|
+
ws = null;
|
|
431
|
+
}
|
|
432
|
+
isConnected = false;
|
|
433
|
+
isConnecting = false;
|
|
434
|
+
reconnectAttempts = 0;
|
|
435
|
+
isBlockSubscribed = false;
|
|
436
|
+
Object.values(pending).forEach((req) => {
|
|
437
|
+
if (req.timeoutId) clearTimeout(req.timeoutId);
|
|
438
|
+
});
|
|
439
|
+
Object.keys(pending).forEach((key) => delete pending[Number(key)]);
|
|
440
|
+
connectionCallbacks.forEach((cb) => {
|
|
441
|
+
if (cb.timeoutId) clearTimeout(cb.timeoutId);
|
|
442
|
+
});
|
|
443
|
+
connectionCallbacks.length = 0;
|
|
444
|
+
}
|
|
445
|
+
var DEFAULT_ENDPOINT, ws, isConnected, isConnecting, requestId, intentionalClose, reconnectAttempts, isBlockSubscribed, lastBlockHeader, pending, blockSubscribers, connectionCallbacks, MAX_RECONNECT_ATTEMPTS, BASE_DELAY, MAX_DELAY, RPC_TIMEOUT, CONNECTION_TIMEOUT;
|
|
446
|
+
var init_network = __esm({
|
|
447
|
+
"l1/network.ts"() {
|
|
448
|
+
"use strict";
|
|
449
|
+
init_addressToScriptHash();
|
|
450
|
+
DEFAULT_ENDPOINT = "wss://fulcrum.unicity.network:50004";
|
|
451
|
+
ws = null;
|
|
452
|
+
isConnected = false;
|
|
453
|
+
isConnecting = false;
|
|
454
|
+
requestId = 0;
|
|
455
|
+
intentionalClose = false;
|
|
456
|
+
reconnectAttempts = 0;
|
|
457
|
+
isBlockSubscribed = false;
|
|
458
|
+
lastBlockHeader = null;
|
|
459
|
+
pending = {};
|
|
460
|
+
blockSubscribers = [];
|
|
461
|
+
connectionCallbacks = [];
|
|
462
|
+
MAX_RECONNECT_ATTEMPTS = 10;
|
|
463
|
+
BASE_DELAY = 2e3;
|
|
464
|
+
MAX_DELAY = 6e4;
|
|
465
|
+
RPC_TIMEOUT = 3e4;
|
|
466
|
+
CONNECTION_TIMEOUT = 3e4;
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
// core/index.ts
|
|
471
|
+
var core_exports = {};
|
|
472
|
+
__export(core_exports, {
|
|
473
|
+
CHARSET: () => CHARSET,
|
|
474
|
+
CurrencyUtils: () => CurrencyUtils,
|
|
475
|
+
DEFAULT_DERIVATION_PATH: () => DEFAULT_DERIVATION_PATH,
|
|
476
|
+
DEFAULT_TOKEN_DECIMALS: () => DEFAULT_TOKEN_DECIMALS,
|
|
477
|
+
Sphere: () => Sphere,
|
|
478
|
+
base58Decode: () => base58Decode,
|
|
479
|
+
base58Encode: () => base58Encode,
|
|
480
|
+
bytesToHex: () => bytesToHex2,
|
|
481
|
+
computeHash160: () => computeHash160,
|
|
482
|
+
convertBits: () => convertBits,
|
|
483
|
+
createAddress: () => createAddress,
|
|
484
|
+
createBech32: () => createBech32,
|
|
485
|
+
createKeyPair: () => createKeyPair,
|
|
486
|
+
createSphere: () => createSphere,
|
|
487
|
+
decodeBech32: () => decodeBech32,
|
|
488
|
+
decrypt: () => decrypt2,
|
|
489
|
+
decryptJson: () => decryptJson,
|
|
490
|
+
decryptMnemonic: () => decryptMnemonic,
|
|
491
|
+
decryptSimple: () => decryptSimple,
|
|
492
|
+
decryptWithSalt: () => decryptWithSalt,
|
|
493
|
+
deriveAddressInfo: () => deriveAddressInfo,
|
|
494
|
+
deriveChildKey: () => deriveChildKey,
|
|
495
|
+
deriveKeyAtPath: () => deriveKeyAtPath,
|
|
496
|
+
deserializeEncrypted: () => deserializeEncrypted,
|
|
497
|
+
doubleSha256: () => doubleSha256,
|
|
498
|
+
ec: () => ec,
|
|
499
|
+
encodeBech32: () => encodeBech32,
|
|
500
|
+
encrypt: () => encrypt2,
|
|
501
|
+
encryptMnemonic: () => encryptMnemonic,
|
|
502
|
+
encryptSimple: () => encryptSimple,
|
|
503
|
+
entropyToMnemonic: () => entropyToMnemonic2,
|
|
504
|
+
extractFromText: () => extractFromText,
|
|
505
|
+
findPattern: () => findPattern,
|
|
506
|
+
formatAmount: () => formatAmount,
|
|
507
|
+
generateAddressInfo: () => generateAddressInfo,
|
|
508
|
+
generateMasterKey: () => generateMasterKey,
|
|
509
|
+
generateMnemonic: () => generateMnemonic2,
|
|
510
|
+
generateRandomKey: () => generateRandomKey,
|
|
511
|
+
getAddressHrp: () => getAddressHrp,
|
|
512
|
+
getPublicKey: () => getPublicKey,
|
|
513
|
+
getSphere: () => getSphere,
|
|
514
|
+
hash160: () => hash160,
|
|
515
|
+
hash160ToBytes: () => hash160ToBytes,
|
|
516
|
+
hexToBytes: () => hexToBytes,
|
|
517
|
+
identityFromMnemonic: () => identityFromMnemonic,
|
|
518
|
+
identityFromMnemonicSync: () => identityFromMnemonicSync,
|
|
519
|
+
importSphere: () => importSphere,
|
|
520
|
+
initSphere: () => initSphere,
|
|
521
|
+
isEncryptedData: () => isEncryptedData,
|
|
522
|
+
isValidBech32: () => isValidBech32,
|
|
523
|
+
isValidPrivateKey: () => isValidPrivateKey,
|
|
524
|
+
loadSphere: () => loadSphere,
|
|
525
|
+
mnemonicToEntropy: () => mnemonicToEntropy2,
|
|
526
|
+
mnemonicToSeed: () => mnemonicToSeed2,
|
|
527
|
+
mnemonicToSeedSync: () => mnemonicToSeedSync2,
|
|
528
|
+
privateKeyToAddressInfo: () => privateKeyToAddressInfo,
|
|
529
|
+
publicKeyToAddress: () => publicKeyToAddress,
|
|
530
|
+
randomBytes: () => randomBytes,
|
|
531
|
+
randomHex: () => randomHex,
|
|
532
|
+
randomUUID: () => randomUUID,
|
|
533
|
+
ripemd160: () => ripemd160,
|
|
534
|
+
scanAddressesImpl: () => scanAddressesImpl,
|
|
535
|
+
serializeEncrypted: () => serializeEncrypted,
|
|
536
|
+
sha256: () => sha256,
|
|
537
|
+
sleep: () => sleep,
|
|
538
|
+
sphereExists: () => sphereExists,
|
|
539
|
+
toHumanReadable: () => toHumanReadable,
|
|
540
|
+
toSmallestUnit: () => toSmallestUnit,
|
|
541
|
+
validateMnemonic: () => validateMnemonic2
|
|
542
|
+
});
|
|
543
|
+
module.exports = __toCommonJS(core_exports);
|
|
544
|
+
|
|
545
|
+
// l1/index.ts
|
|
546
|
+
init_bech32();
|
|
547
|
+
init_addressToScriptHash();
|
|
548
|
+
|
|
549
|
+
// core/crypto.ts
|
|
550
|
+
var bip39 = __toESM(require("bip39"), 1);
|
|
551
|
+
var import_crypto_js2 = __toESM(require("crypto-js"), 1);
|
|
552
|
+
var import_elliptic = __toESM(require("elliptic"), 1);
|
|
553
|
+
init_bech32();
|
|
554
|
+
var ec = new import_elliptic.default.ec("secp256k1");
|
|
555
|
+
var CURVE_ORDER = BigInt(
|
|
556
|
+
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
|
|
557
|
+
);
|
|
558
|
+
var DEFAULT_DERIVATION_PATH = "m/44'/0'/0'";
|
|
559
|
+
function generateMnemonic2(strength = 128) {
|
|
560
|
+
return bip39.generateMnemonic(strength);
|
|
561
|
+
}
|
|
562
|
+
function validateMnemonic2(mnemonic) {
|
|
563
|
+
return bip39.validateMnemonic(mnemonic);
|
|
564
|
+
}
|
|
565
|
+
async function mnemonicToSeed2(mnemonic, passphrase = "") {
|
|
566
|
+
const seedBuffer = await bip39.mnemonicToSeed(mnemonic, passphrase);
|
|
567
|
+
return Buffer.from(seedBuffer).toString("hex");
|
|
568
|
+
}
|
|
569
|
+
function mnemonicToSeedSync2(mnemonic, passphrase = "") {
|
|
570
|
+
const seedBuffer = bip39.mnemonicToSeedSync(mnemonic, passphrase);
|
|
571
|
+
return Buffer.from(seedBuffer).toString("hex");
|
|
572
|
+
}
|
|
573
|
+
function mnemonicToEntropy2(mnemonic) {
|
|
574
|
+
return bip39.mnemonicToEntropy(mnemonic);
|
|
575
|
+
}
|
|
576
|
+
function entropyToMnemonic2(entropy) {
|
|
577
|
+
return bip39.entropyToMnemonic(entropy);
|
|
578
|
+
}
|
|
579
|
+
function generateMasterKey(seedHex) {
|
|
580
|
+
const I = import_crypto_js2.default.HmacSHA512(
|
|
581
|
+
import_crypto_js2.default.enc.Hex.parse(seedHex),
|
|
582
|
+
import_crypto_js2.default.enc.Utf8.parse("Bitcoin seed")
|
|
583
|
+
).toString();
|
|
584
|
+
const IL = I.substring(0, 64);
|
|
585
|
+
const IR = I.substring(64);
|
|
586
|
+
const masterKeyBigInt = BigInt("0x" + IL);
|
|
587
|
+
if (masterKeyBigInt === 0n || masterKeyBigInt >= CURVE_ORDER) {
|
|
588
|
+
throw new Error("Invalid master key generated");
|
|
589
|
+
}
|
|
590
|
+
return {
|
|
591
|
+
privateKey: IL,
|
|
592
|
+
chainCode: IR
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
function deriveChildKey(parentPrivKey, parentChainCode, index) {
|
|
596
|
+
const isHardened = index >= 2147483648;
|
|
597
|
+
let data;
|
|
598
|
+
if (isHardened) {
|
|
599
|
+
const indexHex = index.toString(16).padStart(8, "0");
|
|
600
|
+
data = "00" + parentPrivKey + indexHex;
|
|
601
|
+
} else {
|
|
602
|
+
const keyPair = ec.keyFromPrivate(parentPrivKey, "hex");
|
|
603
|
+
const compressedPubKey = keyPair.getPublic(true, "hex");
|
|
604
|
+
const indexHex = index.toString(16).padStart(8, "0");
|
|
605
|
+
data = compressedPubKey + indexHex;
|
|
606
|
+
}
|
|
607
|
+
const I = import_crypto_js2.default.HmacSHA512(
|
|
608
|
+
import_crypto_js2.default.enc.Hex.parse(data),
|
|
609
|
+
import_crypto_js2.default.enc.Hex.parse(parentChainCode)
|
|
610
|
+
).toString();
|
|
611
|
+
const IL = I.substring(0, 64);
|
|
612
|
+
const IR = I.substring(64);
|
|
613
|
+
const ilBigInt = BigInt("0x" + IL);
|
|
614
|
+
const parentKeyBigInt = BigInt("0x" + parentPrivKey);
|
|
615
|
+
if (ilBigInt >= CURVE_ORDER) {
|
|
616
|
+
throw new Error("Invalid key: IL >= curve order");
|
|
617
|
+
}
|
|
618
|
+
const childKeyBigInt = (ilBigInt + parentKeyBigInt) % CURVE_ORDER;
|
|
619
|
+
if (childKeyBigInt === 0n) {
|
|
620
|
+
throw new Error("Invalid key: child key is zero");
|
|
621
|
+
}
|
|
622
|
+
const childPrivKey = childKeyBigInt.toString(16).padStart(64, "0");
|
|
623
|
+
return {
|
|
624
|
+
privateKey: childPrivKey,
|
|
625
|
+
chainCode: IR
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
function deriveKeyAtPath(masterPrivKey, masterChainCode, path) {
|
|
629
|
+
const pathParts = path.replace("m/", "").split("/");
|
|
630
|
+
let currentKey = masterPrivKey;
|
|
631
|
+
let currentChainCode = masterChainCode;
|
|
632
|
+
for (const part of pathParts) {
|
|
633
|
+
const isHardened = part.endsWith("'") || part.endsWith("h");
|
|
634
|
+
const indexStr = part.replace(/['h]$/, "");
|
|
635
|
+
let index = parseInt(indexStr, 10);
|
|
636
|
+
if (isHardened) {
|
|
637
|
+
index += 2147483648;
|
|
638
|
+
}
|
|
639
|
+
const derived = deriveChildKey(currentKey, currentChainCode, index);
|
|
640
|
+
currentKey = derived.privateKey;
|
|
641
|
+
currentChainCode = derived.chainCode;
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
644
|
+
privateKey: currentKey,
|
|
645
|
+
chainCode: currentChainCode
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
function getPublicKey(privateKey, compressed = true) {
|
|
649
|
+
const keyPair = ec.keyFromPrivate(privateKey, "hex");
|
|
650
|
+
return keyPair.getPublic(compressed, "hex");
|
|
651
|
+
}
|
|
652
|
+
function createKeyPair(privateKey) {
|
|
653
|
+
return {
|
|
654
|
+
privateKey,
|
|
655
|
+
publicKey: getPublicKey(privateKey)
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
function sha256(data, inputEncoding = "hex") {
|
|
659
|
+
const parsed = inputEncoding === "hex" ? import_crypto_js2.default.enc.Hex.parse(data) : import_crypto_js2.default.enc.Utf8.parse(data);
|
|
660
|
+
return import_crypto_js2.default.SHA256(parsed).toString();
|
|
661
|
+
}
|
|
662
|
+
function ripemd160(data, inputEncoding = "hex") {
|
|
663
|
+
const parsed = inputEncoding === "hex" ? import_crypto_js2.default.enc.Hex.parse(data) : import_crypto_js2.default.enc.Utf8.parse(data);
|
|
664
|
+
return import_crypto_js2.default.RIPEMD160(parsed).toString();
|
|
665
|
+
}
|
|
666
|
+
function hash160(data) {
|
|
667
|
+
const sha = sha256(data, "hex");
|
|
668
|
+
return ripemd160(sha, "hex");
|
|
669
|
+
}
|
|
670
|
+
function doubleSha256(data, inputEncoding = "hex") {
|
|
671
|
+
const first = sha256(data, inputEncoding);
|
|
672
|
+
return sha256(first, "hex");
|
|
673
|
+
}
|
|
674
|
+
var computeHash160 = hash160;
|
|
675
|
+
function hash160ToBytes(hash160Hex) {
|
|
676
|
+
const matches = hash160Hex.match(/../g);
|
|
677
|
+
if (!matches) return new Uint8Array(0);
|
|
678
|
+
return Uint8Array.from(matches.map((x) => parseInt(x, 16)));
|
|
679
|
+
}
|
|
680
|
+
function publicKeyToAddress(publicKey, prefix = "alpha", witnessVersion = 0) {
|
|
681
|
+
const pubKeyHash = hash160(publicKey);
|
|
682
|
+
const programBytes = hash160ToBytes(pubKeyHash);
|
|
683
|
+
return encodeBech32(prefix, witnessVersion, programBytes);
|
|
684
|
+
}
|
|
685
|
+
function privateKeyToAddressInfo(privateKey, prefix = "alpha") {
|
|
686
|
+
const publicKey = getPublicKey(privateKey);
|
|
687
|
+
const address = publicKeyToAddress(publicKey, prefix);
|
|
688
|
+
return { address, publicKey };
|
|
689
|
+
}
|
|
690
|
+
function hexToBytes(hex) {
|
|
691
|
+
const matches = hex.match(/../g);
|
|
692
|
+
if (!matches) {
|
|
693
|
+
return new Uint8Array(0);
|
|
640
694
|
}
|
|
641
|
-
return
|
|
695
|
+
return Uint8Array.from(matches.map((x) => parseInt(x, 16)));
|
|
642
696
|
}
|
|
643
|
-
|
|
644
|
-
return
|
|
697
|
+
function bytesToHex2(bytes) {
|
|
698
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
645
699
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
700
|
+
function randomBytes(length) {
|
|
701
|
+
const words = import_crypto_js2.default.lib.WordArray.random(length);
|
|
702
|
+
return words.toString(import_crypto_js2.default.enc.Hex);
|
|
703
|
+
}
|
|
704
|
+
async function identityFromMnemonic(mnemonic, passphrase = "") {
|
|
705
|
+
if (!validateMnemonic2(mnemonic)) {
|
|
706
|
+
throw new Error("Invalid mnemonic phrase");
|
|
653
707
|
}
|
|
708
|
+
const seedHex = await mnemonicToSeed2(mnemonic, passphrase);
|
|
709
|
+
return generateMasterKey(seedHex);
|
|
654
710
|
}
|
|
655
|
-
function
|
|
656
|
-
if (
|
|
657
|
-
|
|
658
|
-
ws.close();
|
|
659
|
-
ws = null;
|
|
711
|
+
function identityFromMnemonicSync(mnemonic, passphrase = "") {
|
|
712
|
+
if (!validateMnemonic2(mnemonic)) {
|
|
713
|
+
throw new Error("Invalid mnemonic phrase");
|
|
660
714
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
715
|
+
const seedHex = mnemonicToSeedSync2(mnemonic, passphrase);
|
|
716
|
+
return generateMasterKey(seedHex);
|
|
717
|
+
}
|
|
718
|
+
function deriveAddressInfo(masterKey, basePath, index, isChange = false, prefix = "alpha") {
|
|
719
|
+
const chain = isChange ? 1 : 0;
|
|
720
|
+
const fullPath = `${basePath}/${chain}/${index}`;
|
|
721
|
+
const derived = deriveKeyAtPath(masterKey.privateKey, masterKey.chainCode, fullPath);
|
|
722
|
+
const publicKey = getPublicKey(derived.privateKey);
|
|
723
|
+
const address = publicKeyToAddress(publicKey, prefix);
|
|
724
|
+
return {
|
|
725
|
+
privateKey: derived.privateKey,
|
|
726
|
+
publicKey,
|
|
727
|
+
address,
|
|
728
|
+
path: fullPath,
|
|
729
|
+
index
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
function generateAddressInfo(privateKey, index, path, prefix = "alpha") {
|
|
733
|
+
const { address, publicKey } = privateKeyToAddressInfo(privateKey, prefix);
|
|
734
|
+
return {
|
|
735
|
+
privateKey,
|
|
736
|
+
publicKey,
|
|
737
|
+
address,
|
|
738
|
+
path,
|
|
739
|
+
index
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// l1/crypto.ts
|
|
744
|
+
var import_crypto_js3 = __toESM(require("crypto-js"), 1);
|
|
745
|
+
|
|
746
|
+
// l1/address.ts
|
|
747
|
+
var import_crypto_js4 = __toESM(require("crypto-js"), 1);
|
|
748
|
+
function generateAddressFromMasterKey(masterPrivateKey, index) {
|
|
749
|
+
const derivationPath = `m/44'/0'/${index}'`;
|
|
750
|
+
const hmacInput = import_crypto_js4.default.enc.Hex.parse(masterPrivateKey);
|
|
751
|
+
const hmacKey = import_crypto_js4.default.enc.Utf8.parse(derivationPath);
|
|
752
|
+
const hmacOutput = import_crypto_js4.default.HmacSHA512(hmacInput, hmacKey).toString();
|
|
753
|
+
const childPrivateKey = hmacOutput.substring(0, 64);
|
|
754
|
+
return generateAddressInfo(childPrivateKey, index, derivationPath);
|
|
673
755
|
}
|
|
674
756
|
|
|
757
|
+
// l1/index.ts
|
|
758
|
+
init_network();
|
|
759
|
+
|
|
675
760
|
// l1/tx.ts
|
|
761
|
+
init_network();
|
|
762
|
+
init_bech32();
|
|
676
763
|
var import_crypto_js5 = __toESM(require("crypto-js"), 1);
|
|
677
764
|
var import_elliptic2 = __toESM(require("elliptic"), 1);
|
|
678
765
|
|
|
679
766
|
// l1/vesting.ts
|
|
767
|
+
init_network();
|
|
680
768
|
var VESTING_THRESHOLD = 28e4;
|
|
681
769
|
var currentBlockHeight = null;
|
|
682
770
|
var VestingClassifier = class {
|
|
@@ -895,6 +983,24 @@ var VestingClassifier = class {
|
|
|
895
983
|
tx.objectStore(this.storeName).clear();
|
|
896
984
|
}
|
|
897
985
|
}
|
|
986
|
+
/**
|
|
987
|
+
* Destroy caches and delete the IndexedDB database entirely.
|
|
988
|
+
*/
|
|
989
|
+
async destroy() {
|
|
990
|
+
this.memoryCache.clear();
|
|
991
|
+
if (this.db) {
|
|
992
|
+
this.db.close();
|
|
993
|
+
this.db = null;
|
|
994
|
+
}
|
|
995
|
+
if (typeof indexedDB !== "undefined") {
|
|
996
|
+
await new Promise((resolve) => {
|
|
997
|
+
const req = indexedDB.deleteDatabase(this.dbName);
|
|
998
|
+
req.onsuccess = () => resolve();
|
|
999
|
+
req.onerror = () => resolve();
|
|
1000
|
+
req.onblocked = () => resolve();
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
898
1004
|
};
|
|
899
1005
|
var vestingClassifier = new VestingClassifier();
|
|
900
1006
|
|
|
@@ -3957,6 +4063,19 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
3957
4063
|
* Initialize module with dependencies
|
|
3958
4064
|
*/
|
|
3959
4065
|
initialize(deps) {
|
|
4066
|
+
this.unsubscribeTransfers?.();
|
|
4067
|
+
this.unsubscribeTransfers = null;
|
|
4068
|
+
this.unsubscribePaymentRequests?.();
|
|
4069
|
+
this.unsubscribePaymentRequests = null;
|
|
4070
|
+
this.unsubscribePaymentRequestResponses?.();
|
|
4071
|
+
this.unsubscribePaymentRequestResponses = null;
|
|
4072
|
+
this.tokens.clear();
|
|
4073
|
+
this.pendingTransfers.clear();
|
|
4074
|
+
this.tombstones = [];
|
|
4075
|
+
this.archivedTokens.clear();
|
|
4076
|
+
this.forkedTokens.clear();
|
|
4077
|
+
this.transactionHistory = [];
|
|
4078
|
+
this.nametag = null;
|
|
3960
4079
|
this.deps = deps;
|
|
3961
4080
|
this.priceProvider = deps.price ?? null;
|
|
3962
4081
|
if (this.l1) {
|
|
@@ -4126,7 +4245,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4126
4245
|
memo: request.memo
|
|
4127
4246
|
});
|
|
4128
4247
|
console.log(`[Payments] Split token sent successfully`);
|
|
4129
|
-
await this.removeToken(splitPlan.tokenToSplit.uiToken.id, recipientNametag);
|
|
4248
|
+
await this.removeToken(splitPlan.tokenToSplit.uiToken.id, recipientNametag, true);
|
|
4130
4249
|
result.txHash = "split-" + Date.now().toString(16);
|
|
4131
4250
|
this.log(`Split transfer completed`);
|
|
4132
4251
|
}
|
|
@@ -4152,7 +4271,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4152
4271
|
});
|
|
4153
4272
|
console.log(`[Payments] Direct token sent successfully`);
|
|
4154
4273
|
this.log(`Token ${token.id} transferred, txHash: ${result.txHash}`);
|
|
4155
|
-
await this.removeToken(token.id, recipientNametag);
|
|
4274
|
+
await this.removeToken(token.id, recipientNametag, true);
|
|
4156
4275
|
}
|
|
4157
4276
|
result.status = "delivered";
|
|
4158
4277
|
await this.save();
|
|
@@ -4301,7 +4420,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4301
4420
|
);
|
|
4302
4421
|
if (result.success) {
|
|
4303
4422
|
const recipientNametag = request.recipient.startsWith("@") ? request.recipient.slice(1) : void 0;
|
|
4304
|
-
await this.removeToken(tokenToSplit.id, recipientNametag);
|
|
4423
|
+
await this.removeToken(tokenToSplit.id, recipientNametag, true);
|
|
4305
4424
|
await this.addToHistory({
|
|
4306
4425
|
type: "SENT",
|
|
4307
4426
|
amount: request.amount,
|
|
@@ -4807,35 +4926,39 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4807
4926
|
const rawAssets = Array.from(assetsMap.values());
|
|
4808
4927
|
let priceMap = null;
|
|
4809
4928
|
if (this.priceProvider && rawAssets.length > 0) {
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
const
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
existing
|
|
4818
|
-
|
|
4819
|
-
|
|
4929
|
+
try {
|
|
4930
|
+
const registry = TokenRegistry.getInstance();
|
|
4931
|
+
const nameToCoins = /* @__PURE__ */ new Map();
|
|
4932
|
+
for (const asset of rawAssets) {
|
|
4933
|
+
const def = registry.getDefinition(asset.coinId);
|
|
4934
|
+
if (def?.name) {
|
|
4935
|
+
const existing = nameToCoins.get(def.name);
|
|
4936
|
+
if (existing) {
|
|
4937
|
+
existing.push(asset.coinId);
|
|
4938
|
+
} else {
|
|
4939
|
+
nameToCoins.set(def.name, [asset.coinId]);
|
|
4940
|
+
}
|
|
4820
4941
|
}
|
|
4821
4942
|
}
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
}
|
|
4943
|
+
if (nameToCoins.size > 0) {
|
|
4944
|
+
const tokenNames = Array.from(nameToCoins.keys());
|
|
4945
|
+
const prices = await this.priceProvider.getPrices(tokenNames);
|
|
4946
|
+
priceMap = /* @__PURE__ */ new Map();
|
|
4947
|
+
for (const [name, coinIds] of nameToCoins) {
|
|
4948
|
+
const price = prices.get(name);
|
|
4949
|
+
if (price) {
|
|
4950
|
+
for (const cid of coinIds) {
|
|
4951
|
+
priceMap.set(cid, {
|
|
4952
|
+
priceUsd: price.priceUsd,
|
|
4953
|
+
priceEur: price.priceEur,
|
|
4954
|
+
change24h: price.change24h
|
|
4955
|
+
});
|
|
4956
|
+
}
|
|
4836
4957
|
}
|
|
4837
4958
|
}
|
|
4838
4959
|
}
|
|
4960
|
+
} catch (error) {
|
|
4961
|
+
console.warn("[Payments] Failed to fetch prices, returning assets without price data:", error);
|
|
4839
4962
|
}
|
|
4840
4963
|
}
|
|
4841
4964
|
return rawAssets.map((raw) => {
|
|
@@ -5038,6 +5161,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5038
5161
|
updatedAt: Date.now(),
|
|
5039
5162
|
sdkData: typeof tokenJson === "string" ? tokenJson : JSON.stringify(tokenJson)
|
|
5040
5163
|
};
|
|
5164
|
+
const loadedTokenId = extractTokenIdFromSdkData(token.sdkData);
|
|
5165
|
+
const loadedStateHash = extractStateHashFromSdkData(token.sdkData);
|
|
5166
|
+
if (loadedTokenId && loadedStateHash && this.isStateTombstoned(loadedTokenId, loadedStateHash)) {
|
|
5167
|
+
this.log(`Skipping tombstoned token file ${tokenId} (${loadedTokenId.slice(0, 8)}...)`);
|
|
5168
|
+
continue;
|
|
5169
|
+
}
|
|
5041
5170
|
this.tokens.set(token.id, token);
|
|
5042
5171
|
this.log(`Loaded token from file: ${tokenId}`);
|
|
5043
5172
|
} catch (tokenError) {
|
|
@@ -5095,6 +5224,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5095
5224
|
this.log(`Warning: Could not create tombstone for token ${tokenId.slice(0, 8)}... (missing tokenId or stateHash)`);
|
|
5096
5225
|
}
|
|
5097
5226
|
this.tokens.delete(tokenId);
|
|
5227
|
+
await this.deleteTokenFiles(token);
|
|
5098
5228
|
if (!skipHistory && token.coinId && token.amount) {
|
|
5099
5229
|
await this.addToHistory({
|
|
5100
5230
|
type: "SENT",
|
|
@@ -5107,6 +5237,31 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5107
5237
|
}
|
|
5108
5238
|
await this.save();
|
|
5109
5239
|
}
|
|
5240
|
+
/**
|
|
5241
|
+
* Delete physical token file(s) from all storage providers.
|
|
5242
|
+
* Finds files by matching the SDK token ID prefix in the filename.
|
|
5243
|
+
*/
|
|
5244
|
+
async deleteTokenFiles(token) {
|
|
5245
|
+
const sdkTokenId = extractTokenIdFromSdkData(token.sdkData);
|
|
5246
|
+
if (!sdkTokenId) return;
|
|
5247
|
+
const tokenIdPrefix = sdkTokenId.slice(0, 16);
|
|
5248
|
+
const providers = this.getTokenStorageProviders();
|
|
5249
|
+
for (const [providerId, provider] of providers) {
|
|
5250
|
+
if (!provider.listTokenIds || !provider.deleteToken) continue;
|
|
5251
|
+
try {
|
|
5252
|
+
const allIds = await provider.listTokenIds();
|
|
5253
|
+
const matchingFiles = allIds.filter(
|
|
5254
|
+
(id) => id.startsWith(`token-${tokenIdPrefix}`)
|
|
5255
|
+
);
|
|
5256
|
+
for (const fileId of matchingFiles) {
|
|
5257
|
+
await provider.deleteToken(fileId);
|
|
5258
|
+
this.log(`Deleted token file ${fileId} from ${providerId}`);
|
|
5259
|
+
}
|
|
5260
|
+
} catch (error) {
|
|
5261
|
+
console.warn(`[Payments] Failed to delete token files from ${providerId}:`, error);
|
|
5262
|
+
}
|
|
5263
|
+
}
|
|
5264
|
+
}
|
|
5110
5265
|
// ===========================================================================
|
|
5111
5266
|
// Public API - Tombstones
|
|
5112
5267
|
// ===========================================================================
|
|
@@ -6611,6 +6766,20 @@ function decryptSimple(ciphertext, password) {
|
|
|
6611
6766
|
}
|
|
6612
6767
|
return result;
|
|
6613
6768
|
}
|
|
6769
|
+
function decryptWithSalt(ciphertext, password, salt) {
|
|
6770
|
+
try {
|
|
6771
|
+
const key = import_crypto_js6.default.PBKDF2(password, salt, {
|
|
6772
|
+
keySize: 256 / 32,
|
|
6773
|
+
iterations: 1e5,
|
|
6774
|
+
hasher: import_crypto_js6.default.algo.SHA256
|
|
6775
|
+
}).toString();
|
|
6776
|
+
const decrypted = import_crypto_js6.default.AES.decrypt(ciphertext, key);
|
|
6777
|
+
const result = decrypted.toString(import_crypto_js6.default.enc.Utf8);
|
|
6778
|
+
return result || null;
|
|
6779
|
+
} catch {
|
|
6780
|
+
return null;
|
|
6781
|
+
}
|
|
6782
|
+
}
|
|
6614
6783
|
function encryptMnemonic(mnemonic, password) {
|
|
6615
6784
|
return encryptSimple(mnemonic, password);
|
|
6616
6785
|
}
|
|
@@ -6638,6 +6807,81 @@ function generateRandomKey(bytes = 32) {
|
|
|
6638
6807
|
return import_crypto_js6.default.lib.WordArray.random(bytes).toString(import_crypto_js6.default.enc.Hex);
|
|
6639
6808
|
}
|
|
6640
6809
|
|
|
6810
|
+
// core/scan.ts
|
|
6811
|
+
async function scanAddressesImpl(deriveAddress, options = {}) {
|
|
6812
|
+
const maxAddresses = options.maxAddresses ?? 50;
|
|
6813
|
+
const gapLimit = options.gapLimit ?? 20;
|
|
6814
|
+
const includeChange = options.includeChange ?? true;
|
|
6815
|
+
const { onProgress, signal, resolveNametag } = options;
|
|
6816
|
+
const { connect: connect2, getBalance: getBalance2 } = await Promise.resolve().then(() => (init_network(), network_exports));
|
|
6817
|
+
await connect2();
|
|
6818
|
+
const foundAddresses = [];
|
|
6819
|
+
let totalBalance = 0;
|
|
6820
|
+
let totalScanned = 0;
|
|
6821
|
+
let nametagsFoundCount = 0;
|
|
6822
|
+
const chains = includeChange ? [false, true] : [false];
|
|
6823
|
+
const totalToScan = maxAddresses * chains.length;
|
|
6824
|
+
for (const isChange of chains) {
|
|
6825
|
+
let consecutiveEmpty = 0;
|
|
6826
|
+
for (let index = 0; index < maxAddresses; index++) {
|
|
6827
|
+
if (signal?.aborted) break;
|
|
6828
|
+
const addrInfo = deriveAddress(index, isChange);
|
|
6829
|
+
totalScanned++;
|
|
6830
|
+
onProgress?.({
|
|
6831
|
+
scanned: totalScanned,
|
|
6832
|
+
total: totalToScan,
|
|
6833
|
+
currentAddress: addrInfo.address,
|
|
6834
|
+
foundCount: foundAddresses.length,
|
|
6835
|
+
currentGap: consecutiveEmpty,
|
|
6836
|
+
nametagsFoundCount
|
|
6837
|
+
});
|
|
6838
|
+
try {
|
|
6839
|
+
const balance = await getBalance2(addrInfo.address);
|
|
6840
|
+
if (balance > 0) {
|
|
6841
|
+
let nametag;
|
|
6842
|
+
if (resolveNametag) {
|
|
6843
|
+
try {
|
|
6844
|
+
const tag = await resolveNametag(addrInfo.address);
|
|
6845
|
+
if (tag) {
|
|
6846
|
+
nametag = tag;
|
|
6847
|
+
nametagsFoundCount++;
|
|
6848
|
+
}
|
|
6849
|
+
} catch {
|
|
6850
|
+
}
|
|
6851
|
+
}
|
|
6852
|
+
foundAddresses.push({
|
|
6853
|
+
index,
|
|
6854
|
+
address: addrInfo.address,
|
|
6855
|
+
path: addrInfo.path,
|
|
6856
|
+
balance,
|
|
6857
|
+
isChange,
|
|
6858
|
+
nametag
|
|
6859
|
+
});
|
|
6860
|
+
totalBalance += balance;
|
|
6861
|
+
consecutiveEmpty = 0;
|
|
6862
|
+
} else {
|
|
6863
|
+
consecutiveEmpty++;
|
|
6864
|
+
}
|
|
6865
|
+
} catch (err) {
|
|
6866
|
+
console.warn(`[scanAddresses] Error checking ${addrInfo.address}:`, err);
|
|
6867
|
+
consecutiveEmpty++;
|
|
6868
|
+
}
|
|
6869
|
+
if (consecutiveEmpty >= gapLimit) {
|
|
6870
|
+
break;
|
|
6871
|
+
}
|
|
6872
|
+
if (totalScanned % 5 === 0) {
|
|
6873
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
6874
|
+
}
|
|
6875
|
+
}
|
|
6876
|
+
if (signal?.aborted) break;
|
|
6877
|
+
}
|
|
6878
|
+
return {
|
|
6879
|
+
addresses: foundAddresses,
|
|
6880
|
+
totalBalance,
|
|
6881
|
+
scannedCount: totalScanned
|
|
6882
|
+
};
|
|
6883
|
+
}
|
|
6884
|
+
|
|
6641
6885
|
// serialization/wallet-text.ts
|
|
6642
6886
|
var import_crypto_js7 = __toESM(require("crypto-js"), 1);
|
|
6643
6887
|
|
|
@@ -6894,10 +7138,11 @@ function parseWalletText(content) {
|
|
|
6894
7138
|
);
|
|
6895
7139
|
const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
|
|
6896
7140
|
const isBIP32 = content.includes("WALLET TYPE: BIP32 hierarchical deterministic wallet") || content.includes("WALLET TYPE: Alpha descriptor wallet") || !!chainCode;
|
|
7141
|
+
const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? "84'/1'/0'" : void 0);
|
|
6897
7142
|
const data = {
|
|
6898
7143
|
masterKey,
|
|
6899
7144
|
chainCode: chainCode ?? void 0,
|
|
6900
|
-
descriptorPath:
|
|
7145
|
+
descriptorPath: effectiveDescriptorPath,
|
|
6901
7146
|
derivationMode: isBIP32 ? "bip32" : "wif_hmac"
|
|
6902
7147
|
};
|
|
6903
7148
|
return {
|
|
@@ -6940,10 +7185,11 @@ function parseAndDecryptWalletText(content, password) {
|
|
|
6940
7185
|
);
|
|
6941
7186
|
const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
|
|
6942
7187
|
const isBIP32 = content.includes("WALLET TYPE: BIP32 hierarchical deterministic wallet") || content.includes("WALLET TYPE: Alpha descriptor wallet") || !!chainCode;
|
|
7188
|
+
const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? "84'/1'/0'" : void 0);
|
|
6943
7189
|
const data = {
|
|
6944
7190
|
masterKey,
|
|
6945
7191
|
chainCode: chainCode ?? void 0,
|
|
6946
|
-
descriptorPath:
|
|
7192
|
+
descriptorPath: effectiveDescriptorPath,
|
|
6947
7193
|
derivationMode: isBIP32 ? "bip32" : "wif_hmac"
|
|
6948
7194
|
};
|
|
6949
7195
|
return {
|
|
@@ -7481,10 +7727,10 @@ var Sphere = class _Sphere {
|
|
|
7481
7727
|
sphere._initialized = true;
|
|
7482
7728
|
_Sphere.instance = sphere;
|
|
7483
7729
|
await sphere.ensureAddressTracked(0);
|
|
7484
|
-
await sphere.syncIdentityWithTransport();
|
|
7485
7730
|
if (options.nametag) {
|
|
7486
7731
|
await sphere.registerNametag(options.nametag);
|
|
7487
7732
|
} else {
|
|
7733
|
+
await sphere.syncIdentityWithTransport();
|
|
7488
7734
|
await sphere.recoverNametagFromTransport();
|
|
7489
7735
|
}
|
|
7490
7736
|
return sphere;
|
|
@@ -7533,6 +7779,9 @@ var Sphere = class _Sphere {
|
|
|
7533
7779
|
throw new Error("Either mnemonic or masterKey is required");
|
|
7534
7780
|
}
|
|
7535
7781
|
await _Sphere.clear({ storage: options.storage, tokenStorage: options.tokenStorage });
|
|
7782
|
+
if (!options.storage.isConnected()) {
|
|
7783
|
+
await options.storage.connect();
|
|
7784
|
+
}
|
|
7536
7785
|
const sphere = new _Sphere(
|
|
7537
7786
|
options.storage,
|
|
7538
7787
|
options.transport,
|
|
@@ -7612,6 +7861,7 @@ var Sphere = class _Sphere {
|
|
|
7612
7861
|
if (tokenStorage?.clear) {
|
|
7613
7862
|
await tokenStorage.clear();
|
|
7614
7863
|
}
|
|
7864
|
+
await vestingClassifier.destroy();
|
|
7615
7865
|
if (_Sphere.instance) {
|
|
7616
7866
|
await _Sphere.instance.destroy();
|
|
7617
7867
|
}
|
|
@@ -7794,7 +8044,7 @@ var Sphere = class _Sphere {
|
|
|
7794
8044
|
return {
|
|
7795
8045
|
source: this._source,
|
|
7796
8046
|
hasMnemonic: this._mnemonic !== null,
|
|
7797
|
-
hasChainCode: this._masterKey?.chainCode
|
|
8047
|
+
hasChainCode: !!this._masterKey?.chainCode,
|
|
7798
8048
|
derivationMode: this._derivationMode,
|
|
7799
8049
|
basePath: this._basePath,
|
|
7800
8050
|
address0
|
|
@@ -7847,7 +8097,7 @@ var Sphere = class _Sphere {
|
|
|
7847
8097
|
let chainCode;
|
|
7848
8098
|
if (this._masterKey) {
|
|
7849
8099
|
masterPrivateKey = this._masterKey.privateKey;
|
|
7850
|
-
chainCode = this._masterKey.chainCode;
|
|
8100
|
+
chainCode = this._masterKey.chainCode || void 0;
|
|
7851
8101
|
}
|
|
7852
8102
|
let mnemonic;
|
|
7853
8103
|
let encrypted = false;
|
|
@@ -7924,7 +8174,7 @@ var Sphere = class _Sphere {
|
|
|
7924
8174
|
}
|
|
7925
8175
|
}
|
|
7926
8176
|
const masterPrivateKey = this._masterKey?.privateKey || "";
|
|
7927
|
-
const chainCode = this._masterKey?.chainCode;
|
|
8177
|
+
const chainCode = this._masterKey?.chainCode || void 0;
|
|
7928
8178
|
const isBIP32 = this._derivationMode === "bip32";
|
|
7929
8179
|
const descriptorPath = this._basePath.replace(/^m\//, "");
|
|
7930
8180
|
if (options.password) {
|
|
@@ -8132,19 +8382,84 @@ var Sphere = class _Sphere {
|
|
|
8132
8382
|
}
|
|
8133
8383
|
if (fileType === "json") {
|
|
8134
8384
|
const content = typeof fileContent === "string" ? fileContent : new TextDecoder().decode(fileContent);
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8385
|
+
let parsed;
|
|
8386
|
+
try {
|
|
8387
|
+
parsed = JSON.parse(content);
|
|
8388
|
+
} catch {
|
|
8389
|
+
return { success: false, error: "Invalid JSON file" };
|
|
8390
|
+
}
|
|
8391
|
+
if (parsed.type === "sphere-wallet") {
|
|
8392
|
+
const result = await _Sphere.importFromJSON({
|
|
8393
|
+
jsonContent: content,
|
|
8394
|
+
password,
|
|
8395
|
+
storage: options.storage,
|
|
8396
|
+
transport: options.transport,
|
|
8397
|
+
oracle: options.oracle,
|
|
8398
|
+
tokenStorage: options.tokenStorage
|
|
8399
|
+
});
|
|
8400
|
+
if (result.success) {
|
|
8401
|
+
const sphere2 = _Sphere.getInstance();
|
|
8402
|
+
return { success: true, sphere: sphere2, mnemonic: result.mnemonic };
|
|
8403
|
+
}
|
|
8404
|
+
if (!password && result.error?.includes("Password required")) {
|
|
8405
|
+
return { success: false, needsPassword: true, error: result.error };
|
|
8406
|
+
}
|
|
8407
|
+
return { success: false, error: result.error };
|
|
8408
|
+
}
|
|
8409
|
+
let masterKey;
|
|
8410
|
+
let mnemonic;
|
|
8411
|
+
if (parsed.encrypted && typeof parsed.encrypted === "object") {
|
|
8412
|
+
if (!password) {
|
|
8413
|
+
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
8414
|
+
}
|
|
8415
|
+
const enc = parsed.encrypted;
|
|
8416
|
+
if (!enc.salt || !enc.masterPrivateKey) {
|
|
8417
|
+
return { success: false, error: "Invalid encrypted wallet format" };
|
|
8418
|
+
}
|
|
8419
|
+
const decryptedKey = decryptWithSalt(enc.masterPrivateKey, password, enc.salt);
|
|
8420
|
+
if (!decryptedKey) {
|
|
8421
|
+
return { success: false, error: "Failed to decrypt - incorrect password?" };
|
|
8422
|
+
}
|
|
8423
|
+
masterKey = decryptedKey;
|
|
8424
|
+
if (enc.mnemonic) {
|
|
8425
|
+
mnemonic = decryptWithSalt(enc.mnemonic, password, enc.salt) ?? void 0;
|
|
8426
|
+
}
|
|
8427
|
+
} else {
|
|
8428
|
+
masterKey = parsed.masterPrivateKey;
|
|
8429
|
+
mnemonic = parsed.mnemonic;
|
|
8430
|
+
}
|
|
8431
|
+
if (!masterKey) {
|
|
8432
|
+
return { success: false, error: "No master key found in wallet JSON" };
|
|
8433
|
+
}
|
|
8434
|
+
const chainCode = parsed.chainCode;
|
|
8435
|
+
const descriptorPath = parsed.descriptorPath;
|
|
8436
|
+
const derivationMode = parsed.derivationMode;
|
|
8437
|
+
const isBIP32 = derivationMode === "bip32" || !!chainCode;
|
|
8438
|
+
const basePath = descriptorPath ? `m/${descriptorPath}` : isBIP32 ? "m/84'/1'/0'" : DEFAULT_BASE_PATH;
|
|
8439
|
+
if (mnemonic) {
|
|
8440
|
+
const sphere2 = await _Sphere.import({
|
|
8441
|
+
mnemonic,
|
|
8442
|
+
basePath,
|
|
8443
|
+
storage: options.storage,
|
|
8444
|
+
transport: options.transport,
|
|
8445
|
+
oracle: options.oracle,
|
|
8446
|
+
tokenStorage: options.tokenStorage,
|
|
8447
|
+
nametag: options.nametag
|
|
8448
|
+
});
|
|
8449
|
+
return { success: true, sphere: sphere2, mnemonic };
|
|
8450
|
+
}
|
|
8451
|
+
const sphere = await _Sphere.import({
|
|
8452
|
+
masterKey,
|
|
8453
|
+
chainCode,
|
|
8454
|
+
basePath,
|
|
8455
|
+
derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac"),
|
|
8138
8456
|
storage: options.storage,
|
|
8139
8457
|
transport: options.transport,
|
|
8140
8458
|
oracle: options.oracle,
|
|
8141
|
-
tokenStorage: options.tokenStorage
|
|
8459
|
+
tokenStorage: options.tokenStorage,
|
|
8460
|
+
nametag: options.nametag
|
|
8142
8461
|
});
|
|
8143
|
-
|
|
8144
|
-
const sphere = _Sphere.getInstance();
|
|
8145
|
-
return { success: true, sphere, mnemonic: result.mnemonic };
|
|
8146
|
-
}
|
|
8147
|
-
return result;
|
|
8462
|
+
return { success: true, sphere };
|
|
8148
8463
|
}
|
|
8149
8464
|
return { success: false, error: "Unsupported file type" };
|
|
8150
8465
|
}
|
|
@@ -8338,7 +8653,7 @@ var Sphere = class _Sphere {
|
|
|
8338
8653
|
* await sphere.switchToAddress(0);
|
|
8339
8654
|
* ```
|
|
8340
8655
|
*/
|
|
8341
|
-
async switchToAddress(index) {
|
|
8656
|
+
async switchToAddress(index, options) {
|
|
8342
8657
|
this.ensureReady();
|
|
8343
8658
|
if (!this._masterKey) {
|
|
8344
8659
|
throw new Error("HD derivation requires master key with chain code. Cannot switch addresses.");
|
|
@@ -8346,11 +8661,27 @@ var Sphere = class _Sphere {
|
|
|
8346
8661
|
if (index < 0) {
|
|
8347
8662
|
throw new Error("Address index must be non-negative");
|
|
8348
8663
|
}
|
|
8664
|
+
const newNametag = options?.nametag?.startsWith("@") ? options.nametag.slice(1) : options?.nametag;
|
|
8665
|
+
if (newNametag && !this.validateNametag(newNametag)) {
|
|
8666
|
+
throw new Error("Invalid nametag format. Use alphanumeric characters, 3-20 chars.");
|
|
8667
|
+
}
|
|
8349
8668
|
const addressInfo = this.deriveAddress(index, false);
|
|
8350
8669
|
const ipnsHash = sha256(addressInfo.publicKey, "hex").slice(0, 40);
|
|
8351
8670
|
const predicateAddress = await deriveL3PredicateAddress(addressInfo.privateKey);
|
|
8352
8671
|
await this.ensureAddressTracked(index);
|
|
8353
8672
|
const addressId = getAddressId(predicateAddress);
|
|
8673
|
+
if (newNametag) {
|
|
8674
|
+
const existing = await this._transport.resolveNametag?.(newNametag);
|
|
8675
|
+
if (existing) {
|
|
8676
|
+
throw new Error(`Nametag @${newNametag} is already taken`);
|
|
8677
|
+
}
|
|
8678
|
+
let nametags = this._addressNametags.get(addressId);
|
|
8679
|
+
if (!nametags) {
|
|
8680
|
+
nametags = /* @__PURE__ */ new Map();
|
|
8681
|
+
this._addressNametags.set(addressId, nametags);
|
|
8682
|
+
}
|
|
8683
|
+
nametags.set(0, newNametag);
|
|
8684
|
+
}
|
|
8354
8685
|
const nametag = this._addressNametags.get(addressId)?.get(0);
|
|
8355
8686
|
this._identity = {
|
|
8356
8687
|
privateKey: addressInfo.privateKey,
|
|
@@ -8364,12 +8695,47 @@ var Sphere = class _Sphere {
|
|
|
8364
8695
|
await this._updateCachedProxyAddress();
|
|
8365
8696
|
await this._storage.set(STORAGE_KEYS_GLOBAL.CURRENT_ADDRESS_INDEX, index.toString());
|
|
8366
8697
|
this._storage.setIdentity(this._identity);
|
|
8367
|
-
this._transport.setIdentity(this._identity);
|
|
8698
|
+
await this._transport.setIdentity(this._identity);
|
|
8368
8699
|
for (const provider of this._tokenStorageProviders.values()) {
|
|
8369
8700
|
provider.setIdentity(this._identity);
|
|
8701
|
+
await provider.initialize();
|
|
8370
8702
|
}
|
|
8371
8703
|
await this.reinitializeModulesForNewAddress();
|
|
8372
|
-
|
|
8704
|
+
if (this._identity.nametag) {
|
|
8705
|
+
await this.syncIdentityWithTransport();
|
|
8706
|
+
}
|
|
8707
|
+
if (newNametag) {
|
|
8708
|
+
await this.persistAddressNametags();
|
|
8709
|
+
if (!this._payments.hasNametag()) {
|
|
8710
|
+
console.log(`[Sphere] Minting nametag token for @${newNametag}...`);
|
|
8711
|
+
try {
|
|
8712
|
+
const result = await this.mintNametag(newNametag);
|
|
8713
|
+
if (result.success) {
|
|
8714
|
+
console.log(`[Sphere] Nametag token minted successfully`);
|
|
8715
|
+
} else {
|
|
8716
|
+
console.warn(`[Sphere] Could not mint nametag token: ${result.error}`);
|
|
8717
|
+
}
|
|
8718
|
+
} catch (err) {
|
|
8719
|
+
console.warn(`[Sphere] Nametag token mint failed:`, err);
|
|
8720
|
+
}
|
|
8721
|
+
}
|
|
8722
|
+
this.emitEvent("nametag:registered", {
|
|
8723
|
+
nametag: newNametag,
|
|
8724
|
+
addressIndex: index
|
|
8725
|
+
});
|
|
8726
|
+
} else if (this._identity.nametag && !this._payments.hasNametag()) {
|
|
8727
|
+
console.log(`[Sphere] Nametag @${this._identity.nametag} has no token after switch, minting...`);
|
|
8728
|
+
try {
|
|
8729
|
+
const result = await this.mintNametag(this._identity.nametag);
|
|
8730
|
+
if (result.success) {
|
|
8731
|
+
console.log(`[Sphere] Nametag token minted successfully after switch`);
|
|
8732
|
+
} else {
|
|
8733
|
+
console.warn(`[Sphere] Could not mint nametag token after switch: ${result.error}`);
|
|
8734
|
+
}
|
|
8735
|
+
} catch (err) {
|
|
8736
|
+
console.warn(`[Sphere] Nametag token mint failed after switch:`, err);
|
|
8737
|
+
}
|
|
8738
|
+
}
|
|
8373
8739
|
this.emitEvent("identity:changed", {
|
|
8374
8740
|
l1Address: this._identity.l1Address,
|
|
8375
8741
|
directAddress: this._identity.directAddress,
|
|
@@ -8391,7 +8757,7 @@ var Sphere = class _Sphere {
|
|
|
8391
8757
|
transport: this._transport,
|
|
8392
8758
|
oracle: this._oracle,
|
|
8393
8759
|
emitEvent,
|
|
8394
|
-
chainCode: this._masterKey?.chainCode,
|
|
8760
|
+
chainCode: this._masterKey?.chainCode || void 0,
|
|
8395
8761
|
price: this._priceProvider ?? void 0
|
|
8396
8762
|
});
|
|
8397
8763
|
this._communications.initialize({
|
|
@@ -8436,6 +8802,9 @@ var Sphere = class _Sphere {
|
|
|
8436
8802
|
if (!this._masterKey) {
|
|
8437
8803
|
throw new Error("HD derivation requires master key with chain code");
|
|
8438
8804
|
}
|
|
8805
|
+
if (this._derivationMode === "wif_hmac") {
|
|
8806
|
+
return generateAddressFromMasterKey(this._masterKey.privateKey, index);
|
|
8807
|
+
}
|
|
8439
8808
|
const info = deriveAddressInfo(
|
|
8440
8809
|
this._masterKey,
|
|
8441
8810
|
this._basePath,
|
|
@@ -8507,6 +8876,66 @@ var Sphere = class _Sphere {
|
|
|
8507
8876
|
}
|
|
8508
8877
|
return addresses;
|
|
8509
8878
|
}
|
|
8879
|
+
/**
|
|
8880
|
+
* Scan blockchain addresses to discover used addresses with balances.
|
|
8881
|
+
* Derives addresses sequentially and checks L1 balance via Fulcrum.
|
|
8882
|
+
* Uses gap limit to stop after N consecutive empty addresses.
|
|
8883
|
+
*
|
|
8884
|
+
* @param options - Scanning options
|
|
8885
|
+
* @returns Scan results with found addresses and total balance
|
|
8886
|
+
*
|
|
8887
|
+
* @example
|
|
8888
|
+
* ```ts
|
|
8889
|
+
* const result = await sphere.scanAddresses({
|
|
8890
|
+
* maxAddresses: 100,
|
|
8891
|
+
* gapLimit: 20,
|
|
8892
|
+
* onProgress: (p) => console.log(`Scanned ${p.scanned}/${p.total}, found ${p.foundCount}`),
|
|
8893
|
+
* });
|
|
8894
|
+
* console.log(`Found ${result.addresses.length} addresses, total: ${result.totalBalance} ALPHA`);
|
|
8895
|
+
* ```
|
|
8896
|
+
*/
|
|
8897
|
+
async scanAddresses(options = {}) {
|
|
8898
|
+
this.ensureReady();
|
|
8899
|
+
if (!this._masterKey) {
|
|
8900
|
+
throw new Error("Address scanning requires HD master key");
|
|
8901
|
+
}
|
|
8902
|
+
const resolveNametag = options.resolveNametag ?? (this._transport.resolveAddressInfo ? async (l1Address) => {
|
|
8903
|
+
try {
|
|
8904
|
+
const info = await this._transport.resolveAddressInfo(l1Address);
|
|
8905
|
+
return info?.nametag ?? null;
|
|
8906
|
+
} catch {
|
|
8907
|
+
return null;
|
|
8908
|
+
}
|
|
8909
|
+
} : void 0);
|
|
8910
|
+
return scanAddressesImpl(
|
|
8911
|
+
(index, isChange) => this._deriveAddressInternal(index, isChange),
|
|
8912
|
+
{ ...options, resolveNametag }
|
|
8913
|
+
);
|
|
8914
|
+
}
|
|
8915
|
+
/**
|
|
8916
|
+
* Bulk-track scanned addresses with visibility and nametag data.
|
|
8917
|
+
* Selected addresses get `hidden: false`, unselected get `hidden: true`.
|
|
8918
|
+
* Performs only 2 storage writes total (tracked addresses + nametags).
|
|
8919
|
+
*/
|
|
8920
|
+
async trackScannedAddresses(entries) {
|
|
8921
|
+
this.ensureReady();
|
|
8922
|
+
for (const { index, hidden, nametag } of entries) {
|
|
8923
|
+
const tracked = await this.ensureAddressTracked(index);
|
|
8924
|
+
if (nametag) {
|
|
8925
|
+
let nametags = this._addressNametags.get(tracked.addressId);
|
|
8926
|
+
if (!nametags) {
|
|
8927
|
+
nametags = /* @__PURE__ */ new Map();
|
|
8928
|
+
this._addressNametags.set(tracked.addressId, nametags);
|
|
8929
|
+
}
|
|
8930
|
+
if (!nametags.has(0)) nametags.set(0, nametag);
|
|
8931
|
+
}
|
|
8932
|
+
if (tracked.hidden !== hidden) {
|
|
8933
|
+
tracked.hidden = hidden;
|
|
8934
|
+
}
|
|
8935
|
+
}
|
|
8936
|
+
await this.persistTrackedAddresses();
|
|
8937
|
+
await this.persistAddressNametags();
|
|
8938
|
+
}
|
|
8510
8939
|
// ===========================================================================
|
|
8511
8940
|
// Public Methods - Status
|
|
8512
8941
|
// ===========================================================================
|
|
@@ -9088,8 +9517,8 @@ var Sphere = class _Sphere {
|
|
|
9088
9517
|
};
|
|
9089
9518
|
this._masterKey = masterKey;
|
|
9090
9519
|
}
|
|
9091
|
-
async initializeIdentityFromMasterKey(masterKey, chainCode,
|
|
9092
|
-
const basePath =
|
|
9520
|
+
async initializeIdentityFromMasterKey(masterKey, chainCode, _derivationPath) {
|
|
9521
|
+
const basePath = this._basePath;
|
|
9093
9522
|
const fullPath = `${basePath}/0/0`;
|
|
9094
9523
|
let privateKey;
|
|
9095
9524
|
if (chainCode) {
|
|
@@ -9100,8 +9529,12 @@ var Sphere = class _Sphere {
|
|
|
9100
9529
|
chainCode
|
|
9101
9530
|
};
|
|
9102
9531
|
} else {
|
|
9103
|
-
|
|
9104
|
-
|
|
9532
|
+
const addr0 = generateAddressFromMasterKey(masterKey, 0);
|
|
9533
|
+
privateKey = addr0.privateKey;
|
|
9534
|
+
this._masterKey = {
|
|
9535
|
+
privateKey: masterKey,
|
|
9536
|
+
chainCode: ""
|
|
9537
|
+
};
|
|
9105
9538
|
}
|
|
9106
9539
|
const publicKey = getPublicKey(privateKey);
|
|
9107
9540
|
const address = publicKeyToAddress(publicKey, "alpha");
|
|
@@ -9120,7 +9553,7 @@ var Sphere = class _Sphere {
|
|
|
9120
9553
|
// ===========================================================================
|
|
9121
9554
|
async initializeProviders() {
|
|
9122
9555
|
this._storage.setIdentity(this._identity);
|
|
9123
|
-
this._transport.setIdentity(this._identity);
|
|
9556
|
+
await this._transport.setIdentity(this._identity);
|
|
9124
9557
|
for (const provider of this._tokenStorageProviders.values()) {
|
|
9125
9558
|
provider.setIdentity(this._identity);
|
|
9126
9559
|
}
|
|
@@ -9141,7 +9574,7 @@ var Sphere = class _Sphere {
|
|
|
9141
9574
|
oracle: this._oracle,
|
|
9142
9575
|
emitEvent,
|
|
9143
9576
|
// Pass chain code for L1 HD derivation
|
|
9144
|
-
chainCode: this._masterKey?.chainCode,
|
|
9577
|
+
chainCode: this._masterKey?.chainCode || void 0,
|
|
9145
9578
|
price: this._priceProvider ?? void 0
|
|
9146
9579
|
});
|
|
9147
9580
|
this._communications.initialize({
|
|
@@ -9228,6 +9661,9 @@ var CurrencyUtils = {
|
|
|
9228
9661
|
toHumanReadable,
|
|
9229
9662
|
format: formatAmount
|
|
9230
9663
|
};
|
|
9664
|
+
|
|
9665
|
+
// core/index.ts
|
|
9666
|
+
init_bech32();
|
|
9231
9667
|
// Annotate the CommonJS export names for ESM import in node:
|
|
9232
9668
|
0 && (module.exports = {
|
|
9233
9669
|
CHARSET,
|
|
@@ -9249,6 +9685,7 @@ var CurrencyUtils = {
|
|
|
9249
9685
|
decryptJson,
|
|
9250
9686
|
decryptMnemonic,
|
|
9251
9687
|
decryptSimple,
|
|
9688
|
+
decryptWithSalt,
|
|
9252
9689
|
deriveAddressInfo,
|
|
9253
9690
|
deriveChildKey,
|
|
9254
9691
|
deriveKeyAtPath,
|
|
@@ -9290,6 +9727,7 @@ var CurrencyUtils = {
|
|
|
9290
9727
|
randomHex,
|
|
9291
9728
|
randomUUID,
|
|
9292
9729
|
ripemd160,
|
|
9730
|
+
scanAddressesImpl,
|
|
9293
9731
|
serializeEncrypted,
|
|
9294
9732
|
sha256,
|
|
9295
9733
|
sleep,
|