@unicitylabs/sphere-sdk 0.5.4 → 0.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/connect/index.cjs +128 -22
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.js +128 -22
- package/dist/connect/index.js.map +1 -1
- package/dist/core/index.cjs +670 -473
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +123 -2
- package/dist/core/index.d.ts +123 -2
- package/dist/core/index.js +667 -473
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +306 -193
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +306 -193
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/browser/ipfs.cjs +134 -19
- package/dist/impl/browser/ipfs.cjs.map +1 -1
- package/dist/impl/browser/ipfs.js +134 -19
- package/dist/impl/browser/ipfs.js.map +1 -1
- package/dist/impl/nodejs/connect/index.cjs +101 -6
- package/dist/impl/nodejs/connect/index.cjs.map +1 -1
- package/dist/impl/nodejs/connect/index.js +101 -6
- package/dist/impl/nodejs/connect/index.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +267 -152
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +2 -1
- package/dist/impl/nodejs/index.d.ts +2 -1
- package/dist/impl/nodejs/index.js +267 -152
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +682 -493
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +124 -8
- package/dist/index.d.ts +124 -8
- package/dist/index.js +680 -493
- package/dist/index.js.map +1 -1
- package/dist/l1/index.cjs +139 -32
- package/dist/l1/index.cjs.map +1 -1
- package/dist/l1/index.js +139 -32
- package/dist/l1/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14,6 +14,125 @@ var __export = (target, all) => {
|
|
|
14
14
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
// core/logger.ts
|
|
18
|
+
function getState() {
|
|
19
|
+
const g = globalThis;
|
|
20
|
+
if (!g[LOGGER_KEY]) {
|
|
21
|
+
g[LOGGER_KEY] = { debug: false, tags: {}, handler: null };
|
|
22
|
+
}
|
|
23
|
+
return g[LOGGER_KEY];
|
|
24
|
+
}
|
|
25
|
+
function isEnabled(tag) {
|
|
26
|
+
const state = getState();
|
|
27
|
+
if (tag in state.tags) return state.tags[tag];
|
|
28
|
+
return state.debug;
|
|
29
|
+
}
|
|
30
|
+
var LOGGER_KEY, logger;
|
|
31
|
+
var init_logger = __esm({
|
|
32
|
+
"core/logger.ts"() {
|
|
33
|
+
"use strict";
|
|
34
|
+
LOGGER_KEY = "__sphere_sdk_logger__";
|
|
35
|
+
logger = {
|
|
36
|
+
/**
|
|
37
|
+
* Configure the logger. Can be called multiple times (last write wins).
|
|
38
|
+
* Typically called by createBrowserProviders(), createNodeProviders(), or Sphere.init().
|
|
39
|
+
*/
|
|
40
|
+
configure(config) {
|
|
41
|
+
const state = getState();
|
|
42
|
+
if (config.debug !== void 0) state.debug = config.debug;
|
|
43
|
+
if (config.handler !== void 0) state.handler = config.handler;
|
|
44
|
+
},
|
|
45
|
+
/**
|
|
46
|
+
* Enable/disable debug logging for a specific tag.
|
|
47
|
+
* Per-tag setting overrides the global debug flag.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* logger.setTagDebug('Nostr', true); // enable only Nostr logs
|
|
52
|
+
* logger.setTagDebug('Nostr', false); // disable Nostr logs even if global debug=true
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
setTagDebug(tag, enabled) {
|
|
56
|
+
getState().tags[tag] = enabled;
|
|
57
|
+
},
|
|
58
|
+
/**
|
|
59
|
+
* Clear per-tag override, falling back to global debug flag.
|
|
60
|
+
*/
|
|
61
|
+
clearTagDebug(tag) {
|
|
62
|
+
delete getState().tags[tag];
|
|
63
|
+
},
|
|
64
|
+
/** Returns true if debug mode is enabled for the given tag (or globally). */
|
|
65
|
+
isDebugEnabled(tag) {
|
|
66
|
+
if (tag) return isEnabled(tag);
|
|
67
|
+
return getState().debug;
|
|
68
|
+
},
|
|
69
|
+
/**
|
|
70
|
+
* Debug-level log. Only shown when debug is enabled (globally or for this tag).
|
|
71
|
+
* Use for detailed operational information.
|
|
72
|
+
*/
|
|
73
|
+
debug(tag, message, ...args) {
|
|
74
|
+
if (!isEnabled(tag)) return;
|
|
75
|
+
const state = getState();
|
|
76
|
+
if (state.handler) {
|
|
77
|
+
state.handler("debug", tag, message, ...args);
|
|
78
|
+
} else {
|
|
79
|
+
console.log(`[${tag}]`, message, ...args);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* Warning-level log. ALWAYS shown regardless of debug flag.
|
|
84
|
+
* Use for important but non-critical issues (timeouts, retries, degraded state).
|
|
85
|
+
*/
|
|
86
|
+
warn(tag, message, ...args) {
|
|
87
|
+
const state = getState();
|
|
88
|
+
if (state.handler) {
|
|
89
|
+
state.handler("warn", tag, message, ...args);
|
|
90
|
+
} else {
|
|
91
|
+
console.warn(`[${tag}]`, message, ...args);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
/**
|
|
95
|
+
* Error-level log. ALWAYS shown regardless of debug flag.
|
|
96
|
+
* Use for critical failures that should never be silenced.
|
|
97
|
+
*/
|
|
98
|
+
error(tag, message, ...args) {
|
|
99
|
+
const state = getState();
|
|
100
|
+
if (state.handler) {
|
|
101
|
+
state.handler("error", tag, message, ...args);
|
|
102
|
+
} else {
|
|
103
|
+
console.error(`[${tag}]`, message, ...args);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
/** Reset all logger state (debug flag, tags, handler). Primarily for tests. */
|
|
107
|
+
reset() {
|
|
108
|
+
const g = globalThis;
|
|
109
|
+
delete g[LOGGER_KEY];
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// core/errors.ts
|
|
116
|
+
function isSphereError(err) {
|
|
117
|
+
return err instanceof SphereError;
|
|
118
|
+
}
|
|
119
|
+
var SphereError;
|
|
120
|
+
var init_errors = __esm({
|
|
121
|
+
"core/errors.ts"() {
|
|
122
|
+
"use strict";
|
|
123
|
+
SphereError = class extends Error {
|
|
124
|
+
code;
|
|
125
|
+
cause;
|
|
126
|
+
constructor(message, code, cause) {
|
|
127
|
+
super(message);
|
|
128
|
+
this.name = "SphereError";
|
|
129
|
+
this.code = code;
|
|
130
|
+
this.cause = cause;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
17
136
|
// constants.ts
|
|
18
137
|
function getAddressStorageKey(addressId, key) {
|
|
19
138
|
return `${addressId}_${key}`;
|
|
@@ -294,11 +413,11 @@ function bech32Checksum(hrp, data) {
|
|
|
294
413
|
}
|
|
295
414
|
function encodeBech32(hrp, version, program) {
|
|
296
415
|
if (version < 0 || version > 16) {
|
|
297
|
-
throw new
|
|
416
|
+
throw new SphereError("Invalid witness version", "VALIDATION_ERROR");
|
|
298
417
|
}
|
|
299
418
|
const converted = convertBits(Array.from(program), 8, 5, true);
|
|
300
419
|
if (!converted) {
|
|
301
|
-
throw new
|
|
420
|
+
throw new SphereError("Failed to convert bits", "VALIDATION_ERROR");
|
|
302
421
|
}
|
|
303
422
|
const data = [version].concat(converted);
|
|
304
423
|
const checksum = bech32Checksum(hrp, data);
|
|
@@ -351,6 +470,7 @@ var CHARSET, GENERATOR, createBech32;
|
|
|
351
470
|
var init_bech32 = __esm({
|
|
352
471
|
"core/bech32.ts"() {
|
|
353
472
|
"use strict";
|
|
473
|
+
init_errors();
|
|
354
474
|
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
355
475
|
GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
|
|
356
476
|
createBech32 = encodeBech32;
|
|
@@ -364,7 +484,7 @@ function bytesToHex(buf) {
|
|
|
364
484
|
}
|
|
365
485
|
function addressToScriptHash(address) {
|
|
366
486
|
const decoded = decodeBech32(address);
|
|
367
|
-
if (!decoded) throw new
|
|
487
|
+
if (!decoded) throw new SphereError("Invalid bech32 address: " + address, "VALIDATION_ERROR");
|
|
368
488
|
const scriptHex = "0014" + bytesToHex(decoded.data);
|
|
369
489
|
const sha = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(scriptHex)).toString();
|
|
370
490
|
return sha.match(/../g).reverse().join("");
|
|
@@ -373,6 +493,7 @@ var init_addressToScriptHash = __esm({
|
|
|
373
493
|
"l1/addressToScriptHash.ts"() {
|
|
374
494
|
"use strict";
|
|
375
495
|
init_bech32();
|
|
496
|
+
init_errors();
|
|
376
497
|
}
|
|
377
498
|
});
|
|
378
499
|
|
|
@@ -432,7 +553,7 @@ function connect(endpoint = DEFAULT_ENDPOINT) {
|
|
|
432
553
|
try {
|
|
433
554
|
ws = new WebSocket(endpoint);
|
|
434
555
|
} catch (err) {
|
|
435
|
-
|
|
556
|
+
logger.error("L1", "WebSocket constructor threw exception:", err);
|
|
436
557
|
isConnecting = false;
|
|
437
558
|
reject(err);
|
|
438
559
|
return;
|
|
@@ -468,7 +589,7 @@ function connect(endpoint = DEFAULT_ENDPOINT) {
|
|
|
468
589
|
return;
|
|
469
590
|
}
|
|
470
591
|
if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
471
|
-
|
|
592
|
+
logger.error("L1", "Max reconnect attempts reached. Giving up.");
|
|
472
593
|
isConnecting = false;
|
|
473
594
|
const error = new Error("Max reconnect attempts reached");
|
|
474
595
|
connectionCallbacks.forEach((cb) => {
|
|
@@ -484,8 +605,9 @@ function connect(endpoint = DEFAULT_ENDPOINT) {
|
|
|
484
605
|
}
|
|
485
606
|
const delay = Math.min(BASE_DELAY * Math.pow(2, reconnectAttempts), MAX_DELAY);
|
|
486
607
|
reconnectAttempts++;
|
|
487
|
-
|
|
488
|
-
|
|
608
|
+
logger.warn(
|
|
609
|
+
"L1",
|
|
610
|
+
`WebSocket closed unexpectedly. Reconnecting in ${delay}ms (attempt ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...`
|
|
489
611
|
);
|
|
490
612
|
setTimeout(() => {
|
|
491
613
|
connect(endpoint).then(() => {
|
|
@@ -502,7 +624,7 @@ function connect(endpoint = DEFAULT_ENDPOINT) {
|
|
|
502
624
|
}, delay);
|
|
503
625
|
};
|
|
504
626
|
ws.onerror = (err) => {
|
|
505
|
-
|
|
627
|
+
logger.error("L1", "WebSocket error:", err);
|
|
506
628
|
};
|
|
507
629
|
ws.onmessage = (msg) => handleMessage(msg);
|
|
508
630
|
});
|
|
@@ -560,7 +682,7 @@ async function getUtxo(address) {
|
|
|
560
682
|
const scripthash = addressToScriptHash(address);
|
|
561
683
|
const result = await rpc("blockchain.scripthash.listunspent", [scripthash]);
|
|
562
684
|
if (!Array.isArray(result)) {
|
|
563
|
-
|
|
685
|
+
logger.warn("L1", "listunspent returned non-array:", result);
|
|
564
686
|
return [];
|
|
565
687
|
}
|
|
566
688
|
return result.map((u) => ({
|
|
@@ -612,7 +734,7 @@ async function getTransactionHistory(address) {
|
|
|
612
734
|
const scriptHash = addressToScriptHash(address);
|
|
613
735
|
const result = await rpc("blockchain.scripthash.get_history", [scriptHash]);
|
|
614
736
|
if (!Array.isArray(result)) {
|
|
615
|
-
|
|
737
|
+
logger.warn("L1", "get_history returned non-array:", result);
|
|
616
738
|
return [];
|
|
617
739
|
}
|
|
618
740
|
return result;
|
|
@@ -628,7 +750,7 @@ async function getCurrentBlockHeight() {
|
|
|
628
750
|
const header = await rpc("blockchain.headers.subscribe", []);
|
|
629
751
|
return header?.height || 0;
|
|
630
752
|
} catch (err) {
|
|
631
|
-
|
|
753
|
+
logger.error("L1", "Error getting current block height:", err);
|
|
632
754
|
return 0;
|
|
633
755
|
}
|
|
634
756
|
}
|
|
@@ -657,6 +779,7 @@ var DEFAULT_ENDPOINT, ws, isConnected, isConnecting, requestId, intentionalClose
|
|
|
657
779
|
var init_network = __esm({
|
|
658
780
|
"l1/network.ts"() {
|
|
659
781
|
"use strict";
|
|
782
|
+
init_logger();
|
|
660
783
|
init_addressToScriptHash();
|
|
661
784
|
init_constants();
|
|
662
785
|
DEFAULT_ENDPOINT = DEFAULT_ELECTRUM_URL;
|
|
@@ -679,7 +802,12 @@ var init_network = __esm({
|
|
|
679
802
|
}
|
|
680
803
|
});
|
|
681
804
|
|
|
805
|
+
// core/Sphere.ts
|
|
806
|
+
init_logger();
|
|
807
|
+
init_errors();
|
|
808
|
+
|
|
682
809
|
// modules/payments/L1PaymentsModule.ts
|
|
810
|
+
init_errors();
|
|
683
811
|
init_constants();
|
|
684
812
|
|
|
685
813
|
// l1/index.ts
|
|
@@ -772,6 +900,7 @@ init_addressToScriptHash();
|
|
|
772
900
|
|
|
773
901
|
// core/crypto.ts
|
|
774
902
|
init_bech32();
|
|
903
|
+
init_errors();
|
|
775
904
|
import * as bip39 from "bip39";
|
|
776
905
|
import CryptoJS2 from "crypto-js";
|
|
777
906
|
import elliptic from "elliptic";
|
|
@@ -798,7 +927,7 @@ function generateMasterKey(seedHex) {
|
|
|
798
927
|
const IR = I.substring(64);
|
|
799
928
|
const masterKeyBigInt = BigInt("0x" + IL);
|
|
800
929
|
if (masterKeyBigInt === 0n || masterKeyBigInt >= CURVE_ORDER) {
|
|
801
|
-
throw new
|
|
930
|
+
throw new SphereError("Invalid master key generated", "VALIDATION_ERROR");
|
|
802
931
|
}
|
|
803
932
|
return {
|
|
804
933
|
privateKey: IL,
|
|
@@ -826,11 +955,11 @@ function deriveChildKey(parentPrivKey, parentChainCode, index) {
|
|
|
826
955
|
const ilBigInt = BigInt("0x" + IL);
|
|
827
956
|
const parentKeyBigInt = BigInt("0x" + parentPrivKey);
|
|
828
957
|
if (ilBigInt >= CURVE_ORDER) {
|
|
829
|
-
throw new
|
|
958
|
+
throw new SphereError("Invalid key: IL >= curve order", "VALIDATION_ERROR");
|
|
830
959
|
}
|
|
831
960
|
const childKeyBigInt = (ilBigInt + parentKeyBigInt) % CURVE_ORDER;
|
|
832
961
|
if (childKeyBigInt === 0n) {
|
|
833
|
-
throw new
|
|
962
|
+
throw new SphereError("Invalid key: child key is zero", "VALIDATION_ERROR");
|
|
834
963
|
}
|
|
835
964
|
const childPrivKey = childKeyBigInt.toString(16).padStart(64, "0");
|
|
836
965
|
return {
|
|
@@ -916,7 +1045,7 @@ function randomBytes(length) {
|
|
|
916
1045
|
}
|
|
917
1046
|
function identityFromMnemonicSync(mnemonic, passphrase = "") {
|
|
918
1047
|
if (!validateMnemonic2(mnemonic)) {
|
|
919
|
-
throw new
|
|
1048
|
+
throw new SphereError("Invalid mnemonic phrase", "INVALID_IDENTITY");
|
|
920
1049
|
}
|
|
921
1050
|
const seedHex = mnemonicToSeedSync2(mnemonic, passphrase);
|
|
922
1051
|
return generateMasterKey(seedHex);
|
|
@@ -1049,12 +1178,19 @@ function generateHDAddress(masterPriv, chainCode, index) {
|
|
|
1049
1178
|
init_network();
|
|
1050
1179
|
|
|
1051
1180
|
// l1/tx.ts
|
|
1181
|
+
init_logger();
|
|
1182
|
+
init_errors();
|
|
1052
1183
|
init_network();
|
|
1053
1184
|
init_bech32();
|
|
1054
1185
|
import CryptoJS5 from "crypto-js";
|
|
1055
1186
|
import elliptic2 from "elliptic";
|
|
1056
1187
|
|
|
1188
|
+
// l1/vestingState.ts
|
|
1189
|
+
init_logger();
|
|
1190
|
+
init_errors();
|
|
1191
|
+
|
|
1057
1192
|
// l1/vesting.ts
|
|
1193
|
+
init_logger();
|
|
1058
1194
|
init_network();
|
|
1059
1195
|
var VESTING_THRESHOLD = 28e4;
|
|
1060
1196
|
var currentBlockHeight = null;
|
|
@@ -1297,7 +1433,7 @@ var VestingClassifier = class {
|
|
|
1297
1433
|
await new Promise((resolve) => {
|
|
1298
1434
|
const req = indexedDB.deleteDatabase(this.dbName);
|
|
1299
1435
|
const timer = setTimeout(() => {
|
|
1300
|
-
|
|
1436
|
+
logger.warn("L1", ` destroy: deleteDatabase timed out for ${this.dbName}`);
|
|
1301
1437
|
resolve();
|
|
1302
1438
|
}, 3e3);
|
|
1303
1439
|
req.onsuccess = () => {
|
|
@@ -1309,7 +1445,7 @@ var VestingClassifier = class {
|
|
|
1309
1445
|
resolve();
|
|
1310
1446
|
};
|
|
1311
1447
|
req.onblocked = () => {
|
|
1312
|
-
|
|
1448
|
+
logger.warn("L1", ` destroy: deleteDatabase blocked for ${this.dbName}, waiting...`);
|
|
1313
1449
|
};
|
|
1314
1450
|
});
|
|
1315
1451
|
}
|
|
@@ -1327,7 +1463,7 @@ var VestingStateManager = class {
|
|
|
1327
1463
|
*/
|
|
1328
1464
|
setMode(mode) {
|
|
1329
1465
|
if (!["all", "vested", "unvested"].includes(mode)) {
|
|
1330
|
-
throw new
|
|
1466
|
+
throw new SphereError(`Invalid vesting mode: ${mode}`, "VALIDATION_ERROR");
|
|
1331
1467
|
}
|
|
1332
1468
|
this.currentMode = mode;
|
|
1333
1469
|
}
|
|
@@ -1364,10 +1500,10 @@ var VestingStateManager = class {
|
|
|
1364
1500
|
}
|
|
1365
1501
|
});
|
|
1366
1502
|
if (result.errors.length > 0) {
|
|
1367
|
-
|
|
1503
|
+
logger.warn("L1", `Vesting classification errors: ${result.errors.length}`);
|
|
1368
1504
|
result.errors.slice(0, 5).forEach((err) => {
|
|
1369
1505
|
const txHash = err.utxo.tx_hash || err.utxo.txid;
|
|
1370
|
-
|
|
1506
|
+
logger.warn("L1", ` ${txHash}: ${err.error}`);
|
|
1371
1507
|
});
|
|
1372
1508
|
}
|
|
1373
1509
|
} finally {
|
|
@@ -1444,6 +1580,7 @@ var VestingStateManager = class {
|
|
|
1444
1580
|
var vestingState = new VestingStateManager();
|
|
1445
1581
|
|
|
1446
1582
|
// l1/addressHelpers.ts
|
|
1583
|
+
init_errors();
|
|
1447
1584
|
var WalletAddressHelper = class {
|
|
1448
1585
|
/**
|
|
1449
1586
|
* Find address by BIP32 derivation path
|
|
@@ -1489,16 +1626,17 @@ var WalletAddressHelper = class {
|
|
|
1489
1626
|
*/
|
|
1490
1627
|
static add(wallet, newAddress) {
|
|
1491
1628
|
if (!newAddress.path) {
|
|
1492
|
-
throw new
|
|
1629
|
+
throw new SphereError("Cannot add address without a path", "INVALID_CONFIG");
|
|
1493
1630
|
}
|
|
1494
1631
|
const existing = this.findByPath(wallet, newAddress.path);
|
|
1495
1632
|
if (existing) {
|
|
1496
1633
|
if (existing.address !== newAddress.address) {
|
|
1497
|
-
throw new
|
|
1634
|
+
throw new SphereError(
|
|
1498
1635
|
`CRITICAL: Attempted to overwrite address for path ${newAddress.path}
|
|
1499
1636
|
Existing: ${existing.address}
|
|
1500
1637
|
New: ${newAddress.address}
|
|
1501
|
-
This indicates master key corruption or derivation logic error
|
|
1638
|
+
This indicates master key corruption or derivation logic error.`,
|
|
1639
|
+
"INVALID_CONFIG"
|
|
1502
1640
|
);
|
|
1503
1641
|
}
|
|
1504
1642
|
return wallet;
|
|
@@ -1557,9 +1695,10 @@ This indicates master key corruption or derivation logic error.`
|
|
|
1557
1695
|
const uniquePaths = new Set(paths);
|
|
1558
1696
|
if (paths.length !== uniquePaths.size) {
|
|
1559
1697
|
const duplicates = paths.filter((p, i) => paths.indexOf(p) !== i);
|
|
1560
|
-
throw new
|
|
1698
|
+
throw new SphereError(
|
|
1561
1699
|
`CRITICAL: Wallet has duplicate paths: ${duplicates.join(", ")}
|
|
1562
|
-
This indicates data corruption. Please restore from backup
|
|
1700
|
+
This indicates data corruption. Please restore from backup.`,
|
|
1701
|
+
"INVALID_CONFIG"
|
|
1563
1702
|
);
|
|
1564
1703
|
}
|
|
1565
1704
|
}
|
|
@@ -1591,11 +1730,11 @@ var DUST = 546;
|
|
|
1591
1730
|
var SAT = 1e8;
|
|
1592
1731
|
function createScriptPubKey(address) {
|
|
1593
1732
|
if (!address || typeof address !== "string") {
|
|
1594
|
-
throw new
|
|
1733
|
+
throw new SphereError("Invalid address: must be a string", "VALIDATION_ERROR");
|
|
1595
1734
|
}
|
|
1596
1735
|
const decoded = decodeBech32(address);
|
|
1597
1736
|
if (!decoded) {
|
|
1598
|
-
throw new
|
|
1737
|
+
throw new SphereError("Invalid bech32 address: " + address, "VALIDATION_ERROR");
|
|
1599
1738
|
}
|
|
1600
1739
|
const dataHex = Array.from(decoded.data).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
1601
1740
|
return "0014" + dataHex;
|
|
@@ -1719,7 +1858,7 @@ function createAndSignTransaction(wallet, txPlan) {
|
|
|
1719
1858
|
privateKeyHex = wallet.masterPrivateKey;
|
|
1720
1859
|
}
|
|
1721
1860
|
if (!privateKeyHex) {
|
|
1722
|
-
throw new
|
|
1861
|
+
throw new SphereError("No private key available for address: " + fromAddress, "INVALID_CONFIG");
|
|
1723
1862
|
}
|
|
1724
1863
|
const keyPair = ec2.keyFromPrivate(privateKeyHex, "hex");
|
|
1725
1864
|
const publicKey = keyPair.getPublic(true, "hex");
|
|
@@ -1818,7 +1957,7 @@ function collectUtxosForAmount(utxoList, amountSats, recipientAddress, senderAdd
|
|
|
1818
1957
|
}
|
|
1819
1958
|
async function createTransactionPlan(wallet, toAddress, amountAlpha, fromAddress) {
|
|
1820
1959
|
if (!decodeBech32(toAddress)) {
|
|
1821
|
-
throw new
|
|
1960
|
+
throw new SphereError("Invalid recipient address", "INVALID_RECIPIENT");
|
|
1822
1961
|
}
|
|
1823
1962
|
const defaultAddr = WalletAddressHelper.getDefault(wallet);
|
|
1824
1963
|
const senderAddress = fromAddress || defaultAddr.address;
|
|
@@ -1827,21 +1966,21 @@ async function createTransactionPlan(wallet, toAddress, amountAlpha, fromAddress
|
|
|
1827
1966
|
const currentMode = vestingState.getMode();
|
|
1828
1967
|
if (vestingState.hasClassifiedData(senderAddress)) {
|
|
1829
1968
|
utxos = vestingState.getFilteredUtxos(senderAddress);
|
|
1830
|
-
|
|
1969
|
+
logger.debug("L1", `Using ${utxos.length} ${currentMode} UTXOs`);
|
|
1831
1970
|
} else {
|
|
1832
1971
|
utxos = await getUtxo(senderAddress);
|
|
1833
|
-
|
|
1972
|
+
logger.debug("L1", `Using ${utxos.length} UTXOs (vesting not classified yet)`);
|
|
1834
1973
|
}
|
|
1835
1974
|
if (!Array.isArray(utxos) || utxos.length === 0) {
|
|
1836
1975
|
const modeText = currentMode !== "all" ? ` (${currentMode} coins)` : "";
|
|
1837
|
-
throw new
|
|
1976
|
+
throw new SphereError(`No UTXOs available${modeText} for address: ` + senderAddress, "INSUFFICIENT_BALANCE");
|
|
1838
1977
|
}
|
|
1839
1978
|
return collectUtxosForAmount(utxos, amountSats, toAddress, senderAddress);
|
|
1840
1979
|
}
|
|
1841
1980
|
async function sendAlpha(wallet, toAddress, amountAlpha, fromAddress) {
|
|
1842
1981
|
const plan = await createTransactionPlan(wallet, toAddress, amountAlpha, fromAddress);
|
|
1843
1982
|
if (!plan.success) {
|
|
1844
|
-
throw new
|
|
1983
|
+
throw new SphereError(plan.error || "Transaction planning failed", "TRANSFER_FAILED");
|
|
1845
1984
|
}
|
|
1846
1985
|
const results = [];
|
|
1847
1986
|
for (const tx of plan.transactions) {
|
|
@@ -1908,7 +2047,7 @@ var L1PaymentsModule = class {
|
|
|
1908
2047
|
*/
|
|
1909
2048
|
async ensureConnected() {
|
|
1910
2049
|
if (this._disabled) {
|
|
1911
|
-
throw new
|
|
2050
|
+
throw new SphereError("L1 provider is disabled", "NOT_INITIALIZED");
|
|
1912
2051
|
}
|
|
1913
2052
|
if (!isWebSocketConnected() && this._config.electrumUrl) {
|
|
1914
2053
|
await connect(this._config.electrumUrl);
|
|
@@ -1963,8 +2102,9 @@ var L1PaymentsModule = class {
|
|
|
1963
2102
|
const l1Address = await this.resolveNametagToL1Address(recipient);
|
|
1964
2103
|
return l1Address;
|
|
1965
2104
|
} catch {
|
|
1966
|
-
throw new
|
|
1967
|
-
`Recipient "${recipient}" is not a valid nametag or L1 address. Use @nametag for explicit nametag or a valid alpha1... address
|
|
2105
|
+
throw new SphereError(
|
|
2106
|
+
`Recipient "${recipient}" is not a valid nametag or L1 address. Use @nametag for explicit nametag or a valid alpha1... address.`,
|
|
2107
|
+
"INVALID_RECIPIENT"
|
|
1968
2108
|
);
|
|
1969
2109
|
}
|
|
1970
2110
|
}
|
|
@@ -1973,15 +2113,16 @@ var L1PaymentsModule = class {
|
|
|
1973
2113
|
*/
|
|
1974
2114
|
async resolveNametagToL1Address(nametag) {
|
|
1975
2115
|
if (!this._transport?.resolve) {
|
|
1976
|
-
throw new
|
|
2116
|
+
throw new SphereError("Transport provider does not support resolution", "TRANSPORT_ERROR");
|
|
1977
2117
|
}
|
|
1978
2118
|
const info = await this._transport.resolve(nametag);
|
|
1979
2119
|
if (!info) {
|
|
1980
|
-
throw new
|
|
2120
|
+
throw new SphereError(`Nametag not found: ${nametag}`, "INVALID_RECIPIENT");
|
|
1981
2121
|
}
|
|
1982
2122
|
if (!info.l1Address) {
|
|
1983
|
-
throw new
|
|
1984
|
-
`Nametag @${nametag} does not have L1 address information. The owner needs to update their nametag registration
|
|
2123
|
+
throw new SphereError(
|
|
2124
|
+
`Nametag @${nametag} does not have L1 address information. The owner needs to update their nametag registration.`,
|
|
2125
|
+
"INVALID_RECIPIENT"
|
|
1985
2126
|
);
|
|
1986
2127
|
}
|
|
1987
2128
|
return info.l1Address;
|
|
@@ -2251,7 +2392,7 @@ var L1PaymentsModule = class {
|
|
|
2251
2392
|
}
|
|
2252
2393
|
ensureInitialized() {
|
|
2253
2394
|
if (!this._initialized) {
|
|
2254
|
-
throw new
|
|
2395
|
+
throw new SphereError("L1PaymentsModule not initialized", "NOT_INITIALIZED");
|
|
2255
2396
|
}
|
|
2256
2397
|
}
|
|
2257
2398
|
_getWatchedAddresses() {
|
|
@@ -2276,6 +2417,7 @@ function createL1PaymentsModule(config) {
|
|
|
2276
2417
|
}
|
|
2277
2418
|
|
|
2278
2419
|
// modules/payments/TokenSplitCalculator.ts
|
|
2420
|
+
init_logger();
|
|
2279
2421
|
import { Token as SdkToken } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
2280
2422
|
import { CoinId } from "@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId";
|
|
2281
2423
|
var TokenSplitCalculator = class {
|
|
@@ -2298,7 +2440,7 @@ var TokenSplitCalculator = class {
|
|
|
2298
2440
|
const sdkToken = await SdkToken.fromJSON(parsed);
|
|
2299
2441
|
const realAmount = this.getTokenBalance(sdkToken, targetCoinIdHex);
|
|
2300
2442
|
if (realAmount <= 0n) {
|
|
2301
|
-
|
|
2443
|
+
logger.warn("TokenSplit", `Token ${t.id} has 0 balance for coinId ${targetCoinIdHex}`);
|
|
2302
2444
|
continue;
|
|
2303
2445
|
}
|
|
2304
2446
|
candidates.push({
|
|
@@ -2307,14 +2449,15 @@ var TokenSplitCalculator = class {
|
|
|
2307
2449
|
uiToken: t
|
|
2308
2450
|
});
|
|
2309
2451
|
} catch (e) {
|
|
2310
|
-
|
|
2452
|
+
logger.warn("TokenSplit", "Failed to parse token", t.id, e);
|
|
2311
2453
|
}
|
|
2312
2454
|
}
|
|
2313
2455
|
candidates.sort((a, b) => a.amount < b.amount ? -1 : 1);
|
|
2314
2456
|
const totalAvailable = candidates.reduce((sum, t) => sum + t.amount, 0n);
|
|
2315
2457
|
if (totalAvailable < targetAmount) {
|
|
2316
|
-
|
|
2317
|
-
|
|
2458
|
+
logger.error(
|
|
2459
|
+
"TokenSplit",
|
|
2460
|
+
`Insufficient funds. Available: ${totalAvailable}, Required: ${targetAmount}`
|
|
2318
2461
|
);
|
|
2319
2462
|
return null;
|
|
2320
2463
|
}
|
|
@@ -2412,6 +2555,8 @@ var TokenSplitCalculator = class {
|
|
|
2412
2555
|
};
|
|
2413
2556
|
|
|
2414
2557
|
// modules/payments/TokenSplitExecutor.ts
|
|
2558
|
+
init_logger();
|
|
2559
|
+
init_errors();
|
|
2415
2560
|
import { Token } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
2416
2561
|
import { TokenId } from "@unicitylabs/state-transition-sdk/lib/token/TokenId";
|
|
2417
2562
|
import { TokenState } from "@unicitylabs/state-transition-sdk/lib/token/TokenState";
|
|
@@ -2451,7 +2596,7 @@ var TokenSplitExecutor = class {
|
|
|
2451
2596
|
}
|
|
2452
2597
|
async executeSplit(tokenToSplit, splitAmount, remainderAmount, coinIdHex, recipientAddress) {
|
|
2453
2598
|
const tokenIdHex = toHex(tokenToSplit.id.bytes);
|
|
2454
|
-
|
|
2599
|
+
logger.debug("TokenSplit", `Splitting token ${tokenIdHex.slice(0, 8)}...`);
|
|
2455
2600
|
const coinId = new CoinId2(fromHex(coinIdHex));
|
|
2456
2601
|
const seedString = `${tokenIdHex}_${splitAmount.toString()}_${remainderAmount.toString()}`;
|
|
2457
2602
|
const recipientTokenId = new TokenId(await sha2562(seedString));
|
|
@@ -2471,23 +2616,23 @@ var TokenSplitExecutor = class {
|
|
|
2471
2616
|
const coinDataB = TokenCoinData.create([[coinId, remainderAmount]]);
|
|
2472
2617
|
builder.createToken(senderTokenId, tokenToSplit.type, new Uint8Array(0), coinDataB, senderAddress, senderSalt, null);
|
|
2473
2618
|
const split = await builder.build(tokenToSplit);
|
|
2474
|
-
|
|
2619
|
+
logger.debug("TokenSplit", "Step 1: Burning original token...");
|
|
2475
2620
|
const burnSalt = await sha2562(seedString + "_burn_salt");
|
|
2476
2621
|
const burnCommitment = await split.createBurnCommitment(burnSalt, this.signingService);
|
|
2477
2622
|
const burnResponse = await this.client.submitTransferCommitment(burnCommitment);
|
|
2478
2623
|
if (burnResponse.status !== "SUCCESS" && burnResponse.status !== "REQUEST_ID_EXISTS") {
|
|
2479
|
-
throw new
|
|
2624
|
+
throw new SphereError(`Burn failed: ${burnResponse.status}`, "TRANSFER_FAILED");
|
|
2480
2625
|
}
|
|
2481
2626
|
const burnInclusionProof = await waitInclusionProof(this.trustBase, this.client, burnCommitment);
|
|
2482
2627
|
const burnTransaction = burnCommitment.toTransaction(burnInclusionProof);
|
|
2483
|
-
|
|
2484
|
-
|
|
2628
|
+
logger.debug("TokenSplit", "Original token burned.");
|
|
2629
|
+
logger.debug("TokenSplit", "Step 2: Minting split tokens...");
|
|
2485
2630
|
const mintCommitments = await split.createSplitMintCommitments(this.trustBase, burnTransaction);
|
|
2486
2631
|
const mintedTokensInfo = [];
|
|
2487
2632
|
for (const commitment of mintCommitments) {
|
|
2488
2633
|
const res = await this.client.submitMintCommitment(commitment);
|
|
2489
2634
|
if (res.status !== "SUCCESS" && res.status !== "REQUEST_ID_EXISTS") {
|
|
2490
|
-
throw new
|
|
2635
|
+
throw new SphereError(`Mint split token failed: ${res.status}`, "TRANSFER_FAILED");
|
|
2491
2636
|
}
|
|
2492
2637
|
const proof = await waitInclusionProof(this.trustBase, this.client, commitment);
|
|
2493
2638
|
const commTokenIdHex = toHex(commitment.transactionData.tokenId.bytes);
|
|
@@ -2500,7 +2645,7 @@ var TokenSplitExecutor = class {
|
|
|
2500
2645
|
salt: commitment.transactionData.salt
|
|
2501
2646
|
});
|
|
2502
2647
|
}
|
|
2503
|
-
|
|
2648
|
+
logger.debug("TokenSplit", "Split tokens minted.");
|
|
2504
2649
|
const recipientInfo = mintedTokensInfo.find((t) => t.isForRecipient);
|
|
2505
2650
|
const senderInfo = mintedTokensInfo.find((t) => !t.isForRecipient);
|
|
2506
2651
|
const createToken = async (info, label) => {
|
|
@@ -2508,12 +2653,12 @@ var TokenSplitExecutor = class {
|
|
|
2508
2653
|
const state = new TokenState(predicate, null);
|
|
2509
2654
|
const token = await Token.mint(this.trustBase, state, info.commitment.toTransaction(info.inclusionProof));
|
|
2510
2655
|
const verification = await token.verify(this.trustBase);
|
|
2511
|
-
if (!verification.isSuccessful) throw new
|
|
2656
|
+
if (!verification.isSuccessful) throw new SphereError(`Token verification failed: ${label}`, "TRANSFER_FAILED");
|
|
2512
2657
|
return token;
|
|
2513
2658
|
};
|
|
2514
2659
|
const recipientTokenBeforeTransfer = await createToken(recipientInfo, "Recipient");
|
|
2515
2660
|
const senderToken = await createToken(senderInfo, "Sender");
|
|
2516
|
-
|
|
2661
|
+
logger.debug("TokenSplit", "Step 3: Transferring to recipient...");
|
|
2517
2662
|
const transferSalt = await sha2562(seedString + "_transfer_salt");
|
|
2518
2663
|
const transferCommitment = await TransferCommitment.create(
|
|
2519
2664
|
recipientTokenBeforeTransfer,
|
|
@@ -2525,11 +2670,11 @@ var TokenSplitExecutor = class {
|
|
|
2525
2670
|
);
|
|
2526
2671
|
const transferRes = await this.client.submitTransferCommitment(transferCommitment);
|
|
2527
2672
|
if (transferRes.status !== "SUCCESS" && transferRes.status !== "REQUEST_ID_EXISTS") {
|
|
2528
|
-
throw new
|
|
2673
|
+
throw new SphereError(`Transfer failed: ${transferRes.status}`, "TRANSFER_FAILED");
|
|
2529
2674
|
}
|
|
2530
2675
|
const transferProof = await waitInclusionProof(this.trustBase, this.client, transferCommitment);
|
|
2531
2676
|
const transferTx = transferCommitment.toTransaction(transferProof);
|
|
2532
|
-
|
|
2677
|
+
logger.debug("TokenSplit", "Split transfer complete!");
|
|
2533
2678
|
return {
|
|
2534
2679
|
tokenForRecipient: recipientTokenBeforeTransfer,
|
|
2535
2680
|
tokenForSender: senderToken,
|
|
@@ -2539,6 +2684,7 @@ var TokenSplitExecutor = class {
|
|
|
2539
2684
|
};
|
|
2540
2685
|
|
|
2541
2686
|
// modules/payments/NametagMinter.ts
|
|
2687
|
+
init_logger();
|
|
2542
2688
|
import { Token as Token2 } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
2543
2689
|
import { TokenId as TokenId2 } from "@unicitylabs/state-transition-sdk/lib/token/TokenId";
|
|
2544
2690
|
import { TokenType } from "@unicitylabs/state-transition-sdk/lib/token/TokenType";
|
|
@@ -2563,10 +2709,8 @@ var NametagMinter = class {
|
|
|
2563
2709
|
this.skipVerification = config.skipVerification ?? false;
|
|
2564
2710
|
this.debug = config.debug ?? false;
|
|
2565
2711
|
}
|
|
2566
|
-
log(...args) {
|
|
2567
|
-
|
|
2568
|
-
console.log("[NametagMinter]", ...args);
|
|
2569
|
-
}
|
|
2712
|
+
log(message, ...args) {
|
|
2713
|
+
logger.debug("NametagMinter", message, ...args);
|
|
2570
2714
|
}
|
|
2571
2715
|
/**
|
|
2572
2716
|
* Check if a nametag is available (not already minted)
|
|
@@ -2753,6 +2897,7 @@ function isValidTokenId(tokenId) {
|
|
|
2753
2897
|
}
|
|
2754
2898
|
|
|
2755
2899
|
// registry/TokenRegistry.ts
|
|
2900
|
+
init_logger();
|
|
2756
2901
|
init_constants();
|
|
2757
2902
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
2758
2903
|
var TokenRegistry = class _TokenRegistry {
|
|
@@ -2974,14 +3119,12 @@ var TokenRegistry = class _TokenRegistry {
|
|
|
2974
3119
|
clearTimeout(timer);
|
|
2975
3120
|
}
|
|
2976
3121
|
if (!response.ok) {
|
|
2977
|
-
|
|
2978
|
-
`[TokenRegistry] Remote fetch failed: HTTP ${response.status} ${response.statusText}`
|
|
2979
|
-
);
|
|
3122
|
+
logger.warn("TokenRegistry", `Remote fetch failed: HTTP ${response.status} ${response.statusText}`);
|
|
2980
3123
|
return false;
|
|
2981
3124
|
}
|
|
2982
3125
|
const data = await response.json();
|
|
2983
3126
|
if (!this.isValidDefinitionsArray(data)) {
|
|
2984
|
-
|
|
3127
|
+
logger.warn("TokenRegistry", "Remote data is not a valid token definitions array");
|
|
2985
3128
|
return false;
|
|
2986
3129
|
}
|
|
2987
3130
|
const definitions = data;
|
|
@@ -2991,7 +3134,7 @@ var TokenRegistry = class _TokenRegistry {
|
|
|
2991
3134
|
return true;
|
|
2992
3135
|
} catch (error) {
|
|
2993
3136
|
const message = error instanceof Error ? error.message : String(error);
|
|
2994
|
-
|
|
3137
|
+
logger.warn("TokenRegistry", `Remote refresh failed: ${message}`);
|
|
2995
3138
|
return false;
|
|
2996
3139
|
}
|
|
2997
3140
|
}
|
|
@@ -3553,7 +3696,13 @@ function countCommittedTransactions(token) {
|
|
|
3553
3696
|
}
|
|
3554
3697
|
}
|
|
3555
3698
|
|
|
3699
|
+
// modules/payments/PaymentsModule.ts
|
|
3700
|
+
init_logger();
|
|
3701
|
+
init_errors();
|
|
3702
|
+
|
|
3556
3703
|
// modules/payments/InstantSplitExecutor.ts
|
|
3704
|
+
init_logger();
|
|
3705
|
+
init_errors();
|
|
3557
3706
|
import { Token as Token3 } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
3558
3707
|
import { TokenId as TokenId3 } from "@unicitylabs/state-transition-sdk/lib/token/TokenId";
|
|
3559
3708
|
import { TokenState as TokenState3 } from "@unicitylabs/state-transition-sdk/lib/token/TokenState";
|
|
@@ -3609,7 +3758,7 @@ var InstantSplitExecutor = class {
|
|
|
3609
3758
|
async buildSplitBundle(tokenToSplit, splitAmount, remainderAmount, coinIdHex, recipientAddress, options) {
|
|
3610
3759
|
const splitGroupId = crypto.randomUUID();
|
|
3611
3760
|
const tokenIdHex = toHex2(tokenToSplit.id.bytes);
|
|
3612
|
-
|
|
3761
|
+
logger.debug("InstantSplit", `Building V5 bundle for token ${tokenIdHex.slice(0, 8)}...`);
|
|
3613
3762
|
const coinId = new CoinId3(fromHex2(coinIdHex));
|
|
3614
3763
|
const seedString = `${tokenIdHex}_${splitAmount.toString()}_${remainderAmount.toString()}_${Date.now()}`;
|
|
3615
3764
|
const recipientTokenId = new TokenId3(await sha2563(seedString));
|
|
@@ -3646,19 +3795,19 @@ var InstantSplitExecutor = class {
|
|
|
3646
3795
|
null
|
|
3647
3796
|
);
|
|
3648
3797
|
const split = await builder.build(tokenToSplit);
|
|
3649
|
-
|
|
3798
|
+
logger.debug("InstantSplit", "Step 1: Creating and submitting burn...");
|
|
3650
3799
|
const burnSalt = await sha2563(seedString + "_burn_salt");
|
|
3651
3800
|
const burnCommitment = await split.createBurnCommitment(burnSalt, this.signingService);
|
|
3652
3801
|
const burnResponse = await this.client.submitTransferCommitment(burnCommitment);
|
|
3653
3802
|
if (burnResponse.status !== "SUCCESS" && burnResponse.status !== "REQUEST_ID_EXISTS") {
|
|
3654
|
-
throw new
|
|
3803
|
+
throw new SphereError(`Burn submission failed: ${burnResponse.status}`, "TRANSFER_FAILED");
|
|
3655
3804
|
}
|
|
3656
|
-
|
|
3805
|
+
logger.debug("InstantSplit", "Step 2: Waiting for burn proof...");
|
|
3657
3806
|
const burnProof = this.devMode ? await this.waitInclusionProofWithDevBypass(burnCommitment, options?.burnProofTimeoutMs) : await waitInclusionProof3(this.trustBase, this.client, burnCommitment);
|
|
3658
3807
|
const burnTransaction = burnCommitment.toTransaction(burnProof);
|
|
3659
|
-
|
|
3808
|
+
logger.debug("InstantSplit", "Burn proof received");
|
|
3660
3809
|
options?.onBurnCompleted?.(JSON.stringify(burnTransaction.toJSON()));
|
|
3661
|
-
|
|
3810
|
+
logger.debug("InstantSplit", "Step 3: Creating mint commitments...");
|
|
3662
3811
|
const mintCommitments = await split.createSplitMintCommitments(this.trustBase, burnTransaction);
|
|
3663
3812
|
const recipientIdHex = toHex2(recipientTokenId.bytes);
|
|
3664
3813
|
const senderIdHex = toHex2(senderTokenId.bytes);
|
|
@@ -3669,9 +3818,9 @@ var InstantSplitExecutor = class {
|
|
|
3669
3818
|
(c) => toHex2(c.transactionData.tokenId.bytes) === senderIdHex
|
|
3670
3819
|
);
|
|
3671
3820
|
if (!recipientMintCommitment || !senderMintCommitment) {
|
|
3672
|
-
throw new
|
|
3821
|
+
throw new SphereError("Failed to find expected mint commitments", "TRANSFER_FAILED");
|
|
3673
3822
|
}
|
|
3674
|
-
|
|
3823
|
+
logger.debug("InstantSplit", "Step 4: Creating transfer commitment...");
|
|
3675
3824
|
const transferSalt = await sha2563(seedString + "_transfer_salt");
|
|
3676
3825
|
const transferCommitment = await this.createTransferCommitmentFromMintData(
|
|
3677
3826
|
recipientMintCommitment.transactionData,
|
|
@@ -3687,7 +3836,7 @@ var InstantSplitExecutor = class {
|
|
|
3687
3836
|
recipientSalt
|
|
3688
3837
|
);
|
|
3689
3838
|
const mintedState = new TokenState3(mintedPredicate, null);
|
|
3690
|
-
|
|
3839
|
+
logger.debug("InstantSplit", "Step 5: Packaging V5 bundle...");
|
|
3691
3840
|
const senderPubkey = toHex2(this.signingService.publicKey);
|
|
3692
3841
|
let nametagTokenJson;
|
|
3693
3842
|
const recipientAddressStr = recipientAddress.toString();
|
|
@@ -3759,7 +3908,7 @@ var InstantSplitExecutor = class {
|
|
|
3759
3908
|
recipientAddress,
|
|
3760
3909
|
options
|
|
3761
3910
|
);
|
|
3762
|
-
|
|
3911
|
+
logger.debug("InstantSplit", "Sending via transport...");
|
|
3763
3912
|
const senderPubkey = toHex2(this.signingService.publicKey);
|
|
3764
3913
|
const nostrEventId = await transport.sendTokenTransfer(recipientPubkey, {
|
|
3765
3914
|
token: JSON.stringify(buildResult.bundle),
|
|
@@ -3771,7 +3920,7 @@ var InstantSplitExecutor = class {
|
|
|
3771
3920
|
}
|
|
3772
3921
|
});
|
|
3773
3922
|
const criticalPathDuration = performance.now() - startTime;
|
|
3774
|
-
|
|
3923
|
+
logger.debug("InstantSplit", `V5 complete in ${criticalPathDuration.toFixed(0)}ms`);
|
|
3775
3924
|
options?.onNostrDelivered?.(nostrEventId);
|
|
3776
3925
|
const backgroundPromise = buildResult.startBackground();
|
|
3777
3926
|
return {
|
|
@@ -3785,7 +3934,7 @@ var InstantSplitExecutor = class {
|
|
|
3785
3934
|
} catch (error) {
|
|
3786
3935
|
const duration = performance.now() - startTime;
|
|
3787
3936
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3788
|
-
|
|
3937
|
+
logger.error("InstantSplit", `Failed after ${duration.toFixed(0)}ms:`, error);
|
|
3789
3938
|
return {
|
|
3790
3939
|
success: false,
|
|
3791
3940
|
criticalPathDurationMs: duration,
|
|
@@ -3834,7 +3983,7 @@ var InstantSplitExecutor = class {
|
|
|
3834
3983
|
* Then waits for sender's mint proof, reconstructs change token, and saves it.
|
|
3835
3984
|
*/
|
|
3836
3985
|
submitBackgroundV5(senderMintCommitment, recipientMintCommitment, transferCommitment, context) {
|
|
3837
|
-
|
|
3986
|
+
logger.debug("InstantSplit", "Background: Starting parallel mint submission...");
|
|
3838
3987
|
const startTime = performance.now();
|
|
3839
3988
|
const submissions = Promise.all([
|
|
3840
3989
|
this.client.submitMintCommitment(senderMintCommitment).then((res) => ({ type: "senderMint", status: res.status })).catch((err) => ({ type: "senderMint", status: "ERROR", error: err })),
|
|
@@ -3843,14 +3992,14 @@ var InstantSplitExecutor = class {
|
|
|
3843
3992
|
]);
|
|
3844
3993
|
return submissions.then(async (results) => {
|
|
3845
3994
|
const submitDuration = performance.now() - startTime;
|
|
3846
|
-
|
|
3995
|
+
logger.debug("InstantSplit", `Background: Submissions complete in ${submitDuration.toFixed(0)}ms`);
|
|
3847
3996
|
context.onProgress?.({
|
|
3848
3997
|
stage: "MINTS_SUBMITTED",
|
|
3849
3998
|
message: `All commitments submitted in ${submitDuration.toFixed(0)}ms`
|
|
3850
3999
|
});
|
|
3851
4000
|
const senderMintResult = results.find((r) => r.type === "senderMint");
|
|
3852
4001
|
if (senderMintResult?.status !== "SUCCESS" && senderMintResult?.status !== "REQUEST_ID_EXISTS") {
|
|
3853
|
-
|
|
4002
|
+
logger.error("InstantSplit", "Background: Sender mint failed - cannot save change token");
|
|
3854
4003
|
context.onProgress?.({
|
|
3855
4004
|
stage: "FAILED",
|
|
3856
4005
|
message: "Sender mint submission failed",
|
|
@@ -3858,12 +4007,12 @@ var InstantSplitExecutor = class {
|
|
|
3858
4007
|
});
|
|
3859
4008
|
return;
|
|
3860
4009
|
}
|
|
3861
|
-
|
|
4010
|
+
logger.debug("InstantSplit", "Background: Waiting for sender mint proof...");
|
|
3862
4011
|
const proofStartTime = performance.now();
|
|
3863
4012
|
try {
|
|
3864
4013
|
const senderMintProof = this.devMode ? await this.waitInclusionProofWithDevBypass(senderMintCommitment) : await waitInclusionProof3(this.trustBase, this.client, senderMintCommitment);
|
|
3865
4014
|
const proofDuration = performance.now() - proofStartTime;
|
|
3866
|
-
|
|
4015
|
+
logger.debug("InstantSplit", `Background: Sender mint proof received in ${proofDuration.toFixed(0)}ms`);
|
|
3867
4016
|
context.onProgress?.({
|
|
3868
4017
|
stage: "MINTS_PROVEN",
|
|
3869
4018
|
message: `Mint proof received in ${proofDuration.toFixed(0)}ms`
|
|
@@ -3881,38 +4030,38 @@ var InstantSplitExecutor = class {
|
|
|
3881
4030
|
if (!this.devMode) {
|
|
3882
4031
|
const verification = await changeToken.verify(this.trustBase);
|
|
3883
4032
|
if (!verification.isSuccessful) {
|
|
3884
|
-
throw new
|
|
4033
|
+
throw new SphereError("Change token verification failed", "TRANSFER_FAILED");
|
|
3885
4034
|
}
|
|
3886
4035
|
}
|
|
3887
|
-
|
|
4036
|
+
logger.debug("InstantSplit", "Background: Change token created");
|
|
3888
4037
|
context.onProgress?.({
|
|
3889
4038
|
stage: "CHANGE_TOKEN_SAVED",
|
|
3890
4039
|
message: "Change token created and verified"
|
|
3891
4040
|
});
|
|
3892
4041
|
if (context.onChangeTokenCreated) {
|
|
3893
4042
|
await context.onChangeTokenCreated(changeToken);
|
|
3894
|
-
|
|
4043
|
+
logger.debug("InstantSplit", "Background: Change token saved");
|
|
3895
4044
|
}
|
|
3896
4045
|
if (context.onStorageSync) {
|
|
3897
4046
|
try {
|
|
3898
4047
|
const syncSuccess = await context.onStorageSync();
|
|
3899
|
-
|
|
4048
|
+
logger.debug("InstantSplit", `Background: Storage sync ${syncSuccess ? "completed" : "deferred"}`);
|
|
3900
4049
|
context.onProgress?.({
|
|
3901
4050
|
stage: "STORAGE_SYNCED",
|
|
3902
4051
|
message: syncSuccess ? "Storage synchronized" : "Sync deferred"
|
|
3903
4052
|
});
|
|
3904
4053
|
} catch (syncError) {
|
|
3905
|
-
|
|
4054
|
+
logger.warn("InstantSplit", "Background: Storage sync error:", syncError);
|
|
3906
4055
|
}
|
|
3907
4056
|
}
|
|
3908
4057
|
const totalDuration = performance.now() - startTime;
|
|
3909
|
-
|
|
4058
|
+
logger.debug("InstantSplit", `Background: Complete in ${totalDuration.toFixed(0)}ms`);
|
|
3910
4059
|
context.onProgress?.({
|
|
3911
4060
|
stage: "COMPLETED",
|
|
3912
4061
|
message: `Background processing complete in ${totalDuration.toFixed(0)}ms`
|
|
3913
4062
|
});
|
|
3914
4063
|
} catch (proofError) {
|
|
3915
|
-
|
|
4064
|
+
logger.error("InstantSplit", "Background: Failed to get sender mint proof:", proofError);
|
|
3916
4065
|
context.onProgress?.({
|
|
3917
4066
|
stage: "FAILED",
|
|
3918
4067
|
message: "Failed to get mint proof",
|
|
@@ -3920,7 +4069,7 @@ var InstantSplitExecutor = class {
|
|
|
3920
4069
|
});
|
|
3921
4070
|
}
|
|
3922
4071
|
}).catch((err) => {
|
|
3923
|
-
|
|
4072
|
+
logger.error("InstantSplit", "Background: Submission batch failed:", err);
|
|
3924
4073
|
context.onProgress?.({
|
|
3925
4074
|
stage: "FAILED",
|
|
3926
4075
|
message: "Background submission failed",
|
|
@@ -3942,7 +4091,7 @@ var InstantSplitExecutor = class {
|
|
|
3942
4091
|
)
|
|
3943
4092
|
]);
|
|
3944
4093
|
} catch {
|
|
3945
|
-
|
|
4094
|
+
logger.debug("InstantSplit", "Dev mode: Using mock proof");
|
|
3946
4095
|
return {
|
|
3947
4096
|
toJSON: () => ({ mock: true })
|
|
3948
4097
|
};
|
|
@@ -3953,6 +4102,8 @@ var InstantSplitExecutor = class {
|
|
|
3953
4102
|
};
|
|
3954
4103
|
|
|
3955
4104
|
// modules/payments/InstantSplitProcessor.ts
|
|
4105
|
+
init_logger();
|
|
4106
|
+
init_errors();
|
|
3956
4107
|
import { Token as Token4 } from "@unicitylabs/state-transition-sdk/lib/token/Token";
|
|
3957
4108
|
import { TokenState as TokenState4 } from "@unicitylabs/state-transition-sdk/lib/token/TokenState";
|
|
3958
4109
|
import { TokenType as TokenType2 } from "@unicitylabs/state-transition-sdk/lib/token/TokenType";
|
|
@@ -4047,27 +4198,27 @@ var InstantSplitProcessor = class {
|
|
|
4047
4198
|
* 5. Create recipient's final state and finalize token
|
|
4048
4199
|
*/
|
|
4049
4200
|
async processV5Bundle(bundle, signingService, senderPubkey, options) {
|
|
4050
|
-
|
|
4201
|
+
logger.debug("InstantSplit", "Processing V5 bundle...");
|
|
4051
4202
|
const startTime = performance.now();
|
|
4052
4203
|
try {
|
|
4053
4204
|
if (bundle.senderPubkey !== senderPubkey) {
|
|
4054
|
-
|
|
4205
|
+
logger.warn("InstantSplit", "Sender pubkey mismatch (non-fatal)");
|
|
4055
4206
|
}
|
|
4056
4207
|
const burnTxJson = JSON.parse(bundle.burnTransaction);
|
|
4057
4208
|
const _burnTransaction = await TransferTransaction.fromJSON(burnTxJson);
|
|
4058
|
-
|
|
4209
|
+
logger.debug("InstantSplit", "Burn transaction validated");
|
|
4059
4210
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
4060
4211
|
const mintData = await MintTransactionData2.fromJSON(mintDataJson);
|
|
4061
4212
|
const mintCommitment = await MintCommitment2.create(mintData);
|
|
4062
|
-
|
|
4213
|
+
logger.debug("InstantSplit", "Mint commitment recreated");
|
|
4063
4214
|
const mintResponse = await this.client.submitMintCommitment(mintCommitment);
|
|
4064
4215
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
4065
|
-
throw new
|
|
4216
|
+
throw new SphereError(`Mint submission failed: ${mintResponse.status}`, "TRANSFER_FAILED");
|
|
4066
4217
|
}
|
|
4067
|
-
|
|
4218
|
+
logger.debug("InstantSplit", `Mint submitted: ${mintResponse.status}`);
|
|
4068
4219
|
const mintProof = this.devMode ? await this.waitInclusionProofWithDevBypass(mintCommitment, options?.proofTimeoutMs) : await waitInclusionProof4(this.trustBase, this.client, mintCommitment);
|
|
4069
4220
|
const mintTransaction = mintCommitment.toTransaction(mintProof);
|
|
4070
|
-
|
|
4221
|
+
logger.debug("InstantSplit", "Mint proof received");
|
|
4071
4222
|
const tokenType = new TokenType2(fromHex3(bundle.tokenTypeHex));
|
|
4072
4223
|
const senderMintedStateJson = JSON.parse(bundle.mintedTokenStateJson);
|
|
4073
4224
|
const tokenJson = {
|
|
@@ -4078,17 +4229,17 @@ var InstantSplitProcessor = class {
|
|
|
4078
4229
|
nametags: []
|
|
4079
4230
|
};
|
|
4080
4231
|
const mintedToken = await Token4.fromJSON(tokenJson);
|
|
4081
|
-
|
|
4232
|
+
logger.debug("InstantSplit", "Minted token reconstructed from sender state");
|
|
4082
4233
|
const transferCommitmentJson = JSON.parse(bundle.transferCommitment);
|
|
4083
4234
|
const transferCommitment = await TransferCommitment3.fromJSON(transferCommitmentJson);
|
|
4084
4235
|
const transferResponse = await this.client.submitTransferCommitment(transferCommitment);
|
|
4085
4236
|
if (transferResponse.status !== "SUCCESS" && transferResponse.status !== "REQUEST_ID_EXISTS") {
|
|
4086
|
-
throw new
|
|
4237
|
+
throw new SphereError(`Transfer submission failed: ${transferResponse.status}`, "TRANSFER_FAILED");
|
|
4087
4238
|
}
|
|
4088
|
-
|
|
4239
|
+
logger.debug("InstantSplit", `Transfer submitted: ${transferResponse.status}`);
|
|
4089
4240
|
const transferProof = this.devMode ? await this.waitInclusionProofWithDevBypass(transferCommitment, options?.proofTimeoutMs) : await waitInclusionProof4(this.trustBase, this.client, transferCommitment);
|
|
4090
4241
|
const transferTransaction = transferCommitment.toTransaction(transferProof);
|
|
4091
|
-
|
|
4242
|
+
logger.debug("InstantSplit", "Transfer proof received");
|
|
4092
4243
|
const transferSalt = fromHex3(bundle.transferSaltHex);
|
|
4093
4244
|
const finalRecipientPredicate = await UnmaskedPredicate4.create(
|
|
4094
4245
|
mintData.tokenId,
|
|
@@ -4098,42 +4249,43 @@ var InstantSplitProcessor = class {
|
|
|
4098
4249
|
transferSalt
|
|
4099
4250
|
);
|
|
4100
4251
|
const finalRecipientState = new TokenState4(finalRecipientPredicate, null);
|
|
4101
|
-
|
|
4252
|
+
logger.debug("InstantSplit", "Final recipient state created");
|
|
4102
4253
|
let nametagTokens = [];
|
|
4103
4254
|
const recipientAddressStr = bundle.recipientAddressJson;
|
|
4104
4255
|
if (recipientAddressStr.startsWith("PROXY://")) {
|
|
4105
|
-
|
|
4256
|
+
logger.debug("InstantSplit", "PROXY address detected, finding nametag token...");
|
|
4106
4257
|
if (bundle.nametagTokenJson) {
|
|
4107
4258
|
try {
|
|
4108
4259
|
const nametagToken = await Token4.fromJSON(JSON.parse(bundle.nametagTokenJson));
|
|
4109
4260
|
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
4110
4261
|
const proxy = await ProxyAddress.fromTokenId(nametagToken.id);
|
|
4111
4262
|
if (proxy.address !== recipientAddressStr) {
|
|
4112
|
-
|
|
4263
|
+
logger.warn("InstantSplit", "Nametag PROXY address mismatch, ignoring bundle token");
|
|
4113
4264
|
} else {
|
|
4114
4265
|
nametagTokens = [nametagToken];
|
|
4115
|
-
|
|
4266
|
+
logger.debug("InstantSplit", "Using nametag token from bundle (address validated)");
|
|
4116
4267
|
}
|
|
4117
4268
|
} catch (err) {
|
|
4118
|
-
|
|
4269
|
+
logger.warn("InstantSplit", "Failed to parse nametag token from bundle:", err);
|
|
4119
4270
|
}
|
|
4120
4271
|
}
|
|
4121
4272
|
if (nametagTokens.length === 0 && options?.findNametagToken) {
|
|
4122
4273
|
const token = await options.findNametagToken(recipientAddressStr);
|
|
4123
4274
|
if (token) {
|
|
4124
4275
|
nametagTokens = [token];
|
|
4125
|
-
|
|
4276
|
+
logger.debug("InstantSplit", "Found nametag token via callback");
|
|
4126
4277
|
}
|
|
4127
4278
|
}
|
|
4128
4279
|
if (nametagTokens.length === 0 && !this.devMode) {
|
|
4129
|
-
throw new
|
|
4130
|
-
`PROXY address transfer requires nametag token for verification. Address: ${recipientAddressStr}
|
|
4280
|
+
throw new SphereError(
|
|
4281
|
+
`PROXY address transfer requires nametag token for verification. Address: ${recipientAddressStr}`,
|
|
4282
|
+
"TRANSFER_FAILED"
|
|
4131
4283
|
);
|
|
4132
4284
|
}
|
|
4133
4285
|
}
|
|
4134
4286
|
let finalToken;
|
|
4135
4287
|
if (this.devMode) {
|
|
4136
|
-
|
|
4288
|
+
logger.debug("InstantSplit", "Dev mode: finalizing without verification");
|
|
4137
4289
|
const tokenJson2 = mintedToken.toJSON();
|
|
4138
4290
|
tokenJson2.state = finalRecipientState.toJSON();
|
|
4139
4291
|
tokenJson2.transactions = [transferTransaction.toJSON()];
|
|
@@ -4147,16 +4299,16 @@ var InstantSplitProcessor = class {
|
|
|
4147
4299
|
nametagTokens
|
|
4148
4300
|
);
|
|
4149
4301
|
}
|
|
4150
|
-
|
|
4302
|
+
logger.debug("InstantSplit", "Token finalized");
|
|
4151
4303
|
if (!this.devMode) {
|
|
4152
4304
|
const verification = await finalToken.verify(this.trustBase);
|
|
4153
4305
|
if (!verification.isSuccessful) {
|
|
4154
|
-
throw new
|
|
4306
|
+
throw new SphereError(`Token verification failed`, "TRANSFER_FAILED");
|
|
4155
4307
|
}
|
|
4156
|
-
|
|
4308
|
+
logger.debug("InstantSplit", "Token verified");
|
|
4157
4309
|
}
|
|
4158
4310
|
const duration = performance.now() - startTime;
|
|
4159
|
-
|
|
4311
|
+
logger.debug("InstantSplit", `V5 bundle processed in ${duration.toFixed(0)}ms`);
|
|
4160
4312
|
return {
|
|
4161
4313
|
success: true,
|
|
4162
4314
|
token: finalToken,
|
|
@@ -4165,7 +4317,7 @@ var InstantSplitProcessor = class {
|
|
|
4165
4317
|
} catch (error) {
|
|
4166
4318
|
const duration = performance.now() - startTime;
|
|
4167
4319
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4168
|
-
|
|
4320
|
+
logger.error("InstantSplit", "V5 processing failed:", error);
|
|
4169
4321
|
return {
|
|
4170
4322
|
success: false,
|
|
4171
4323
|
error: errorMessage,
|
|
@@ -4191,30 +4343,30 @@ var InstantSplitProcessor = class {
|
|
|
4191
4343
|
durationMs: 0
|
|
4192
4344
|
};
|
|
4193
4345
|
}
|
|
4194
|
-
|
|
4346
|
+
logger.debug("InstantSplit", "Processing V4 bundle (dev mode)...");
|
|
4195
4347
|
const startTime = performance.now();
|
|
4196
4348
|
try {
|
|
4197
4349
|
const burnCommitmentJson = JSON.parse(bundle.burnCommitment);
|
|
4198
4350
|
const burnCommitment = await TransferCommitment3.fromJSON(burnCommitmentJson);
|
|
4199
4351
|
const burnResponse = await this.client.submitTransferCommitment(burnCommitment);
|
|
4200
4352
|
if (burnResponse.status !== "SUCCESS" && burnResponse.status !== "REQUEST_ID_EXISTS") {
|
|
4201
|
-
throw new
|
|
4353
|
+
throw new SphereError(`Burn submission failed: ${burnResponse.status}`, "TRANSFER_FAILED");
|
|
4202
4354
|
}
|
|
4203
4355
|
await this.waitInclusionProofWithDevBypass(burnCommitment, options?.proofTimeoutMs);
|
|
4204
|
-
|
|
4356
|
+
logger.debug("InstantSplit", "V4: Burn proof received");
|
|
4205
4357
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
4206
4358
|
const mintData = await MintTransactionData2.fromJSON(mintDataJson);
|
|
4207
4359
|
const mintCommitment = await MintCommitment2.create(mintData);
|
|
4208
4360
|
const mintResponse = await this.client.submitMintCommitment(mintCommitment);
|
|
4209
4361
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
4210
|
-
throw new
|
|
4362
|
+
throw new SphereError(`Mint submission failed: ${mintResponse.status}`, "TRANSFER_FAILED");
|
|
4211
4363
|
}
|
|
4212
4364
|
const mintProof = await this.waitInclusionProofWithDevBypass(
|
|
4213
4365
|
mintCommitment,
|
|
4214
4366
|
options?.proofTimeoutMs
|
|
4215
4367
|
);
|
|
4216
4368
|
const mintTransaction = mintCommitment.toTransaction(mintProof);
|
|
4217
|
-
|
|
4369
|
+
logger.debug("InstantSplit", "V4: Mint proof received");
|
|
4218
4370
|
const tokenType = new TokenType2(fromHex3(bundle.tokenTypeHex));
|
|
4219
4371
|
const recipientSalt = fromHex3(bundle.recipientSaltHex);
|
|
4220
4372
|
const recipientPredicate = await UnmaskedPredicate4.create(
|
|
@@ -4233,19 +4385,19 @@ var InstantSplitProcessor = class {
|
|
|
4233
4385
|
nametags: []
|
|
4234
4386
|
};
|
|
4235
4387
|
const mintedToken = await Token4.fromJSON(tokenJson);
|
|
4236
|
-
|
|
4388
|
+
logger.debug("InstantSplit", "V4: Minted token reconstructed");
|
|
4237
4389
|
const transferCommitmentJson = JSON.parse(bundle.transferCommitment);
|
|
4238
4390
|
const transferCommitment = await TransferCommitment3.fromJSON(transferCommitmentJson);
|
|
4239
4391
|
const transferResponse = await this.client.submitTransferCommitment(transferCommitment);
|
|
4240
4392
|
if (transferResponse.status !== "SUCCESS" && transferResponse.status !== "REQUEST_ID_EXISTS") {
|
|
4241
|
-
throw new
|
|
4393
|
+
throw new SphereError(`Transfer submission failed: ${transferResponse.status}`, "TRANSFER_FAILED");
|
|
4242
4394
|
}
|
|
4243
4395
|
const transferProof = await this.waitInclusionProofWithDevBypass(
|
|
4244
4396
|
transferCommitment,
|
|
4245
4397
|
options?.proofTimeoutMs
|
|
4246
4398
|
);
|
|
4247
4399
|
const transferTransaction = transferCommitment.toTransaction(transferProof);
|
|
4248
|
-
|
|
4400
|
+
logger.debug("InstantSplit", "V4: Transfer proof received");
|
|
4249
4401
|
const transferSalt = fromHex3(bundle.transferSaltHex);
|
|
4250
4402
|
const finalPredicate = await UnmaskedPredicate4.create(
|
|
4251
4403
|
mintData.tokenId,
|
|
@@ -4259,9 +4411,9 @@ var InstantSplitProcessor = class {
|
|
|
4259
4411
|
finalTokenJson.state = finalState.toJSON();
|
|
4260
4412
|
finalTokenJson.transactions = [transferTransaction.toJSON()];
|
|
4261
4413
|
const finalToken = await Token4.fromJSON(finalTokenJson);
|
|
4262
|
-
|
|
4414
|
+
logger.debug("InstantSplit", "V4: Token finalized");
|
|
4263
4415
|
const duration = performance.now() - startTime;
|
|
4264
|
-
|
|
4416
|
+
logger.debug("InstantSplit", `V4 bundle processed in ${duration.toFixed(0)}ms`);
|
|
4265
4417
|
return {
|
|
4266
4418
|
success: true,
|
|
4267
4419
|
token: finalToken,
|
|
@@ -4270,7 +4422,7 @@ var InstantSplitProcessor = class {
|
|
|
4270
4422
|
} catch (error) {
|
|
4271
4423
|
const duration = performance.now() - startTime;
|
|
4272
4424
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4273
|
-
|
|
4425
|
+
logger.error("InstantSplit", "V4 processing failed:", error);
|
|
4274
4426
|
return {
|
|
4275
4427
|
success: false,
|
|
4276
4428
|
error: errorMessage,
|
|
@@ -4291,7 +4443,7 @@ var InstantSplitProcessor = class {
|
|
|
4291
4443
|
)
|
|
4292
4444
|
]);
|
|
4293
4445
|
} catch {
|
|
4294
|
-
|
|
4446
|
+
logger.debug("InstantSplit", "Dev mode: Using mock proof");
|
|
4295
4447
|
return {
|
|
4296
4448
|
toJSON: () => ({ mock: true })
|
|
4297
4449
|
};
|
|
@@ -4476,7 +4628,7 @@ async function parseTokenInfo(tokenData) {
|
|
|
4476
4628
|
}
|
|
4477
4629
|
}
|
|
4478
4630
|
} catch (error) {
|
|
4479
|
-
|
|
4631
|
+
logger.warn("Payments", "Failed to parse token info:", error);
|
|
4480
4632
|
}
|
|
4481
4633
|
return defaultInfo;
|
|
4482
4634
|
}
|
|
@@ -4679,11 +4831,6 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4679
4831
|
}
|
|
4680
4832
|
/** Price provider (optional) */
|
|
4681
4833
|
priceProvider = null;
|
|
4682
|
-
log(...args) {
|
|
4683
|
-
if (this.moduleConfig.debug) {
|
|
4684
|
-
console.log("[PaymentsModule]", ...args);
|
|
4685
|
-
}
|
|
4686
|
-
}
|
|
4687
4834
|
// ===========================================================================
|
|
4688
4835
|
// Lifecycle
|
|
4689
4836
|
// ===========================================================================
|
|
@@ -4750,11 +4897,11 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4750
4897
|
if (txfData._history && txfData._history.length > 0) {
|
|
4751
4898
|
await this.importRemoteHistoryEntries(txfData._history);
|
|
4752
4899
|
}
|
|
4753
|
-
|
|
4900
|
+
logger.debug("Payments", `Loaded metadata from provider ${id}`);
|
|
4754
4901
|
break;
|
|
4755
4902
|
}
|
|
4756
4903
|
} catch (err) {
|
|
4757
|
-
|
|
4904
|
+
logger.error("Payments", `Failed to load from provider ${id}:`, err);
|
|
4758
4905
|
}
|
|
4759
4906
|
}
|
|
4760
4907
|
for (const [id, token] of this.tokens) {
|
|
@@ -4763,14 +4910,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4763
4910
|
const data = JSON.parse(token.sdkData);
|
|
4764
4911
|
if (data?._placeholder) {
|
|
4765
4912
|
this.tokens.delete(id);
|
|
4766
|
-
|
|
4913
|
+
logger.debug("Payments", `Removed stale placeholder token: ${id}`);
|
|
4767
4914
|
}
|
|
4768
4915
|
}
|
|
4769
4916
|
} catch {
|
|
4770
4917
|
}
|
|
4771
4918
|
}
|
|
4772
4919
|
const loadedTokens = Array.from(this.tokens.values()).map((t) => `${t.id.slice(0, 12)}(${t.status})`);
|
|
4773
|
-
|
|
4920
|
+
logger.debug("Payments", `load(): from TXF providers: ${this.tokens.size} tokens [${loadedTokens.join(", ")}]`);
|
|
4774
4921
|
await this.loadPendingV5Tokens();
|
|
4775
4922
|
await this.loadProcessedSplitGroupIds();
|
|
4776
4923
|
await this.loadProcessedCombinedTransferIds();
|
|
@@ -4786,8 +4933,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4786
4933
|
};
|
|
4787
4934
|
this.loadedPromise = doLoad();
|
|
4788
4935
|
await this.loadedPromise;
|
|
4789
|
-
this.resolveUnconfirmed().catch(() =>
|
|
4790
|
-
});
|
|
4936
|
+
this.resolveUnconfirmed().catch((err) => logger.debug("Payments", "resolveUnconfirmed failed", err));
|
|
4791
4937
|
this.scheduleResolveUnconfirmed();
|
|
4792
4938
|
}
|
|
4793
4939
|
/**
|
|
@@ -4840,11 +4986,11 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4840
4986
|
const signingService = await this.createSigningService();
|
|
4841
4987
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
4842
4988
|
if (!stClient) {
|
|
4843
|
-
throw new
|
|
4989
|
+
throw new SphereError("State transition client not available. Oracle provider must implement getStateTransitionClient()", "AGGREGATOR_ERROR");
|
|
4844
4990
|
}
|
|
4845
4991
|
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
4846
4992
|
if (!trustBase) {
|
|
4847
|
-
throw new
|
|
4993
|
+
throw new SphereError("Trust base not available. Oracle provider must implement getTrustBase()", "AGGREGATOR_ERROR");
|
|
4848
4994
|
}
|
|
4849
4995
|
const calculator = new TokenSplitCalculator();
|
|
4850
4996
|
const availableTokens = Array.from(this.tokens.values());
|
|
@@ -4854,7 +5000,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4854
5000
|
request.coinId
|
|
4855
5001
|
);
|
|
4856
5002
|
if (!splitPlan) {
|
|
4857
|
-
throw new
|
|
5003
|
+
throw new SphereError("Insufficient balance", "INSUFFICIENT_BALANCE");
|
|
4858
5004
|
}
|
|
4859
5005
|
const tokensToSend = splitPlan.tokensToTransferDirectly.map((t) => t.uiToken);
|
|
4860
5006
|
if (splitPlan.tokenToSplit) {
|
|
@@ -4872,7 +5018,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4872
5018
|
const transferMode = request.transferMode ?? "instant";
|
|
4873
5019
|
if (transferMode === "conservative") {
|
|
4874
5020
|
if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
|
|
4875
|
-
|
|
5021
|
+
logger.debug("Payments", "Executing conservative split...");
|
|
4876
5022
|
const splitExecutor = new TokenSplitExecutor({
|
|
4877
5023
|
stateTransitionClient: stClient,
|
|
4878
5024
|
trustBase,
|
|
@@ -4900,7 +5046,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4900
5046
|
sdkData: JSON.stringify(changeTokenData)
|
|
4901
5047
|
};
|
|
4902
5048
|
await this.addToken(changeUiToken);
|
|
4903
|
-
|
|
5049
|
+
logger.debug("Payments", `Conservative split: change token saved: ${changeUiToken.id}`);
|
|
4904
5050
|
await this.deps.transport.sendTokenTransfer(recipientPubkey, {
|
|
4905
5051
|
sourceToken: JSON.stringify(splitResult.tokenForRecipient.toJSON()),
|
|
4906
5052
|
transferTx: JSON.stringify(splitResult.recipientTransferTx.toJSON()),
|
|
@@ -4914,15 +5060,15 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4914
5060
|
method: "split",
|
|
4915
5061
|
requestIdHex: splitRequestIdHex
|
|
4916
5062
|
});
|
|
4917
|
-
|
|
5063
|
+
logger.debug("Payments", "Conservative split transfer completed");
|
|
4918
5064
|
}
|
|
4919
5065
|
for (const tokenWithAmount of splitPlan.tokensToTransferDirectly) {
|
|
4920
5066
|
const token = tokenWithAmount.uiToken;
|
|
4921
5067
|
const commitment = await this.createSdkCommitment(token, recipientAddress, signingService);
|
|
4922
|
-
|
|
5068
|
+
logger.debug("Payments", `CONSERVATIVE: Sending direct token ${token.id.slice(0, 8)}... to ${recipientPubkey.slice(0, 8)}...`);
|
|
4923
5069
|
const submitResponse = await stClient.submitTransferCommitment(commitment);
|
|
4924
5070
|
if (submitResponse.status !== "SUCCESS" && submitResponse.status !== "REQUEST_ID_EXISTS") {
|
|
4925
|
-
throw new
|
|
5071
|
+
throw new SphereError(`Transfer commitment failed: ${submitResponse.status}`, "TRANSFER_FAILED");
|
|
4926
5072
|
}
|
|
4927
5073
|
const inclusionProof = await waitInclusionProof5(trustBase, stClient, commitment);
|
|
4928
5074
|
const transferTx = commitment.toTransaction(inclusionProof);
|
|
@@ -4931,7 +5077,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4931
5077
|
transferTx: JSON.stringify(transferTx.toJSON()),
|
|
4932
5078
|
memo: request.memo
|
|
4933
5079
|
});
|
|
4934
|
-
|
|
5080
|
+
logger.debug("Payments", "CONSERVATIVE: Direct token sent successfully");
|
|
4935
5081
|
const requestIdBytes = commitment.requestId;
|
|
4936
5082
|
const requestIdHex = requestIdBytes instanceof Uint8Array ? Array.from(requestIdBytes).map((b) => b.toString(16).padStart(2, "0")).join("") : String(requestIdBytes);
|
|
4937
5083
|
result.tokenTransfers.push({
|
|
@@ -4939,7 +5085,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4939
5085
|
method: "direct",
|
|
4940
5086
|
requestIdHex
|
|
4941
5087
|
});
|
|
4942
|
-
|
|
5088
|
+
logger.debug("Payments", `Token ${token.id} sent via CONSERVATIVE, requestId: ${requestIdHex}`);
|
|
4943
5089
|
await this.removeToken(token.id);
|
|
4944
5090
|
}
|
|
4945
5091
|
} else {
|
|
@@ -4948,7 +5094,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4948
5094
|
let changeTokenPlaceholderId = null;
|
|
4949
5095
|
let builtSplit = null;
|
|
4950
5096
|
if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
|
|
4951
|
-
|
|
5097
|
+
logger.debug("Payments", "Building instant split bundle...");
|
|
4952
5098
|
const executor = new InstantSplitExecutor({
|
|
4953
5099
|
stateTransitionClient: stClient,
|
|
4954
5100
|
trustBase,
|
|
@@ -4982,7 +5128,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4982
5128
|
sdkData: JSON.stringify(changeTokenData)
|
|
4983
5129
|
};
|
|
4984
5130
|
await this.addToken(uiToken);
|
|
4985
|
-
|
|
5131
|
+
logger.debug("Payments", `Change token saved via background: ${uiToken.id}`);
|
|
4986
5132
|
},
|
|
4987
5133
|
onStorageSync: async () => {
|
|
4988
5134
|
await this.save();
|
|
@@ -4990,7 +5136,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4990
5136
|
}
|
|
4991
5137
|
}
|
|
4992
5138
|
);
|
|
4993
|
-
|
|
5139
|
+
logger.debug("Payments", `Split bundle built: splitGroupId=${builtSplit.splitGroupId}`);
|
|
4994
5140
|
}
|
|
4995
5141
|
const directCommitments = await Promise.all(
|
|
4996
5142
|
splitPlan.tokensToTransferDirectly.map(
|
|
@@ -5017,8 +5163,9 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5017
5163
|
senderPubkey,
|
|
5018
5164
|
memo: request.memo
|
|
5019
5165
|
};
|
|
5020
|
-
|
|
5021
|
-
|
|
5166
|
+
logger.debug(
|
|
5167
|
+
"Payments",
|
|
5168
|
+
`Sending V6 combined bundle: transfer=${result.id.slice(0, 8)}... split=${!!builtSplit} direct=${directTokenEntries.length}`
|
|
5022
5169
|
);
|
|
5023
5170
|
await this.deps.transport.sendTokenTransfer(recipientPubkey, {
|
|
5024
5171
|
token: JSON.stringify(combinedBundle),
|
|
@@ -5026,7 +5173,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5026
5173
|
memo: request.memo,
|
|
5027
5174
|
sender: { transportPubkey: senderPubkey }
|
|
5028
5175
|
});
|
|
5029
|
-
|
|
5176
|
+
logger.debug("Payments", "V6 combined bundle sent successfully");
|
|
5030
5177
|
if (builtSplit) {
|
|
5031
5178
|
const bgPromise = builtSplit.startBackground();
|
|
5032
5179
|
this.pendingBackgroundTasks.push(bgPromise);
|
|
@@ -5047,11 +5194,11 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5047
5194
|
sdkData: JSON.stringify({ _placeholder: true })
|
|
5048
5195
|
};
|
|
5049
5196
|
this.tokens.set(placeholder.id, placeholder);
|
|
5050
|
-
|
|
5197
|
+
logger.debug("Payments", `Placeholder change token created: ${placeholder.id} (${placeholder.amount})`);
|
|
5051
5198
|
}
|
|
5052
5199
|
for (const commitment of directCommitments) {
|
|
5053
5200
|
stClient.submitTransferCommitment(commitment).catch(
|
|
5054
|
-
(err) =>
|
|
5201
|
+
(err) => logger.error("Payments", "Background commitment submit failed:", err)
|
|
5055
5202
|
);
|
|
5056
5203
|
}
|
|
5057
5204
|
if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
|
|
@@ -5074,7 +5221,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5074
5221
|
});
|
|
5075
5222
|
await this.removeToken(token.id);
|
|
5076
5223
|
}
|
|
5077
|
-
|
|
5224
|
+
logger.debug("Payments", "V6 combined transfer completed");
|
|
5078
5225
|
}
|
|
5079
5226
|
result.status = "delivered";
|
|
5080
5227
|
await this.save();
|
|
@@ -5165,11 +5312,11 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5165
5312
|
const signingService = await this.createSigningService();
|
|
5166
5313
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
5167
5314
|
if (!stClient) {
|
|
5168
|
-
throw new
|
|
5315
|
+
throw new SphereError("State transition client not available", "AGGREGATOR_ERROR");
|
|
5169
5316
|
}
|
|
5170
5317
|
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
5171
5318
|
if (!trustBase) {
|
|
5172
|
-
throw new
|
|
5319
|
+
throw new SphereError("Trust base not available", "AGGREGATOR_ERROR");
|
|
5173
5320
|
}
|
|
5174
5321
|
const calculator = new TokenSplitCalculator();
|
|
5175
5322
|
const availableTokens = Array.from(this.tokens.values());
|
|
@@ -5179,10 +5326,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5179
5326
|
request.coinId
|
|
5180
5327
|
);
|
|
5181
5328
|
if (!splitPlan) {
|
|
5182
|
-
throw new
|
|
5329
|
+
throw new SphereError("Insufficient balance", "INSUFFICIENT_BALANCE");
|
|
5183
5330
|
}
|
|
5184
5331
|
if (!splitPlan.requiresSplit || !splitPlan.tokenToSplit) {
|
|
5185
|
-
|
|
5332
|
+
logger.debug("Payments", "No split required, falling back to standard send()");
|
|
5186
5333
|
const result2 = await this.send(request);
|
|
5187
5334
|
return {
|
|
5188
5335
|
success: result2.status === "completed",
|
|
@@ -5190,7 +5337,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5190
5337
|
error: result2.error
|
|
5191
5338
|
};
|
|
5192
5339
|
}
|
|
5193
|
-
|
|
5340
|
+
logger.debug("Payments", `InstantSplit: amount=${splitPlan.splitAmount}, remainder=${splitPlan.remainderAmount}`);
|
|
5194
5341
|
const tokenToSplit = splitPlan.tokenToSplit.uiToken;
|
|
5195
5342
|
tokenToSplit.status = "transferring";
|
|
5196
5343
|
this.tokens.set(tokenToSplit.id, tokenToSplit);
|
|
@@ -5228,7 +5375,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5228
5375
|
sdkData: JSON.stringify(changeTokenData)
|
|
5229
5376
|
};
|
|
5230
5377
|
await this.addToken(uiToken);
|
|
5231
|
-
|
|
5378
|
+
logger.debug("Payments", `Change token saved via background: ${uiToken.id}`);
|
|
5232
5379
|
},
|
|
5233
5380
|
onStorageSync: async () => {
|
|
5234
5381
|
await this.save();
|
|
@@ -5283,7 +5430,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5283
5430
|
async saveUnconfirmedV5Token(bundle, senderPubkey, deferPersistence = false) {
|
|
5284
5431
|
const deterministicId = `v5split_${bundle.splitGroupId}`;
|
|
5285
5432
|
if (this.tokens.has(deterministicId) || this.processedSplitGroupIds.has(bundle.splitGroupId)) {
|
|
5286
|
-
|
|
5433
|
+
logger.debug("Payments", `V5 bundle ${bundle.splitGroupId.slice(0, 12)}... already processed, skipping`);
|
|
5287
5434
|
return null;
|
|
5288
5435
|
}
|
|
5289
5436
|
const registry = TokenRegistry.getInstance();
|
|
@@ -5333,7 +5480,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5333
5480
|
const nostrTokenId = extractTokenIdFromSdkData(sdkData);
|
|
5334
5481
|
const nostrStateHash = extractStateHashFromSdkData(sdkData);
|
|
5335
5482
|
if (nostrTokenId && nostrStateHash && this.isStateTombstoned(nostrTokenId, nostrStateHash)) {
|
|
5336
|
-
|
|
5483
|
+
logger.debug("Payments", `NOSTR-FIRST: Rejecting tombstoned token ${nostrTokenId.slice(0, 8)}..._${nostrStateHash.slice(0, 8)}...`);
|
|
5337
5484
|
return null;
|
|
5338
5485
|
}
|
|
5339
5486
|
if (nostrTokenId) {
|
|
@@ -5342,14 +5489,16 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5342
5489
|
if (existingTokenId !== nostrTokenId) continue;
|
|
5343
5490
|
const existingStateHash = extractStateHashFromSdkData(existing.sdkData);
|
|
5344
5491
|
if (nostrStateHash && existingStateHash === nostrStateHash) {
|
|
5345
|
-
|
|
5346
|
-
|
|
5492
|
+
logger.debug(
|
|
5493
|
+
"Payments",
|
|
5494
|
+
`NOSTR-FIRST: Skipping duplicate token state ${nostrTokenId.slice(0, 8)}..._${nostrStateHash.slice(0, 8)}...`
|
|
5347
5495
|
);
|
|
5348
5496
|
return null;
|
|
5349
5497
|
}
|
|
5350
5498
|
if (!skipGenesisDedup) {
|
|
5351
|
-
|
|
5352
|
-
|
|
5499
|
+
logger.debug(
|
|
5500
|
+
"Payments",
|
|
5501
|
+
`NOSTR-FIRST: Skipping replay of finalized token ${nostrTokenId.slice(0, 8)}...`
|
|
5353
5502
|
);
|
|
5354
5503
|
return null;
|
|
5355
5504
|
}
|
|
@@ -5381,7 +5530,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5381
5530
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
5382
5531
|
if (stClient) {
|
|
5383
5532
|
const response = await stClient.submitTransferCommitment(commitment);
|
|
5384
|
-
|
|
5533
|
+
logger.debug("Payments", `NOSTR-FIRST recipient commitment submit: ${response.status}`);
|
|
5385
5534
|
}
|
|
5386
5535
|
}
|
|
5387
5536
|
this.addProofPollingJob({
|
|
@@ -5396,7 +5545,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5396
5545
|
}
|
|
5397
5546
|
});
|
|
5398
5547
|
} catch (err) {
|
|
5399
|
-
|
|
5548
|
+
logger.error("Payments", "Failed to parse commitment for proof polling:", err);
|
|
5400
5549
|
}
|
|
5401
5550
|
return token;
|
|
5402
5551
|
}
|
|
@@ -5418,11 +5567,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5418
5567
|
await this.loadedPromise;
|
|
5419
5568
|
}
|
|
5420
5569
|
if (this.processedCombinedTransferIds.has(bundle.transferId)) {
|
|
5421
|
-
|
|
5570
|
+
logger.debug("Payments", `V6 combined transfer ${bundle.transferId.slice(0, 12)}... already processed, skipping`);
|
|
5422
5571
|
return;
|
|
5423
5572
|
}
|
|
5424
|
-
|
|
5425
|
-
|
|
5573
|
+
logger.debug(
|
|
5574
|
+
"Payments",
|
|
5575
|
+
`Processing V6 combined transfer ${bundle.transferId.slice(0, 12)}... (split=${!!bundle.splitBundle}, direct=${bundle.directTokens.length})`
|
|
5426
5576
|
);
|
|
5427
5577
|
const allTokens = [];
|
|
5428
5578
|
const tokenBreakdown = [];
|
|
@@ -5436,7 +5586,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5436
5586
|
allTokens.push(splitToken);
|
|
5437
5587
|
tokenBreakdown.push({ id: splitToken.id, amount: splitToken.amount, source: "split" });
|
|
5438
5588
|
} else {
|
|
5439
|
-
|
|
5589
|
+
logger.warn("Payments", `V6: split token was deduped/failed \u2014 amount=${bundle.splitBundle.amount}`);
|
|
5440
5590
|
}
|
|
5441
5591
|
}
|
|
5442
5592
|
const directResults = await Promise.all(
|
|
@@ -5451,13 +5601,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5451
5601
|
tokenBreakdown.push({ id: token.id, amount: token.amount, source: "direct" });
|
|
5452
5602
|
} else {
|
|
5453
5603
|
const entry = bundle.directTokens[i];
|
|
5454
|
-
|
|
5455
|
-
|
|
5604
|
+
logger.warn(
|
|
5605
|
+
"Payments",
|
|
5606
|
+
`V6: direct token #${i} dropped (amount=${entry.amount}, tokenId=${entry.tokenId?.slice(0, 12) ?? "N/A"})`
|
|
5456
5607
|
);
|
|
5457
5608
|
}
|
|
5458
5609
|
}
|
|
5459
5610
|
if (allTokens.length === 0) {
|
|
5460
|
-
|
|
5611
|
+
logger.debug("Payments", "V6 combined transfer: all tokens deduped, nothing to save");
|
|
5461
5612
|
return;
|
|
5462
5613
|
}
|
|
5463
5614
|
this.processedCombinedTransferIds.add(bundle.transferId);
|
|
@@ -5473,7 +5624,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5473
5624
|
TransferCommitment4.fromJSON(commitment).then(
|
|
5474
5625
|
(c) => stClient.submitTransferCommitment(c)
|
|
5475
5626
|
).catch(
|
|
5476
|
-
(err) =>
|
|
5627
|
+
(err) => logger.error("Payments", "V6 background commitment submit failed:", err)
|
|
5477
5628
|
);
|
|
5478
5629
|
}
|
|
5479
5630
|
}
|
|
@@ -5500,8 +5651,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5500
5651
|
tokenIds: tokenBreakdown
|
|
5501
5652
|
});
|
|
5502
5653
|
if (bundle.splitBundle) {
|
|
5503
|
-
this.resolveUnconfirmed().catch(() =>
|
|
5504
|
-
});
|
|
5654
|
+
this.resolveUnconfirmed().catch((err) => logger.debug("Payments", "resolveUnconfirmed failed", err));
|
|
5505
5655
|
this.scheduleResolveUnconfirmed();
|
|
5506
5656
|
}
|
|
5507
5657
|
}
|
|
@@ -5579,8 +5729,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5579
5729
|
receivedAt: Date.now()
|
|
5580
5730
|
});
|
|
5581
5731
|
await this.save();
|
|
5582
|
-
this.resolveUnconfirmed().catch(() =>
|
|
5583
|
-
});
|
|
5732
|
+
this.resolveUnconfirmed().catch((err) => logger.debug("Payments", "resolveUnconfirmed failed", err));
|
|
5584
5733
|
this.scheduleResolveUnconfirmed();
|
|
5585
5734
|
return { success: true, durationMs: 0 };
|
|
5586
5735
|
} catch (error) {
|
|
@@ -5601,11 +5750,11 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5601
5750
|
const signingService = await this.createSigningService();
|
|
5602
5751
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
5603
5752
|
if (!stClient) {
|
|
5604
|
-
throw new
|
|
5753
|
+
throw new SphereError("State transition client not available", "AGGREGATOR_ERROR");
|
|
5605
5754
|
}
|
|
5606
5755
|
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
5607
5756
|
if (!trustBase) {
|
|
5608
|
-
throw new
|
|
5757
|
+
throw new SphereError("Trust base not available", "AGGREGATOR_ERROR");
|
|
5609
5758
|
}
|
|
5610
5759
|
const devMode = this.deps.oracle.isDevMode?.() ?? false;
|
|
5611
5760
|
const processor = new InstantSplitProcessor({
|
|
@@ -5628,10 +5777,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5628
5777
|
if (proxy.address === proxyAddress) {
|
|
5629
5778
|
return nametagToken;
|
|
5630
5779
|
}
|
|
5631
|
-
|
|
5780
|
+
logger.debug("Payments", `Nametag PROXY address mismatch: ${proxy.address} !== ${proxyAddress}`);
|
|
5632
5781
|
return null;
|
|
5633
5782
|
} catch (err) {
|
|
5634
|
-
|
|
5783
|
+
logger.debug("Payments", "Failed to parse nametag token:", err);
|
|
5635
5784
|
return null;
|
|
5636
5785
|
}
|
|
5637
5786
|
}
|
|
@@ -5739,7 +5888,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5739
5888
|
status: "pending"
|
|
5740
5889
|
};
|
|
5741
5890
|
this.outgoingPaymentRequests.set(requestId2, outgoingRequest);
|
|
5742
|
-
|
|
5891
|
+
logger.debug("Payments", `Payment request sent: ${eventId}`);
|
|
5743
5892
|
return {
|
|
5744
5893
|
success: true,
|
|
5745
5894
|
requestId: requestId2,
|
|
@@ -5747,7 +5896,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5747
5896
|
};
|
|
5748
5897
|
} catch (error) {
|
|
5749
5898
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
5750
|
-
|
|
5899
|
+
logger.debug("Payments", `Failed to send payment request: ${errorMsg}`);
|
|
5751
5900
|
return {
|
|
5752
5901
|
success: false,
|
|
5753
5902
|
error: errorMsg
|
|
@@ -5836,10 +5985,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5836
5985
|
async payPaymentRequest(requestId2, memo) {
|
|
5837
5986
|
const request = this.paymentRequests.find((r) => r.id === requestId2);
|
|
5838
5987
|
if (!request) {
|
|
5839
|
-
throw new
|
|
5988
|
+
throw new SphereError(`Payment request not found: ${requestId2}`, "VALIDATION_ERROR");
|
|
5840
5989
|
}
|
|
5841
5990
|
if (request.status !== "pending" && request.status !== "accepted") {
|
|
5842
|
-
throw new
|
|
5991
|
+
throw new SphereError(`Payment request is not pending or accepted: ${request.status}`, "VALIDATION_ERROR");
|
|
5843
5992
|
}
|
|
5844
5993
|
this.updatePaymentRequestStatus(requestId2, "accepted");
|
|
5845
5994
|
try {
|
|
@@ -5894,10 +6043,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5894
6043
|
try {
|
|
5895
6044
|
handler(request);
|
|
5896
6045
|
} catch (error) {
|
|
5897
|
-
|
|
6046
|
+
logger.debug("Payments", "Payment request handler error:", error);
|
|
5898
6047
|
}
|
|
5899
6048
|
}
|
|
5900
|
-
|
|
6049
|
+
logger.debug("Payments", `Incoming payment request: ${request.id} for ${request.amount} ${request.symbol}`);
|
|
5901
6050
|
}
|
|
5902
6051
|
// ===========================================================================
|
|
5903
6052
|
// Public API - Outgoing Payment Requests
|
|
@@ -6016,10 +6165,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6016
6165
|
try {
|
|
6017
6166
|
handler(response);
|
|
6018
6167
|
} catch (error) {
|
|
6019
|
-
|
|
6168
|
+
logger.debug("Payments", "Payment request response handler error:", error);
|
|
6020
6169
|
}
|
|
6021
6170
|
}
|
|
6022
|
-
|
|
6171
|
+
logger.debug("Payments", `Received payment request response: ${response.id} type: ${response.responseType}`);
|
|
6023
6172
|
}
|
|
6024
6173
|
/**
|
|
6025
6174
|
* Send a response to a payment request (used internally by accept/reject/pay methods)
|
|
@@ -6028,7 +6177,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6028
6177
|
const request = this.paymentRequests.find((r) => r.id === requestId2);
|
|
6029
6178
|
if (!request) return;
|
|
6030
6179
|
if (!this.deps?.transport.sendPaymentRequestResponse) {
|
|
6031
|
-
|
|
6180
|
+
logger.debug("Payments", "Transport does not support sendPaymentRequestResponse");
|
|
6032
6181
|
return;
|
|
6033
6182
|
}
|
|
6034
6183
|
try {
|
|
@@ -6039,9 +6188,9 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6039
6188
|
transferId
|
|
6040
6189
|
};
|
|
6041
6190
|
await this.deps.transport.sendPaymentRequestResponse(request.senderPubkey, payload);
|
|
6042
|
-
|
|
6191
|
+
logger.debug("Payments", `Sent payment request response: ${responseType} for ${requestId2}`);
|
|
6043
6192
|
} catch (error) {
|
|
6044
|
-
|
|
6193
|
+
logger.debug("Payments", "Failed to send payment request response:", error);
|
|
6045
6194
|
}
|
|
6046
6195
|
}
|
|
6047
6196
|
// ===========================================================================
|
|
@@ -6065,7 +6214,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6065
6214
|
async receive(options, callback) {
|
|
6066
6215
|
this.ensureInitialized();
|
|
6067
6216
|
if (!this.deps.transport.fetchPendingEvents) {
|
|
6068
|
-
throw new
|
|
6217
|
+
throw new SphereError("Transport provider does not support fetchPendingEvents", "TRANSPORT_ERROR");
|
|
6069
6218
|
}
|
|
6070
6219
|
const opts = options ?? {};
|
|
6071
6220
|
const tokensBefore = new Set(this.tokens.keys());
|
|
@@ -6212,7 +6361,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6212
6361
|
});
|
|
6213
6362
|
}
|
|
6214
6363
|
} catch (error) {
|
|
6215
|
-
|
|
6364
|
+
logger.warn("Payments", "Failed to fetch prices, returning assets without price data:", error);
|
|
6216
6365
|
}
|
|
6217
6366
|
return rawAssets;
|
|
6218
6367
|
}
|
|
@@ -6334,24 +6483,24 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6334
6483
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
6335
6484
|
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
6336
6485
|
if (!stClient || !trustBase) {
|
|
6337
|
-
|
|
6486
|
+
logger.debug("Payments", `[V5-RESOLVE] resolveUnconfirmed: EARLY EXIT \u2014 stClient=${!!stClient} trustBase=${!!trustBase}`);
|
|
6338
6487
|
return result;
|
|
6339
6488
|
}
|
|
6340
6489
|
const signingService = await this.createSigningService();
|
|
6341
6490
|
const submittedCount = Array.from(this.tokens.values()).filter((t) => t.status === "submitted").length;
|
|
6342
|
-
|
|
6491
|
+
logger.debug("Payments", `[V5-RESOLVE] resolveUnconfirmed: ${submittedCount} submitted token(s) to process`);
|
|
6343
6492
|
for (const [tokenId, token] of this.tokens) {
|
|
6344
6493
|
if (token.status !== "submitted") continue;
|
|
6345
6494
|
const pending2 = this.parsePendingFinalization(token.sdkData);
|
|
6346
6495
|
if (!pending2) {
|
|
6347
|
-
|
|
6496
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 16)}: no pending finalization metadata, skipping`);
|
|
6348
6497
|
result.stillPending++;
|
|
6349
6498
|
continue;
|
|
6350
6499
|
}
|
|
6351
6500
|
if (pending2.type === "v5_bundle") {
|
|
6352
|
-
|
|
6501
|
+
logger.debug("Payments", `[V5-RESOLVE] Processing ${tokenId.slice(0, 16)}... stage=${pending2.stage} attempt=${pending2.attemptCount}`);
|
|
6353
6502
|
const progress = await this.resolveV5Token(tokenId, token, pending2, stClient, trustBase, signingService);
|
|
6354
|
-
|
|
6503
|
+
logger.debug("Payments", `[V5-RESOLVE] Result for ${tokenId.slice(0, 16)}...: ${progress} (stage now: ${pending2.stage})`);
|
|
6355
6504
|
result.details.push({ tokenId, stage: pending2.stage, status: progress });
|
|
6356
6505
|
if (progress === "resolved") result.resolved++;
|
|
6357
6506
|
else if (progress === "failed") result.failed++;
|
|
@@ -6359,7 +6508,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6359
6508
|
}
|
|
6360
6509
|
}
|
|
6361
6510
|
if (result.resolved > 0 || result.failed > 0 || result.stillPending > 0) {
|
|
6362
|
-
|
|
6511
|
+
logger.debug("Payments", `[V5-RESOLVE] Saving: resolved=${result.resolved} failed=${result.failed} stillPending=${result.stillPending}`);
|
|
6363
6512
|
await this.save();
|
|
6364
6513
|
}
|
|
6365
6514
|
return result;
|
|
@@ -6375,19 +6524,19 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6375
6524
|
(t) => t.status === "submitted"
|
|
6376
6525
|
);
|
|
6377
6526
|
if (!hasUnconfirmed) {
|
|
6378
|
-
|
|
6527
|
+
logger.debug("Payments", "[V5-RESOLVE] scheduleResolveUnconfirmed: no submitted tokens, not starting timer");
|
|
6379
6528
|
return;
|
|
6380
6529
|
}
|
|
6381
|
-
|
|
6530
|
+
logger.debug("Payments", `[V5-RESOLVE] scheduleResolveUnconfirmed: starting periodic retry (every ${_PaymentsModule.RESOLVE_UNCONFIRMED_INTERVAL_MS}ms)`);
|
|
6382
6531
|
this.resolveUnconfirmedTimer = setInterval(async () => {
|
|
6383
6532
|
try {
|
|
6384
6533
|
const result = await this.resolveUnconfirmed();
|
|
6385
6534
|
if (result.stillPending === 0) {
|
|
6386
|
-
|
|
6535
|
+
logger.debug("Payments", "[V5-RESOLVE] All tokens resolved, stopping periodic retry");
|
|
6387
6536
|
this.stopResolveUnconfirmedPolling();
|
|
6388
6537
|
}
|
|
6389
6538
|
} catch (err) {
|
|
6390
|
-
|
|
6539
|
+
logger.debug("Payments", "[V5-RESOLVE] Periodic retry error:", err);
|
|
6391
6540
|
}
|
|
6392
6541
|
}, _PaymentsModule.RESOLVE_UNCONFIRMED_INTERVAL_MS);
|
|
6393
6542
|
}
|
|
@@ -6409,57 +6558,57 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6409
6558
|
pending2.lastAttemptAt = Date.now();
|
|
6410
6559
|
try {
|
|
6411
6560
|
if (pending2.stage === "RECEIVED") {
|
|
6412
|
-
|
|
6561
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: RECEIVED \u2192 submitting mint commitment...`);
|
|
6413
6562
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
6414
6563
|
const mintData = await MintTransactionData3.fromJSON(mintDataJson);
|
|
6415
6564
|
const mintCommitment = await MintCommitment3.create(mintData);
|
|
6416
6565
|
const mintResponse = await stClient.submitMintCommitment(mintCommitment);
|
|
6417
|
-
|
|
6566
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint response status=${mintResponse.status}`);
|
|
6418
6567
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
6419
|
-
throw new
|
|
6568
|
+
throw new SphereError(`Mint submission failed: ${mintResponse.status}`, "TRANSFER_FAILED");
|
|
6420
6569
|
}
|
|
6421
6570
|
pending2.stage = "MINT_SUBMITTED";
|
|
6422
6571
|
this.updatePendingFinalization(token, pending2);
|
|
6423
6572
|
}
|
|
6424
6573
|
if (pending2.stage === "MINT_SUBMITTED") {
|
|
6425
|
-
|
|
6574
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: MINT_SUBMITTED \u2192 checking mint proof...`);
|
|
6426
6575
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
6427
6576
|
const mintData = await MintTransactionData3.fromJSON(mintDataJson);
|
|
6428
6577
|
const mintCommitment = await MintCommitment3.create(mintData);
|
|
6429
6578
|
const proof = await this.quickProofCheck(stClient, trustBase, mintCommitment);
|
|
6430
6579
|
if (!proof) {
|
|
6431
|
-
|
|
6580
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint proof not yet available, staying MINT_SUBMITTED`);
|
|
6432
6581
|
this.updatePendingFinalization(token, pending2);
|
|
6433
6582
|
return "pending";
|
|
6434
6583
|
}
|
|
6435
|
-
|
|
6584
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint proof obtained!`);
|
|
6436
6585
|
pending2.mintProofJson = JSON.stringify(proof);
|
|
6437
6586
|
pending2.stage = "MINT_PROVEN";
|
|
6438
6587
|
this.updatePendingFinalization(token, pending2);
|
|
6439
6588
|
}
|
|
6440
6589
|
if (pending2.stage === "MINT_PROVEN") {
|
|
6441
|
-
|
|
6590
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: MINT_PROVEN \u2192 submitting transfer commitment...`);
|
|
6442
6591
|
const transferCommitmentJson = JSON.parse(bundle.transferCommitment);
|
|
6443
6592
|
const transferCommitment = await TransferCommitment4.fromJSON(transferCommitmentJson);
|
|
6444
6593
|
const transferResponse = await stClient.submitTransferCommitment(transferCommitment);
|
|
6445
|
-
|
|
6594
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: transfer response status=${transferResponse.status}`);
|
|
6446
6595
|
if (transferResponse.status !== "SUCCESS" && transferResponse.status !== "REQUEST_ID_EXISTS") {
|
|
6447
|
-
throw new
|
|
6596
|
+
throw new SphereError(`Transfer submission failed: ${transferResponse.status}`, "TRANSFER_FAILED");
|
|
6448
6597
|
}
|
|
6449
6598
|
pending2.stage = "TRANSFER_SUBMITTED";
|
|
6450
6599
|
this.updatePendingFinalization(token, pending2);
|
|
6451
6600
|
}
|
|
6452
6601
|
if (pending2.stage === "TRANSFER_SUBMITTED") {
|
|
6453
|
-
|
|
6602
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: TRANSFER_SUBMITTED \u2192 checking transfer proof...`);
|
|
6454
6603
|
const transferCommitmentJson = JSON.parse(bundle.transferCommitment);
|
|
6455
6604
|
const transferCommitment = await TransferCommitment4.fromJSON(transferCommitmentJson);
|
|
6456
6605
|
const proof = await this.quickProofCheck(stClient, trustBase, transferCommitment);
|
|
6457
6606
|
if (!proof) {
|
|
6458
|
-
|
|
6607
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: transfer proof not yet available, staying TRANSFER_SUBMITTED`);
|
|
6459
6608
|
this.updatePendingFinalization(token, pending2);
|
|
6460
6609
|
return "pending";
|
|
6461
6610
|
}
|
|
6462
|
-
|
|
6611
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: transfer proof obtained! Finalizing...`);
|
|
6463
6612
|
const finalizedToken = await this.finalizeFromV5Bundle(bundle, pending2, signingService, stClient, trustBase);
|
|
6464
6613
|
const confirmedToken = {
|
|
6465
6614
|
id: token.id,
|
|
@@ -6481,12 +6630,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6481
6630
|
tokens: [confirmedToken],
|
|
6482
6631
|
tokenTransfers: []
|
|
6483
6632
|
});
|
|
6484
|
-
|
|
6633
|
+
logger.debug("Payments", `V5 token resolved: ${tokenId.slice(0, 8)}...`);
|
|
6485
6634
|
return "resolved";
|
|
6486
6635
|
}
|
|
6487
6636
|
return "pending";
|
|
6488
6637
|
} catch (error) {
|
|
6489
|
-
|
|
6638
|
+
logger.error("Payments", `resolveV5Token failed for ${tokenId.slice(0, 8)}:`, error);
|
|
6490
6639
|
if (pending2.attemptCount > 50) {
|
|
6491
6640
|
token.status = "invalid";
|
|
6492
6641
|
token.updatedAt = Date.now();
|
|
@@ -6623,19 +6772,19 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6623
6772
|
}
|
|
6624
6773
|
if (pendingTokens.length > 0) {
|
|
6625
6774
|
const json = JSON.stringify(pendingTokens);
|
|
6626
|
-
|
|
6775
|
+
logger.debug("Payments", `[V5-PERSIST] Saving ${pendingTokens.length} pending V5 token(s): ${pendingTokens.map((t) => t.id.slice(0, 16)).join(", ")} (${json.length} bytes)`);
|
|
6627
6776
|
await this.deps.storage.set(
|
|
6628
6777
|
STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS,
|
|
6629
6778
|
json
|
|
6630
6779
|
);
|
|
6631
6780
|
const verify = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS);
|
|
6632
6781
|
if (!verify) {
|
|
6633
|
-
|
|
6782
|
+
logger.error("Payments", "[V5-PERSIST] CRITICAL: KV write succeeded but read-back is empty!");
|
|
6634
6783
|
} else {
|
|
6635
|
-
|
|
6784
|
+
logger.debug("Payments", `[V5-PERSIST] Verified: read-back ${verify.length} bytes`);
|
|
6636
6785
|
}
|
|
6637
6786
|
} else {
|
|
6638
|
-
|
|
6787
|
+
logger.debug("Payments", `[V5-PERSIST] No pending V5 tokens to save (total tokens: ${this.tokens.size}), clearing KV`);
|
|
6639
6788
|
await this.deps.storage.set(STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS, "");
|
|
6640
6789
|
}
|
|
6641
6790
|
}
|
|
@@ -6645,21 +6794,21 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6645
6794
|
*/
|
|
6646
6795
|
async loadPendingV5Tokens() {
|
|
6647
6796
|
const data = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS);
|
|
6648
|
-
|
|
6797
|
+
logger.debug("Payments", `[V5-PERSIST] loadPendingV5Tokens: KV data = ${data ? `${data.length} bytes` : "null/empty"}`);
|
|
6649
6798
|
if (!data) return;
|
|
6650
6799
|
try {
|
|
6651
6800
|
const pendingTokens = JSON.parse(data);
|
|
6652
|
-
|
|
6801
|
+
logger.debug("Payments", `[V5-PERSIST] Parsed ${pendingTokens.length} pending V5 token(s): ${pendingTokens.map((t) => t.id.slice(0, 16)).join(", ")}`);
|
|
6653
6802
|
for (const token of pendingTokens) {
|
|
6654
6803
|
if (!this.tokens.has(token.id)) {
|
|
6655
6804
|
this.tokens.set(token.id, token);
|
|
6656
|
-
|
|
6805
|
+
logger.debug("Payments", `[V5-PERSIST] Restored token ${token.id.slice(0, 16)} (status=${token.status})`);
|
|
6657
6806
|
} else {
|
|
6658
|
-
|
|
6807
|
+
logger.debug("Payments", `[V5-PERSIST] Token ${token.id.slice(0, 16)} already in map, skipping`);
|
|
6659
6808
|
}
|
|
6660
6809
|
}
|
|
6661
6810
|
} catch (err) {
|
|
6662
|
-
|
|
6811
|
+
logger.error("Payments", "[V5-PERSIST] Failed to parse pending V5 tokens:", err);
|
|
6663
6812
|
}
|
|
6664
6813
|
}
|
|
6665
6814
|
/**
|
|
@@ -6712,13 +6861,13 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6712
6861
|
const incomingStateHash = extractStateHashFromSdkData(token.sdkData);
|
|
6713
6862
|
const incomingStateKey = incomingTokenId && incomingStateHash ? createTokenStateKey(incomingTokenId, incomingStateHash) : null;
|
|
6714
6863
|
if (incomingTokenId && incomingStateHash && this.isStateTombstoned(incomingTokenId, incomingStateHash)) {
|
|
6715
|
-
|
|
6864
|
+
logger.debug("Payments", `Rejecting tombstoned token: ${incomingTokenId.slice(0, 8)}..._${incomingStateHash.slice(0, 8)}...`);
|
|
6716
6865
|
return false;
|
|
6717
6866
|
}
|
|
6718
6867
|
if (incomingStateKey) {
|
|
6719
6868
|
for (const [_existingId, existing] of this.tokens) {
|
|
6720
6869
|
if (isSameTokenState(existing, token)) {
|
|
6721
|
-
|
|
6870
|
+
logger.debug("Payments", `Duplicate token state ignored: ${incomingTokenId?.slice(0, 8)}..._${incomingStateHash?.slice(0, 8)}...`);
|
|
6722
6871
|
return false;
|
|
6723
6872
|
}
|
|
6724
6873
|
}
|
|
@@ -6730,19 +6879,19 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6730
6879
|
continue;
|
|
6731
6880
|
}
|
|
6732
6881
|
if (existing.status === "spent" || existing.status === "invalid") {
|
|
6733
|
-
|
|
6882
|
+
logger.debug("Payments", `Replacing spent/invalid token ${incomingTokenId?.slice(0, 8)}...`);
|
|
6734
6883
|
this.tokens.delete(existingId);
|
|
6735
6884
|
break;
|
|
6736
6885
|
}
|
|
6737
6886
|
if (incomingStateHash && existingStateHash && incomingStateHash !== existingStateHash) {
|
|
6738
|
-
|
|
6887
|
+
logger.debug("Payments", `Token ${incomingTokenId?.slice(0, 8)}... state updated: ${existingStateHash.slice(0, 8)}... -> ${incomingStateHash.slice(0, 8)}...`);
|
|
6739
6888
|
await this.archiveToken(existing);
|
|
6740
6889
|
this.tokens.delete(existingId);
|
|
6741
6890
|
break;
|
|
6742
6891
|
}
|
|
6743
6892
|
if (!incomingStateHash || !existingStateHash) {
|
|
6744
6893
|
if (existingId !== token.id) {
|
|
6745
|
-
|
|
6894
|
+
logger.debug("Payments", `Token ${incomingTokenId?.slice(0, 8)}... .id changed, replacing`);
|
|
6746
6895
|
await this.archiveToken(existing);
|
|
6747
6896
|
this.tokens.delete(existingId);
|
|
6748
6897
|
break;
|
|
@@ -6753,7 +6902,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6753
6902
|
this.tokens.set(token.id, token);
|
|
6754
6903
|
await this.archiveToken(token);
|
|
6755
6904
|
await this.save();
|
|
6756
|
-
|
|
6905
|
+
logger.debug("Payments", `Added token ${token.id}, total: ${this.tokens.size}`);
|
|
6757
6906
|
return true;
|
|
6758
6907
|
}
|
|
6759
6908
|
/**
|
|
@@ -6783,7 +6932,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6783
6932
|
}
|
|
6784
6933
|
await this.archiveToken(token);
|
|
6785
6934
|
await this.save();
|
|
6786
|
-
|
|
6935
|
+
logger.debug("Payments", `Updated token ${token.id}`);
|
|
6787
6936
|
}
|
|
6788
6937
|
/**
|
|
6789
6938
|
* Remove a token from the wallet.
|
|
@@ -6806,10 +6955,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6806
6955
|
);
|
|
6807
6956
|
if (!alreadyTombstoned) {
|
|
6808
6957
|
this.tombstones.push(tombstone);
|
|
6809
|
-
|
|
6958
|
+
logger.debug("Payments", `Created tombstone for ${tombstone.tokenId.slice(0, 8)}..._${tombstone.stateHash.slice(0, 8)}...`);
|
|
6810
6959
|
}
|
|
6811
6960
|
} else {
|
|
6812
|
-
|
|
6961
|
+
logger.debug("Payments", `Warning: Could not create tombstone for token ${tokenId.slice(0, 8)}... (missing tokenId or stateHash)`);
|
|
6813
6962
|
}
|
|
6814
6963
|
this.tokens.delete(tokenId);
|
|
6815
6964
|
await this.save();
|
|
@@ -6866,7 +7015,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6866
7015
|
}
|
|
6867
7016
|
for (const token of tokensToRemove) {
|
|
6868
7017
|
this.tokens.delete(token.id);
|
|
6869
|
-
|
|
7018
|
+
logger.debug("Payments", `Removed tombstoned token ${token.id.slice(0, 8)}...`);
|
|
6870
7019
|
removedCount++;
|
|
6871
7020
|
}
|
|
6872
7021
|
for (const remoteTombstone of remoteTombstones) {
|
|
@@ -6892,7 +7041,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6892
7041
|
this.tombstones = pruneTombstonesByAge(this.tombstones, maxAge);
|
|
6893
7042
|
if (this.tombstones.length < originalCount) {
|
|
6894
7043
|
await this.save();
|
|
6895
|
-
|
|
7044
|
+
logger.debug("Payments", `Pruned tombstones from ${originalCount} to ${this.tombstones.length}`);
|
|
6896
7045
|
}
|
|
6897
7046
|
}
|
|
6898
7047
|
// ===========================================================================
|
|
@@ -6964,7 +7113,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6964
7113
|
const originalCount = this.archivedTokens.size;
|
|
6965
7114
|
this.archivedTokens = pruneMapByCount(this.archivedTokens, maxCount);
|
|
6966
7115
|
await this.save();
|
|
6967
|
-
|
|
7116
|
+
logger.debug("Payments", `Pruned archived tokens from ${originalCount} to ${this.archivedTokens.size}`);
|
|
6968
7117
|
}
|
|
6969
7118
|
// ===========================================================================
|
|
6970
7119
|
// Public API - Forked Tokens
|
|
@@ -6993,7 +7142,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6993
7142
|
const key = `${tokenId}_${stateHash}`;
|
|
6994
7143
|
if (this.forkedTokens.has(key)) return;
|
|
6995
7144
|
this.forkedTokens.set(key, txfToken);
|
|
6996
|
-
|
|
7145
|
+
logger.debug("Payments", `Stored forked token ${tokenId.slice(0, 8)}... state ${stateHash.slice(0, 12)}...`);
|
|
6997
7146
|
await this.save();
|
|
6998
7147
|
}
|
|
6999
7148
|
/**
|
|
@@ -7025,7 +7174,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7025
7174
|
const originalCount = this.forkedTokens.size;
|
|
7026
7175
|
this.forkedTokens = pruneMapByCount(this.forkedTokens, maxCount);
|
|
7027
7176
|
await this.save();
|
|
7028
|
-
|
|
7177
|
+
logger.debug("Payments", `Pruned forked tokens from ${originalCount} to ${this.forkedTokens.size}`);
|
|
7029
7178
|
}
|
|
7030
7179
|
// ===========================================================================
|
|
7031
7180
|
// Public API - Transaction History
|
|
@@ -7105,7 +7254,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7105
7254
|
const imported = await provider.importHistoryEntries?.(records) ?? 0;
|
|
7106
7255
|
if (imported > 0) {
|
|
7107
7256
|
this._historyCache = await provider.getHistoryEntries();
|
|
7108
|
-
|
|
7257
|
+
logger.debug("Payments", `Migrated ${imported} history entries from KV to history store`);
|
|
7109
7258
|
}
|
|
7110
7259
|
await this.deps.storage.remove(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
|
|
7111
7260
|
} catch {
|
|
@@ -7181,7 +7330,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7181
7330
|
this.nametags.push(nametag);
|
|
7182
7331
|
}
|
|
7183
7332
|
await this.save();
|
|
7184
|
-
|
|
7333
|
+
logger.debug("Payments", `Nametag set: ${nametag.name}`);
|
|
7185
7334
|
}
|
|
7186
7335
|
/**
|
|
7187
7336
|
* Get the current (first) nametag data.
|
|
@@ -7230,7 +7379,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7230
7379
|
const parsed = parseTxfStorageData(result.data);
|
|
7231
7380
|
if (parsed.nametags.length > 0) {
|
|
7232
7381
|
this.nametags = parsed.nametags;
|
|
7233
|
-
|
|
7382
|
+
logger.debug("Payments", `Reloaded ${parsed.nametags.length} nametag(s) from storage`);
|
|
7234
7383
|
return;
|
|
7235
7384
|
}
|
|
7236
7385
|
}
|
|
@@ -7283,7 +7432,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7283
7432
|
const result = await minter.mintNametag(nametag, ownerAddress);
|
|
7284
7433
|
if (result.success && result.nametagData) {
|
|
7285
7434
|
await this.setNametag(result.nametagData);
|
|
7286
|
-
|
|
7435
|
+
logger.debug("Payments", `Nametag minted and saved: ${result.nametagData.name}`);
|
|
7287
7436
|
this.deps.emitEvent("nametag:registered", {
|
|
7288
7437
|
nametag: result.nametagData.name,
|
|
7289
7438
|
addressIndex: 0
|
|
@@ -7293,7 +7442,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7293
7442
|
return result;
|
|
7294
7443
|
} catch (error) {
|
|
7295
7444
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
7296
|
-
|
|
7445
|
+
logger.debug("Payments", "mintNametag failed:", errorMsg);
|
|
7297
7446
|
return {
|
|
7298
7447
|
success: false,
|
|
7299
7448
|
error: errorMsg
|
|
@@ -7391,7 +7540,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7391
7540
|
restoredCount++;
|
|
7392
7541
|
}
|
|
7393
7542
|
if (restoredCount > 0) {
|
|
7394
|
-
|
|
7543
|
+
logger.debug("Payments", `Sync: restored ${restoredCount} token(s) lost by loadFromStorageData`);
|
|
7395
7544
|
}
|
|
7396
7545
|
if (this.nametags.length === 0 && savedNametags.length > 0) {
|
|
7397
7546
|
this.nametags = savedNametags;
|
|
@@ -7400,7 +7549,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7400
7549
|
if (txfData._history && txfData._history.length > 0) {
|
|
7401
7550
|
const imported = await this.importRemoteHistoryEntries(txfData._history);
|
|
7402
7551
|
if (imported > 0) {
|
|
7403
|
-
|
|
7552
|
+
logger.debug("Payments", `Imported ${imported} history entries from IPFS sync`);
|
|
7404
7553
|
}
|
|
7405
7554
|
}
|
|
7406
7555
|
totalAdded += result.added;
|
|
@@ -7413,7 +7562,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7413
7562
|
removed: result.removed
|
|
7414
7563
|
});
|
|
7415
7564
|
} catch (providerError) {
|
|
7416
|
-
|
|
7565
|
+
logger.warn("Payments", `Sync failed for provider ${providerId}:`, providerError);
|
|
7417
7566
|
this.deps.emitEvent("sync:provider", {
|
|
7418
7567
|
providerId,
|
|
7419
7568
|
success: false,
|
|
@@ -7451,7 +7600,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7451
7600
|
if (provider.onEvent) {
|
|
7452
7601
|
const unsub = provider.onEvent((event) => {
|
|
7453
7602
|
if (event.type === "storage:remote-updated") {
|
|
7454
|
-
|
|
7603
|
+
logger.debug("Payments", "Remote update detected from provider", providerId, event.data);
|
|
7455
7604
|
this.debouncedSyncFromRemoteUpdate(providerId, event.data);
|
|
7456
7605
|
}
|
|
7457
7606
|
});
|
|
@@ -7493,7 +7642,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7493
7642
|
removed: result.removed
|
|
7494
7643
|
});
|
|
7495
7644
|
}).catch((err) => {
|
|
7496
|
-
|
|
7645
|
+
logger.debug("Payments", "Auto-sync from remote update failed:", err);
|
|
7497
7646
|
});
|
|
7498
7647
|
}, _PaymentsModule.SYNC_DEBOUNCE_MS);
|
|
7499
7648
|
}
|
|
@@ -7598,8 +7747,9 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7598
7747
|
}
|
|
7599
7748
|
return recipient;
|
|
7600
7749
|
}
|
|
7601
|
-
throw new
|
|
7602
|
-
`Cannot resolve transport pubkey for "${recipient}". No binding event found. The recipient must publish their identity first
|
|
7750
|
+
throw new SphereError(
|
|
7751
|
+
`Cannot resolve transport pubkey for "${recipient}". No binding event found. The recipient must publish their identity first.`,
|
|
7752
|
+
"INVALID_RECIPIENT"
|
|
7603
7753
|
);
|
|
7604
7754
|
}
|
|
7605
7755
|
/**
|
|
@@ -7670,32 +7820,33 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7670
7820
|
return AddressFactory.createAddress(recipient);
|
|
7671
7821
|
}
|
|
7672
7822
|
if (recipient.length === 66 && /^[0-9a-fA-F]+$/.test(recipient)) {
|
|
7673
|
-
|
|
7823
|
+
logger.debug("Payments", "Creating DirectAddress from 33-byte compressed pubkey");
|
|
7674
7824
|
return this.createDirectAddressFromPubkey(recipient);
|
|
7675
7825
|
}
|
|
7676
7826
|
const info = peerInfo ?? await this.deps?.transport.resolve?.(recipient) ?? null;
|
|
7677
7827
|
if (!info) {
|
|
7678
|
-
throw new
|
|
7679
|
-
`Recipient "${recipient}" not found. Use @nametag, a valid PROXY:/DIRECT: address, or a 33-byte hex pubkey
|
|
7828
|
+
throw new SphereError(
|
|
7829
|
+
`Recipient "${recipient}" not found. Use @nametag, a valid PROXY:/DIRECT: address, or a 33-byte hex pubkey.`,
|
|
7830
|
+
"INVALID_RECIPIENT"
|
|
7680
7831
|
);
|
|
7681
7832
|
}
|
|
7682
7833
|
const nametag = recipient.startsWith("@") ? recipient.slice(1) : info.nametag || recipient;
|
|
7683
7834
|
if (addressMode === "proxy") {
|
|
7684
|
-
|
|
7835
|
+
logger.debug("Payments", `Using PROXY address for "${nametag}" (forced)`);
|
|
7685
7836
|
return ProxyAddress.fromNameTag(nametag);
|
|
7686
7837
|
}
|
|
7687
7838
|
if (addressMode === "direct") {
|
|
7688
7839
|
if (!info.directAddress) {
|
|
7689
|
-
throw new
|
|
7840
|
+
throw new SphereError(`"${nametag}" has no DirectAddress stored. It may be a legacy registration.`, "INVALID_RECIPIENT");
|
|
7690
7841
|
}
|
|
7691
|
-
|
|
7842
|
+
logger.debug("Payments", `Using DirectAddress for "${nametag}" (forced): ${info.directAddress.slice(0, 30)}...`);
|
|
7692
7843
|
return AddressFactory.createAddress(info.directAddress);
|
|
7693
7844
|
}
|
|
7694
7845
|
if (info.directAddress) {
|
|
7695
|
-
|
|
7846
|
+
logger.debug("Payments", `Using DirectAddress for "${nametag}": ${info.directAddress.slice(0, 30)}...`);
|
|
7696
7847
|
return AddressFactory.createAddress(info.directAddress);
|
|
7697
7848
|
}
|
|
7698
|
-
|
|
7849
|
+
logger.debug("Payments", `Using PROXY address for legacy nametag "${nametag}"`);
|
|
7699
7850
|
return ProxyAddress.fromNameTag(nametag);
|
|
7700
7851
|
}
|
|
7701
7852
|
/**
|
|
@@ -7708,7 +7859,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7708
7859
|
const sourceTokenInput = typeof payload.sourceToken === "string" ? JSON.parse(payload.sourceToken) : payload.sourceToken;
|
|
7709
7860
|
const commitmentInput = typeof payload.commitmentData === "string" ? JSON.parse(payload.commitmentData) : payload.commitmentData;
|
|
7710
7861
|
if (!sourceTokenInput || !commitmentInput) {
|
|
7711
|
-
|
|
7862
|
+
logger.warn("Payments", "Invalid NOSTR-FIRST transfer format");
|
|
7712
7863
|
return;
|
|
7713
7864
|
}
|
|
7714
7865
|
const token = await this.saveCommitmentOnlyToken(
|
|
@@ -7739,7 +7890,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7739
7890
|
tokenId: nostrTokenId || token.id
|
|
7740
7891
|
});
|
|
7741
7892
|
} catch (error) {
|
|
7742
|
-
|
|
7893
|
+
logger.error("Payments", "Failed to process NOSTR-FIRST transfer:", error);
|
|
7743
7894
|
}
|
|
7744
7895
|
}
|
|
7745
7896
|
/**
|
|
@@ -7764,18 +7915,19 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7764
7915
|
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
7765
7916
|
let proxyNametag = this.getNametag();
|
|
7766
7917
|
if (!proxyNametag?.token) {
|
|
7767
|
-
|
|
7918
|
+
logger.debug("Payments", "Nametag missing in memory, attempting reload from storage...");
|
|
7768
7919
|
await this.reloadNametagsFromStorage();
|
|
7769
7920
|
proxyNametag = this.getNametag();
|
|
7770
7921
|
}
|
|
7771
7922
|
if (!proxyNametag?.token) {
|
|
7772
|
-
throw new
|
|
7923
|
+
throw new SphereError("Cannot finalize PROXY transfer - no nametag token", "VALIDATION_ERROR");
|
|
7773
7924
|
}
|
|
7774
7925
|
const nametagToken = await SdkToken2.fromJSON(proxyNametag.token);
|
|
7775
7926
|
const proxy = await ProxyAddress.fromTokenId(nametagToken.id);
|
|
7776
7927
|
if (proxy.address !== recipientAddress.address) {
|
|
7777
|
-
throw new
|
|
7778
|
-
`PROXY address mismatch: nametag resolves to ${proxy.address} but transfer targets ${recipientAddress.address}
|
|
7928
|
+
throw new SphereError(
|
|
7929
|
+
`PROXY address mismatch: nametag resolves to ${proxy.address} but transfer targets ${recipientAddress.address}`,
|
|
7930
|
+
"VALIDATION_ERROR"
|
|
7779
7931
|
);
|
|
7780
7932
|
}
|
|
7781
7933
|
nametagTokens = [nametagToken];
|
|
@@ -7795,12 +7947,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7795
7947
|
try {
|
|
7796
7948
|
const token = this.tokens.get(tokenId);
|
|
7797
7949
|
if (!token) {
|
|
7798
|
-
|
|
7950
|
+
logger.debug("Payments", `Token ${tokenId} not found for finalization`);
|
|
7799
7951
|
return;
|
|
7800
7952
|
}
|
|
7801
7953
|
const commitment = await TransferCommitment4.fromJSON(commitmentInput);
|
|
7802
7954
|
if (!this.deps.oracle.waitForProofSdk) {
|
|
7803
|
-
|
|
7955
|
+
logger.debug("Payments", "Cannot finalize - no waitForProofSdk");
|
|
7804
7956
|
token.status = "confirmed";
|
|
7805
7957
|
token.updatedAt = Date.now();
|
|
7806
7958
|
await this.save();
|
|
@@ -7812,7 +7964,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7812
7964
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
7813
7965
|
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
7814
7966
|
if (!stClient || !trustBase) {
|
|
7815
|
-
|
|
7967
|
+
logger.debug("Payments", "Cannot finalize - missing state transition client or trust base");
|
|
7816
7968
|
token.status = "confirmed";
|
|
7817
7969
|
token.updatedAt = Date.now();
|
|
7818
7970
|
await this.save();
|
|
@@ -7832,7 +7984,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7832
7984
|
};
|
|
7833
7985
|
this.tokens.set(tokenId, finalizedToken);
|
|
7834
7986
|
await this.save();
|
|
7835
|
-
|
|
7987
|
+
logger.debug("Payments", `NOSTR-FIRST: Token ${tokenId.slice(0, 8)}... finalized and confirmed`);
|
|
7836
7988
|
this.deps.emitEvent("transfer:confirmed", {
|
|
7837
7989
|
id: crypto.randomUUID(),
|
|
7838
7990
|
status: "completed",
|
|
@@ -7840,7 +7992,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7840
7992
|
tokenTransfers: []
|
|
7841
7993
|
});
|
|
7842
7994
|
} catch (error) {
|
|
7843
|
-
|
|
7995
|
+
logger.error("Payments", "Failed to finalize received token:", error);
|
|
7844
7996
|
const token = this.tokens.get(tokenId);
|
|
7845
7997
|
if (token && token.status === "submitted") {
|
|
7846
7998
|
token.status = "confirmed";
|
|
@@ -7855,7 +8007,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7855
8007
|
}
|
|
7856
8008
|
try {
|
|
7857
8009
|
const payload = transfer.payload;
|
|
7858
|
-
|
|
8010
|
+
logger.debug("Payments", "handleIncomingTransfer: keys=", Object.keys(payload).join(","));
|
|
7859
8011
|
let combinedBundle = null;
|
|
7860
8012
|
if (isCombinedTransferBundleV6(payload)) {
|
|
7861
8013
|
combinedBundle = payload;
|
|
@@ -7869,12 +8021,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7869
8021
|
}
|
|
7870
8022
|
}
|
|
7871
8023
|
if (combinedBundle) {
|
|
7872
|
-
|
|
8024
|
+
logger.debug("Payments", "Processing COMBINED_TRANSFER V6 bundle...");
|
|
7873
8025
|
try {
|
|
7874
8026
|
await this.processCombinedTransferBundle(combinedBundle, transfer.senderTransportPubkey);
|
|
7875
|
-
|
|
8027
|
+
logger.debug("Payments", "COMBINED_TRANSFER V6 processed successfully");
|
|
7876
8028
|
} catch (err) {
|
|
7877
|
-
|
|
8029
|
+
logger.error("Payments", "COMBINED_TRANSFER V6 processing error:", err);
|
|
7878
8030
|
}
|
|
7879
8031
|
return;
|
|
7880
8032
|
}
|
|
@@ -7891,7 +8043,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7891
8043
|
}
|
|
7892
8044
|
}
|
|
7893
8045
|
if (instantBundle) {
|
|
7894
|
-
|
|
8046
|
+
logger.debug("Payments", "Processing INSTANT_SPLIT bundle...");
|
|
7895
8047
|
try {
|
|
7896
8048
|
const result = await this.processInstantSplitBundle(
|
|
7897
8049
|
instantBundle,
|
|
@@ -7899,28 +8051,28 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7899
8051
|
payload.memo
|
|
7900
8052
|
);
|
|
7901
8053
|
if (result.success) {
|
|
7902
|
-
|
|
8054
|
+
logger.debug("Payments", "INSTANT_SPLIT processed successfully");
|
|
7903
8055
|
} else {
|
|
7904
|
-
|
|
8056
|
+
logger.warn("Payments", "INSTANT_SPLIT processing failed:", result.error);
|
|
7905
8057
|
}
|
|
7906
8058
|
} catch (err) {
|
|
7907
|
-
|
|
8059
|
+
logger.error("Payments", "INSTANT_SPLIT processing error:", err);
|
|
7908
8060
|
}
|
|
7909
8061
|
return;
|
|
7910
8062
|
}
|
|
7911
8063
|
if (payload.sourceToken && payload.commitmentData && !payload.transferTx) {
|
|
7912
|
-
|
|
8064
|
+
logger.debug("Payments", "NOSTR-FIRST commitment-only transfer detected");
|
|
7913
8065
|
await this.handleCommitmentOnlyTransfer(transfer, payload);
|
|
7914
8066
|
return;
|
|
7915
8067
|
}
|
|
7916
8068
|
let tokenData;
|
|
7917
8069
|
let finalizedSdkToken = null;
|
|
7918
8070
|
if (payload.sourceToken && payload.transferTx) {
|
|
7919
|
-
|
|
8071
|
+
logger.debug("Payments", "Processing Sphere wallet format transfer...");
|
|
7920
8072
|
const sourceTokenInput = typeof payload.sourceToken === "string" ? JSON.parse(payload.sourceToken) : payload.sourceToken;
|
|
7921
8073
|
const transferTxInput = typeof payload.transferTx === "string" ? JSON.parse(payload.transferTx) : payload.transferTx;
|
|
7922
8074
|
if (!sourceTokenInput || !transferTxInput) {
|
|
7923
|
-
|
|
8075
|
+
logger.warn("Payments", "Invalid Sphere wallet transfer format");
|
|
7924
8076
|
return;
|
|
7925
8077
|
}
|
|
7926
8078
|
let sourceToken;
|
|
@@ -7928,7 +8080,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7928
8080
|
try {
|
|
7929
8081
|
sourceToken = await SdkToken2.fromJSON(sourceTokenInput);
|
|
7930
8082
|
} catch (err) {
|
|
7931
|
-
|
|
8083
|
+
logger.error("Payments", "Failed to parse sourceToken:", err);
|
|
7932
8084
|
return;
|
|
7933
8085
|
}
|
|
7934
8086
|
try {
|
|
@@ -7942,16 +8094,16 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7942
8094
|
const commitment = await TransferCommitment4.fromJSON(transferTxInput);
|
|
7943
8095
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
7944
8096
|
if (!stClient) {
|
|
7945
|
-
|
|
8097
|
+
logger.error("Payments", "Cannot process commitment - no state transition client");
|
|
7946
8098
|
return;
|
|
7947
8099
|
}
|
|
7948
8100
|
const response = await stClient.submitTransferCommitment(commitment);
|
|
7949
8101
|
if (response.status !== "SUCCESS" && response.status !== "REQUEST_ID_EXISTS") {
|
|
7950
|
-
|
|
8102
|
+
logger.error("Payments", "Transfer commitment submission failed:", response.status);
|
|
7951
8103
|
return;
|
|
7952
8104
|
}
|
|
7953
8105
|
if (!this.deps.oracle.waitForProofSdk) {
|
|
7954
|
-
|
|
8106
|
+
logger.error("Payments", "Cannot wait for proof - missing oracle method");
|
|
7955
8107
|
return;
|
|
7956
8108
|
}
|
|
7957
8109
|
const inclusionProof = await this.deps.oracle.waitForProofSdk(commitment);
|
|
@@ -7963,7 +8115,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7963
8115
|
const commitment = await TransferCommitment4.fromJSON(transferTxInput);
|
|
7964
8116
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
7965
8117
|
if (!stClient || !this.deps.oracle.waitForProofSdk) {
|
|
7966
|
-
throw new
|
|
8118
|
+
throw new SphereError("Cannot submit commitment - missing oracle methods", "AGGREGATOR_ERROR");
|
|
7967
8119
|
}
|
|
7968
8120
|
await stClient.submitTransferCommitment(commitment);
|
|
7969
8121
|
const inclusionProof = await this.deps.oracle.waitForProofSdk(commitment);
|
|
@@ -7971,33 +8123,33 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7971
8123
|
}
|
|
7972
8124
|
}
|
|
7973
8125
|
} catch (err) {
|
|
7974
|
-
|
|
8126
|
+
logger.error("Payments", "Failed to parse transferTx:", err);
|
|
7975
8127
|
return;
|
|
7976
8128
|
}
|
|
7977
8129
|
try {
|
|
7978
8130
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
7979
8131
|
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
7980
8132
|
if (!stClient || !trustBase) {
|
|
7981
|
-
|
|
8133
|
+
logger.error("Payments", "Cannot finalize - missing state transition client or trust base. Token rejected.");
|
|
7982
8134
|
return;
|
|
7983
8135
|
}
|
|
7984
8136
|
finalizedSdkToken = await this.finalizeTransferToken(sourceToken, transferTx, stClient, trustBase);
|
|
7985
8137
|
tokenData = finalizedSdkToken.toJSON();
|
|
7986
8138
|
const addressScheme = transferTx.data.recipient.scheme;
|
|
7987
|
-
|
|
8139
|
+
logger.debug("Payments", `${addressScheme === AddressScheme.PROXY ? "PROXY" : "DIRECT"} finalization successful`);
|
|
7988
8140
|
} catch (finalizeError) {
|
|
7989
|
-
|
|
8141
|
+
logger.error("Payments", "Finalization FAILED - token rejected:", finalizeError);
|
|
7990
8142
|
return;
|
|
7991
8143
|
}
|
|
7992
8144
|
} else if (payload.token) {
|
|
7993
8145
|
tokenData = payload.token;
|
|
7994
8146
|
} else {
|
|
7995
|
-
|
|
8147
|
+
logger.warn("Payments", "Unknown transfer payload format");
|
|
7996
8148
|
return;
|
|
7997
8149
|
}
|
|
7998
8150
|
const validation = await this.deps.oracle.validateToken(tokenData);
|
|
7999
8151
|
if (!validation.valid) {
|
|
8000
|
-
|
|
8152
|
+
logger.warn("Payments", "Received invalid token");
|
|
8001
8153
|
return;
|
|
8002
8154
|
}
|
|
8003
8155
|
const tokenInfo = await parseTokenInfo(tokenData);
|
|
@@ -8038,12 +8190,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8038
8190
|
receivedAt: transfer.timestamp
|
|
8039
8191
|
};
|
|
8040
8192
|
this.deps.emitEvent("transfer:incoming", incomingTransfer);
|
|
8041
|
-
|
|
8193
|
+
logger.debug("Payments", `Incoming transfer processed: ${token.id}, ${token.amount} ${token.symbol}`);
|
|
8042
8194
|
} else {
|
|
8043
|
-
|
|
8195
|
+
logger.debug("Payments", `Duplicate transfer ignored: ${token.id}, ${token.amount} ${token.symbol}`);
|
|
8044
8196
|
}
|
|
8045
8197
|
} catch (error) {
|
|
8046
|
-
|
|
8198
|
+
logger.error("Payments", "Failed to process incoming transfer:", error);
|
|
8047
8199
|
}
|
|
8048
8200
|
}
|
|
8049
8201
|
// ===========================================================================
|
|
@@ -8058,15 +8210,15 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8058
8210
|
if (existingArchive) {
|
|
8059
8211
|
if (isIncrementalUpdate(existingArchive, txf)) {
|
|
8060
8212
|
this.archivedTokens.set(tokenId, txf);
|
|
8061
|
-
|
|
8213
|
+
logger.debug("Payments", `Updated archived token ${tokenId.slice(0, 8)}...`);
|
|
8062
8214
|
} else {
|
|
8063
8215
|
const stateHash = getCurrentStateHash(txf) || "";
|
|
8064
8216
|
await this.storeForkedToken(tokenId, stateHash, txf);
|
|
8065
|
-
|
|
8217
|
+
logger.debug("Payments", `Archived token ${tokenId.slice(0, 8)}... is a fork`);
|
|
8066
8218
|
}
|
|
8067
8219
|
} else {
|
|
8068
8220
|
this.archivedTokens.set(tokenId, txf);
|
|
8069
|
-
|
|
8221
|
+
logger.debug("Payments", `Archived token ${tokenId.slice(0, 8)}...`);
|
|
8070
8222
|
}
|
|
8071
8223
|
}
|
|
8072
8224
|
// ===========================================================================
|
|
@@ -8078,20 +8230,20 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8078
8230
|
const txf = tokenToTxf(t);
|
|
8079
8231
|
return `${t.id.slice(0, 12)}(${t.status},txf=${!!txf})`;
|
|
8080
8232
|
});
|
|
8081
|
-
|
|
8233
|
+
logger.debug("Payments", `save(): providers=${providers.size}, tokens=[${tokenStats.join(", ")}]`);
|
|
8082
8234
|
if (providers.size > 0) {
|
|
8083
8235
|
const data = await this.createStorageData();
|
|
8084
8236
|
const dataKeys = Object.keys(data).filter((k) => k.startsWith("token-"));
|
|
8085
|
-
|
|
8237
|
+
logger.debug("Payments", `save(): TXF keys=${dataKeys.length} (${dataKeys.join(", ")})`);
|
|
8086
8238
|
for (const [id, provider] of providers) {
|
|
8087
8239
|
try {
|
|
8088
8240
|
await provider.save(data);
|
|
8089
8241
|
} catch (err) {
|
|
8090
|
-
|
|
8242
|
+
logger.error("Payments", `Failed to save to provider ${id}:`, err);
|
|
8091
8243
|
}
|
|
8092
8244
|
}
|
|
8093
8245
|
} else {
|
|
8094
|
-
|
|
8246
|
+
logger.debug("Payments", "save(): No token storage providers - TXF not persisted");
|
|
8095
8247
|
}
|
|
8096
8248
|
await this.savePendingV5Tokens();
|
|
8097
8249
|
}
|
|
@@ -8129,14 +8281,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8129
8281
|
}
|
|
8130
8282
|
loadFromStorageData(data) {
|
|
8131
8283
|
const parsed = parseTxfStorageData(data);
|
|
8132
|
-
|
|
8284
|
+
logger.debug("Payments", `loadFromStorageData: parsed ${parsed.tokens.length} tokens, ${parsed.tombstones.length} tombstones, errors=[${parsed.validationErrors.join("; ")}]`);
|
|
8133
8285
|
this.tombstones = parsed.tombstones;
|
|
8134
8286
|
this.tokens.clear();
|
|
8135
8287
|
for (const token of parsed.tokens) {
|
|
8136
8288
|
const sdkTokenId = extractTokenIdFromSdkData(token.sdkData);
|
|
8137
8289
|
const stateHash = extractStateHashFromSdkData(token.sdkData);
|
|
8138
8290
|
if (sdkTokenId && stateHash && this.isStateTombstoned(sdkTokenId, stateHash)) {
|
|
8139
|
-
|
|
8291
|
+
logger.debug("Payments", `Skipping tombstoned token ${sdkTokenId.slice(0, 8)}... during load (exact state match)`);
|
|
8140
8292
|
continue;
|
|
8141
8293
|
}
|
|
8142
8294
|
this.tokens.set(token.id, token);
|
|
@@ -8156,12 +8308,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8156
8308
|
try {
|
|
8157
8309
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
8158
8310
|
if (!stClient) {
|
|
8159
|
-
|
|
8311
|
+
logger.debug("Payments", "Cannot submit commitment - no state transition client");
|
|
8160
8312
|
return;
|
|
8161
8313
|
}
|
|
8162
8314
|
const response = await stClient.submitTransferCommitment(commitment);
|
|
8163
8315
|
if (response.status !== "SUCCESS" && response.status !== "REQUEST_ID_EXISTS") {
|
|
8164
|
-
|
|
8316
|
+
logger.debug("Payments", `Transfer commitment submission failed: ${response.status}`);
|
|
8165
8317
|
const token = this.tokens.get(tokenId);
|
|
8166
8318
|
if (token) {
|
|
8167
8319
|
token.status = "invalid";
|
|
@@ -8181,7 +8333,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8181
8333
|
onProofReceived
|
|
8182
8334
|
});
|
|
8183
8335
|
} catch (error) {
|
|
8184
|
-
|
|
8336
|
+
logger.debug("Payments", "submitAndPollForProof error:", error);
|
|
8185
8337
|
}
|
|
8186
8338
|
}
|
|
8187
8339
|
/**
|
|
@@ -8189,7 +8341,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8189
8341
|
*/
|
|
8190
8342
|
addProofPollingJob(job) {
|
|
8191
8343
|
this.proofPollingJobs.set(job.tokenId, job);
|
|
8192
|
-
|
|
8344
|
+
logger.debug("Payments", `Added proof polling job for token ${job.tokenId.slice(0, 8)}...`);
|
|
8193
8345
|
this.startProofPolling();
|
|
8194
8346
|
}
|
|
8195
8347
|
/**
|
|
@@ -8198,7 +8350,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8198
8350
|
startProofPolling() {
|
|
8199
8351
|
if (this.proofPollingInterval) return;
|
|
8200
8352
|
if (this.proofPollingJobs.size === 0) return;
|
|
8201
|
-
|
|
8353
|
+
logger.debug("Payments", "Starting proof polling...");
|
|
8202
8354
|
this.proofPollingInterval = setInterval(
|
|
8203
8355
|
() => this.processProofPollingQueue(),
|
|
8204
8356
|
_PaymentsModule.PROOF_POLLING_INTERVAL_MS
|
|
@@ -8211,7 +8363,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8211
8363
|
if (this.proofPollingInterval) {
|
|
8212
8364
|
clearInterval(this.proofPollingInterval);
|
|
8213
8365
|
this.proofPollingInterval = null;
|
|
8214
|
-
|
|
8366
|
+
logger.debug("Payments", "Stopped proof polling");
|
|
8215
8367
|
}
|
|
8216
8368
|
}
|
|
8217
8369
|
/**
|
|
@@ -8228,7 +8380,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8228
8380
|
job.attemptCount++;
|
|
8229
8381
|
job.lastAttemptAt = Date.now();
|
|
8230
8382
|
if (job.attemptCount >= _PaymentsModule.PROOF_POLLING_MAX_ATTEMPTS) {
|
|
8231
|
-
|
|
8383
|
+
logger.debug("Payments", `Proof polling timeout for token ${tokenId.slice(0, 8)}...`);
|
|
8232
8384
|
const token2 = this.tokens.get(tokenId);
|
|
8233
8385
|
if (token2 && token2.status === "submitted") {
|
|
8234
8386
|
token2.status = "invalid";
|
|
@@ -8267,12 +8419,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8267
8419
|
token.updatedAt = Date.now();
|
|
8268
8420
|
this.tokens.set(tokenId, token);
|
|
8269
8421
|
await this.save();
|
|
8270
|
-
|
|
8422
|
+
logger.debug("Payments", `Proof received for token ${tokenId.slice(0, 8)}..., status: spent`);
|
|
8271
8423
|
}
|
|
8272
8424
|
job.onProofReceived?.(tokenId);
|
|
8273
8425
|
completedJobs.push(tokenId);
|
|
8274
8426
|
} catch (error) {
|
|
8275
|
-
|
|
8427
|
+
logger.debug("Payments", `Proof polling attempt ${job.attemptCount} for ${tokenId.slice(0, 8)}...: ${error}`);
|
|
8276
8428
|
}
|
|
8277
8429
|
}
|
|
8278
8430
|
for (const tokenId of completedJobs) {
|
|
@@ -8287,7 +8439,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8287
8439
|
// ===========================================================================
|
|
8288
8440
|
ensureInitialized() {
|
|
8289
8441
|
if (!this.deps) {
|
|
8290
|
-
throw new
|
|
8442
|
+
throw new SphereError("PaymentsModule not initialized", "NOT_INITIALIZED");
|
|
8291
8443
|
}
|
|
8292
8444
|
}
|
|
8293
8445
|
};
|
|
@@ -8295,7 +8447,12 @@ function createPaymentsModule(config) {
|
|
|
8295
8447
|
return new PaymentsModule(config);
|
|
8296
8448
|
}
|
|
8297
8449
|
|
|
8450
|
+
// modules/payments/BackgroundCommitmentService.ts
|
|
8451
|
+
init_logger();
|
|
8452
|
+
init_errors();
|
|
8453
|
+
|
|
8298
8454
|
// modules/payments/TokenRecoveryService.ts
|
|
8455
|
+
init_logger();
|
|
8299
8456
|
import { TokenId as TokenId4 } from "@unicitylabs/state-transition-sdk/lib/token/TokenId";
|
|
8300
8457
|
import { TokenState as TokenState6 } from "@unicitylabs/state-transition-sdk/lib/token/TokenState";
|
|
8301
8458
|
import { TokenType as TokenType4 } from "@unicitylabs/state-transition-sdk/lib/token/TokenType";
|
|
@@ -8304,6 +8461,8 @@ import { HashAlgorithm as HashAlgorithm6 } from "@unicitylabs/state-transition-s
|
|
|
8304
8461
|
import { UnmaskedPredicate as UnmaskedPredicate6 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate";
|
|
8305
8462
|
|
|
8306
8463
|
// modules/communications/CommunicationsModule.ts
|
|
8464
|
+
init_logger();
|
|
8465
|
+
init_errors();
|
|
8307
8466
|
init_constants();
|
|
8308
8467
|
var CommunicationsModule = class {
|
|
8309
8468
|
config;
|
|
@@ -8398,7 +8557,7 @@ var CommunicationsModule = class {
|
|
|
8398
8557
|
}
|
|
8399
8558
|
if (myMessages.length > 0) {
|
|
8400
8559
|
await this.save();
|
|
8401
|
-
|
|
8560
|
+
logger.debug("Communications", `Migrated ${myMessages.length} messages to per-address storage`);
|
|
8402
8561
|
}
|
|
8403
8562
|
}
|
|
8404
8563
|
}
|
|
@@ -8484,7 +8643,7 @@ var CommunicationsModule = class {
|
|
|
8484
8643
|
const msg = this.messages.get(id);
|
|
8485
8644
|
if (msg && msg.senderPubkey !== this.deps.identity.chainPubkey) {
|
|
8486
8645
|
this.deps.transport.sendReadReceipt(msg.senderPubkey, id).catch((err) => {
|
|
8487
|
-
|
|
8646
|
+
logger.warn("Communications", "Failed to send read receipt:", err);
|
|
8488
8647
|
});
|
|
8489
8648
|
}
|
|
8490
8649
|
}
|
|
@@ -8684,7 +8843,7 @@ var CommunicationsModule = class {
|
|
|
8684
8843
|
try {
|
|
8685
8844
|
handler(message);
|
|
8686
8845
|
} catch (error) {
|
|
8687
|
-
|
|
8846
|
+
logger.error("Communications", "Handler error:", error);
|
|
8688
8847
|
}
|
|
8689
8848
|
}
|
|
8690
8849
|
if (this.config.autoSave) {
|
|
@@ -8703,7 +8862,7 @@ var CommunicationsModule = class {
|
|
|
8703
8862
|
try {
|
|
8704
8863
|
handler(composing);
|
|
8705
8864
|
} catch (error) {
|
|
8706
|
-
|
|
8865
|
+
logger.error("Communications", "Composing handler error:", error);
|
|
8707
8866
|
}
|
|
8708
8867
|
}
|
|
8709
8868
|
}
|
|
@@ -8721,7 +8880,7 @@ var CommunicationsModule = class {
|
|
|
8721
8880
|
try {
|
|
8722
8881
|
handler(message);
|
|
8723
8882
|
} catch (error) {
|
|
8724
|
-
|
|
8883
|
+
logger.error("Communications", "Handler error:", error);
|
|
8725
8884
|
}
|
|
8726
8885
|
}
|
|
8727
8886
|
}
|
|
@@ -8762,7 +8921,7 @@ var CommunicationsModule = class {
|
|
|
8762
8921
|
const nametag = recipient.slice(1);
|
|
8763
8922
|
const pubkey = await this.deps.transport.resolveNametag?.(nametag);
|
|
8764
8923
|
if (!pubkey) {
|
|
8765
|
-
throw new
|
|
8924
|
+
throw new SphereError(`Nametag not found: ${recipient}`, "INVALID_RECIPIENT");
|
|
8766
8925
|
}
|
|
8767
8926
|
return { pubkey, nametag };
|
|
8768
8927
|
}
|
|
@@ -8770,7 +8929,7 @@ var CommunicationsModule = class {
|
|
|
8770
8929
|
}
|
|
8771
8930
|
ensureInitialized() {
|
|
8772
8931
|
if (!this.deps) {
|
|
8773
|
-
throw new
|
|
8932
|
+
throw new SphereError("CommunicationsModule not initialized", "NOT_INITIALIZED");
|
|
8774
8933
|
}
|
|
8775
8934
|
}
|
|
8776
8935
|
};
|
|
@@ -8779,6 +8938,8 @@ function createCommunicationsModule(config) {
|
|
|
8779
8938
|
}
|
|
8780
8939
|
|
|
8781
8940
|
// modules/groupchat/GroupChatModule.ts
|
|
8941
|
+
init_logger();
|
|
8942
|
+
init_errors();
|
|
8782
8943
|
init_constants();
|
|
8783
8944
|
import {
|
|
8784
8945
|
NostrClient,
|
|
@@ -8929,13 +9090,15 @@ var GroupChatModule = class {
|
|
|
8929
9090
|
for (const subId of this.subscriptionIds) {
|
|
8930
9091
|
try {
|
|
8931
9092
|
this.client.unsubscribe(subId);
|
|
8932
|
-
} catch {
|
|
9093
|
+
} catch (err) {
|
|
9094
|
+
logger.debug("GroupChat", "Failed to unsubscribe", err);
|
|
8933
9095
|
}
|
|
8934
9096
|
}
|
|
8935
9097
|
this.subscriptionIds = [];
|
|
8936
9098
|
try {
|
|
8937
9099
|
this.client.disconnect();
|
|
8938
|
-
} catch {
|
|
9100
|
+
} catch (err) {
|
|
9101
|
+
logger.debug("GroupChat", "Failed to disconnect", err);
|
|
8939
9102
|
}
|
|
8940
9103
|
this.client = null;
|
|
8941
9104
|
}
|
|
@@ -8975,7 +9138,8 @@ var GroupChatModule = class {
|
|
|
8975
9138
|
for (const subId of this.subscriptionIds) {
|
|
8976
9139
|
try {
|
|
8977
9140
|
this.client.unsubscribe(subId);
|
|
8978
|
-
} catch {
|
|
9141
|
+
} catch (err) {
|
|
9142
|
+
logger.debug("GroupChat", "Failed to unsubscribe", err);
|
|
8979
9143
|
}
|
|
8980
9144
|
}
|
|
8981
9145
|
this.subscriptionIds = [];
|
|
@@ -9010,21 +9174,21 @@ var GroupChatModule = class {
|
|
|
9010
9174
|
}
|
|
9011
9175
|
this.deps.emitEvent("groupchat:connection", { connected: true });
|
|
9012
9176
|
} catch (error) {
|
|
9013
|
-
|
|
9177
|
+
logger.error("GroupChat", "Failed to connect to relays", error);
|
|
9014
9178
|
this.deps.emitEvent("groupchat:connection", { connected: false });
|
|
9015
9179
|
this.scheduleReconnect();
|
|
9016
9180
|
}
|
|
9017
9181
|
}
|
|
9018
9182
|
scheduleReconnect() {
|
|
9019
9183
|
if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
|
|
9020
|
-
|
|
9184
|
+
logger.error("GroupChat", "Max reconnection attempts reached");
|
|
9021
9185
|
return;
|
|
9022
9186
|
}
|
|
9023
9187
|
this.reconnectAttempts++;
|
|
9024
9188
|
this.reconnectTimer = setTimeout(() => {
|
|
9025
9189
|
this.reconnectTimer = null;
|
|
9026
9190
|
if (this.deps) {
|
|
9027
|
-
this.connect().catch(
|
|
9191
|
+
this.connect().catch((err) => logger.error("GroupChat", "Reconnect failed:", err));
|
|
9028
9192
|
}
|
|
9029
9193
|
}, this.config.reconnectDelayMs);
|
|
9030
9194
|
}
|
|
@@ -9259,7 +9423,8 @@ var GroupChatModule = class {
|
|
|
9259
9423
|
},
|
|
9260
9424
|
onComplete: () => {
|
|
9261
9425
|
},
|
|
9262
|
-
timeoutMs: 15e3
|
|
9426
|
+
timeoutMs: 15e3,
|
|
9427
|
+
timeoutLabel: "restoreJoinedGroups"
|
|
9263
9428
|
}
|
|
9264
9429
|
);
|
|
9265
9430
|
if (groupIdsWithMembership.size === 0) return [];
|
|
@@ -9276,7 +9441,8 @@ var GroupChatModule = class {
|
|
|
9276
9441
|
this.fetchMessages(groupId)
|
|
9277
9442
|
]);
|
|
9278
9443
|
}
|
|
9279
|
-
} catch {
|
|
9444
|
+
} catch (error) {
|
|
9445
|
+
logger.warn("GroupChat", "Failed to restore group", groupId, error);
|
|
9280
9446
|
}
|
|
9281
9447
|
}
|
|
9282
9448
|
if (restoredGroups.length > 0) {
|
|
@@ -9309,7 +9475,8 @@ var GroupChatModule = class {
|
|
|
9309
9475
|
},
|
|
9310
9476
|
onComplete: () => {
|
|
9311
9477
|
},
|
|
9312
|
-
timeoutMs: 1e4
|
|
9478
|
+
timeoutMs: 1e4,
|
|
9479
|
+
timeoutLabel: "fetchAvailableGroups(metadata)"
|
|
9313
9480
|
}
|
|
9314
9481
|
),
|
|
9315
9482
|
this.oneshotSubscription(
|
|
@@ -9324,7 +9491,8 @@ var GroupChatModule = class {
|
|
|
9324
9491
|
},
|
|
9325
9492
|
onComplete: () => {
|
|
9326
9493
|
},
|
|
9327
|
-
timeoutMs: 1e4
|
|
9494
|
+
timeoutMs: 1e4,
|
|
9495
|
+
timeoutLabel: "fetchAvailableGroups(members)"
|
|
9328
9496
|
}
|
|
9329
9497
|
)
|
|
9330
9498
|
]);
|
|
@@ -9383,7 +9551,7 @@ var GroupChatModule = class {
|
|
|
9383
9551
|
return true;
|
|
9384
9552
|
}
|
|
9385
9553
|
}
|
|
9386
|
-
|
|
9554
|
+
logger.error("GroupChat", "Failed to join group", error);
|
|
9387
9555
|
return false;
|
|
9388
9556
|
}
|
|
9389
9557
|
}
|
|
@@ -9413,7 +9581,7 @@ var GroupChatModule = class {
|
|
|
9413
9581
|
this.persistAll();
|
|
9414
9582
|
return true;
|
|
9415
9583
|
}
|
|
9416
|
-
|
|
9584
|
+
logger.error("GroupChat", "Failed to leave group", error);
|
|
9417
9585
|
return false;
|
|
9418
9586
|
}
|
|
9419
9587
|
}
|
|
@@ -9464,10 +9632,8 @@ var GroupChatModule = class {
|
|
|
9464
9632
|
kind: NIP29_KINDS.JOIN_REQUEST,
|
|
9465
9633
|
tags: [["h", group.id]],
|
|
9466
9634
|
content: ""
|
|
9467
|
-
}).catch(() =>
|
|
9468
|
-
|
|
9469
|
-
await this.fetchAndSaveMembers(group.id).catch(() => {
|
|
9470
|
-
});
|
|
9635
|
+
}).catch((err) => logger.debug("GroupChat", "Background operation failed", err));
|
|
9636
|
+
await this.fetchAndSaveMembers(group.id).catch((err) => logger.debug("GroupChat", "Failed to fetch members", group.id, err));
|
|
9471
9637
|
this.saveMemberToMemory({
|
|
9472
9638
|
pubkey: creatorPubkey,
|
|
9473
9639
|
groupId: group.id,
|
|
@@ -9479,7 +9645,7 @@ var GroupChatModule = class {
|
|
|
9479
9645
|
this.schedulePersist();
|
|
9480
9646
|
return group;
|
|
9481
9647
|
} catch (error) {
|
|
9482
|
-
|
|
9648
|
+
logger.error("GroupChat", "Failed to create group", error);
|
|
9483
9649
|
return null;
|
|
9484
9650
|
}
|
|
9485
9651
|
}
|
|
@@ -9506,7 +9672,7 @@ var GroupChatModule = class {
|
|
|
9506
9672
|
}
|
|
9507
9673
|
return false;
|
|
9508
9674
|
} catch (error) {
|
|
9509
|
-
|
|
9675
|
+
logger.error("GroupChat", "Failed to delete group", error);
|
|
9510
9676
|
return false;
|
|
9511
9677
|
}
|
|
9512
9678
|
}
|
|
@@ -9526,7 +9692,7 @@ var GroupChatModule = class {
|
|
|
9526
9692
|
});
|
|
9527
9693
|
return eventId ? inviteCode : null;
|
|
9528
9694
|
} catch (error) {
|
|
9529
|
-
|
|
9695
|
+
logger.error("GroupChat", "Failed to create invite", error);
|
|
9530
9696
|
return null;
|
|
9531
9697
|
}
|
|
9532
9698
|
}
|
|
@@ -9576,7 +9742,7 @@ var GroupChatModule = class {
|
|
|
9576
9742
|
}
|
|
9577
9743
|
return null;
|
|
9578
9744
|
} catch (error) {
|
|
9579
|
-
|
|
9745
|
+
logger.error("GroupChat", "Failed to send message", error);
|
|
9580
9746
|
return null;
|
|
9581
9747
|
}
|
|
9582
9748
|
}
|
|
@@ -9615,7 +9781,8 @@ var GroupChatModule = class {
|
|
|
9615
9781
|
this.schedulePersist();
|
|
9616
9782
|
return fetchedMessages;
|
|
9617
9783
|
},
|
|
9618
|
-
timeoutMs: 1e4
|
|
9784
|
+
timeoutMs: 1e4,
|
|
9785
|
+
timeoutLabel: `fetchMessages(${groupId})`
|
|
9619
9786
|
});
|
|
9620
9787
|
}
|
|
9621
9788
|
// ===========================================================================
|
|
@@ -9676,7 +9843,7 @@ var GroupChatModule = class {
|
|
|
9676
9843
|
}
|
|
9677
9844
|
return false;
|
|
9678
9845
|
} catch (error) {
|
|
9679
|
-
|
|
9846
|
+
logger.error("GroupChat", "Failed to kick user", error);
|
|
9680
9847
|
return false;
|
|
9681
9848
|
}
|
|
9682
9849
|
}
|
|
@@ -9699,7 +9866,7 @@ var GroupChatModule = class {
|
|
|
9699
9866
|
}
|
|
9700
9867
|
return false;
|
|
9701
9868
|
} catch (error) {
|
|
9702
|
-
|
|
9869
|
+
logger.error("GroupChat", "Failed to delete message", error);
|
|
9703
9870
|
return false;
|
|
9704
9871
|
}
|
|
9705
9872
|
}
|
|
@@ -9984,7 +10151,7 @@ var GroupChatModule = class {
|
|
|
9984
10151
|
this.persistTimer = setTimeout(() => {
|
|
9985
10152
|
this.persistTimer = null;
|
|
9986
10153
|
this.persistPromise = this.doPersistAll().catch((err) => {
|
|
9987
|
-
|
|
10154
|
+
logger.error("GroupChat", "Persistence error:", err);
|
|
9988
10155
|
}).finally(() => {
|
|
9989
10156
|
this.persistPromise = null;
|
|
9990
10157
|
});
|
|
@@ -10164,7 +10331,8 @@ var GroupChatModule = class {
|
|
|
10164
10331
|
if (state.subId) {
|
|
10165
10332
|
try {
|
|
10166
10333
|
this.client.unsubscribe(state.subId);
|
|
10167
|
-
} catch {
|
|
10334
|
+
} catch (err) {
|
|
10335
|
+
logger.debug("GroupChat", "Failed to unsubscribe", err);
|
|
10168
10336
|
}
|
|
10169
10337
|
const idx = this.subscriptionIds.indexOf(state.subId);
|
|
10170
10338
|
if (idx >= 0) this.subscriptionIds.splice(idx, 1);
|
|
@@ -10179,12 +10347,17 @@ var GroupChatModule = class {
|
|
|
10179
10347
|
});
|
|
10180
10348
|
state.subId = subId;
|
|
10181
10349
|
this.subscriptionIds.push(subId);
|
|
10182
|
-
setTimeout(
|
|
10350
|
+
setTimeout(() => {
|
|
10351
|
+
if (!done && opts.timeoutLabel) {
|
|
10352
|
+
logger.warn("GroupChat", `${opts.timeoutLabel} timed out`);
|
|
10353
|
+
}
|
|
10354
|
+
finish();
|
|
10355
|
+
}, opts.timeoutMs ?? 5e3);
|
|
10183
10356
|
});
|
|
10184
10357
|
}
|
|
10185
10358
|
ensureInitialized() {
|
|
10186
10359
|
if (!this.deps) {
|
|
10187
|
-
throw new
|
|
10360
|
+
throw new SphereError("GroupChatModule not initialized", "NOT_INITIALIZED");
|
|
10188
10361
|
}
|
|
10189
10362
|
}
|
|
10190
10363
|
async ensureConnected() {
|
|
@@ -12353,6 +12526,7 @@ var Pointk1 = /* @__PURE__ */ weierstrass(secp256k1_CURVE, {
|
|
|
12353
12526
|
var secp256k1 = /* @__PURE__ */ ecdsa(Pointk1, sha2564);
|
|
12354
12527
|
|
|
12355
12528
|
// modules/market/MarketModule.ts
|
|
12529
|
+
init_errors();
|
|
12356
12530
|
var DEFAULT_MARKET_API_URL = "https://market-api.unicity.network";
|
|
12357
12531
|
function hexToBytes3(hex) {
|
|
12358
12532
|
const len = hex.length >> 1;
|
|
@@ -12542,7 +12716,7 @@ var MarketModule = class {
|
|
|
12542
12716
|
// ---------------------------------------------------------------------------
|
|
12543
12717
|
ensureIdentity() {
|
|
12544
12718
|
if (!this.identity) {
|
|
12545
|
-
throw new
|
|
12719
|
+
throw new SphereError("MarketModule not initialized \u2014 call initialize() first", "NOT_INITIALIZED");
|
|
12546
12720
|
}
|
|
12547
12721
|
}
|
|
12548
12722
|
/** Register the agent's public key with the server (idempotent) */
|
|
@@ -12568,7 +12742,7 @@ var MarketModule = class {
|
|
|
12568
12742
|
data = JSON.parse(text);
|
|
12569
12743
|
} catch {
|
|
12570
12744
|
}
|
|
12571
|
-
throw new
|
|
12745
|
+
throw new SphereError(data?.error ?? `Agent registration failed: HTTP ${res.status}`, "NETWORK_ERROR");
|
|
12572
12746
|
}
|
|
12573
12747
|
async parseResponse(res) {
|
|
12574
12748
|
const text = await res.text();
|
|
@@ -12576,9 +12750,9 @@ var MarketModule = class {
|
|
|
12576
12750
|
try {
|
|
12577
12751
|
data = JSON.parse(text);
|
|
12578
12752
|
} catch {
|
|
12579
|
-
throw new
|
|
12753
|
+
throw new SphereError(`Market API error: HTTP ${res.status} \u2014 unexpected response (not JSON)`, "NETWORK_ERROR");
|
|
12580
12754
|
}
|
|
12581
|
-
if (!res.ok) throw new
|
|
12755
|
+
if (!res.ok) throw new SphereError(data.error ?? `HTTP ${res.status}`, "NETWORK_ERROR");
|
|
12582
12756
|
return data;
|
|
12583
12757
|
}
|
|
12584
12758
|
async apiPost(path, body) {
|
|
@@ -12633,6 +12807,8 @@ function createMarketModule(config) {
|
|
|
12633
12807
|
init_constants();
|
|
12634
12808
|
|
|
12635
12809
|
// core/encryption.ts
|
|
12810
|
+
init_errors();
|
|
12811
|
+
init_logger();
|
|
12636
12812
|
import CryptoJS6 from "crypto-js";
|
|
12637
12813
|
function encryptSimple(plaintext, password) {
|
|
12638
12814
|
return CryptoJS6.AES.encrypt(plaintext, password).toString();
|
|
@@ -12641,7 +12817,7 @@ function decryptSimple(ciphertext, password) {
|
|
|
12641
12817
|
const decrypted = CryptoJS6.AES.decrypt(ciphertext, password);
|
|
12642
12818
|
const result = decrypted.toString(CryptoJS6.enc.Utf8);
|
|
12643
12819
|
if (!result) {
|
|
12644
|
-
throw new
|
|
12820
|
+
throw new SphereError("Decryption failed: invalid password or corrupted data", "DECRYPTION_ERROR");
|
|
12645
12821
|
}
|
|
12646
12822
|
return result;
|
|
12647
12823
|
}
|
|
@@ -12655,12 +12831,14 @@ function decryptWithSalt(ciphertext, password, salt) {
|
|
|
12655
12831
|
const decrypted = CryptoJS6.AES.decrypt(ciphertext, key);
|
|
12656
12832
|
const result = decrypted.toString(CryptoJS6.enc.Utf8);
|
|
12657
12833
|
return result || null;
|
|
12658
|
-
} catch {
|
|
12834
|
+
} catch (err) {
|
|
12835
|
+
logger.debug("Encryption", "decryptWithSalt failed", err);
|
|
12659
12836
|
return null;
|
|
12660
12837
|
}
|
|
12661
12838
|
}
|
|
12662
12839
|
|
|
12663
12840
|
// core/scan.ts
|
|
12841
|
+
init_logger();
|
|
12664
12842
|
async function scanAddressesImpl(deriveAddress, options = {}) {
|
|
12665
12843
|
const maxAddresses = options.maxAddresses ?? 50;
|
|
12666
12844
|
const gapLimit = options.gapLimit ?? 20;
|
|
@@ -12699,7 +12877,8 @@ async function scanAddressesImpl(deriveAddress, options = {}) {
|
|
|
12699
12877
|
nametag = tag;
|
|
12700
12878
|
nametagsFoundCount++;
|
|
12701
12879
|
}
|
|
12702
|
-
} catch {
|
|
12880
|
+
} catch (err) {
|
|
12881
|
+
logger.debug("Sphere", "Nametag resolution failed during scan", err);
|
|
12703
12882
|
}
|
|
12704
12883
|
}
|
|
12705
12884
|
foundAddresses.push({
|
|
@@ -12716,7 +12895,7 @@ async function scanAddressesImpl(deriveAddress, options = {}) {
|
|
|
12716
12895
|
consecutiveEmpty++;
|
|
12717
12896
|
}
|
|
12718
12897
|
} catch (err) {
|
|
12719
|
-
|
|
12898
|
+
logger.warn("Sphere", `scanAddresses: Error checking ${addrInfo.address}:`, err);
|
|
12720
12899
|
consecutiveEmpty++;
|
|
12721
12900
|
}
|
|
12722
12901
|
if (consecutiveEmpty >= gapLimit) {
|
|
@@ -12808,6 +12987,7 @@ init_network();
|
|
|
12808
12987
|
import CryptoJS7 from "crypto-js";
|
|
12809
12988
|
|
|
12810
12989
|
// core/utils.ts
|
|
12990
|
+
init_errors();
|
|
12811
12991
|
var SECP256K1_ORDER = BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
|
|
12812
12992
|
var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
12813
12993
|
function isValidPrivateKey(hex) {
|
|
@@ -12847,7 +13027,7 @@ function base58Decode(str) {
|
|
|
12847
13027
|
for (let i = 0; i < str.length; i++) {
|
|
12848
13028
|
const char = str[i];
|
|
12849
13029
|
if (!(char in ALPHABET_MAP)) {
|
|
12850
|
-
throw new
|
|
13030
|
+
throw new SphereError("Invalid base58 character: " + char, "VALIDATION_ERROR");
|
|
12851
13031
|
}
|
|
12852
13032
|
num = num * BigInt(58) + BigInt(ALPHABET_MAP[char]);
|
|
12853
13033
|
}
|
|
@@ -13129,6 +13309,7 @@ function parseAndDecryptWalletText(content, password) {
|
|
|
13129
13309
|
}
|
|
13130
13310
|
|
|
13131
13311
|
// serialization/wallet-dat.ts
|
|
13312
|
+
init_errors();
|
|
13132
13313
|
import CryptoJS8 from "crypto-js";
|
|
13133
13314
|
function uint8ArrayToWordArray(u8arr) {
|
|
13134
13315
|
const hex = bytesToHex2(u8arr);
|
|
@@ -13314,7 +13495,7 @@ async function decryptCMasterKey(cmk, password, onProgress) {
|
|
|
13314
13495
|
);
|
|
13315
13496
|
const result = CryptoJS8.enc.Hex.stringify(decrypted);
|
|
13316
13497
|
if (!result || result.length !== 64) {
|
|
13317
|
-
throw new
|
|
13498
|
+
throw new SphereError("Master key decryption failed - incorrect password", "DECRYPTION_ERROR");
|
|
13318
13499
|
}
|
|
13319
13500
|
return result;
|
|
13320
13501
|
}
|
|
@@ -13609,6 +13790,7 @@ var Sphere = class _Sphere {
|
|
|
13609
13790
|
* ```
|
|
13610
13791
|
*/
|
|
13611
13792
|
static async init(options) {
|
|
13793
|
+
if (options.debug) logger.configure({ debug: true });
|
|
13612
13794
|
_Sphere.configureTokenRegistry(options.storage, options.network);
|
|
13613
13795
|
const groupChat = _Sphere.resolveGroupChatConfig(options.groupChat, options.network);
|
|
13614
13796
|
const market = _Sphere.resolveMarketConfig(options.market);
|
|
@@ -13636,8 +13818,9 @@ var Sphere = class _Sphere {
|
|
|
13636
13818
|
mnemonic = _Sphere.generateMnemonic();
|
|
13637
13819
|
generatedMnemonic = mnemonic;
|
|
13638
13820
|
} else {
|
|
13639
|
-
throw new
|
|
13640
|
-
"No wallet exists and no mnemonic provided. Provide a mnemonic or set autoGenerate: true."
|
|
13821
|
+
throw new SphereError(
|
|
13822
|
+
"No wallet exists and no mnemonic provided. Provide a mnemonic or set autoGenerate: true.",
|
|
13823
|
+
"INVALID_CONFIG"
|
|
13641
13824
|
);
|
|
13642
13825
|
}
|
|
13643
13826
|
}
|
|
@@ -13710,11 +13893,12 @@ var Sphere = class _Sphere {
|
|
|
13710
13893
|
* Create new wallet with mnemonic
|
|
13711
13894
|
*/
|
|
13712
13895
|
static async create(options) {
|
|
13896
|
+
if (options.debug) logger.configure({ debug: true });
|
|
13713
13897
|
if (!options.mnemonic || !_Sphere.validateMnemonic(options.mnemonic)) {
|
|
13714
|
-
throw new
|
|
13898
|
+
throw new SphereError("Invalid mnemonic", "INVALID_IDENTITY");
|
|
13715
13899
|
}
|
|
13716
13900
|
if (await _Sphere.exists(options.storage)) {
|
|
13717
|
-
throw new
|
|
13901
|
+
throw new SphereError("Wallet already exists. Use Sphere.load() or Sphere.clear() first.", "ALREADY_INITIALIZED");
|
|
13718
13902
|
}
|
|
13719
13903
|
const progress = options.onProgress;
|
|
13720
13904
|
if (!options.storage.isConnected()) {
|
|
@@ -13760,10 +13944,10 @@ var Sphere = class _Sphere {
|
|
|
13760
13944
|
const discoverOpts = typeof options.discoverAddresses === "object" ? { ...options.discoverAddresses, autoTrack: options.discoverAddresses.autoTrack ?? true } : { autoTrack: true };
|
|
13761
13945
|
const result = await sphere.discoverAddresses(discoverOpts);
|
|
13762
13946
|
if (result.addresses.length > 0) {
|
|
13763
|
-
|
|
13947
|
+
logger.debug("Sphere", `Address discovery: found ${result.addresses.length} address(es)`);
|
|
13764
13948
|
}
|
|
13765
13949
|
} catch (err) {
|
|
13766
|
-
|
|
13950
|
+
logger.warn("Sphere", "Address discovery failed (non-fatal):", err);
|
|
13767
13951
|
}
|
|
13768
13952
|
}
|
|
13769
13953
|
progress?.({ step: "complete", message: "Wallet created" });
|
|
@@ -13773,8 +13957,9 @@ var Sphere = class _Sphere {
|
|
|
13773
13957
|
* Load existing wallet from storage
|
|
13774
13958
|
*/
|
|
13775
13959
|
static async load(options) {
|
|
13960
|
+
if (options.debug) logger.configure({ debug: true });
|
|
13776
13961
|
if (!await _Sphere.exists(options.storage)) {
|
|
13777
|
-
throw new
|
|
13962
|
+
throw new SphereError("No wallet found. Use Sphere.create() to create a new wallet.", "NOT_INITIALIZED");
|
|
13778
13963
|
}
|
|
13779
13964
|
const progress = options.onProgress;
|
|
13780
13965
|
_Sphere.configureTokenRegistry(options.storage, options.network);
|
|
@@ -13805,16 +13990,16 @@ var Sphere = class _Sphere {
|
|
|
13805
13990
|
_Sphere.instance = sphere;
|
|
13806
13991
|
if (sphere._identity?.nametag && !sphere._payments.hasNametag()) {
|
|
13807
13992
|
progress?.({ step: "registering_nametag", message: "Restoring nametag token..." });
|
|
13808
|
-
|
|
13993
|
+
logger.debug("Sphere", `Nametag @${sphere._identity.nametag} has no token, attempting to mint...`);
|
|
13809
13994
|
try {
|
|
13810
13995
|
const result = await sphere.mintNametag(sphere._identity.nametag);
|
|
13811
13996
|
if (result.success) {
|
|
13812
|
-
|
|
13997
|
+
logger.debug("Sphere", `Nametag token minted successfully on load`);
|
|
13813
13998
|
} else {
|
|
13814
|
-
|
|
13999
|
+
logger.warn("Sphere", `Could not mint nametag token: ${result.error}`);
|
|
13815
14000
|
}
|
|
13816
14001
|
} catch (err) {
|
|
13817
|
-
|
|
14002
|
+
logger.warn("Sphere", `Nametag token mint failed:`, err);
|
|
13818
14003
|
}
|
|
13819
14004
|
}
|
|
13820
14005
|
if (options.discoverAddresses !== false && sphere._transport.discoverAddresses && sphere._masterKey) {
|
|
@@ -13823,10 +14008,10 @@ var Sphere = class _Sphere {
|
|
|
13823
14008
|
const discoverOpts = typeof options.discoverAddresses === "object" ? { ...options.discoverAddresses, autoTrack: options.discoverAddresses.autoTrack ?? true } : { autoTrack: true };
|
|
13824
14009
|
const result = await sphere.discoverAddresses(discoverOpts);
|
|
13825
14010
|
if (result.addresses.length > 0) {
|
|
13826
|
-
|
|
14011
|
+
logger.debug("Sphere", `Address discovery: found ${result.addresses.length} address(es)`);
|
|
13827
14012
|
}
|
|
13828
14013
|
} catch (err) {
|
|
13829
|
-
|
|
14014
|
+
logger.warn("Sphere", "Address discovery failed (non-fatal):", err);
|
|
13830
14015
|
}
|
|
13831
14016
|
}
|
|
13832
14017
|
progress?.({ step: "complete", message: "Wallet loaded" });
|
|
@@ -13836,24 +14021,25 @@ var Sphere = class _Sphere {
|
|
|
13836
14021
|
* Import wallet from mnemonic or master key
|
|
13837
14022
|
*/
|
|
13838
14023
|
static async import(options) {
|
|
14024
|
+
if (options.debug) logger.configure({ debug: true });
|
|
13839
14025
|
if (!options.mnemonic && !options.masterKey) {
|
|
13840
|
-
throw new
|
|
14026
|
+
throw new SphereError("Either mnemonic or masterKey is required", "INVALID_CONFIG");
|
|
13841
14027
|
}
|
|
13842
14028
|
const progress = options.onProgress;
|
|
13843
|
-
|
|
14029
|
+
logger.debug("Sphere", "Starting import...");
|
|
13844
14030
|
const needsClear = _Sphere.instance !== null || await _Sphere.exists(options.storage);
|
|
13845
14031
|
if (needsClear) {
|
|
13846
14032
|
progress?.({ step: "clearing", message: "Clearing previous wallet data..." });
|
|
13847
|
-
|
|
14033
|
+
logger.debug("Sphere", "Clearing existing wallet data...");
|
|
13848
14034
|
await _Sphere.clear({ storage: options.storage, tokenStorage: options.tokenStorage });
|
|
13849
|
-
|
|
14035
|
+
logger.debug("Sphere", "Clear done");
|
|
13850
14036
|
} else {
|
|
13851
|
-
|
|
14037
|
+
logger.debug("Sphere", "No existing wallet \u2014 skipping clear");
|
|
13852
14038
|
}
|
|
13853
14039
|
if (!options.storage.isConnected()) {
|
|
13854
|
-
|
|
14040
|
+
logger.debug("Sphere", "Reconnecting storage...");
|
|
13855
14041
|
await options.storage.connect();
|
|
13856
|
-
|
|
14042
|
+
logger.debug("Sphere", "Storage reconnected");
|
|
13857
14043
|
}
|
|
13858
14044
|
const groupChatConfig = _Sphere.resolveGroupChatConfig(options.groupChat);
|
|
13859
14045
|
const marketConfig = _Sphere.resolveMarketConfig(options.market);
|
|
@@ -13871,14 +14057,14 @@ var Sphere = class _Sphere {
|
|
|
13871
14057
|
progress?.({ step: "storing_keys", message: "Storing wallet keys..." });
|
|
13872
14058
|
if (options.mnemonic) {
|
|
13873
14059
|
if (!_Sphere.validateMnemonic(options.mnemonic)) {
|
|
13874
|
-
throw new
|
|
14060
|
+
throw new SphereError("Invalid mnemonic", "INVALID_IDENTITY");
|
|
13875
14061
|
}
|
|
13876
|
-
|
|
14062
|
+
logger.debug("Sphere", "Storing mnemonic...");
|
|
13877
14063
|
await sphere.storeMnemonic(options.mnemonic, options.derivationPath, options.basePath);
|
|
13878
|
-
|
|
14064
|
+
logger.debug("Sphere", "Initializing identity from mnemonic...");
|
|
13879
14065
|
await sphere.initializeIdentityFromMnemonic(options.mnemonic, options.derivationPath);
|
|
13880
14066
|
} else if (options.masterKey) {
|
|
13881
|
-
|
|
14067
|
+
logger.debug("Sphere", "Storing master key...");
|
|
13882
14068
|
await sphere.storeMasterKey(
|
|
13883
14069
|
options.masterKey,
|
|
13884
14070
|
options.chainCode,
|
|
@@ -13886,7 +14072,7 @@ var Sphere = class _Sphere {
|
|
|
13886
14072
|
options.basePath,
|
|
13887
14073
|
options.derivationMode
|
|
13888
14074
|
);
|
|
13889
|
-
|
|
14075
|
+
logger.debug("Sphere", "Initializing identity from master key...");
|
|
13890
14076
|
await sphere.initializeIdentityFromMasterKey(
|
|
13891
14077
|
options.masterKey,
|
|
13892
14078
|
options.chainCode,
|
|
@@ -13894,38 +14080,38 @@ var Sphere = class _Sphere {
|
|
|
13894
14080
|
);
|
|
13895
14081
|
}
|
|
13896
14082
|
progress?.({ step: "initializing", message: "Initializing wallet..." });
|
|
13897
|
-
|
|
14083
|
+
logger.debug("Sphere", "Initializing providers...");
|
|
13898
14084
|
await sphere.initializeProviders();
|
|
13899
|
-
|
|
14085
|
+
logger.debug("Sphere", "Providers initialized. Initializing modules...");
|
|
13900
14086
|
await sphere.initializeModules();
|
|
13901
|
-
|
|
14087
|
+
logger.debug("Sphere", "Modules initialized");
|
|
13902
14088
|
if (!options.nametag) {
|
|
13903
14089
|
progress?.({ step: "recovering_nametag", message: "Recovering nametag..." });
|
|
13904
|
-
|
|
14090
|
+
logger.debug("Sphere", "Recovering nametag from transport...");
|
|
13905
14091
|
await sphere.recoverNametagFromTransport();
|
|
13906
|
-
|
|
14092
|
+
logger.debug("Sphere", "Nametag recovery done");
|
|
13907
14093
|
progress?.({ step: "syncing_identity", message: "Publishing identity..." });
|
|
13908
14094
|
await sphere.syncIdentityWithTransport();
|
|
13909
14095
|
}
|
|
13910
14096
|
progress?.({ step: "finalizing", message: "Finalizing wallet..." });
|
|
13911
|
-
|
|
14097
|
+
logger.debug("Sphere", "Finalizing wallet creation...");
|
|
13912
14098
|
await sphere.finalizeWalletCreation();
|
|
13913
14099
|
sphere._initialized = true;
|
|
13914
14100
|
_Sphere.instance = sphere;
|
|
13915
|
-
|
|
14101
|
+
logger.debug("Sphere", "Tracking address 0...");
|
|
13916
14102
|
await sphere.ensureAddressTracked(0);
|
|
13917
14103
|
if (options.nametag) {
|
|
13918
14104
|
progress?.({ step: "registering_nametag", message: "Registering nametag..." });
|
|
13919
|
-
|
|
14105
|
+
logger.debug("Sphere", "Registering nametag...");
|
|
13920
14106
|
await sphere.registerNametag(options.nametag);
|
|
13921
14107
|
}
|
|
13922
14108
|
if (sphere._tokenStorageProviders.size > 0) {
|
|
13923
14109
|
progress?.({ step: "syncing_tokens", message: "Syncing tokens..." });
|
|
13924
14110
|
try {
|
|
13925
14111
|
const syncResult = await sphere._payments.sync();
|
|
13926
|
-
|
|
14112
|
+
logger.debug("Sphere", `Auto-sync: +${syncResult.added} -${syncResult.removed}`);
|
|
13927
14113
|
} catch (err) {
|
|
13928
|
-
|
|
14114
|
+
logger.warn("Sphere", "Auto-sync failed (non-fatal):", err);
|
|
13929
14115
|
}
|
|
13930
14116
|
}
|
|
13931
14117
|
if (options.discoverAddresses !== false && sphere._transport.discoverAddresses) {
|
|
@@ -13934,14 +14120,14 @@ var Sphere = class _Sphere {
|
|
|
13934
14120
|
const discoverOpts = typeof options.discoverAddresses === "object" ? { ...options.discoverAddresses, autoTrack: options.discoverAddresses.autoTrack ?? true } : { autoTrack: true };
|
|
13935
14121
|
const result = await sphere.discoverAddresses(discoverOpts);
|
|
13936
14122
|
if (result.addresses.length > 0) {
|
|
13937
|
-
|
|
14123
|
+
logger.debug("Sphere", `Address discovery: found ${result.addresses.length} address(es)`);
|
|
13938
14124
|
}
|
|
13939
14125
|
} catch (err) {
|
|
13940
|
-
|
|
14126
|
+
logger.warn("Sphere", "Address discovery failed (non-fatal):", err);
|
|
13941
14127
|
}
|
|
13942
14128
|
}
|
|
13943
14129
|
progress?.({ step: "complete", message: "Import complete" });
|
|
13944
|
-
|
|
14130
|
+
logger.debug("Sphere", "Import complete");
|
|
13945
14131
|
return sphere;
|
|
13946
14132
|
}
|
|
13947
14133
|
/**
|
|
@@ -13967,26 +14153,26 @@ var Sphere = class _Sphere {
|
|
|
13967
14153
|
const storage = "get" in storageOrOptions ? storageOrOptions : storageOrOptions.storage;
|
|
13968
14154
|
const tokenStorage = "get" in storageOrOptions ? void 0 : storageOrOptions.tokenStorage;
|
|
13969
14155
|
if (_Sphere.instance) {
|
|
13970
|
-
|
|
14156
|
+
logger.debug("Sphere", "Destroying Sphere instance...");
|
|
13971
14157
|
await _Sphere.instance.destroy();
|
|
13972
|
-
|
|
14158
|
+
logger.debug("Sphere", "Sphere instance destroyed");
|
|
13973
14159
|
}
|
|
13974
|
-
|
|
14160
|
+
logger.debug("Sphere", "Clearing L1 vesting cache...");
|
|
13975
14161
|
await vestingClassifier.destroy();
|
|
13976
|
-
|
|
14162
|
+
logger.debug("Sphere", "Yielding 50ms for IDB transaction settlement...");
|
|
13977
14163
|
await new Promise((r) => setTimeout(r, 50));
|
|
13978
14164
|
if (tokenStorage?.clear) {
|
|
13979
|
-
|
|
14165
|
+
logger.debug("Sphere", "Clearing token storage...");
|
|
13980
14166
|
try {
|
|
13981
14167
|
await tokenStorage.clear();
|
|
13982
|
-
|
|
14168
|
+
logger.debug("Sphere", "Token storage cleared");
|
|
13983
14169
|
} catch (err) {
|
|
13984
|
-
|
|
14170
|
+
logger.warn("Sphere", "Token storage clear failed:", err);
|
|
13985
14171
|
}
|
|
13986
14172
|
} else {
|
|
13987
|
-
|
|
14173
|
+
logger.debug("Sphere", "No token storage provider to clear");
|
|
13988
14174
|
}
|
|
13989
|
-
|
|
14175
|
+
logger.debug("Sphere", "Clearing KV storage...");
|
|
13990
14176
|
if (!storage.isConnected()) {
|
|
13991
14177
|
try {
|
|
13992
14178
|
await storage.connect();
|
|
@@ -13995,11 +14181,11 @@ var Sphere = class _Sphere {
|
|
|
13995
14181
|
}
|
|
13996
14182
|
if (storage.isConnected()) {
|
|
13997
14183
|
await storage.clear();
|
|
13998
|
-
|
|
14184
|
+
logger.debug("Sphere", "KV storage cleared");
|
|
13999
14185
|
} else {
|
|
14000
|
-
|
|
14186
|
+
logger.debug("Sphere", "KV storage not connected, skipping");
|
|
14001
14187
|
}
|
|
14002
|
-
|
|
14188
|
+
logger.debug("Sphere", "Done");
|
|
14003
14189
|
}
|
|
14004
14190
|
/**
|
|
14005
14191
|
* Get current instance
|
|
@@ -14091,7 +14277,7 @@ var Sphere = class _Sphere {
|
|
|
14091
14277
|
*/
|
|
14092
14278
|
async addTokenStorageProvider(provider) {
|
|
14093
14279
|
if (this._tokenStorageProviders.has(provider.id)) {
|
|
14094
|
-
throw new
|
|
14280
|
+
throw new SphereError(`Token storage provider '${provider.id}' already exists`, "INVALID_CONFIG");
|
|
14095
14281
|
}
|
|
14096
14282
|
if (this._identity) {
|
|
14097
14283
|
provider.setIdentity(this._identity);
|
|
@@ -14211,7 +14397,7 @@ var Sphere = class _Sphere {
|
|
|
14211
14397
|
exportToJSON(options = {}) {
|
|
14212
14398
|
this.ensureReady();
|
|
14213
14399
|
if (!this._masterKey && !this._identity) {
|
|
14214
|
-
throw new
|
|
14400
|
+
throw new SphereError("Wallet not initialized", "NOT_INITIALIZED");
|
|
14215
14401
|
}
|
|
14216
14402
|
const addressCount = options.addressCount || 1;
|
|
14217
14403
|
const addresses = [];
|
|
@@ -14291,7 +14477,7 @@ var Sphere = class _Sphere {
|
|
|
14291
14477
|
exportToTxt(options = {}) {
|
|
14292
14478
|
this.ensureReady();
|
|
14293
14479
|
if (!this._masterKey && !this._identity) {
|
|
14294
|
-
throw new
|
|
14480
|
+
throw new SphereError("Wallet not initialized", "NOT_INITIALIZED");
|
|
14295
14481
|
}
|
|
14296
14482
|
const addressCount = options.addressCount || 1;
|
|
14297
14483
|
const addresses = [];
|
|
@@ -14730,7 +14916,7 @@ var Sphere = class _Sphere {
|
|
|
14730
14916
|
this.ensureReady();
|
|
14731
14917
|
const entry = this._trackedAddresses.get(index);
|
|
14732
14918
|
if (!entry) {
|
|
14733
|
-
throw new
|
|
14919
|
+
throw new SphereError(`Address at index ${index} is not tracked. Switch to it first.`, "INVALID_CONFIG");
|
|
14734
14920
|
}
|
|
14735
14921
|
if (entry.hidden === hidden) return;
|
|
14736
14922
|
entry.hidden = hidden;
|
|
@@ -14760,14 +14946,14 @@ var Sphere = class _Sphere {
|
|
|
14760
14946
|
async switchToAddress(index, options) {
|
|
14761
14947
|
this.ensureReady();
|
|
14762
14948
|
if (!this._masterKey) {
|
|
14763
|
-
throw new
|
|
14949
|
+
throw new SphereError("HD derivation requires master key with chain code. Cannot switch addresses.", "INVALID_CONFIG");
|
|
14764
14950
|
}
|
|
14765
14951
|
if (index < 0) {
|
|
14766
|
-
throw new
|
|
14952
|
+
throw new SphereError("Address index must be non-negative", "INVALID_CONFIG");
|
|
14767
14953
|
}
|
|
14768
14954
|
const newNametag = options?.nametag ? this.cleanNametag(options.nametag) : void 0;
|
|
14769
14955
|
if (newNametag && !isValidNametag(newNametag)) {
|
|
14770
|
-
throw new
|
|
14956
|
+
throw new SphereError("Invalid nametag format. Use lowercase alphanumeric, underscore, or hyphen (3-20 chars), or a valid phone number.", "VALIDATION_ERROR");
|
|
14771
14957
|
}
|
|
14772
14958
|
const addressInfo = this.deriveAddress(index, false);
|
|
14773
14959
|
const ipnsHash = sha256(addressInfo.publicKey, "hex").slice(0, 40);
|
|
@@ -14777,7 +14963,7 @@ var Sphere = class _Sphere {
|
|
|
14777
14963
|
if (newNametag) {
|
|
14778
14964
|
const existing = await this._transport.resolveNametag?.(newNametag);
|
|
14779
14965
|
if (existing) {
|
|
14780
|
-
throw new
|
|
14966
|
+
throw new SphereError(`Nametag @${newNametag} is already taken`, "VALIDATION_ERROR");
|
|
14781
14967
|
}
|
|
14782
14968
|
let nametags = this._addressNametags.get(addressId);
|
|
14783
14969
|
if (!nametags) {
|
|
@@ -14800,12 +14986,12 @@ var Sphere = class _Sphere {
|
|
|
14800
14986
|
await this._storage.set(STORAGE_KEYS_GLOBAL.CURRENT_ADDRESS_INDEX, index.toString());
|
|
14801
14987
|
this._storage.setIdentity(this._identity);
|
|
14802
14988
|
await this._transport.setIdentity(this._identity);
|
|
14803
|
-
|
|
14989
|
+
logger.debug("Sphere", `switchToAddress(${index}): re-initializing ${this._tokenStorageProviders.size} token storage provider(s)`);
|
|
14804
14990
|
for (const [providerId, provider] of this._tokenStorageProviders.entries()) {
|
|
14805
|
-
|
|
14991
|
+
logger.debug("Sphere", `switchToAddress(${index}): shutdown provider=${providerId}`);
|
|
14806
14992
|
await provider.shutdown();
|
|
14807
14993
|
provider.setIdentity(this._identity);
|
|
14808
|
-
|
|
14994
|
+
logger.debug("Sphere", `switchToAddress(${index}): initialize provider=${providerId}`);
|
|
14809
14995
|
await provider.initialize();
|
|
14810
14996
|
}
|
|
14811
14997
|
await this.reinitializeModulesForNewAddress();
|
|
@@ -14816,9 +15002,9 @@ var Sphere = class _Sphere {
|
|
|
14816
15002
|
nametag: this._identity.nametag,
|
|
14817
15003
|
addressIndex: index
|
|
14818
15004
|
});
|
|
14819
|
-
|
|
15005
|
+
logger.debug("Sphere", `Switched to address ${index}:`, this._identity.l1Address);
|
|
14820
15006
|
this.postSwitchSync(index, newNametag).catch((err) => {
|
|
14821
|
-
|
|
15007
|
+
logger.warn("Sphere", `Post-switch sync failed for address ${index}:`, err);
|
|
14822
15008
|
});
|
|
14823
15009
|
}
|
|
14824
15010
|
/**
|
|
@@ -14832,16 +15018,16 @@ var Sphere = class _Sphere {
|
|
|
14832
15018
|
if (newNametag) {
|
|
14833
15019
|
await this.persistAddressNametags();
|
|
14834
15020
|
if (!this._payments.hasNametag()) {
|
|
14835
|
-
|
|
15021
|
+
logger.debug("Sphere", `Minting nametag token for @${newNametag}...`);
|
|
14836
15022
|
try {
|
|
14837
15023
|
const result = await this.mintNametag(newNametag);
|
|
14838
15024
|
if (result.success) {
|
|
14839
|
-
|
|
15025
|
+
logger.debug("Sphere", `Nametag token minted successfully`);
|
|
14840
15026
|
} else {
|
|
14841
|
-
|
|
15027
|
+
logger.warn("Sphere", `Could not mint nametag token: ${result.error}`);
|
|
14842
15028
|
}
|
|
14843
15029
|
} catch (err) {
|
|
14844
|
-
|
|
15030
|
+
logger.warn("Sphere", `Nametag token mint failed:`, err);
|
|
14845
15031
|
}
|
|
14846
15032
|
}
|
|
14847
15033
|
this.emitEvent("nametag:registered", {
|
|
@@ -14849,16 +15035,16 @@ var Sphere = class _Sphere {
|
|
|
14849
15035
|
addressIndex: index
|
|
14850
15036
|
});
|
|
14851
15037
|
} else if (this._identity?.nametag && !this._payments.hasNametag()) {
|
|
14852
|
-
|
|
15038
|
+
logger.debug("Sphere", `Nametag @${this._identity.nametag} has no token after switch, minting...`);
|
|
14853
15039
|
try {
|
|
14854
15040
|
const result = await this.mintNametag(this._identity.nametag);
|
|
14855
15041
|
if (result.success) {
|
|
14856
|
-
|
|
15042
|
+
logger.debug("Sphere", `Nametag token minted successfully after switch`);
|
|
14857
15043
|
} else {
|
|
14858
|
-
|
|
15044
|
+
logger.warn("Sphere", `Could not mint nametag token after switch: ${result.error}`);
|
|
14859
15045
|
}
|
|
14860
15046
|
} catch (err) {
|
|
14861
|
-
|
|
15047
|
+
logger.warn("Sphere", `Nametag token mint failed after switch:`, err);
|
|
14862
15048
|
}
|
|
14863
15049
|
}
|
|
14864
15050
|
}
|
|
@@ -14897,7 +15083,7 @@ var Sphere = class _Sphere {
|
|
|
14897
15083
|
await this._groupChat?.load();
|
|
14898
15084
|
await this._market?.load();
|
|
14899
15085
|
this._payments.sync().catch((err) => {
|
|
14900
|
-
|
|
15086
|
+
logger.warn("Sphere", "Post-switch sync failed:", err);
|
|
14901
15087
|
});
|
|
14902
15088
|
}
|
|
14903
15089
|
/**
|
|
@@ -14931,7 +15117,7 @@ var Sphere = class _Sphere {
|
|
|
14931
15117
|
*/
|
|
14932
15118
|
_deriveAddressInternal(index, isChange = false) {
|
|
14933
15119
|
if (!this._masterKey) {
|
|
14934
|
-
throw new
|
|
15120
|
+
throw new SphereError("HD derivation requires master key with chain code", "INVALID_CONFIG");
|
|
14935
15121
|
}
|
|
14936
15122
|
if (this._derivationMode === "wif_hmac") {
|
|
14937
15123
|
return generateAddressFromMasterKey(this._masterKey.privateKey, index);
|
|
@@ -14961,7 +15147,7 @@ var Sphere = class _Sphere {
|
|
|
14961
15147
|
deriveAddressAtPath(path) {
|
|
14962
15148
|
this.ensureReady();
|
|
14963
15149
|
if (!this._masterKey) {
|
|
14964
|
-
throw new
|
|
15150
|
+
throw new SphereError("HD derivation requires master key with chain code", "INVALID_CONFIG");
|
|
14965
15151
|
}
|
|
14966
15152
|
const match = path.match(/\/(\d+)$/);
|
|
14967
15153
|
const index = match ? parseInt(match[1], 10) : 0;
|
|
@@ -15028,13 +15214,14 @@ var Sphere = class _Sphere {
|
|
|
15028
15214
|
async scanAddresses(options = {}) {
|
|
15029
15215
|
this.ensureReady();
|
|
15030
15216
|
if (!this._masterKey) {
|
|
15031
|
-
throw new
|
|
15217
|
+
throw new SphereError("Address scanning requires HD master key", "INVALID_CONFIG");
|
|
15032
15218
|
}
|
|
15033
15219
|
const resolveNametag = options.resolveNametag ?? (this._transport.resolveAddressInfo ? async (l1Address) => {
|
|
15034
15220
|
try {
|
|
15035
15221
|
const info = await this._transport.resolveAddressInfo(l1Address);
|
|
15036
15222
|
return info?.nametag ?? null;
|
|
15037
|
-
} catch {
|
|
15223
|
+
} catch (err) {
|
|
15224
|
+
logger.debug("Sphere", "Nametag resolution failed during scan", err);
|
|
15038
15225
|
return null;
|
|
15039
15226
|
}
|
|
15040
15227
|
} : void 0);
|
|
@@ -15085,10 +15272,10 @@ var Sphere = class _Sphere {
|
|
|
15085
15272
|
async discoverAddresses(options = {}) {
|
|
15086
15273
|
this.ensureReady();
|
|
15087
15274
|
if (!this._masterKey) {
|
|
15088
|
-
throw new
|
|
15275
|
+
throw new SphereError("Address discovery requires HD master key", "INVALID_CONFIG");
|
|
15089
15276
|
}
|
|
15090
15277
|
if (!this._transport.discoverAddresses) {
|
|
15091
|
-
throw new
|
|
15278
|
+
throw new SphereError("Transport provider does not support address discovery", "INVALID_CONFIG");
|
|
15092
15279
|
}
|
|
15093
15280
|
const includeL1Scan = options.includeL1Scan ?? true;
|
|
15094
15281
|
const transportResult = await discoverAddressesImpl(
|
|
@@ -15144,7 +15331,7 @@ var Sphere = class _Sphere {
|
|
|
15144
15331
|
}
|
|
15145
15332
|
transportResult.addresses.sort((a, b) => a.index - b.index);
|
|
15146
15333
|
} catch (err) {
|
|
15147
|
-
|
|
15334
|
+
logger.warn("Sphere", "L1 scan failed during discovery (non-fatal):", err);
|
|
15148
15335
|
}
|
|
15149
15336
|
}
|
|
15150
15337
|
if (options.autoTrack && transportResult.addresses.length > 0) {
|
|
@@ -15242,7 +15429,7 @@ var Sphere = class _Sphere {
|
|
|
15242
15429
|
*/
|
|
15243
15430
|
async disableProvider(providerId) {
|
|
15244
15431
|
if (providerId === this._storage.id) {
|
|
15245
|
-
throw new
|
|
15432
|
+
throw new SphereError("Cannot disable the main storage provider", "INVALID_CONFIG");
|
|
15246
15433
|
}
|
|
15247
15434
|
const provider = this.findProviderById(providerId);
|
|
15248
15435
|
if (!provider) return false;
|
|
@@ -15445,10 +15632,10 @@ var Sphere = class _Sphere {
|
|
|
15445
15632
|
this.ensureReady();
|
|
15446
15633
|
const cleanNametag = this.cleanNametag(nametag);
|
|
15447
15634
|
if (!isValidNametag(cleanNametag)) {
|
|
15448
|
-
throw new
|
|
15635
|
+
throw new SphereError("Invalid nametag format. Use lowercase alphanumeric, underscore, or hyphen (3-20 chars), or a valid phone number.", "VALIDATION_ERROR");
|
|
15449
15636
|
}
|
|
15450
15637
|
if (this._identity?.nametag) {
|
|
15451
|
-
throw new
|
|
15638
|
+
throw new SphereError(`Nametag already registered for address ${this._currentAddressIndex}: @${this._identity.nametag}`, "ALREADY_INITIALIZED");
|
|
15452
15639
|
}
|
|
15453
15640
|
if (this._transport.publishIdentityBinding) {
|
|
15454
15641
|
const success = await this._transport.publishIdentityBinding(
|
|
@@ -15458,7 +15645,7 @@ var Sphere = class _Sphere {
|
|
|
15458
15645
|
cleanNametag
|
|
15459
15646
|
);
|
|
15460
15647
|
if (!success) {
|
|
15461
|
-
throw new
|
|
15648
|
+
throw new SphereError("Failed to register nametag. It may already be taken.", "VALIDATION_ERROR");
|
|
15462
15649
|
}
|
|
15463
15650
|
}
|
|
15464
15651
|
this._identity.nametag = cleanNametag;
|
|
@@ -15474,19 +15661,19 @@ var Sphere = class _Sphere {
|
|
|
15474
15661
|
}
|
|
15475
15662
|
await this.persistAddressNametags();
|
|
15476
15663
|
if (!this._payments.hasNametag()) {
|
|
15477
|
-
|
|
15664
|
+
logger.debug("Sphere", `Minting nametag token for @${cleanNametag}...`);
|
|
15478
15665
|
const result = await this.mintNametag(cleanNametag);
|
|
15479
15666
|
if (!result.success) {
|
|
15480
|
-
|
|
15667
|
+
logger.warn("Sphere", `Failed to mint nametag token: ${result.error}`);
|
|
15481
15668
|
} else {
|
|
15482
|
-
|
|
15669
|
+
logger.debug("Sphere", `Nametag token minted successfully`);
|
|
15483
15670
|
}
|
|
15484
15671
|
}
|
|
15485
15672
|
this.emitEvent("nametag:registered", {
|
|
15486
15673
|
nametag: cleanNametag,
|
|
15487
15674
|
addressIndex: this._currentAddressIndex
|
|
15488
15675
|
});
|
|
15489
|
-
|
|
15676
|
+
logger.debug("Sphere", `Nametag registered for address ${this._currentAddressIndex}:`, cleanNametag);
|
|
15490
15677
|
}
|
|
15491
15678
|
/**
|
|
15492
15679
|
* Persist tracked addresses to storage (only minimal fields via StorageProvider)
|
|
@@ -15726,13 +15913,13 @@ var Sphere = class _Sphere {
|
|
|
15726
15913
|
this._identity.directAddress || "",
|
|
15727
15914
|
recoveredNametag
|
|
15728
15915
|
);
|
|
15729
|
-
|
|
15916
|
+
logger.debug("Sphere", `Migrated legacy binding with nametag @${recoveredNametag}`);
|
|
15730
15917
|
return;
|
|
15731
15918
|
}
|
|
15732
15919
|
}
|
|
15733
15920
|
const needsUpdate = !existing.directAddress || !existing.l1Address || !existing.chainPubkey || this._identity?.nametag && !existing.nametag;
|
|
15734
15921
|
if (needsUpdate) {
|
|
15735
|
-
|
|
15922
|
+
logger.debug("Sphere", "Existing binding incomplete, re-publishing with full data");
|
|
15736
15923
|
await this._transport.publishIdentityBinding(
|
|
15737
15924
|
this._identity.chainPubkey,
|
|
15738
15925
|
this._identity.l1Address,
|
|
@@ -15741,11 +15928,11 @@ var Sphere = class _Sphere {
|
|
|
15741
15928
|
);
|
|
15742
15929
|
return;
|
|
15743
15930
|
}
|
|
15744
|
-
|
|
15931
|
+
logger.debug("Sphere", "Existing binding found, skipping re-publish");
|
|
15745
15932
|
return;
|
|
15746
15933
|
}
|
|
15747
15934
|
} catch (e) {
|
|
15748
|
-
|
|
15935
|
+
logger.warn("Sphere", "resolve() failed, skipping publish to avoid overwrite", e);
|
|
15749
15936
|
return;
|
|
15750
15937
|
}
|
|
15751
15938
|
}
|
|
@@ -15757,12 +15944,12 @@ var Sphere = class _Sphere {
|
|
|
15757
15944
|
nametag || void 0
|
|
15758
15945
|
);
|
|
15759
15946
|
if (success) {
|
|
15760
|
-
|
|
15947
|
+
logger.debug("Sphere", `Identity binding published${nametag ? ` with nametag @${nametag}` : ""}`);
|
|
15761
15948
|
} else if (nametag) {
|
|
15762
|
-
|
|
15949
|
+
logger.warn("Sphere", `Nametag @${nametag} is taken by another pubkey`);
|
|
15763
15950
|
}
|
|
15764
15951
|
} catch (error) {
|
|
15765
|
-
|
|
15952
|
+
logger.warn("Sphere", `Identity binding sync failed:`, error);
|
|
15766
15953
|
}
|
|
15767
15954
|
}
|
|
15768
15955
|
/**
|
|
@@ -15915,7 +16102,7 @@ var Sphere = class _Sphere {
|
|
|
15915
16102
|
if (encryptedMnemonic) {
|
|
15916
16103
|
const mnemonic = this.decrypt(encryptedMnemonic);
|
|
15917
16104
|
if (!mnemonic) {
|
|
15918
|
-
throw new
|
|
16105
|
+
throw new SphereError("Failed to decrypt mnemonic", "STORAGE_ERROR");
|
|
15919
16106
|
}
|
|
15920
16107
|
this._mnemonic = mnemonic;
|
|
15921
16108
|
this._source = "mnemonic";
|
|
@@ -15923,7 +16110,7 @@ var Sphere = class _Sphere {
|
|
|
15923
16110
|
} else if (encryptedMasterKey) {
|
|
15924
16111
|
const masterKey = this.decrypt(encryptedMasterKey);
|
|
15925
16112
|
if (!masterKey) {
|
|
15926
|
-
throw new
|
|
16113
|
+
throw new SphereError("Failed to decrypt master key", "STORAGE_ERROR");
|
|
15927
16114
|
}
|
|
15928
16115
|
this._mnemonic = null;
|
|
15929
16116
|
if (this._source === "unknown") {
|
|
@@ -15935,7 +16122,7 @@ var Sphere = class _Sphere {
|
|
|
15935
16122
|
derivationPath ?? void 0
|
|
15936
16123
|
);
|
|
15937
16124
|
} else {
|
|
15938
|
-
throw new
|
|
16125
|
+
throw new SphereError("No wallet data found in storage", "NOT_INITIALIZED");
|
|
15939
16126
|
}
|
|
15940
16127
|
if (this._identity) {
|
|
15941
16128
|
this._storage.setIdentity(this._identity);
|
|
@@ -15957,7 +16144,7 @@ var Sphere = class _Sphere {
|
|
|
15957
16144
|
nametag
|
|
15958
16145
|
};
|
|
15959
16146
|
this._storage.setIdentity(this._identity);
|
|
15960
|
-
|
|
16147
|
+
logger.debug("Sphere", `Restored to address ${this._currentAddressIndex}:`, this._identity.l1Address);
|
|
15961
16148
|
} else if (this._identity && nametag) {
|
|
15962
16149
|
this._identity.nametag = nametag;
|
|
15963
16150
|
}
|
|
@@ -16148,7 +16335,7 @@ var Sphere = class _Sphere {
|
|
|
16148
16335
|
// ===========================================================================
|
|
16149
16336
|
ensureReady() {
|
|
16150
16337
|
if (!this._initialized) {
|
|
16151
|
-
throw new
|
|
16338
|
+
throw new SphereError("Sphere not initialized", "NOT_INITIALIZED");
|
|
16152
16339
|
}
|
|
16153
16340
|
}
|
|
16154
16341
|
emitEvent(type, data) {
|
|
@@ -16158,7 +16345,7 @@ var Sphere = class _Sphere {
|
|
|
16158
16345
|
try {
|
|
16159
16346
|
handler(data);
|
|
16160
16347
|
} catch (error) {
|
|
16161
|
-
|
|
16348
|
+
logger.error("Sphere", "Event handler error:", error);
|
|
16162
16349
|
}
|
|
16163
16350
|
}
|
|
16164
16351
|
}
|
|
@@ -16195,6 +16382,7 @@ var getSphere = Sphere.getInstance.bind(Sphere);
|
|
|
16195
16382
|
var sphereExists = Sphere.exists.bind(Sphere);
|
|
16196
16383
|
|
|
16197
16384
|
// core/currency.ts
|
|
16385
|
+
init_logger();
|
|
16198
16386
|
var DEFAULT_TOKEN_DECIMALS = 18;
|
|
16199
16387
|
function toSmallestUnit(amount, decimals = DEFAULT_TOKEN_DECIMALS) {
|
|
16200
16388
|
if (!amount) return 0n;
|
|
@@ -16203,7 +16391,8 @@ function toSmallestUnit(amount, decimals = DEFAULT_TOKEN_DECIMALS) {
|
|
|
16203
16391
|
const [integer, fraction = ""] = str.split(".");
|
|
16204
16392
|
const paddedFraction = fraction.padEnd(decimals, "0").slice(0, decimals);
|
|
16205
16393
|
return BigInt(integer + paddedFraction);
|
|
16206
|
-
} catch {
|
|
16394
|
+
} catch (err) {
|
|
16395
|
+
logger.debug("Currency", "toSmallestUnit conversion failed", err);
|
|
16207
16396
|
return 0n;
|
|
16208
16397
|
}
|
|
16209
16398
|
}
|
|
@@ -16227,6 +16416,8 @@ function formatAmount(amount, options = {}) {
|
|
|
16227
16416
|
|
|
16228
16417
|
// core/index.ts
|
|
16229
16418
|
init_bech32();
|
|
16419
|
+
init_logger();
|
|
16420
|
+
init_errors();
|
|
16230
16421
|
|
|
16231
16422
|
// core/network-health.ts
|
|
16232
16423
|
init_constants();
|
|
@@ -16418,6 +16609,9 @@ async function runCustomCheck(name, checkFn, timeoutMs) {
|
|
|
16418
16609
|
// index.ts
|
|
16419
16610
|
init_constants();
|
|
16420
16611
|
|
|
16612
|
+
// types/index.ts
|
|
16613
|
+
init_errors();
|
|
16614
|
+
|
|
16421
16615
|
// types/payment-session.ts
|
|
16422
16616
|
function createPaymentSession(params) {
|
|
16423
16617
|
const now = Date.now();
|
|
@@ -16477,22 +16671,11 @@ function createPaymentSessionError(code, message, recoverable = false, details)
|
|
|
16477
16671
|
};
|
|
16478
16672
|
}
|
|
16479
16673
|
|
|
16480
|
-
// types/index.ts
|
|
16481
|
-
var SphereError = class extends Error {
|
|
16482
|
-
code;
|
|
16483
|
-
cause;
|
|
16484
|
-
constructor(message, code, cause) {
|
|
16485
|
-
super(message);
|
|
16486
|
-
this.name = "SphereError";
|
|
16487
|
-
this.code = code;
|
|
16488
|
-
this.cause = cause;
|
|
16489
|
-
}
|
|
16490
|
-
};
|
|
16491
|
-
|
|
16492
16674
|
// index.ts
|
|
16493
16675
|
init_constants();
|
|
16494
16676
|
|
|
16495
16677
|
// validation/token-validator.ts
|
|
16678
|
+
init_logger();
|
|
16496
16679
|
var TokenValidator = class {
|
|
16497
16680
|
aggregatorClient = null;
|
|
16498
16681
|
trustBase = null;
|
|
@@ -16617,7 +16800,7 @@ var TokenValidator = class {
|
|
|
16617
16800
|
};
|
|
16618
16801
|
}
|
|
16619
16802
|
} catch (err) {
|
|
16620
|
-
|
|
16803
|
+
logger.warn("Validation", "SDK verification skipped:", err instanceof Error ? err.message : err);
|
|
16621
16804
|
}
|
|
16622
16805
|
}
|
|
16623
16806
|
return { isValid: true };
|
|
@@ -16662,7 +16845,7 @@ var TokenValidator = class {
|
|
|
16662
16845
|
});
|
|
16663
16846
|
return isSpent;
|
|
16664
16847
|
} catch (err) {
|
|
16665
|
-
|
|
16848
|
+
logger.warn("Validation", "Error checking token state:", err);
|
|
16666
16849
|
return false;
|
|
16667
16850
|
}
|
|
16668
16851
|
}
|
|
@@ -16825,6 +17008,8 @@ import {
|
|
|
16825
17008
|
} from "@unicitylabs/nostr-js-sdk";
|
|
16826
17009
|
|
|
16827
17010
|
// price/CoinGeckoPriceProvider.ts
|
|
17011
|
+
init_logger();
|
|
17012
|
+
init_errors();
|
|
16828
17013
|
init_constants();
|
|
16829
17014
|
var CoinGeckoPriceProvider = class {
|
|
16830
17015
|
platform = "coingecko";
|
|
@@ -16876,7 +17061,7 @@ var CoinGeckoPriceProvider = class {
|
|
|
16876
17061
|
const allCovered = uncachedNames.every((n) => this.fetchNames.has(n));
|
|
16877
17062
|
if (allCovered) {
|
|
16878
17063
|
if (this.debug) {
|
|
16879
|
-
|
|
17064
|
+
logger.debug("CoinGecko", "Deduplicating request, reusing in-flight fetch");
|
|
16880
17065
|
}
|
|
16881
17066
|
const fetched = await this.fetchPromise;
|
|
16882
17067
|
for (const name of uncachedNames) {
|
|
@@ -16915,7 +17100,7 @@ var CoinGeckoPriceProvider = class {
|
|
|
16915
17100
|
headers["x-cg-pro-api-key"] = this.apiKey;
|
|
16916
17101
|
}
|
|
16917
17102
|
if (this.debug) {
|
|
16918
|
-
|
|
17103
|
+
logger.debug("CoinGecko", `Fetching prices for: ${uncachedNames.join(", ")}`);
|
|
16919
17104
|
}
|
|
16920
17105
|
const response = await fetch(url, {
|
|
16921
17106
|
headers,
|
|
@@ -16925,7 +17110,7 @@ var CoinGeckoPriceProvider = class {
|
|
|
16925
17110
|
if (response.status === 429) {
|
|
16926
17111
|
this.extendCacheOnRateLimit(uncachedNames);
|
|
16927
17112
|
}
|
|
16928
|
-
throw new
|
|
17113
|
+
throw new SphereError(`CoinGecko API error: ${response.status} ${response.statusText}`, "NETWORK_ERROR");
|
|
16929
17114
|
}
|
|
16930
17115
|
const data = await response.json();
|
|
16931
17116
|
for (const [name, values] of Object.entries(data)) {
|
|
@@ -16955,12 +17140,12 @@ var CoinGeckoPriceProvider = class {
|
|
|
16955
17140
|
}
|
|
16956
17141
|
}
|
|
16957
17142
|
if (this.debug) {
|
|
16958
|
-
|
|
17143
|
+
logger.debug("CoinGecko", `Fetched ${result.size} prices`);
|
|
16959
17144
|
}
|
|
16960
17145
|
this.saveToStorage();
|
|
16961
17146
|
} catch (error) {
|
|
16962
17147
|
if (this.debug) {
|
|
16963
|
-
|
|
17148
|
+
logger.warn("CoinGecko", "Fetch failed, using stale cache:", error);
|
|
16964
17149
|
}
|
|
16965
17150
|
for (const name of uncachedNames) {
|
|
16966
17151
|
const stale = this.cache.get(name);
|
|
@@ -17010,7 +17195,7 @@ var CoinGeckoPriceProvider = class {
|
|
|
17010
17195
|
}
|
|
17011
17196
|
}
|
|
17012
17197
|
if (this.debug) {
|
|
17013
|
-
|
|
17198
|
+
logger.debug("CoinGecko", `Loaded ${Object.keys(data).length} prices from persistent cache`);
|
|
17014
17199
|
}
|
|
17015
17200
|
} catch {
|
|
17016
17201
|
}
|
|
@@ -17027,8 +17212,7 @@ var CoinGeckoPriceProvider = class {
|
|
|
17027
17212
|
Promise.all([
|
|
17028
17213
|
this.storage.set(STORAGE_KEYS_GLOBAL.PRICE_CACHE, JSON.stringify(data)),
|
|
17029
17214
|
this.storage.set(STORAGE_KEYS_GLOBAL.PRICE_CACHE_TS, String(Date.now()))
|
|
17030
|
-
]).catch(() =>
|
|
17031
|
-
});
|
|
17215
|
+
]).catch((err) => logger.debug("Price", "Cache save failed (non-critical)", err));
|
|
17032
17216
|
}
|
|
17033
17217
|
// ===========================================================================
|
|
17034
17218
|
// Rate-limit handling
|
|
@@ -17047,7 +17231,7 @@ var CoinGeckoPriceProvider = class {
|
|
|
17047
17231
|
}
|
|
17048
17232
|
}
|
|
17049
17233
|
if (this.debug) {
|
|
17050
|
-
|
|
17234
|
+
logger.warn("CoinGecko", `Rate-limited (429), extended cache TTL by ${backoffMs / 1e3}s`);
|
|
17051
17235
|
}
|
|
17052
17236
|
}
|
|
17053
17237
|
async getPrice(tokenName) {
|
|
@@ -17060,12 +17244,13 @@ var CoinGeckoPriceProvider = class {
|
|
|
17060
17244
|
};
|
|
17061
17245
|
|
|
17062
17246
|
// price/index.ts
|
|
17247
|
+
init_errors();
|
|
17063
17248
|
function createPriceProvider(config) {
|
|
17064
17249
|
switch (config.platform) {
|
|
17065
17250
|
case "coingecko":
|
|
17066
17251
|
return new CoinGeckoPriceProvider(config);
|
|
17067
17252
|
default:
|
|
17068
|
-
throw new
|
|
17253
|
+
throw new SphereError(`Unsupported price platform: ${String(config.platform)}`, "INVALID_CONFIG");
|
|
17069
17254
|
}
|
|
17070
17255
|
}
|
|
17071
17256
|
export {
|
|
@@ -17174,6 +17359,7 @@ export {
|
|
|
17174
17359
|
isPaymentSessionTimedOut,
|
|
17175
17360
|
isPhoneNumber2 as isPhoneNumber,
|
|
17176
17361
|
isSQLiteDatabase,
|
|
17362
|
+
isSphereError,
|
|
17177
17363
|
isTextWalletEncrypted,
|
|
17178
17364
|
isTokenKey,
|
|
17179
17365
|
isValidBech32,
|
|
@@ -17184,6 +17370,7 @@ export {
|
|
|
17184
17370
|
isWalletTextFormat,
|
|
17185
17371
|
keyFromTokenId,
|
|
17186
17372
|
loadSphere,
|
|
17373
|
+
logger,
|
|
17187
17374
|
mnemonicToSeedSync2 as mnemonicToSeedSync,
|
|
17188
17375
|
normalizeNametag3 as normalizeNametag,
|
|
17189
17376
|
normalizeSdkTokenToStorage,
|