@unicitylabs/sphere-sdk 0.2.1 → 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.
@@ -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 createBech32 = encodeBech32;
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
- // core/crypto.ts
228
- var bip39 = __toESM(require("bip39"), 1);
229
- var import_crypto_js2 = __toESM(require("crypto-js"), 1);
230
- var import_elliptic = __toESM(require("elliptic"), 1);
231
- var ec = new import_elliptic.default.ec("secp256k1");
232
- var CURVE_ORDER = BigInt(
233
- "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
234
- );
235
- var DEFAULT_DERIVATION_PATH = "m/44'/0'/0'";
236
- function generateMnemonic2(strength = 128) {
237
- return bip39.generateMnemonic(strength);
238
- }
239
- function validateMnemonic2(mnemonic) {
240
- return bip39.validateMnemonic(mnemonic);
241
- }
242
- async function mnemonicToSeed2(mnemonic, passphrase = "") {
243
- const seedBuffer = await bip39.mnemonicToSeed(mnemonic, passphrase);
244
- return Buffer.from(seedBuffer).toString("hex");
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 generateMasterKey(seedHex) {
257
- const I = import_crypto_js2.default.HmacSHA512(
258
- import_crypto_js2.default.enc.Hex.parse(seedHex),
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
- privateKey: IL,
269
- chainCode: IR
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 deriveChildKey(parentPrivKey, parentChainCode, index) {
273
- const isHardened = index >= 2147483648;
274
- let data;
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
- const childKeyBigInt = (ilBigInt + parentKeyBigInt) % CURVE_ORDER;
296
- if (childKeyBigInt === 0n) {
297
- throw new Error("Invalid key: child key is zero");
217
+ if (isConnecting) {
218
+ return waitForConnection();
298
219
  }
299
- const childPrivKey = childKeyBigInt.toString(16).padStart(64, "0");
300
- return {
301
- privateKey: childPrivKey,
302
- chainCode: IR
303
- };
304
- }
305
- function deriveKeyAtPath(masterPrivKey, masterChainCode, path) {
306
- const pathParts = path.replace("m/", "").split("/");
307
- let currentKey = masterPrivKey;
308
- let currentChainCode = masterChainCode;
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;
@@ -604,79 +347,424 @@ async function rpc(method, params = []) {
604
347
  ws.send(JSON.stringify({ jsonrpc: "2.0", id, method, params }));
605
348
  });
606
349
  }
607
- async function getUtxo(address) {
608
- const scripthash = addressToScriptHash(address);
609
- const result = await rpc("blockchain.scripthash.listunspent", [scripthash]);
610
- if (!Array.isArray(result)) {
611
- console.warn("listunspent returned non-array:", result);
612
- return [];
613
- }
614
- return result.map((u) => ({
615
- tx_hash: u.tx_hash,
616
- tx_pos: u.tx_pos,
617
- value: u.value,
618
- height: u.height,
619
- address
620
- }));
350
+ async function getUtxo(address) {
351
+ const scripthash = addressToScriptHash(address);
352
+ const result = await rpc("blockchain.scripthash.listunspent", [scripthash]);
353
+ if (!Array.isArray(result)) {
354
+ console.warn("listunspent returned non-array:", result);
355
+ return [];
356
+ }
357
+ return result.map((u) => ({
358
+ tx_hash: u.tx_hash,
359
+ tx_pos: u.tx_pos,
360
+ value: u.value,
361
+ height: u.height,
362
+ address
363
+ }));
364
+ }
365
+ async function getBalance(address) {
366
+ const scriptHash = addressToScriptHash(address);
367
+ const result = await rpc("blockchain.scripthash.get_balance", [scriptHash]);
368
+ const confirmed = result.confirmed || 0;
369
+ const unconfirmed = result.unconfirmed || 0;
370
+ const totalSats = confirmed + unconfirmed;
371
+ const alpha = totalSats / 1e8;
372
+ return alpha;
373
+ }
374
+ async function broadcast(rawHex) {
375
+ return await rpc("blockchain.transaction.broadcast", [rawHex]);
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
+ }
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)));
621
679
  }
