@unicitylabs/sphere-sdk 0.2.1 → 0.2.3
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/README.md +22 -69
- package/dist/core/index.cjs +944 -465
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +127 -6
- package/dist/core/index.d.ts +127 -6
- package/dist/core/index.js +833 -351
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +115 -19
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +115 -19
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/browser/ipfs.cjs +3 -1
- package/dist/impl/browser/ipfs.cjs.map +1 -1
- package/dist/impl/browser/ipfs.js +3 -1
- package/dist/impl/browser/ipfs.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +85 -17
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +22 -0
- package/dist/impl/nodejs/index.d.ts +22 -0
- package/dist/impl/nodejs/index.js +85 -17
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +1083 -644
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +118 -6
- package/dist/index.d.ts +118 -6
- package/dist/index.js +993 -554
- 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,6 +374,31 @@ async function getBalance(address) {
|
|
|
631
374
|
async function broadcast(rawHex) {
|
|
632
375
|
return await rpc("blockchain.transaction.broadcast", [rawHex]);
|
|
633
376
|
}
|
|
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
|
+
}
|
|
634
402
|
async function getTransactionHistory(address) {
|
|
635
403
|
const scriptHash = addressToScriptHash(address);
|
|
636
404
|
const result = await rpc("blockchain.scripthash.get_history", [scriptHash]);
|
|
@@ -638,45 +406,365 @@ async function getTransactionHistory(address) {
|
|
|
638
406
|
console.warn("get_history returned non-array:", result);
|
|
639
407
|
return [];
|
|
640
408
|
}
|
|
641
|
-
return result;
|
|
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);
|
|
694
|
+
}
|
|
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
|
|
|
@@ -1447,7 +1553,7 @@ var L1PaymentsModule = class {
|
|
|
1447
1553
|
_transport;
|
|
1448
1554
|
constructor(config) {
|
|
1449
1555
|
this._config = {
|
|
1450
|
-
electrumUrl: config?.electrumUrl ?? "wss://fulcrum.
|
|
1556
|
+
electrumUrl: config?.electrumUrl ?? "wss://fulcrum.unicity.network:50004",
|
|
1451
1557
|
network: config?.network ?? "mainnet",
|
|
1452
1558
|
defaultFeeRate: config?.defaultFeeRate ?? 10,
|
|
1453
1559
|
enableVesting: config?.enableVesting ?? true
|
|
@@ -1479,10 +1585,17 @@ var L1PaymentsModule = class {
|
|
|
1479
1585
|
});
|
|
1480
1586
|
}
|
|
1481
1587
|
}
|
|
1482
|
-
|
|
1588
|
+
this._initialized = true;
|
|
1589
|
+
}
|
|
1590
|
+
/**
|
|
1591
|
+
* Ensure the Fulcrum WebSocket is connected. Called lazily before any
|
|
1592
|
+
* operation that needs the network. If the singleton is already connected
|
|
1593
|
+
* (e.g. by the address scanner), this is a no-op.
|
|
1594
|
+
*/
|
|
1595
|
+
async ensureConnected() {
|
|
1596
|
+
if (!isWebSocketConnected() && this._config.electrumUrl) {
|
|
1483
1597
|
await connect(this._config.electrumUrl);
|
|
1484
1598
|
}
|
|
1485
|
-
this._initialized = true;
|
|
1486
1599
|
}
|
|
1487
1600
|
destroy() {
|
|
1488
1601
|
if (isWebSocketConnected()) {
|
|
@@ -1540,6 +1653,7 @@ var L1PaymentsModule = class {
|
|
|
1540
1653
|
}
|
|
1541
1654
|
async send(request) {
|
|
1542
1655
|
this.ensureInitialized();
|
|
1656
|
+
await this.ensureConnected();
|
|
1543
1657
|
if (!this._wallet || !this._identity) {
|
|
1544
1658
|
return { success: false, error: "No wallet available" };
|
|
1545
1659
|
}
|
|
@@ -1574,6 +1688,7 @@ var L1PaymentsModule = class {
|
|
|
1574
1688
|
}
|
|
1575
1689
|
async getBalance() {
|
|
1576
1690
|
this.ensureInitialized();
|
|
1691
|
+
await this.ensureConnected();
|
|
1577
1692
|
const addresses = this._getWatchedAddresses();
|
|
1578
1693
|
let totalAlpha = 0;
|
|
1579
1694
|
let vestedSats = BigInt(0);
|
|
@@ -1605,6 +1720,7 @@ var L1PaymentsModule = class {
|
|
|
1605
1720
|
}
|
|
1606
1721
|
async getUtxos() {
|
|
1607
1722
|
this.ensureInitialized();
|
|
1723
|
+
await this.ensureConnected();
|
|
1608
1724
|
const result = [];
|
|
1609
1725
|
const currentHeight = await getCurrentBlockHeight();
|
|
1610
1726
|
const allUtxos = await this._getAllUtxos();
|
|
@@ -1640,42 +1756,73 @@ var L1PaymentsModule = class {
|
|
|
1640
1756
|
return result;
|
|
1641
1757
|
}
|
|
1642
1758
|
async getHistory(limit) {
|
|
1759
|
+
await this.ensureConnected();
|
|
1643
1760
|
this.ensureInitialized();
|
|
1644
1761
|
const addresses = this._getWatchedAddresses();
|
|
1645
1762
|
const transactions = [];
|
|
1646
1763
|
const seenTxids = /* @__PURE__ */ new Set();
|
|
1647
1764
|
const currentHeight = await getCurrentBlockHeight();
|
|
1765
|
+
const txCache = /* @__PURE__ */ new Map();
|
|
1766
|
+
const fetchTx = async (txid) => {
|
|
1767
|
+
if (txCache.has(txid)) return txCache.get(txid);
|
|
1768
|
+
const detail = await getTransaction(txid);
|
|
1769
|
+
txCache.set(txid, detail);
|
|
1770
|
+
return detail;
|
|
1771
|
+
};
|
|
1772
|
+
const addressSet = new Set(addresses.map((a) => a.toLowerCase()));
|
|
1648
1773
|
for (const address of addresses) {
|
|
1649
1774
|
const history = await getTransactionHistory(address);
|
|
1650
1775
|
for (const item of history) {
|
|
1651
1776
|
if (seenTxids.has(item.tx_hash)) continue;
|
|
1652
1777
|
seenTxids.add(item.tx_hash);
|
|
1653
|
-
const tx = await
|
|
1778
|
+
const tx = await fetchTx(item.tx_hash);
|
|
1654
1779
|
if (!tx) continue;
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1780
|
+
let isSend = false;
|
|
1781
|
+
for (const vin of tx.vin ?? []) {
|
|
1782
|
+
if (!vin.txid) continue;
|
|
1783
|
+
const prevTx = await fetchTx(vin.txid);
|
|
1784
|
+
if (prevTx?.vout?.[vin.vout]) {
|
|
1785
|
+
const prevOut = prevTx.vout[vin.vout];
|
|
1786
|
+
const prevAddrs = [
|
|
1787
|
+
...prevOut.scriptPubKey?.addresses ?? [],
|
|
1788
|
+
...prevOut.scriptPubKey?.address ? [prevOut.scriptPubKey.address] : []
|
|
1789
|
+
];
|
|
1790
|
+
if (prevAddrs.some((a) => addressSet.has(a.toLowerCase()))) {
|
|
1791
|
+
isSend = true;
|
|
1792
|
+
break;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
let amountToUs = 0;
|
|
1797
|
+
let amountToOthers = 0;
|
|
1659
1798
|
let txAddress = address;
|
|
1799
|
+
let externalAddress = "";
|
|
1660
1800
|
if (tx.vout) {
|
|
1661
1801
|
for (const vout of tx.vout) {
|
|
1662
|
-
const voutAddresses =
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
const
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1802
|
+
const voutAddresses = [
|
|
1803
|
+
...vout.scriptPubKey?.addresses ?? [],
|
|
1804
|
+
...vout.scriptPubKey?.address ? [vout.scriptPubKey.address] : []
|
|
1805
|
+
];
|
|
1806
|
+
const isOurs = voutAddresses.some((a) => addressSet.has(a.toLowerCase()));
|
|
1807
|
+
const valueSats = Math.floor((vout.value ?? 0) * 1e8);
|
|
1808
|
+
if (isOurs) {
|
|
1809
|
+
amountToUs += valueSats;
|
|
1810
|
+
if (!txAddress) txAddress = voutAddresses[0];
|
|
1811
|
+
} else {
|
|
1812
|
+
amountToOthers += valueSats;
|
|
1813
|
+
if (!externalAddress && voutAddresses.length > 0) {
|
|
1814
|
+
externalAddress = voutAddresses[0];
|
|
1815
|
+
}
|
|
1671
1816
|
}
|
|
1672
1817
|
}
|
|
1673
1818
|
}
|
|
1819
|
+
const amount = isSend ? amountToOthers.toString() : amountToUs.toString();
|
|
1820
|
+
const displayAddress = isSend ? externalAddress || txAddress : txAddress;
|
|
1674
1821
|
transactions.push({
|
|
1675
1822
|
txid: item.tx_hash,
|
|
1676
1823
|
type: isSend ? "send" : "receive",
|
|
1677
1824
|
amount,
|
|
1678
|
-
address:
|
|
1825
|
+
address: displayAddress,
|
|
1679
1826
|
confirmations: item.height > 0 ? currentHeight - item.height : 0,
|
|
1680
1827
|
timestamp: tx.time ? tx.time * 1e3 : Date.now(),
|
|
1681
1828
|
blockHeight: item.height > 0 ? item.height : void 0
|
|
@@ -1687,6 +1834,7 @@ var L1PaymentsModule = class {
|
|
|
1687
1834
|
}
|
|
1688
1835
|
async getTransaction(txid) {
|
|
1689
1836
|
this.ensureInitialized();
|
|
1837
|
+
await this.ensureConnected();
|
|
1690
1838
|
const tx = await getTransaction(txid);
|
|
1691
1839
|
if (!tx) return null;
|
|
1692
1840
|
const addresses = this._getWatchedAddresses();
|
|
@@ -1722,6 +1870,7 @@ var L1PaymentsModule = class {
|
|
|
1722
1870
|
}
|
|
1723
1871
|
async estimateFee(to, amount) {
|
|
1724
1872
|
this.ensureInitialized();
|
|
1873
|
+
await this.ensureConnected();
|
|
1725
1874
|
if (!this._wallet) {
|
|
1726
1875
|
return { fee: "0", feeRate: this._config.defaultFeeRate ?? 10 };
|
|
1727
1876
|
}
|
|
@@ -2241,7 +2390,9 @@ var STORAGE_KEYS_GLOBAL = {
|
|
|
2241
2390
|
/** Nametag cache per address (separate from tracked addresses registry) */
|
|
2242
2391
|
ADDRESS_NAMETAGS: "address_nametags",
|
|
2243
2392
|
/** Active addresses registry (JSON: TrackedAddressesStorage) */
|
|
2244
|
-
TRACKED_ADDRESSES: "tracked_addresses"
|
|
2393
|
+
TRACKED_ADDRESSES: "tracked_addresses",
|
|
2394
|
+
/** Last processed Nostr wallet event timestamp (unix seconds), keyed per pubkey */
|
|
2395
|
+
LAST_WALLET_EVENT_TS: "last_wallet_event_ts"
|
|
2245
2396
|
};
|
|
2246
2397
|
var STORAGE_KEYS_ADDRESS = {
|
|
2247
2398
|
/** Pending transfers for this address */
|
|
@@ -3645,7 +3796,7 @@ async function parseTokenInfo(tokenData) {
|
|
|
3645
3796
|
try {
|
|
3646
3797
|
const sdkToken = await import_Token6.Token.fromJSON(data);
|
|
3647
3798
|
if (sdkToken.id) {
|
|
3648
|
-
defaultInfo.tokenId = sdkToken.id.
|
|
3799
|
+
defaultInfo.tokenId = sdkToken.id.toJSON();
|
|
3649
3800
|
}
|
|
3650
3801
|
if (sdkToken.coins && sdkToken.coins.coins) {
|
|
3651
3802
|
const rawCoins = sdkToken.coins.coins;
|
|
@@ -3936,8 +4087,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
3936
4087
|
maxRetries: config?.maxRetries ?? 3,
|
|
3937
4088
|
debug: config?.debug ?? false
|
|
3938
4089
|
};
|
|
3939
|
-
|
|
3940
|
-
this.l1 = l1Enabled ? new L1PaymentsModule(config?.l1) : null;
|
|
4090
|
+
this.l1 = config?.l1 === null ? null : new L1PaymentsModule(config?.l1);
|
|
3941
4091
|
}
|
|
3942
4092
|
/** Get module configuration */
|
|
3943
4093
|
getConfig() {
|
|
@@ -4139,7 +4289,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4139
4289
|
memo: request.memo
|
|
4140
4290
|
});
|
|
4141
4291
|
console.log(`[Payments] Split token sent successfully`);
|
|
4142
|
-
await this.removeToken(splitPlan.tokenToSplit.uiToken.id, recipientNametag);
|
|
4292
|
+
await this.removeToken(splitPlan.tokenToSplit.uiToken.id, recipientNametag, true);
|
|
4143
4293
|
result.txHash = "split-" + Date.now().toString(16);
|
|
4144
4294
|
this.log(`Split transfer completed`);
|
|
4145
4295
|
}
|
|
@@ -4165,7 +4315,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4165
4315
|
});
|
|
4166
4316
|
console.log(`[Payments] Direct token sent successfully`);
|
|
4167
4317
|
this.log(`Token ${token.id} transferred, txHash: ${result.txHash}`);
|
|
4168
|
-
await this.removeToken(token.id, recipientNametag);
|
|
4318
|
+
await this.removeToken(token.id, recipientNametag, true);
|
|
4169
4319
|
}
|
|
4170
4320
|
result.status = "delivered";
|
|
4171
4321
|
await this.save();
|
|
@@ -4314,7 +4464,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4314
4464
|
);
|
|
4315
4465
|
if (result.success) {
|
|
4316
4466
|
const recipientNametag = request.recipient.startsWith("@") ? request.recipient.slice(1) : void 0;
|
|
4317
|
-
await this.removeToken(tokenToSplit.id, recipientNametag);
|
|
4467
|
+
await this.removeToken(tokenToSplit.id, recipientNametag, true);
|
|
4318
4468
|
await this.addToHistory({
|
|
4319
4469
|
type: "SENT",
|
|
4320
4470
|
amount: request.amount,
|
|
@@ -4599,13 +4749,16 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4599
4749
|
if (this.paymentRequests.find((r) => r.id === transportRequest.id)) {
|
|
4600
4750
|
return;
|
|
4601
4751
|
}
|
|
4752
|
+
const coinId = transportRequest.request.coinId;
|
|
4753
|
+
const registry = TokenRegistry.getInstance();
|
|
4754
|
+
const coinDef = registry.getDefinition(coinId);
|
|
4602
4755
|
const request = {
|
|
4603
4756
|
id: transportRequest.id,
|
|
4604
4757
|
senderPubkey: transportRequest.senderTransportPubkey,
|
|
4758
|
+
senderNametag: transportRequest.senderNametag,
|
|
4605
4759
|
amount: transportRequest.request.amount,
|
|
4606
|
-
coinId
|
|
4607
|
-
symbol:
|
|
4608
|
-
// Use coinId as symbol for now
|
|
4760
|
+
coinId,
|
|
4761
|
+
symbol: coinDef?.symbol || coinId.slice(0, 8),
|
|
4609
4762
|
message: transportRequest.request.message,
|
|
4610
4763
|
recipientNametag: transportRequest.request.recipientNametag,
|
|
4611
4764
|
requestId: transportRequest.request.requestId,
|
|
@@ -4820,35 +4973,39 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4820
4973
|
const rawAssets = Array.from(assetsMap.values());
|
|
4821
4974
|
let priceMap = null;
|
|
4822
4975
|
if (this.priceProvider && rawAssets.length > 0) {
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
const
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
existing
|
|
4831
|
-
|
|
4832
|
-
|
|
4976
|
+
try {
|
|
4977
|
+
const registry = TokenRegistry.getInstance();
|
|
4978
|
+
const nameToCoins = /* @__PURE__ */ new Map();
|
|
4979
|
+
for (const asset of rawAssets) {
|
|
4980
|
+
const def = registry.getDefinition(asset.coinId);
|
|
4981
|
+
if (def?.name) {
|
|
4982
|
+
const existing = nameToCoins.get(def.name);
|
|
4983
|
+
if (existing) {
|
|
4984
|
+
existing.push(asset.coinId);
|
|
4985
|
+
} else {
|
|
4986
|
+
nameToCoins.set(def.name, [asset.coinId]);
|
|
4987
|
+
}
|
|
4833
4988
|
}
|
|
4834
4989
|
}
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
}
|
|
4990
|
+
if (nameToCoins.size > 0) {
|
|
4991
|
+
const tokenNames = Array.from(nameToCoins.keys());
|
|
4992
|
+
const prices = await this.priceProvider.getPrices(tokenNames);
|
|
4993
|
+
priceMap = /* @__PURE__ */ new Map();
|
|
4994
|
+
for (const [name, coinIds] of nameToCoins) {
|
|
4995
|
+
const price = prices.get(name);
|
|
4996
|
+
if (price) {
|
|
4997
|
+
for (const cid of coinIds) {
|
|
4998
|
+
priceMap.set(cid, {
|
|
4999
|
+
priceUsd: price.priceUsd,
|
|
5000
|
+
priceEur: price.priceEur,
|
|
5001
|
+
change24h: price.change24h
|
|
5002
|
+
});
|
|
5003
|
+
}
|
|
4849
5004
|
}
|
|
4850
5005
|
}
|
|
4851
5006
|
}
|
|
5007
|
+
} catch (error) {
|
|
5008
|
+
console.warn("[Payments] Failed to fetch prices, returning assets without price data:", error);
|
|
4852
5009
|
}
|
|
4853
5010
|
}
|
|
4854
5011
|
return rawAssets.map((raw) => {
|
|
@@ -5051,6 +5208,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5051
5208
|
updatedAt: Date.now(),
|
|
5052
5209
|
sdkData: typeof tokenJson === "string" ? tokenJson : JSON.stringify(tokenJson)
|
|
5053
5210
|
};
|
|
5211
|
+
const loadedTokenId = extractTokenIdFromSdkData(token.sdkData);
|
|
5212
|
+
const loadedStateHash = extractStateHashFromSdkData(token.sdkData);
|
|
5213
|
+
if (loadedTokenId && loadedStateHash && this.isStateTombstoned(loadedTokenId, loadedStateHash)) {
|
|
5214
|
+
this.log(`Skipping tombstoned token file ${tokenId} (${loadedTokenId.slice(0, 8)}...)`);
|
|
5215
|
+
continue;
|
|
5216
|
+
}
|
|
5054
5217
|
this.tokens.set(token.id, token);
|
|
5055
5218
|
this.log(`Loaded token from file: ${tokenId}`);
|
|
5056
5219
|
} catch (tokenError) {
|
|
@@ -5108,6 +5271,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5108
5271
|
this.log(`Warning: Could not create tombstone for token ${tokenId.slice(0, 8)}... (missing tokenId or stateHash)`);
|
|
5109
5272
|
}
|
|
5110
5273
|
this.tokens.delete(tokenId);
|
|
5274
|
+
await this.deleteTokenFiles(token);
|
|
5111
5275
|
if (!skipHistory && token.coinId && token.amount) {
|
|
5112
5276
|
await this.addToHistory({
|
|
5113
5277
|
type: "SENT",
|
|
@@ -5120,6 +5284,31 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5120
5284
|
}
|
|
5121
5285
|
await this.save();
|
|
5122
5286
|
}
|
|
5287
|
+
/**
|
|
5288
|
+
* Delete physical token file(s) from all storage providers.
|
|
5289
|
+
* Finds files by matching the SDK token ID prefix in the filename.
|
|
5290
|
+
*/
|
|
5291
|
+
async deleteTokenFiles(token) {
|
|
5292
|
+
const sdkTokenId = extractTokenIdFromSdkData(token.sdkData);
|
|
5293
|
+
if (!sdkTokenId) return;
|
|
5294
|
+
const tokenIdPrefix = sdkTokenId.slice(0, 16);
|
|
5295
|
+
const providers = this.getTokenStorageProviders();
|
|
5296
|
+
for (const [providerId, provider] of providers) {
|
|
5297
|
+
if (!provider.listTokenIds || !provider.deleteToken) continue;
|
|
5298
|
+
try {
|
|
5299
|
+
const allIds = await provider.listTokenIds();
|
|
5300
|
+
const matchingFiles = allIds.filter(
|
|
5301
|
+
(id) => id.startsWith(`token-${tokenIdPrefix}`)
|
|
5302
|
+
);
|
|
5303
|
+
for (const fileId of matchingFiles) {
|
|
5304
|
+
await provider.deleteToken(fileId);
|
|
5305
|
+
this.log(`Deleted token file ${fileId} from ${providerId}`);
|
|
5306
|
+
}
|
|
5307
|
+
} catch (error) {
|
|
5308
|
+
console.warn(`[Payments] Failed to delete token files from ${providerId}:`, error);
|
|
5309
|
+
}
|
|
5310
|
+
}
|
|
5311
|
+
}
|
|
5123
5312
|
// ===========================================================================
|
|
5124
5313
|
// Public API - Tombstones
|
|
5125
5314
|
// ===========================================================================
|
|
@@ -6624,6 +6813,20 @@ function decryptSimple(ciphertext, password) {
|
|
|
6624
6813
|
}
|
|
6625
6814
|
return result;
|
|
6626
6815
|
}
|
|
6816
|
+
function decryptWithSalt(ciphertext, password, salt) {
|
|
6817
|
+
try {
|
|
6818
|
+
const key = import_crypto_js6.default.PBKDF2(password, salt, {
|
|
6819
|
+
keySize: 256 / 32,
|
|
6820
|
+
iterations: 1e5,
|
|
6821
|
+
hasher: import_crypto_js6.default.algo.SHA256
|
|
6822
|
+
}).toString();
|
|
6823
|
+
const decrypted = import_crypto_js6.default.AES.decrypt(ciphertext, key);
|
|
6824
|
+
const result = decrypted.toString(import_crypto_js6.default.enc.Utf8);
|
|
6825
|
+
return result || null;
|
|
6826
|
+
} catch {
|
|
6827
|
+
return null;
|
|
6828
|
+
}
|
|
6829
|
+
}
|
|
6627
6830
|
function encryptMnemonic(mnemonic, password) {
|
|
6628
6831
|
return encryptSimple(mnemonic, password);
|
|
6629
6832
|
}
|
|
@@ -6651,6 +6854,81 @@ function generateRandomKey(bytes = 32) {
|
|
|
6651
6854
|
return import_crypto_js6.default.lib.WordArray.random(bytes).toString(import_crypto_js6.default.enc.Hex);
|
|
6652
6855
|
}
|
|
6653
6856
|
|
|
6857
|
+
// core/scan.ts
|
|
6858
|
+
async function scanAddressesImpl(deriveAddress, options = {}) {
|
|
6859
|
+
const maxAddresses = options.maxAddresses ?? 50;
|
|
6860
|
+
const gapLimit = options.gapLimit ?? 20;
|
|
6861
|
+
const includeChange = options.includeChange ?? true;
|
|
6862
|
+
const { onProgress, signal, resolveNametag } = options;
|
|
6863
|
+
const { connect: connect2, getBalance: getBalance2 } = await Promise.resolve().then(() => (init_network(), network_exports));
|
|
6864
|
+
await connect2();
|
|
6865
|
+
const foundAddresses = [];
|
|
6866
|
+
let totalBalance = 0;
|
|
6867
|
+
let totalScanned = 0;
|
|
6868
|
+
let nametagsFoundCount = 0;
|
|
6869
|
+
const chains = includeChange ? [false, true] : [false];
|
|
6870
|
+
const totalToScan = maxAddresses * chains.length;
|
|
6871
|
+
for (const isChange of chains) {
|
|
6872
|
+
let consecutiveEmpty = 0;
|
|
6873
|
+
for (let index = 0; index < maxAddresses; index++) {
|
|
6874
|
+
if (signal?.aborted) break;
|
|
6875
|
+
const addrInfo = deriveAddress(index, isChange);
|
|
6876
|
+
totalScanned++;
|
|
6877
|
+
onProgress?.({
|
|
6878
|
+
scanned: totalScanned,
|
|
6879
|
+
total: totalToScan,
|
|
6880
|
+
currentAddress: addrInfo.address,
|
|
6881
|
+
foundCount: foundAddresses.length,
|
|
6882
|
+
currentGap: consecutiveEmpty,
|
|
6883
|
+
nametagsFoundCount
|
|
6884
|
+
});
|
|
6885
|
+
try {
|
|
6886
|
+
const balance = await getBalance2(addrInfo.address);
|
|
6887
|
+
if (balance > 0) {
|
|
6888
|
+
let nametag;
|
|
6889
|
+
if (resolveNametag) {
|
|
6890
|
+
try {
|
|
6891
|
+
const tag = await resolveNametag(addrInfo.address);
|
|
6892
|
+
if (tag) {
|
|
6893
|
+
nametag = tag;
|
|
6894
|
+
nametagsFoundCount++;
|
|
6895
|
+
}
|
|
6896
|
+
} catch {
|
|
6897
|
+
}
|
|
6898
|
+
}
|
|
6899
|
+
foundAddresses.push({
|
|
6900
|
+
index,
|
|
6901
|
+
address: addrInfo.address,
|
|
6902
|
+
path: addrInfo.path,
|
|
6903
|
+
balance,
|
|
6904
|
+
isChange,
|
|
6905
|
+
nametag
|
|
6906
|
+
});
|
|
6907
|
+
totalBalance += balance;
|
|
6908
|
+
consecutiveEmpty = 0;
|
|
6909
|
+
} else {
|
|
6910
|
+
consecutiveEmpty++;
|
|
6911
|
+
}
|
|
6912
|
+
} catch (err) {
|
|
6913
|
+
console.warn(`[scanAddresses] Error checking ${addrInfo.address}:`, err);
|
|
6914
|
+
consecutiveEmpty++;
|
|
6915
|
+
}
|
|
6916
|
+
if (consecutiveEmpty >= gapLimit) {
|
|
6917
|
+
break;
|
|
6918
|
+
}
|
|
6919
|
+
if (totalScanned % 5 === 0) {
|
|
6920
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
6921
|
+
}
|
|
6922
|
+
}
|
|
6923
|
+
if (signal?.aborted) break;
|
|
6924
|
+
}
|
|
6925
|
+
return {
|
|
6926
|
+
addresses: foundAddresses,
|
|
6927
|
+
totalBalance,
|
|
6928
|
+
scannedCount: totalScanned
|
|
6929
|
+
};
|
|
6930
|
+
}
|
|
6931
|
+
|
|
6654
6932
|
// serialization/wallet-text.ts
|
|
6655
6933
|
var import_crypto_js7 = __toESM(require("crypto-js"), 1);
|
|
6656
6934
|
|
|
@@ -6907,10 +7185,11 @@ function parseWalletText(content) {
|
|
|
6907
7185
|
);
|
|
6908
7186
|
const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
|
|
6909
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);
|
|
6910
7189
|
const data = {
|
|
6911
7190
|
masterKey,
|
|
6912
7191
|
chainCode: chainCode ?? void 0,
|
|
6913
|
-
descriptorPath:
|
|
7192
|
+
descriptorPath: effectiveDescriptorPath,
|
|
6914
7193
|
derivationMode: isBIP32 ? "bip32" : "wif_hmac"
|
|
6915
7194
|
};
|
|
6916
7195
|
return {
|
|
@@ -6953,10 +7232,11 @@ function parseAndDecryptWalletText(content, password) {
|
|
|
6953
7232
|
);
|
|
6954
7233
|
const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
|
|
6955
7234
|
const isBIP32 = content.includes("WALLET TYPE: BIP32 hierarchical deterministic wallet") || content.includes("WALLET TYPE: Alpha descriptor wallet") || !!chainCode;
|
|
7235
|
+
const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? "84'/1'/0'" : void 0);
|
|
6956
7236
|
const data = {
|
|
6957
7237
|
masterKey,
|
|
6958
7238
|
chainCode: chainCode ?? void 0,
|
|
6959
|
-
descriptorPath:
|
|
7239
|
+
descriptorPath: effectiveDescriptorPath,
|
|
6960
7240
|
derivationMode: isBIP32 ? "bip32" : "wif_hmac"
|
|
6961
7241
|
};
|
|
6962
7242
|
return {
|
|
@@ -7497,8 +7777,8 @@ var Sphere = class _Sphere {
|
|
|
7497
7777
|
if (options.nametag) {
|
|
7498
7778
|
await sphere.registerNametag(options.nametag);
|
|
7499
7779
|
} else {
|
|
7500
|
-
await sphere.syncIdentityWithTransport();
|
|
7501
7780
|
await sphere.recoverNametagFromTransport();
|
|
7781
|
+
await sphere.syncIdentityWithTransport();
|
|
7502
7782
|
}
|
|
7503
7783
|
return sphere;
|
|
7504
7784
|
}
|
|
@@ -7545,7 +7825,15 @@ var Sphere = class _Sphere {
|
|
|
7545
7825
|
if (!options.mnemonic && !options.masterKey) {
|
|
7546
7826
|
throw new Error("Either mnemonic or masterKey is required");
|
|
7547
7827
|
}
|
|
7828
|
+
console.log("[Sphere.import] Starting import...");
|
|
7829
|
+
console.log("[Sphere.import] Clearing existing wallet data...");
|
|
7548
7830
|
await _Sphere.clear({ storage: options.storage, tokenStorage: options.tokenStorage });
|
|
7831
|
+
console.log("[Sphere.import] Clear done");
|
|
7832
|
+
if (!options.storage.isConnected()) {
|
|
7833
|
+
console.log("[Sphere.import] Reconnecting storage...");
|
|
7834
|
+
await options.storage.connect();
|
|
7835
|
+
console.log("[Sphere.import] Storage reconnected");
|
|
7836
|
+
}
|
|
7549
7837
|
const sphere = new _Sphere(
|
|
7550
7838
|
options.storage,
|
|
7551
7839
|
options.transport,
|
|
@@ -7558,9 +7846,12 @@ var Sphere = class _Sphere {
|
|
|
7558
7846
|
if (!_Sphere.validateMnemonic(options.mnemonic)) {
|
|
7559
7847
|
throw new Error("Invalid mnemonic");
|
|
7560
7848
|
}
|
|
7849
|
+
console.log("[Sphere.import] Storing mnemonic...");
|
|
7561
7850
|
await sphere.storeMnemonic(options.mnemonic, options.derivationPath, options.basePath);
|
|
7851
|
+
console.log("[Sphere.import] Initializing identity from mnemonic...");
|
|
7562
7852
|
await sphere.initializeIdentityFromMnemonic(options.mnemonic, options.derivationPath);
|
|
7563
7853
|
} else if (options.masterKey) {
|
|
7854
|
+
console.log("[Sphere.import] Storing master key...");
|
|
7564
7855
|
await sphere.storeMasterKey(
|
|
7565
7856
|
options.masterKey,
|
|
7566
7857
|
options.chainCode,
|
|
@@ -7568,24 +7859,35 @@ var Sphere = class _Sphere {
|
|
|
7568
7859
|
options.basePath,
|
|
7569
7860
|
options.derivationMode
|
|
7570
7861
|
);
|
|
7862
|
+
console.log("[Sphere.import] Initializing identity from master key...");
|
|
7571
7863
|
await sphere.initializeIdentityFromMasterKey(
|
|
7572
7864
|
options.masterKey,
|
|
7573
7865
|
options.chainCode,
|
|
7574
7866
|
options.derivationPath
|
|
7575
7867
|
);
|
|
7576
7868
|
}
|
|
7869
|
+
console.log("[Sphere.import] Initializing providers...");
|
|
7577
7870
|
await sphere.initializeProviders();
|
|
7871
|
+
console.log("[Sphere.import] Providers initialized. Initializing modules...");
|
|
7578
7872
|
await sphere.initializeModules();
|
|
7873
|
+
console.log("[Sphere.import] Modules initialized");
|
|
7579
7874
|
if (!options.nametag) {
|
|
7875
|
+
console.log("[Sphere.import] Recovering nametag from transport...");
|
|
7580
7876
|
await sphere.recoverNametagFromTransport();
|
|
7877
|
+
console.log("[Sphere.import] Nametag recovery done");
|
|
7878
|
+
await sphere.syncIdentityWithTransport();
|
|
7581
7879
|
}
|
|
7880
|
+
console.log("[Sphere.import] Finalizing wallet creation...");
|
|
7582
7881
|
await sphere.finalizeWalletCreation();
|
|
7583
7882
|
sphere._initialized = true;
|
|
7584
7883
|
_Sphere.instance = sphere;
|
|
7884
|
+
console.log("[Sphere.import] Tracking address 0...");
|
|
7585
7885
|
await sphere.ensureAddressTracked(0);
|
|
7586
7886
|
if (options.nametag) {
|
|
7887
|
+
console.log("[Sphere.import] Registering nametag...");
|
|
7587
7888
|
await sphere.registerNametag(options.nametag);
|
|
7588
7889
|
}
|
|
7890
|
+
console.log("[Sphere.import] Import complete");
|
|
7589
7891
|
return sphere;
|
|
7590
7892
|
}
|
|
7591
7893
|
/**
|
|
@@ -7610,6 +7912,10 @@ var Sphere = class _Sphere {
|
|
|
7610
7912
|
static async clear(storageOrOptions) {
|
|
7611
7913
|
const storage = "get" in storageOrOptions ? storageOrOptions : storageOrOptions.storage;
|
|
7612
7914
|
const tokenStorage = "get" in storageOrOptions ? void 0 : storageOrOptions.tokenStorage;
|
|
7915
|
+
if (!storage.isConnected()) {
|
|
7916
|
+
await storage.connect();
|
|
7917
|
+
}
|
|
7918
|
+
console.log("[Sphere.clear] Removing storage keys...");
|
|
7613
7919
|
await storage.remove(STORAGE_KEYS_GLOBAL.MNEMONIC);
|
|
7614
7920
|
await storage.remove(STORAGE_KEYS_GLOBAL.MASTER_KEY);
|
|
7615
7921
|
await storage.remove(STORAGE_KEYS_GLOBAL.CHAIN_CODE);
|
|
@@ -7622,11 +7928,30 @@ var Sphere = class _Sphere {
|
|
|
7622
7928
|
await storage.remove(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS);
|
|
7623
7929
|
await storage.remove(STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS);
|
|
7624
7930
|
await storage.remove(STORAGE_KEYS_ADDRESS.OUTBOX);
|
|
7931
|
+
console.log("[Sphere.clear] Storage keys removed");
|
|
7625
7932
|
if (tokenStorage?.clear) {
|
|
7626
|
-
|
|
7933
|
+
console.log("[Sphere.clear] Clearing token storage...");
|
|
7934
|
+
try {
|
|
7935
|
+
await Promise.race([
|
|
7936
|
+
tokenStorage.clear(),
|
|
7937
|
+
new Promise(
|
|
7938
|
+
(_, reject) => setTimeout(() => reject(new Error("tokenStorage.clear() timed out after 2s")), 2e3)
|
|
7939
|
+
)
|
|
7940
|
+
]);
|
|
7941
|
+
console.log("[Sphere.clear] Token storage cleared");
|
|
7942
|
+
} catch (err) {
|
|
7943
|
+
console.warn("[Sphere.clear] Token storage clear failed/timed out:", err);
|
|
7944
|
+
}
|
|
7627
7945
|
}
|
|
7946
|
+
console.log("[Sphere.clear] Destroying vesting classifier...");
|
|
7947
|
+
await vestingClassifier.destroy();
|
|
7948
|
+
console.log("[Sphere.clear] Vesting classifier destroyed");
|
|
7628
7949
|
if (_Sphere.instance) {
|
|
7950
|
+
console.log("[Sphere.clear] Destroying Sphere instance...");
|
|
7629
7951
|
await _Sphere.instance.destroy();
|
|
7952
|
+
console.log("[Sphere.clear] Sphere instance destroyed");
|
|
7953
|
+
} else {
|
|
7954
|
+
console.log("[Sphere.clear] No Sphere instance to destroy");
|
|
7630
7955
|
}
|
|
7631
7956
|
}
|
|
7632
7957
|
/**
|
|
@@ -7807,7 +8132,7 @@ var Sphere = class _Sphere {
|
|
|
7807
8132
|
return {
|
|
7808
8133
|
source: this._source,
|
|
7809
8134
|
hasMnemonic: this._mnemonic !== null,
|
|
7810
|
-
hasChainCode: this._masterKey?.chainCode
|
|
8135
|
+
hasChainCode: !!this._masterKey?.chainCode,
|
|
7811
8136
|
derivationMode: this._derivationMode,
|
|
7812
8137
|
basePath: this._basePath,
|
|
7813
8138
|
address0
|
|
@@ -7860,7 +8185,7 @@ var Sphere = class _Sphere {
|
|
|
7860
8185
|
let chainCode;
|
|
7861
8186
|
if (this._masterKey) {
|
|
7862
8187
|
masterPrivateKey = this._masterKey.privateKey;
|
|
7863
|
-
chainCode = this._masterKey.chainCode;
|
|
8188
|
+
chainCode = this._masterKey.chainCode || void 0;
|
|
7864
8189
|
}
|
|
7865
8190
|
let mnemonic;
|
|
7866
8191
|
let encrypted = false;
|
|
@@ -7937,7 +8262,7 @@ var Sphere = class _Sphere {
|
|
|
7937
8262
|
}
|
|
7938
8263
|
}
|
|
7939
8264
|
const masterPrivateKey = this._masterKey?.privateKey || "";
|
|
7940
|
-
const chainCode = this._masterKey?.chainCode;
|
|
8265
|
+
const chainCode = this._masterKey?.chainCode || void 0;
|
|
7941
8266
|
const isBIP32 = this._derivationMode === "bip32";
|
|
7942
8267
|
const descriptorPath = this._basePath.replace(/^m\//, "");
|
|
7943
8268
|
if (options.password) {
|
|
@@ -8007,7 +8332,8 @@ var Sphere = class _Sphere {
|
|
|
8007
8332
|
storage: options.storage,
|
|
8008
8333
|
transport: options.transport,
|
|
8009
8334
|
oracle: options.oracle,
|
|
8010
|
-
tokenStorage: options.tokenStorage
|
|
8335
|
+
tokenStorage: options.tokenStorage,
|
|
8336
|
+
l1: options.l1
|
|
8011
8337
|
});
|
|
8012
8338
|
return { success: true, mnemonic };
|
|
8013
8339
|
}
|
|
@@ -8020,7 +8346,8 @@ var Sphere = class _Sphere {
|
|
|
8020
8346
|
storage: options.storage,
|
|
8021
8347
|
transport: options.transport,
|
|
8022
8348
|
oracle: options.oracle,
|
|
8023
|
-
tokenStorage: options.tokenStorage
|
|
8349
|
+
tokenStorage: options.tokenStorage,
|
|
8350
|
+
l1: options.l1
|
|
8024
8351
|
});
|
|
8025
8352
|
return { success: true };
|
|
8026
8353
|
}
|
|
@@ -8079,7 +8406,8 @@ var Sphere = class _Sphere {
|
|
|
8079
8406
|
transport: options.transport,
|
|
8080
8407
|
oracle: options.oracle,
|
|
8081
8408
|
tokenStorage: options.tokenStorage,
|
|
8082
|
-
nametag: options.nametag
|
|
8409
|
+
nametag: options.nametag,
|
|
8410
|
+
l1: options.l1
|
|
8083
8411
|
});
|
|
8084
8412
|
return { success: true, sphere, mnemonic };
|
|
8085
8413
|
}
|
|
@@ -8108,7 +8436,8 @@ var Sphere = class _Sphere {
|
|
|
8108
8436
|
transport: options.transport,
|
|
8109
8437
|
oracle: options.oracle,
|
|
8110
8438
|
tokenStorage: options.tokenStorage,
|
|
8111
|
-
nametag: options.nametag
|
|
8439
|
+
nametag: options.nametag,
|
|
8440
|
+
l1: options.l1
|
|
8112
8441
|
});
|
|
8113
8442
|
return { success: true, sphere };
|
|
8114
8443
|
}
|
|
@@ -8139,25 +8468,94 @@ var Sphere = class _Sphere {
|
|
|
8139
8468
|
transport: options.transport,
|
|
8140
8469
|
oracle: options.oracle,
|
|
8141
8470
|
tokenStorage: options.tokenStorage,
|
|
8142
|
-
nametag: options.nametag
|
|
8471
|
+
nametag: options.nametag,
|
|
8472
|
+
l1: options.l1
|
|
8143
8473
|
});
|
|
8144
8474
|
return { success: true, sphere };
|
|
8145
8475
|
}
|
|
8146
8476
|
if (fileType === "json") {
|
|
8147
8477
|
const content = typeof fileContent === "string" ? fileContent : new TextDecoder().decode(fileContent);
|
|
8148
|
-
|
|
8149
|
-
|
|
8150
|
-
|
|
8478
|
+
let parsed;
|
|
8479
|
+
try {
|
|
8480
|
+
parsed = JSON.parse(content);
|
|
8481
|
+
} catch {
|
|
8482
|
+
return { success: false, error: "Invalid JSON file" };
|
|
8483
|
+
}
|
|
8484
|
+
if (parsed.type === "sphere-wallet") {
|
|
8485
|
+
const result = await _Sphere.importFromJSON({
|
|
8486
|
+
jsonContent: content,
|
|
8487
|
+
password,
|
|
8488
|
+
storage: options.storage,
|
|
8489
|
+
transport: options.transport,
|
|
8490
|
+
oracle: options.oracle,
|
|
8491
|
+
tokenStorage: options.tokenStorage,
|
|
8492
|
+
l1: options.l1
|
|
8493
|
+
});
|
|
8494
|
+
if (result.success) {
|
|
8495
|
+
const sphere2 = _Sphere.getInstance();
|
|
8496
|
+
return { success: true, sphere: sphere2, mnemonic: result.mnemonic };
|
|
8497
|
+
}
|
|
8498
|
+
if (!password && result.error?.includes("Password required")) {
|
|
8499
|
+
return { success: false, needsPassword: true, error: result.error };
|
|
8500
|
+
}
|
|
8501
|
+
return { success: false, error: result.error };
|
|
8502
|
+
}
|
|
8503
|
+
let masterKey;
|
|
8504
|
+
let mnemonic;
|
|
8505
|
+
if (parsed.encrypted && typeof parsed.encrypted === "object") {
|
|
8506
|
+
if (!password) {
|
|
8507
|
+
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
8508
|
+
}
|
|
8509
|
+
const enc = parsed.encrypted;
|
|
8510
|
+
if (!enc.salt || !enc.masterPrivateKey) {
|
|
8511
|
+
return { success: false, error: "Invalid encrypted wallet format" };
|
|
8512
|
+
}
|
|
8513
|
+
const decryptedKey = decryptWithSalt(enc.masterPrivateKey, password, enc.salt);
|
|
8514
|
+
if (!decryptedKey) {
|
|
8515
|
+
return { success: false, error: "Failed to decrypt - incorrect password?" };
|
|
8516
|
+
}
|
|
8517
|
+
masterKey = decryptedKey;
|
|
8518
|
+
if (enc.mnemonic) {
|
|
8519
|
+
mnemonic = decryptWithSalt(enc.mnemonic, password, enc.salt) ?? void 0;
|
|
8520
|
+
}
|
|
8521
|
+
} else {
|
|
8522
|
+
masterKey = parsed.masterPrivateKey;
|
|
8523
|
+
mnemonic = parsed.mnemonic;
|
|
8524
|
+
}
|
|
8525
|
+
if (!masterKey) {
|
|
8526
|
+
return { success: false, error: "No master key found in wallet JSON" };
|
|
8527
|
+
}
|
|
8528
|
+
const chainCode = parsed.chainCode;
|
|
8529
|
+
const descriptorPath = parsed.descriptorPath;
|
|
8530
|
+
const derivationMode = parsed.derivationMode;
|
|
8531
|
+
const isBIP32 = derivationMode === "bip32" || !!chainCode;
|
|
8532
|
+
const basePath = descriptorPath ? `m/${descriptorPath}` : isBIP32 ? "m/84'/1'/0'" : DEFAULT_BASE_PATH;
|
|
8533
|
+
if (mnemonic) {
|
|
8534
|
+
const sphere2 = await _Sphere.import({
|
|
8535
|
+
mnemonic,
|
|
8536
|
+
basePath,
|
|
8537
|
+
storage: options.storage,
|
|
8538
|
+
transport: options.transport,
|
|
8539
|
+
oracle: options.oracle,
|
|
8540
|
+
tokenStorage: options.tokenStorage,
|
|
8541
|
+
nametag: options.nametag,
|
|
8542
|
+
l1: options.l1
|
|
8543
|
+
});
|
|
8544
|
+
return { success: true, sphere: sphere2, mnemonic };
|
|
8545
|
+
}
|
|
8546
|
+
const sphere = await _Sphere.import({
|
|
8547
|
+
masterKey,
|
|
8548
|
+
chainCode,
|
|
8549
|
+
basePath,
|
|
8550
|
+
derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac"),
|
|
8151
8551
|
storage: options.storage,
|
|
8152
8552
|
transport: options.transport,
|
|
8153
8553
|
oracle: options.oracle,
|
|
8154
|
-
tokenStorage: options.tokenStorage
|
|
8554
|
+
tokenStorage: options.tokenStorage,
|
|
8555
|
+
nametag: options.nametag,
|
|
8556
|
+
l1: options.l1
|
|
8155
8557
|
});
|
|
8156
|
-
|
|
8157
|
-
const sphere = _Sphere.getInstance();
|
|
8158
|
-
return { success: true, sphere, mnemonic: result.mnemonic };
|
|
8159
|
-
}
|
|
8160
|
-
return result;
|
|
8558
|
+
return { success: true, sphere };
|
|
8161
8559
|
}
|
|
8162
8560
|
return { success: false, error: "Unsupported file type" };
|
|
8163
8561
|
}
|
|
@@ -8455,7 +8853,7 @@ var Sphere = class _Sphere {
|
|
|
8455
8853
|
transport: this._transport,
|
|
8456
8854
|
oracle: this._oracle,
|
|
8457
8855
|
emitEvent,
|
|
8458
|
-
chainCode: this._masterKey?.chainCode,
|
|
8856
|
+
chainCode: this._masterKey?.chainCode || void 0,
|
|
8459
8857
|
price: this._priceProvider ?? void 0
|
|
8460
8858
|
});
|
|
8461
8859
|
this._communications.initialize({
|
|
@@ -8500,6 +8898,9 @@ var Sphere = class _Sphere {
|
|
|
8500
8898
|
if (!this._masterKey) {
|
|
8501
8899
|
throw new Error("HD derivation requires master key with chain code");
|
|
8502
8900
|
}
|
|
8901
|
+
if (this._derivationMode === "wif_hmac") {
|
|
8902
|
+
return generateAddressFromMasterKey(this._masterKey.privateKey, index);
|
|
8903
|
+
}
|
|
8503
8904
|
const info = deriveAddressInfo(
|
|
8504
8905
|
this._masterKey,
|
|
8505
8906
|
this._basePath,
|
|
@@ -8571,6 +8972,66 @@ var Sphere = class _Sphere {
|
|
|
8571
8972
|
}
|
|
8572
8973
|
return addresses;
|
|
8573
8974
|
}
|
|
8975
|
+
/**
|
|
8976
|
+
* Scan blockchain addresses to discover used addresses with balances.
|
|
8977
|
+
* Derives addresses sequentially and checks L1 balance via Fulcrum.
|
|
8978
|
+
* Uses gap limit to stop after N consecutive empty addresses.
|
|
8979
|
+
*
|
|
8980
|
+
* @param options - Scanning options
|
|
8981
|
+
* @returns Scan results with found addresses and total balance
|
|
8982
|
+
*
|
|
8983
|
+
* @example
|
|
8984
|
+
* ```ts
|
|
8985
|
+
* const result = await sphere.scanAddresses({
|
|
8986
|
+
* maxAddresses: 100,
|
|
8987
|
+
* gapLimit: 20,
|
|
8988
|
+
* onProgress: (p) => console.log(`Scanned ${p.scanned}/${p.total}, found ${p.foundCount}`),
|
|
8989
|
+
* });
|
|
8990
|
+
* console.log(`Found ${result.addresses.length} addresses, total: ${result.totalBalance} ALPHA`);
|
|
8991
|
+
* ```
|
|
8992
|
+
*/
|
|
8993
|
+
async scanAddresses(options = {}) {
|
|
8994
|
+
this.ensureReady();
|
|
8995
|
+
if (!this._masterKey) {
|
|
8996
|
+
throw new Error("Address scanning requires HD master key");
|
|
8997
|
+
}
|
|
8998
|
+
const resolveNametag = options.resolveNametag ?? (this._transport.resolveAddressInfo ? async (l1Address) => {
|
|
8999
|
+
try {
|
|
9000
|
+
const info = await this._transport.resolveAddressInfo(l1Address);
|
|
9001
|
+
return info?.nametag ?? null;
|
|
9002
|
+
} catch {
|
|
9003
|
+
return null;
|
|
9004
|
+
}
|
|
9005
|
+
} : void 0);
|
|
9006
|
+
return scanAddressesImpl(
|
|
9007
|
+
(index, isChange) => this._deriveAddressInternal(index, isChange),
|
|
9008
|
+
{ ...options, resolveNametag }
|
|
9009
|
+
);
|
|
9010
|
+
}
|
|
9011
|
+
/**
|
|
9012
|
+
* Bulk-track scanned addresses with visibility and nametag data.
|
|
9013
|
+
* Selected addresses get `hidden: false`, unselected get `hidden: true`.
|
|
9014
|
+
* Performs only 2 storage writes total (tracked addresses + nametags).
|
|
9015
|
+
*/
|
|
9016
|
+
async trackScannedAddresses(entries) {
|
|
9017
|
+
this.ensureReady();
|
|
9018
|
+
for (const { index, hidden, nametag } of entries) {
|
|
9019
|
+
const tracked = await this.ensureAddressTracked(index);
|
|
9020
|
+
if (nametag) {
|
|
9021
|
+
let nametags = this._addressNametags.get(tracked.addressId);
|
|
9022
|
+
if (!nametags) {
|
|
9023
|
+
nametags = /* @__PURE__ */ new Map();
|
|
9024
|
+
this._addressNametags.set(tracked.addressId, nametags);
|
|
9025
|
+
}
|
|
9026
|
+
if (!nametags.has(0)) nametags.set(0, nametag);
|
|
9027
|
+
}
|
|
9028
|
+
if (tracked.hidden !== hidden) {
|
|
9029
|
+
tracked.hidden = hidden;
|
|
9030
|
+
}
|
|
9031
|
+
}
|
|
9032
|
+
await this.persistTrackedAddresses();
|
|
9033
|
+
await this.persistAddressNametags();
|
|
9034
|
+
}
|
|
8574
9035
|
// ===========================================================================
|
|
8575
9036
|
// Public Methods - Status
|
|
8576
9037
|
// ===========================================================================
|
|
@@ -8955,35 +9416,40 @@ var Sphere = class _Sphere {
|
|
|
8955
9416
|
if (this._identity?.nametag) {
|
|
8956
9417
|
return;
|
|
8957
9418
|
}
|
|
8958
|
-
|
|
9419
|
+
let recoveredNametag = null;
|
|
9420
|
+
if (this._transport.recoverNametag) {
|
|
9421
|
+
try {
|
|
9422
|
+
recoveredNametag = await this._transport.recoverNametag();
|
|
9423
|
+
} catch {
|
|
9424
|
+
}
|
|
9425
|
+
}
|
|
9426
|
+
if (!recoveredNametag && this._transport.resolveAddressInfo && this._identity?.l1Address) {
|
|
9427
|
+
try {
|
|
9428
|
+
const info = await this._transport.resolveAddressInfo(this._identity.l1Address);
|
|
9429
|
+
if (info?.nametag) {
|
|
9430
|
+
recoveredNametag = info.nametag;
|
|
9431
|
+
}
|
|
9432
|
+
} catch {
|
|
9433
|
+
}
|
|
9434
|
+
}
|
|
9435
|
+
if (!recoveredNametag) {
|
|
8959
9436
|
return;
|
|
8960
9437
|
}
|
|
8961
9438
|
try {
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
|
|
8966
|
-
|
|
8967
|
-
|
|
8968
|
-
|
|
8969
|
-
|
|
8970
|
-
|
|
8971
|
-
nametags = /* @__PURE__ */ new Map();
|
|
8972
|
-
this._addressNametags.set(entry.addressId, nametags);
|
|
8973
|
-
}
|
|
8974
|
-
const nextIndex = nametags.size;
|
|
8975
|
-
nametags.set(nextIndex, recoveredNametag);
|
|
8976
|
-
await this.persistAddressNametags();
|
|
8977
|
-
if (this._transport.publishIdentityBinding) {
|
|
8978
|
-
await this._transport.publishIdentityBinding(
|
|
8979
|
-
this._identity.chainPubkey,
|
|
8980
|
-
this._identity.l1Address,
|
|
8981
|
-
this._identity.directAddress || "",
|
|
8982
|
-
recoveredNametag
|
|
8983
|
-
);
|
|
8984
|
-
}
|
|
8985
|
-
this.emitEvent("nametag:recovered", { nametag: recoveredNametag });
|
|
9439
|
+
if (this._identity) {
|
|
9440
|
+
this._identity.nametag = recoveredNametag;
|
|
9441
|
+
await this._updateCachedProxyAddress();
|
|
9442
|
+
}
|
|
9443
|
+
const entry = await this.ensureAddressTracked(this._currentAddressIndex);
|
|
9444
|
+
let nametags = this._addressNametags.get(entry.addressId);
|
|
9445
|
+
if (!nametags) {
|
|
9446
|
+
nametags = /* @__PURE__ */ new Map();
|
|
9447
|
+
this._addressNametags.set(entry.addressId, nametags);
|
|
8986
9448
|
}
|
|
9449
|
+
const nextIndex = nametags.size;
|
|
9450
|
+
nametags.set(nextIndex, recoveredNametag);
|
|
9451
|
+
await this.persistAddressNametags();
|
|
9452
|
+
this.emitEvent("nametag:recovered", { nametag: recoveredNametag });
|
|
8987
9453
|
} catch {
|
|
8988
9454
|
}
|
|
8989
9455
|
}
|
|
@@ -9152,8 +9618,8 @@ var Sphere = class _Sphere {
|
|
|
9152
9618
|
};
|
|
9153
9619
|
this._masterKey = masterKey;
|
|
9154
9620
|
}
|
|
9155
|
-
async initializeIdentityFromMasterKey(masterKey, chainCode,
|
|
9156
|
-
const basePath =
|
|
9621
|
+
async initializeIdentityFromMasterKey(masterKey, chainCode, _derivationPath) {
|
|
9622
|
+
const basePath = this._basePath;
|
|
9157
9623
|
const fullPath = `${basePath}/0/0`;
|
|
9158
9624
|
let privateKey;
|
|
9159
9625
|
if (chainCode) {
|
|
@@ -9164,8 +9630,12 @@ var Sphere = class _Sphere {
|
|
|
9164
9630
|
chainCode
|
|
9165
9631
|
};
|
|
9166
9632
|
} else {
|
|
9167
|
-
|
|
9168
|
-
|
|
9633
|
+
const addr0 = generateAddressFromMasterKey(masterKey, 0);
|
|
9634
|
+
privateKey = addr0.privateKey;
|
|
9635
|
+
this._masterKey = {
|
|
9636
|
+
privateKey: masterKey,
|
|
9637
|
+
chainCode: ""
|
|
9638
|
+
};
|
|
9169
9639
|
}
|
|
9170
9640
|
const publicKey = getPublicKey(privateKey);
|
|
9171
9641
|
const address = publicKeyToAddress(publicKey, "alpha");
|
|
@@ -9188,8 +9658,12 @@ var Sphere = class _Sphere {
|
|
|
9188
9658
|
for (const provider of this._tokenStorageProviders.values()) {
|
|
9189
9659
|
provider.setIdentity(this._identity);
|
|
9190
9660
|
}
|
|
9191
|
-
|
|
9192
|
-
|
|
9661
|
+
if (!this._storage.isConnected()) {
|
|
9662
|
+
await this._storage.connect();
|
|
9663
|
+
}
|
|
9664
|
+
if (!this._transport.isConnected()) {
|
|
9665
|
+
await this._transport.connect();
|
|
9666
|
+
}
|
|
9193
9667
|
await this._oracle.initialize();
|
|
9194
9668
|
for (const provider of this._tokenStorageProviders.values()) {
|
|
9195
9669
|
await provider.initialize();
|
|
@@ -9205,7 +9679,7 @@ var Sphere = class _Sphere {
|
|
|
9205
9679
|
oracle: this._oracle,
|
|
9206
9680
|
emitEvent,
|
|
9207
9681
|
// Pass chain code for L1 HD derivation
|
|
9208
|
-
chainCode: this._masterKey?.chainCode,
|
|
9682
|
+
chainCode: this._masterKey?.chainCode || void 0,
|
|
9209
9683
|
price: this._priceProvider ?? void 0
|
|
9210
9684
|
});
|
|
9211
9685
|
this._communications.initialize({
|
|
@@ -9292,6 +9766,9 @@ var CurrencyUtils = {
|
|
|
9292
9766
|
toHumanReadable,
|
|
9293
9767
|
format: formatAmount
|
|
9294
9768
|
};
|
|
9769
|
+
|
|
9770
|
+
// core/index.ts
|
|
9771
|
+
init_bech32();
|
|
9295
9772
|
// Annotate the CommonJS export names for ESM import in node:
|
|
9296
9773
|
0 && (module.exports = {
|
|
9297
9774
|
CHARSET,
|
|
@@ -9313,6 +9790,7 @@ var CurrencyUtils = {
|
|
|
9313
9790
|
decryptJson,
|
|
9314
9791
|
decryptMnemonic,
|
|
9315
9792
|
decryptSimple,
|
|
9793
|
+
decryptWithSalt,
|
|
9316
9794
|
deriveAddressInfo,
|
|
9317
9795
|
deriveChildKey,
|
|
9318
9796
|
deriveKeyAtPath,
|
|
@@ -9354,6 +9832,7 @@ var CurrencyUtils = {
|
|
|
9354
9832
|
randomHex,
|
|
9355
9833
|
randomUUID,
|
|
9356
9834
|
ripemd160,
|
|
9835
|
+
scanAddressesImpl,
|
|
9357
9836
|
serializeEncrypted,
|
|
9358
9837
|
sha256,
|
|
9359
9838
|
sleep,
|