622
- async function getBalance(address) {
623
- const scriptHash = addressToScriptHash(address);
624
- const result = await rpc("blockchain.scripthash.get_balance", [scriptHash]);
625
- const confirmed = result.confirmed || 0;
626
- const unconfirmed = result.unconfirmed || 0;
627
- const totalSats = confirmed + unconfirmed;
628
- const alpha = totalSats / 1e8;
629
- return alpha;
680
+ function publicKeyToAddress(publicKey, prefix = "alpha", witnessVersion = 0) {
681
+ const pubKeyHash = hash160(publicKey);
682
+ const programBytes = hash160ToBytes(pubKeyHash);
683
+ return encodeBech32(prefix, witnessVersion, programBytes);
630
684
  }
631
- async function broadcast(rawHex) {
632
- return await rpc("blockchain.transaction.broadcast", [rawHex]);
685
+ function privateKeyToAddressInfo(privateKey, prefix = "alpha") {
686
+ const publicKey = getPublicKey(privateKey);
687
+ const address = publicKeyToAddress(publicKey, prefix);
688
+ return { address, publicKey };
633
689
  }
634
- async function getTransactionHistory(address) {
635
- const scriptHash = addressToScriptHash(address);
636
- const result = await rpc("blockchain.scripthash.get_history", [scriptHash]);
637
- if (!Array.isArray(result)) {
638
- console.warn("get_history returned non-array:", result);
639
- return [];
690
+ function hexToBytes(hex) {
691
+ const matches = hex.match(/../g);
692
+ if (!matches) {
693
+ return new Uint8Array(0);
640
694
  }
641
- return result;
695
+ return Uint8Array.from(matches.map((x) => parseInt(x, 16)));
642
696
  }
643
- async function getTransaction(txid) {
644
- return await rpc("blockchain.transaction.get", [txid, true]);
697
+ function bytesToHex2(bytes) {
698
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
645
699
  }
646
- async function getCurrentBlockHeight() {
647
- try {
648
- const header = await rpc("blockchain.headers.subscribe", []);
649
- return header?.height || 0;
650
- } catch (err) {
651
- console.error("Error getting current block height:", err);
652
- return 0;
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 disconnect() {
656
- if (ws) {
657
- intentionalClose = true;
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
- isConnected = false;
662
- isConnecting = false;
663
- reconnectAttempts = 0;
664
- isBlockSubscribed = false;
665
- Object.values(pending).forEach((req) => {
666
- if (req.timeoutId) clearTimeout(req.timeoutId);
667
- });
668
- Object.keys(pending).forEach((key) => delete pending[Number(key)]);
669
- connectionCallbacks.forEach((cb) => {
670
- if (cb.timeoutId) clearTimeout(cb.timeoutId);
671
- });
672
- connectionCallbacks.length = 0;
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
 
@@ -4139,7 +4245,7 @@ var PaymentsModule = class _PaymentsModule {
4139
4245
  memo: request.memo
4140
4246
  });
4141
4247
  console.log(`[Payments] Split token sent successfully`);
4142
- await this.removeToken(splitPlan.tokenToSplit.uiToken.id, recipientNametag);
4248
+ await this.removeToken(splitPlan.tokenToSplit.uiToken.id, recipientNametag, true);
4143
4249
  result.txHash = "split-" + Date.now().toString(16);
4144
4250
  this.log(`Split transfer completed`);
4145
4251
  }
@@ -4165,7 +4271,7 @@ var PaymentsModule = class _PaymentsModule {
4165
4271
  });
4166
4272
  console.log(`[Payments] Direct token sent successfully`);
4167
4273
  this.log(`Token ${token.id} transferred, txHash: ${result.txHash}`);
4168
- await this.removeToken(token.id, recipientNametag);
4274
+ await this.removeToken(token.id, recipientNametag, true);
4169
4275
  }
4170
4276
  result.status = "delivered";
4171
4277
  await this.save();
@@ -4314,7 +4420,7 @@ var PaymentsModule = class _PaymentsModule {
4314
4420
  );
4315
4421
  if (result.success) {
4316
4422
  const recipientNametag = request.recipient.startsWith("@") ? request.recipient.slice(1) : void 0;
4317
- await this.removeToken(tokenToSplit.id, recipientNametag);
4423
+ await this.removeToken(tokenToSplit.id, recipientNametag, true);
4318
4424
  await this.addToHistory({
4319
4425
  type: "SENT",
4320
4426
  amount: request.amount,
@@ -4820,35 +4926,39 @@ var PaymentsModule = class _PaymentsModule {
4820
4926
  const rawAssets = Array.from(assetsMap.values());
4821
4927
  let priceMap = null;
4822
4928
  if (this.priceProvider && rawAssets.length > 0) {
4823
- const registry = TokenRegistry.getInstance();
4824
- const nameToCoins = /* @__PURE__ */ new Map();
4825
- for (const asset of rawAssets) {
4826
- const def = registry.getDefinition(asset.coinId);
4827
- if (def?.name) {
4828
- const existing = nameToCoins.get(def.name);
4829
- if (existing) {
4830
- existing.push(asset.coinId);
4831
- } else {
4832
- nameToCoins.set(def.name, [asset.coinId]);
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
+ }
4833
4941
  }
4834
4942
  }
4835
- }
4836
- if (nameToCoins.size > 0) {
4837
- const tokenNames = Array.from(nameToCoins.keys());
4838
- const prices = await this.priceProvider.getPrices(tokenNames);
4839
- priceMap = /* @__PURE__ */ new Map();
4840
- for (const [name, coinIds] of nameToCoins) {
4841
- const price = prices.get(name);
4842
- if (price) {
4843
- for (const cid of coinIds) {
4844
- priceMap.set(cid, {
4845
- priceUsd: price.priceUsd,
4846
- priceEur: price.priceEur,
4847
- change24h: price.change24h
4848
- });
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
+ }
4849
4957
  }
4850
4958
  }
4851
4959
  }
4960
+ } catch (error) {
4961
+ console.warn("[Payments] Failed to fetch prices, returning assets without price data:", error);
4852
4962
  }
4853
4963
  }
4854
4964
  return rawAssets.map((raw) => {
@@ -5051,6 +5161,12 @@ var PaymentsModule = class _PaymentsModule {
5051
5161
  updatedAt: Date.now(),
5052
5162
  sdkData: typeof tokenJson === "string" ? tokenJson : JSON.stringify(tokenJson)
5053
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
+ }
5054
5170
  this.tokens.set(token.id, token);
5055
5171
  this.log(`Loaded token from file: ${tokenId}`);
5056
5172
  } catch (tokenError) {
@@ -5108,6 +5224,7 @@ var PaymentsModule = class _PaymentsModule {
5108
5224
  this.log(`Warning: Could not create tombstone for token ${tokenId.slice(0, 8)}... (missing tokenId or stateHash)`);
5109
5225
  }
5110
5226
  this.tokens.delete(tokenId);
5227
+ await this.deleteTokenFiles(token);
5111
5228
  if (!skipHistory && token.coinId && token.amount) {
5112
5229
  await this.addToHistory({
5113
5230
  type: "SENT",
@@ -5120,6 +5237,31 @@ var PaymentsModule = class _PaymentsModule {
5120
5237
  }
5121
5238
  await this.save();
5122
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
+ }
5123
5265
  // ===========================================================================
5124
5266
  // Public API - Tombstones
5125
5267
  // ===========================================================================
@@ -6624,6 +6766,20 @@ function decryptSimple(ciphertext, password) {
6624
6766
  }
6625
6767
  return result;
6626
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
+ }
6627
6783
  function encryptMnemonic(mnemonic, password) {
6628
6784
  return encryptSimple(mnemonic, password);
6629
6785
  }
@@ -6651,6 +6807,81 @@ function generateRandomKey(bytes = 32) {
6651
6807
  return import_crypto_js6.default.lib.WordArray.random(bytes).toString(import_crypto_js6.default.enc.Hex);
6652
6808
  }
6653
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
+
6654
6885
  // serialization/wallet-text.ts
6655
6886
  var import_crypto_js7 = __toESM(require("crypto-js"), 1);
6656
6887
 
@@ -6907,10 +7138,11 @@ function parseWalletText(content) {
6907
7138
  );
6908
7139
  const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
6909
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);
6910
7142
  const data = {
6911
7143
  masterKey,
6912
7144
  chainCode: chainCode ?? void 0,
6913
- descriptorPath: descriptorPath ?? void 0,
7145
+ descriptorPath: effectiveDescriptorPath,
6914
7146
  derivationMode: isBIP32 ? "bip32" : "wif_hmac"
6915
7147
  };
6916
7148
  return {
@@ -6953,10 +7185,11 @@ function parseAndDecryptWalletText(content, password) {
6953
7185
  );
6954
7186
  const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
6955
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);
6956
7189
  const data = {
6957
7190
  masterKey,
6958
7191
  chainCode: chainCode ?? void 0,
6959
- descriptorPath: descriptorPath ?? void 0,
7192
+ descriptorPath: effectiveDescriptorPath,
6960
7193
  derivationMode: isBIP32 ? "bip32" : "wif_hmac"
6961
7194
  };
6962
7195
  return {
@@ -7546,6 +7779,9 @@ var Sphere = class _Sphere {
7546
7779
  throw new Error("Either mnemonic or masterKey is required");
7547
7780
  }
7548
7781
  await _Sphere.clear({ storage: options.storage, tokenStorage: options.tokenStorage });
7782
+ if (!options.storage.isConnected()) {
7783
+ await options.storage.connect();
7784
+ }
7549
7785
  const sphere = new _Sphere(
7550
7786
  options.storage,
7551
7787
  options.transport,
@@ -7625,6 +7861,7 @@ var Sphere = class _Sphere {
7625
7861
  if (tokenStorage?.clear) {
7626
7862
  await tokenStorage.clear();
7627
7863
  }
7864
+ await vestingClassifier.destroy();
7628
7865
  if (_Sphere.instance) {
7629
7866
  await _Sphere.instance.destroy();
7630
7867
  }
@@ -7807,7 +8044,7 @@ var Sphere = class _Sphere {
7807
8044
  return {
7808
8045
  source: this._source,
7809
8046
  hasMnemonic: this._mnemonic !== null,
7810
- hasChainCode: this._masterKey?.chainCode !== void 0,
8047
+ hasChainCode: !!this._masterKey?.chainCode,
7811
8048
  derivationMode: this._derivationMode,
7812
8049
  basePath: this._basePath,
7813
8050
  address0
@@ -7860,7 +8097,7 @@ var Sphere = class _Sphere {
7860
8097
  let chainCode;
7861
8098
  if (this._masterKey) {
7862
8099
  masterPrivateKey = this._masterKey.privateKey;
7863
- chainCode = this._masterKey.chainCode;
8100
+ chainCode = this._masterKey.chainCode || void 0;
7864
8101
  }
7865
8102
  let mnemonic;
7866
8103
  let encrypted = false;
@@ -7937,7 +8174,7 @@ var Sphere = class _Sphere {
7937
8174
  }
7938
8175
  }
7939
8176
  const masterPrivateKey = this._masterKey?.privateKey || "";
7940
- const chainCode = this._masterKey?.chainCode;
8177
+ const chainCode = this._masterKey?.chainCode || void 0;
7941
8178
  const isBIP32 = this._derivationMode === "bip32";
7942
8179
  const descriptorPath = this._basePath.replace(/^m\//, "");
7943
8180
  if (options.password) {
@@ -8145,19 +8382,84 @@ var Sphere = class _Sphere {
8145
8382
  }
8146
8383
  if (fileType === "json") {
8147
8384
  const content = typeof fileContent === "string" ? fileContent : new TextDecoder().decode(fileContent);
8148
- const result = await _Sphere.importFromJSON({
8149
- jsonContent: content,
8150
- password,
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"),
8151
8456
  storage: options.storage,
8152
8457
  transport: options.transport,
8153
8458
  oracle: options.oracle,
8154
- tokenStorage: options.tokenStorage
8459
+ tokenStorage: options.tokenStorage,
8460
+ nametag: options.nametag
8155
8461
  });
8156
- if (result.success) {
8157
- const sphere = _Sphere.getInstance();
8158
- return { success: true, sphere, mnemonic: result.mnemonic };
8159
- }
8160
- return result;
8462
+ return { success: true, sphere };
8161
8463
  }
8162
8464
  return { success: false, error: "Unsupported file type" };
8163
8465
  }
@@ -8455,7 +8757,7 @@ var Sphere = class _Sphere {
8455
8757
  transport: this._transport,
8456
8758
  oracle: this._oracle,
8457
8759
  emitEvent,
8458
- chainCode: this._masterKey?.chainCode,
8760
+ chainCode: this._masterKey?.chainCode || void 0,
8459
8761
  price: this._priceProvider ?? void 0
8460
8762
  });
8461
8763
  this._communications.initialize({
@@ -8500,6 +8802,9 @@ var Sphere = class _Sphere {
8500
8802
  if (!this._masterKey) {
8501
8803
  throw new Error("HD derivation requires master key with chain code");
8502
8804
  }
8805
+ if (this._derivationMode === "wif_hmac") {
8806
+ return generateAddressFromMasterKey(this._masterKey.privateKey, index);
8807
+ }
8503
8808
  const info = deriveAddressInfo(
8504
8809
  this._masterKey,
8505
8810
  this._basePath,
@@ -8571,6 +8876,66 @@ var Sphere = class _Sphere {
8571
8876
  }
8572
8877
  return addresses;
8573
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
+ }
8574
8939
  // ===========================================================================
8575
8940
  // Public Methods - Status
8576
8941
  // ===========================================================================
@@ -9152,8 +9517,8 @@ var Sphere = class _Sphere {
9152
9517
  };
9153
9518
  this._masterKey = masterKey;
9154
9519
  }
9155
- async initializeIdentityFromMasterKey(masterKey, chainCode, derivationPath) {
9156
- const basePath = derivationPath ?? DEFAULT_BASE_PATH;
9520
+ async initializeIdentityFromMasterKey(masterKey, chainCode, _derivationPath) {
9521
+ const basePath = this._basePath;
9157
9522
  const fullPath = `${basePath}/0/0`;
9158
9523
  let privateKey;
9159
9524
  if (chainCode) {
@@ -9164,8 +9529,12 @@ var Sphere = class _Sphere {
9164
9529
  chainCode
9165
9530
  };
9166
9531
  } else {
9167
- privateKey = masterKey;
9168
- this._masterKey = null;
9532
+ const addr0 = generateAddressFromMasterKey(masterKey, 0);
9533
+ privateKey = addr0.privateKey;
9534
+ this._masterKey = {
9535
+ privateKey: masterKey,
9536
+ chainCode: ""
9537
+ };
9169
9538
  }
9170
9539
  const publicKey = getPublicKey(privateKey);
9171
9540
  const address = publicKeyToAddress(publicKey, "alpha");
@@ -9205,7 +9574,7 @@ var Sphere = class _Sphere {
9205
9574
  oracle: this._oracle,
9206
9575
  emitEvent,
9207
9576
  // Pass chain code for L1 HD derivation
9208
- chainCode: this._masterKey?.chainCode,
9577
+ chainCode: this._masterKey?.chainCode || void 0,
9209
9578
  price: this._priceProvider ?? void 0
9210
9579
  });
9211
9580
  this._communications.initialize({
@@ -9292,6 +9661,9 @@ var CurrencyUtils = {
9292
9661
  toHumanReadable,
9293
9662
  format: formatAmount
9294
9663
  };
9664
+
9665
+ // core/index.ts
9666
+ init_bech32();
9295
9667
  // Annotate the CommonJS export names for ESM import in node:
9296
9668
  0 && (module.exports = {
9297
9669
  CHARSET,
@@ -9313,6 +9685,7 @@ var CurrencyUtils = {
9313
9685
  decryptJson,
9314
9686
  decryptMnemonic,
9315
9687
  decryptSimple,
9688
+ decryptWithSalt,
9316
9689
  deriveAddressInfo,
9317
9690
  deriveChildKey,
9318
9691
  deriveKeyAtPath,
@@ -9354,6 +9727,7 @@ var CurrencyUtils = {
9354
9727
  randomHex,
9355
9728
  randomUUID,
9356
9729
  ripemd160,
9730
+ scanAddressesImpl,
9357
9731
  serializeEncrypted,
9358
9732
  sha256,
9359
9733
  sleep,