@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.cjs
CHANGED
|
@@ -30,6 +30,125 @@ var __toESM = (mod2, isNodeMode, target) => (target = mod2 != null ? __create(__
|
|
|
30
30
|
));
|
|
31
31
|
var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2);
|
|
32
32
|
|
|
33
|
+
// core/logger.ts
|
|
34
|
+
function getState() {
|
|
35
|
+
const g = globalThis;
|
|
36
|
+
if (!g[LOGGER_KEY]) {
|
|
37
|
+
g[LOGGER_KEY] = { debug: false, tags: {}, handler: null };
|
|
38
|
+
}
|
|
39
|
+
return g[LOGGER_KEY];
|
|
40
|
+
}
|
|
41
|
+
function isEnabled(tag) {
|
|
42
|
+
const state = getState();
|
|
43
|
+
if (tag in state.tags) return state.tags[tag];
|
|
44
|
+
return state.debug;
|
|
45
|
+
}
|
|
46
|
+
var LOGGER_KEY, logger;
|
|
47
|
+
var init_logger = __esm({
|
|
48
|
+
"core/logger.ts"() {
|
|
49
|
+
"use strict";
|
|
50
|
+
LOGGER_KEY = "__sphere_sdk_logger__";
|
|
51
|
+
logger = {
|
|
52
|
+
/**
|
|
53
|
+
* Configure the logger. Can be called multiple times (last write wins).
|
|
54
|
+
* Typically called by createBrowserProviders(), createNodeProviders(), or Sphere.init().
|
|
55
|
+
*/
|
|
56
|
+
configure(config) {
|
|
57
|
+
const state = getState();
|
|
58
|
+
if (config.debug !== void 0) state.debug = config.debug;
|
|
59
|
+
if (config.handler !== void 0) state.handler = config.handler;
|
|
60
|
+
},
|
|
61
|
+
/**
|
|
62
|
+
* Enable/disable debug logging for a specific tag.
|
|
63
|
+
* Per-tag setting overrides the global debug flag.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* logger.setTagDebug('Nostr', true); // enable only Nostr logs
|
|
68
|
+
* logger.setTagDebug('Nostr', false); // disable Nostr logs even if global debug=true
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
setTagDebug(tag, enabled) {
|
|
72
|
+
getState().tags[tag] = enabled;
|
|
73
|
+
},
|
|
74
|
+
/**
|
|
75
|
+
* Clear per-tag override, falling back to global debug flag.
|
|
76
|
+
*/
|
|
77
|
+
clearTagDebug(tag) {
|
|
78
|
+
delete getState().tags[tag];
|
|
79
|
+
},
|
|
80
|
+
/** Returns true if debug mode is enabled for the given tag (or globally). */
|
|
81
|
+
isDebugEnabled(tag) {
|
|
82
|
+
if (tag) return isEnabled(tag);
|
|
83
|
+
return getState().debug;
|
|
84
|
+
},
|
|
85
|
+
/**
|
|
86
|
+
* Debug-level log. Only shown when debug is enabled (globally or for this tag).
|
|
87
|
+
* Use for detailed operational information.
|
|
88
|
+
*/
|
|
89
|
+
debug(tag, message, ...args) {
|
|
90
|
+
if (!isEnabled(tag)) return;
|
|
91
|
+
const state = getState();
|
|
92
|
+
if (state.handler) {
|
|
93
|
+
state.handler("debug", tag, message, ...args);
|
|
94
|
+
} else {
|
|
95
|
+
console.log(`[${tag}]`, message, ...args);
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
/**
|
|
99
|
+
* Warning-level log. ALWAYS shown regardless of debug flag.
|
|
100
|
+
* Use for important but non-critical issues (timeouts, retries, degraded state).
|
|
101
|
+
*/
|
|
102
|
+
warn(tag, message, ...args) {
|
|
103
|
+
const state = getState();
|
|
104
|
+
if (state.handler) {
|
|
105
|
+
state.handler("warn", tag, message, ...args);
|
|
106
|
+
} else {
|
|
107
|
+
console.warn(`[${tag}]`, message, ...args);
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
/**
|
|
111
|
+
* Error-level log. ALWAYS shown regardless of debug flag.
|
|
112
|
+
* Use for critical failures that should never be silenced.
|
|
113
|
+
*/
|
|
114
|
+
error(tag, message, ...args) {
|
|
115
|
+
const state = getState();
|
|
116
|
+
if (state.handler) {
|
|
117
|
+
state.handler("error", tag, message, ...args);
|
|
118
|
+
} else {
|
|
119
|
+
console.error(`[${tag}]`, message, ...args);
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
/** Reset all logger state (debug flag, tags, handler). Primarily for tests. */
|
|
123
|
+
reset() {
|
|
124
|
+
const g = globalThis;
|
|
125
|
+
delete g[LOGGER_KEY];
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// core/errors.ts
|
|
132
|
+
function isSphereError(err) {
|
|
133
|
+
return err instanceof SphereError;
|
|
134
|
+
}
|
|
135
|
+
var SphereError;
|
|
136
|
+
var init_errors = __esm({
|
|
137
|
+
"core/errors.ts"() {
|
|
138
|
+
"use strict";
|
|
139
|
+
SphereError = class extends Error {
|
|
140
|
+
code;
|
|
141
|
+
cause;
|
|
142
|
+
constructor(message, code, cause) {
|
|
143
|
+
super(message);
|
|
144
|
+
this.name = "SphereError";
|
|
145
|
+
this.code = code;
|
|
146
|
+
this.cause = cause;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
33
152
|
// constants.ts
|
|
34
153
|
function getAddressStorageKey(addressId, key) {
|
|
35
154
|
return `${addressId}_${key}`;
|
|
@@ -310,11 +429,11 @@ function bech32Checksum(hrp, data) {
|
|
|
310
429
|
}
|
|
311
430
|
function encodeBech32(hrp, version, program) {
|
|
312
431
|
if (version < 0 || version > 16) {
|
|
313
|
-
throw new
|
|
432
|
+
throw new SphereError("Invalid witness version", "VALIDATION_ERROR");
|
|
314
433
|
}
|
|
315
434
|
const converted = convertBits(Array.from(program), 8, 5, true);
|
|
316
435
|
if (!converted) {
|
|
317
|
-
throw new
|
|
436
|
+
throw new SphereError("Failed to convert bits", "VALIDATION_ERROR");
|
|
318
437
|
}
|
|
319
438
|
const data = [version].concat(converted);
|
|
320
439
|
const checksum = bech32Checksum(hrp, data);
|
|
@@ -367,6 +486,7 @@ var CHARSET, GENERATOR, createBech32;
|
|
|
367
486
|
var init_bech32 = __esm({
|
|
368
487
|
"core/bech32.ts"() {
|
|
369
488
|
"use strict";
|
|
489
|
+
init_errors();
|
|
370
490
|
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
371
491
|
GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
|
|
372
492
|
createBech32 = encodeBech32;
|
|
@@ -379,7 +499,7 @@ function bytesToHex(buf) {
|
|
|
379
499
|
}
|
|
380
500
|
function addressToScriptHash(address) {
|
|
381
501
|
const decoded = decodeBech32(address);
|
|
382
|
-
if (!decoded) throw new
|
|
502
|
+
if (!decoded) throw new SphereError("Invalid bech32 address: " + address, "VALIDATION_ERROR");
|
|
383
503
|
const scriptHex = "0014" + bytesToHex(decoded.data);
|
|
384
504
|
const sha = import_crypto_js.default.SHA256(import_crypto_js.default.enc.Hex.parse(scriptHex)).toString();
|
|
385
505
|
return sha.match(/../g).reverse().join("");
|
|
@@ -389,6 +509,7 @@ var init_addressToScriptHash = __esm({
|
|
|
389
509
|
"l1/addressToScriptHash.ts"() {
|
|
390
510
|
"use strict";
|
|
391
511
|
init_bech32();
|
|
512
|
+
init_errors();
|
|
392
513
|
import_crypto_js = __toESM(require("crypto-js"), 1);
|
|
393
514
|
}
|
|
394
515
|
});
|
|
@@ -449,7 +570,7 @@ function connect(endpoint = DEFAULT_ENDPOINT) {
|
|
|
449
570
|
try {
|
|
450
571
|
ws = new WebSocket(endpoint);
|
|
451
572
|
} catch (err) {
|
|
452
|
-
|
|
573
|
+
logger.error("L1", "WebSocket constructor threw exception:", err);
|
|
453
574
|
isConnecting = false;
|
|
454
575
|
reject(err);
|
|
455
576
|
return;
|
|
@@ -485,7 +606,7 @@ function connect(endpoint = DEFAULT_ENDPOINT) {
|
|
|
485
606
|
return;
|
|
486
607
|
}
|
|
487
608
|
if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
488
|
-
|
|
609
|
+
logger.error("L1", "Max reconnect attempts reached. Giving up.");
|
|
489
610
|
isConnecting = false;
|
|
490
611
|
const error = new Error("Max reconnect attempts reached");
|
|
491
612
|
connectionCallbacks.forEach((cb) => {
|
|
@@ -501,8 +622,9 @@ function connect(endpoint = DEFAULT_ENDPOINT) {
|
|
|
501
622
|
}
|
|
502
623
|
const delay = Math.min(BASE_DELAY * Math.pow(2, reconnectAttempts), MAX_DELAY);
|
|
503
624
|
reconnectAttempts++;
|
|
504
|
-
|
|
505
|
-
|
|
625
|
+
logger.warn(
|
|
626
|
+
"L1",
|
|
627
|
+
`WebSocket closed unexpectedly. Reconnecting in ${delay}ms (attempt ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...`
|
|
506
628
|
);
|
|
507
629
|
setTimeout(() => {
|
|
508
630
|
connect(endpoint).then(() => {
|
|
@@ -519,7 +641,7 @@ function connect(endpoint = DEFAULT_ENDPOINT) {
|
|
|
519
641
|
}, delay);
|
|
520
642
|
};
|
|
521
643
|
ws.onerror = (err) => {
|
|
522
|
-
|
|
644
|
+
logger.error("L1", "WebSocket error:", err);
|
|
523
645
|
};
|
|
524
646
|
ws.onmessage = (msg) => handleMessage(msg);
|
|
525
647
|
});
|
|
@@ -577,7 +699,7 @@ async function getUtxo(address) {
|
|
|
577
699
|
const scripthash = addressToScriptHash(address);
|
|
578
700
|
const result = await rpc("blockchain.scripthash.listunspent", [scripthash]);
|
|
579
701
|
if (!Array.isArray(result)) {
|
|
580
|
-
|
|
702
|
+
logger.warn("L1", "listunspent returned non-array:", result);
|
|
581
703
|
return [];
|
|
582
704
|
}
|
|
583
705
|
return result.map((u) => ({
|
|
@@ -629,7 +751,7 @@ async function getTransactionHistory(address) {
|
|
|
629
751
|
const scriptHash = addressToScriptHash(address);
|
|
630
752
|
const result = await rpc("blockchain.scripthash.get_history", [scriptHash]);
|
|
631
753
|
if (!Array.isArray(result)) {
|
|
632
|
-
|
|
754
|
+
logger.warn("L1", "get_history returned non-array:", result);
|
|
633
755
|
return [];
|
|
634
756
|
}
|
|
635
757
|
return result;
|
|
@@ -645,7 +767,7 @@ async function getCurrentBlockHeight() {
|
|
|
645
767
|
const header = await rpc("blockchain.headers.subscribe", []);
|
|
646
768
|
return header?.height || 0;
|
|
647
769
|
} catch (err) {
|
|
648
|
-
|
|
770
|
+
logger.error("L1", "Error getting current block height:", err);
|
|
649
771
|
return 0;
|
|
650
772
|
}
|
|
651
773
|
}
|
|
@@ -674,6 +796,7 @@ var DEFAULT_ENDPOINT, ws, isConnected, isConnecting, requestId, intentionalClose
|
|
|
674
796
|
var init_network = __esm({
|
|
675
797
|
"l1/network.ts"() {
|
|
676
798
|
"use strict";
|
|
799
|
+
init_logger();
|
|
677
800
|
init_addressToScriptHash();
|
|
678
801
|
init_constants();
|
|
679
802
|
DEFAULT_ENDPOINT = DEFAULT_ELECTRUM_URL;
|
|
@@ -804,6 +927,7 @@ __export(index_exports, {
|
|
|
804
927
|
isPaymentSessionTimedOut: () => isPaymentSessionTimedOut,
|
|
805
928
|
isPhoneNumber: () => import_nostr_js_sdk4.isPhoneNumber,
|
|
806
929
|
isSQLiteDatabase: () => isSQLiteDatabase,
|
|
930
|
+
isSphereError: () => isSphereError,
|
|
807
931
|
isTextWalletEncrypted: () => isTextWalletEncrypted,
|
|
808
932
|
isTokenKey: () => isTokenKey,
|
|
809
933
|
isValidBech32: () => isValidBech32,
|
|
@@ -814,6 +938,7 @@ __export(index_exports, {
|
|
|
814
938
|
isWalletTextFormat: () => isWalletTextFormat,
|
|
815
939
|
keyFromTokenId: () => keyFromTokenId,
|
|
816
940
|
loadSphere: () => loadSphere,
|
|
941
|
+
logger: () => logger,
|
|
817
942
|
mnemonicToSeedSync: () => mnemonicToSeedSync2,
|
|
818
943
|
normalizeNametag: () => import_nostr_js_sdk4.normalizeNametag,
|
|
819
944
|
normalizeSdkTokenToStorage: () => normalizeSdkTokenToStorage,
|
|
@@ -841,7 +966,12 @@ __export(index_exports, {
|
|
|
841
966
|
});
|
|
842
967
|
module.exports = __toCommonJS(index_exports);
|
|
843
968
|
|
|
969
|
+
// core/Sphere.ts
|
|
970
|
+
init_logger();
|
|
971
|
+
init_errors();
|
|
972
|
+
|
|
844
973
|
// modules/payments/L1PaymentsModule.ts
|
|
974
|
+
init_errors();
|
|
845
975
|
init_constants();
|
|
846
976
|
|
|
847
977
|
// l1/index.ts
|
|
@@ -937,6 +1067,7 @@ var bip39 = __toESM(require("bip39"), 1);
|
|
|
937
1067
|
var import_crypto_js2 = __toESM(require("crypto-js"), 1);
|
|
938
1068
|
var import_elliptic = __toESM(require("elliptic"), 1);
|
|
939
1069
|
init_bech32();
|
|
1070
|
+
init_errors();
|
|
940
1071
|
var ec = new import_elliptic.default.ec("secp256k1");
|
|
941
1072
|
var CURVE_ORDER = BigInt(
|
|
942
1073
|
"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
|
|
@@ -960,7 +1091,7 @@ function generateMasterKey(seedHex) {
|
|
|
960
1091
|
const IR = I.substring(64);
|
|
961
1092
|
const masterKeyBigInt = BigInt("0x" + IL);
|
|
962
1093
|
if (masterKeyBigInt === 0n || masterKeyBigInt >= CURVE_ORDER) {
|
|
963
|
-
throw new
|
|
1094
|
+
throw new SphereError("Invalid master key generated", "VALIDATION_ERROR");
|
|
964
1095
|
}
|
|
965
1096
|
return {
|
|
966
1097
|
privateKey: IL,
|
|
@@ -988,11 +1119,11 @@ function deriveChildKey(parentPrivKey, parentChainCode, index) {
|
|
|
988
1119
|
const ilBigInt = BigInt("0x" + IL);
|
|
989
1120
|
const parentKeyBigInt = BigInt("0x" + parentPrivKey);
|
|
990
1121
|
if (ilBigInt >= CURVE_ORDER) {
|
|
991
|
-
throw new
|
|
1122
|
+
throw new SphereError("Invalid key: IL >= curve order", "VALIDATION_ERROR");
|
|
992
1123
|
}
|
|
993
1124
|
const childKeyBigInt = (ilBigInt + parentKeyBigInt) % CURVE_ORDER;
|
|
994
1125
|
if (childKeyBigInt === 0n) {
|
|
995
|
-
throw new
|
|
1126
|
+
throw new SphereError("Invalid key: child key is zero", "VALIDATION_ERROR");
|
|
996
1127
|
}
|
|
997
1128
|
const childPrivKey = childKeyBigInt.toString(16).padStart(64, "0");
|
|
998
1129
|
return {
|
|
@@ -1078,7 +1209,7 @@ function randomBytes(length) {
|
|
|
1078
1209
|
}
|
|
1079
1210
|
function identityFromMnemonicSync(mnemonic, passphrase = "") {
|
|
1080
1211
|
if (!validateMnemonic2(mnemonic)) {
|
|
1081
|
-
throw new
|
|
1212
|
+
throw new SphereError("Invalid mnemonic phrase", "INVALID_IDENTITY");
|
|
1082
1213
|
}
|
|
1083
1214
|
const seedHex = mnemonicToSeedSync2(mnemonic, passphrase);
|
|
1084
1215
|
return generateMasterKey(seedHex);
|
|
@@ -1211,12 +1342,19 @@ function generateHDAddress(masterPriv, chainCode, index) {
|
|
|
1211
1342
|
init_network();
|
|
1212
1343
|
|
|
1213
1344
|
// l1/tx.ts
|
|
1345
|
+
init_logger();
|
|
1346
|
+
init_errors();
|
|
1214
1347
|
init_network();
|
|
1215
1348
|
init_bech32();
|
|
1216
1349
|
var import_crypto_js5 = __toESM(require("crypto-js"), 1);
|
|
1217
1350
|
var import_elliptic2 = __toESM(require("elliptic"), 1);
|
|
1218
1351
|
|
|
1352
|
+
// l1/vestingState.ts
|
|
1353
|
+
init_logger();
|
|
1354
|
+
init_errors();
|
|
1355
|
+
|
|
1219
1356
|
// l1/vesting.ts
|
|
1357
|
+
init_logger();
|
|
1220
1358
|
init_network();
|
|
1221
1359
|
var VESTING_THRESHOLD = 28e4;
|
|
1222
1360
|
var currentBlockHeight = null;
|
|
@@ -1459,7 +1597,7 @@ var VestingClassifier = class {
|
|
|
1459
1597
|
await new Promise((resolve) => {
|
|
1460
1598
|
const req = indexedDB.deleteDatabase(this.dbName);
|
|
1461
1599
|
const timer = setTimeout(() => {
|
|
1462
|
-
|
|
1600
|
+
logger.warn("L1", ` destroy: deleteDatabase timed out for ${this.dbName}`);
|
|
1463
1601
|
resolve();
|
|
1464
1602
|
}, 3e3);
|
|
1465
1603
|
req.onsuccess = () => {
|
|
@@ -1471,7 +1609,7 @@ var VestingClassifier = class {
|
|
|
1471
1609
|
resolve();
|
|
1472
1610
|
};
|
|
1473
1611
|
req.onblocked = () => {
|
|
1474
|
-
|
|
1612
|
+
logger.warn("L1", ` destroy: deleteDatabase blocked for ${this.dbName}, waiting...`);
|
|
1475
1613
|
};
|
|
1476
1614
|
});
|
|
1477
1615
|
}
|
|
@@ -1489,7 +1627,7 @@ var VestingStateManager = class {
|
|
|
1489
1627
|
*/
|
|
1490
1628
|
setMode(mode) {
|
|
1491
1629
|
if (!["all", "vested", "unvested"].includes(mode)) {
|
|
1492
|
-
throw new
|
|
1630
|
+
throw new SphereError(`Invalid vesting mode: ${mode}`, "VALIDATION_ERROR");
|
|
1493
1631
|
}
|
|
1494
1632
|
this.currentMode = mode;
|
|
1495
1633
|
}
|
|
@@ -1526,10 +1664,10 @@ var VestingStateManager = class {
|
|
|
1526
1664
|
}
|
|
1527
1665
|
});
|
|
1528
1666
|
if (result.errors.length > 0) {
|
|
1529
|
-
|
|
1667
|
+
logger.warn("L1", `Vesting classification errors: ${result.errors.length}`);
|
|
1530
1668
|
result.errors.slice(0, 5).forEach((err) => {
|
|
1531
1669
|
const txHash = err.utxo.tx_hash || err.utxo.txid;
|
|
1532
|
-
|
|
1670
|
+
logger.warn("L1", ` ${txHash}: ${err.error}`);
|
|
1533
1671
|
});
|
|
1534
1672
|
}
|
|
1535
1673
|
} finally {
|
|
@@ -1606,6 +1744,7 @@ var VestingStateManager = class {
|
|
|
1606
1744
|
var vestingState = new VestingStateManager();
|
|
1607
1745
|
|
|
1608
1746
|
// l1/addressHelpers.ts
|
|
1747
|
+
init_errors();
|
|
1609
1748
|
var WalletAddressHelper = class {
|
|
1610
1749
|
/**
|
|
1611
1750
|
* Find address by BIP32 derivation path
|
|
@@ -1651,16 +1790,17 @@ var WalletAddressHelper = class {
|
|
|
1651
1790
|
*/
|
|
1652
1791
|
static add(wallet, newAddress) {
|
|
1653
1792
|
if (!newAddress.path) {
|
|
1654
|
-
throw new
|
|
1793
|
+
throw new SphereError("Cannot add address without a path", "INVALID_CONFIG");
|
|
1655
1794
|
}
|
|
1656
1795
|
const existing = this.findByPath(wallet, newAddress.path);
|
|
1657
1796
|
if (existing) {
|
|
1658
1797
|
if (existing.address !== newAddress.address) {
|
|
1659
|
-
throw new
|
|
1798
|
+
throw new SphereError(
|
|
1660
1799
|
`CRITICAL: Attempted to overwrite address for path ${newAddress.path}
|
|
1661
1800
|
Existing: ${existing.address}
|
|
1662
1801
|
New: ${newAddress.address}
|
|
1663
|
-
This indicates master key corruption or derivation logic error
|
|
1802
|
+
This indicates master key corruption or derivation logic error.`,
|
|
1803
|
+
"INVALID_CONFIG"
|
|
1664
1804
|
);
|
|
1665
1805
|
}
|
|
1666
1806
|
return wallet;
|
|
@@ -1719,9 +1859,10 @@ This indicates master key corruption or derivation logic error.`
|
|
|
1719
1859
|
const uniquePaths = new Set(paths);
|
|
1720
1860
|
if (paths.length !== uniquePaths.size) {
|
|
1721
1861
|
const duplicates = paths.filter((p, i) => paths.indexOf(p) !== i);
|
|
1722
|
-
throw new
|
|
1862
|
+
throw new SphereError(
|
|
1723
1863
|
`CRITICAL: Wallet has duplicate paths: ${duplicates.join(", ")}
|
|
1724
|
-
This indicates data corruption. Please restore from backup
|
|
1864
|
+
This indicates data corruption. Please restore from backup.`,
|
|
1865
|
+
"INVALID_CONFIG"
|
|
1725
1866
|
);
|
|
1726
1867
|
}
|
|
1727
1868
|
}
|
|
@@ -1753,11 +1894,11 @@ var DUST = 546;
|
|
|
1753
1894
|
var SAT = 1e8;
|
|
1754
1895
|
function createScriptPubKey(address) {
|
|
1755
1896
|
if (!address || typeof address !== "string") {
|
|
1756
|
-
throw new
|
|
1897
|
+
throw new SphereError("Invalid address: must be a string", "VALIDATION_ERROR");
|
|
1757
1898
|
}
|
|
1758
1899
|
const decoded = decodeBech32(address);
|
|
1759
1900
|
if (!decoded) {
|
|
1760
|
-
throw new
|
|
1901
|
+
throw new SphereError("Invalid bech32 address: " + address, "VALIDATION_ERROR");
|
|
1761
1902
|
}
|
|
1762
1903
|
const dataHex = Array.from(decoded.data).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
1763
1904
|
return "0014" + dataHex;
|
|
@@ -1881,7 +2022,7 @@ function createAndSignTransaction(wallet, txPlan) {
|
|
|
1881
2022
|
privateKeyHex = wallet.masterPrivateKey;
|
|
1882
2023
|
}
|
|
1883
2024
|
if (!privateKeyHex) {
|
|
1884
|
-
throw new
|
|
2025
|
+
throw new SphereError("No private key available for address: " + fromAddress, "INVALID_CONFIG");
|
|
1885
2026
|
}
|
|
1886
2027
|
const keyPair = ec2.keyFromPrivate(privateKeyHex, "hex");
|
|
1887
2028
|
const publicKey = keyPair.getPublic(true, "hex");
|
|
@@ -1980,7 +2121,7 @@ function collectUtxosForAmount(utxoList, amountSats, recipientAddress, senderAdd
|
|
|
1980
2121
|
}
|
|
1981
2122
|
async function createTransactionPlan(wallet, toAddress, amountAlpha, fromAddress) {
|
|
1982
2123
|
if (!decodeBech32(toAddress)) {
|
|
1983
|
-
throw new
|
|
2124
|
+
throw new SphereError("Invalid recipient address", "INVALID_RECIPIENT");
|
|
1984
2125
|
}
|
|
1985
2126
|
const defaultAddr = WalletAddressHelper.getDefault(wallet);
|
|
1986
2127
|
const senderAddress = fromAddress || defaultAddr.address;
|
|
@@ -1989,21 +2130,21 @@ async function createTransactionPlan(wallet, toAddress, amountAlpha, fromAddress
|
|
|
1989
2130
|
const currentMode = vestingState.getMode();
|
|
1990
2131
|
if (vestingState.hasClassifiedData(senderAddress)) {
|
|
1991
2132
|
utxos = vestingState.getFilteredUtxos(senderAddress);
|
|
1992
|
-
|
|
2133
|
+
logger.debug("L1", `Using ${utxos.length} ${currentMode} UTXOs`);
|
|
1993
2134
|
} else {
|
|
1994
2135
|
utxos = await getUtxo(senderAddress);
|
|
1995
|
-
|
|
2136
|
+
logger.debug("L1", `Using ${utxos.length} UTXOs (vesting not classified yet)`);
|
|
1996
2137
|
}
|
|
1997
2138
|
if (!Array.isArray(utxos) || utxos.length === 0) {
|
|
1998
2139
|
const modeText = currentMode !== "all" ? ` (${currentMode} coins)` : "";
|
|
1999
|
-
throw new
|
|
2140
|
+
throw new SphereError(`No UTXOs available${modeText} for address: ` + senderAddress, "INSUFFICIENT_BALANCE");
|
|
2000
2141
|
}
|
|
2001
2142
|
return collectUtxosForAmount(utxos, amountSats, toAddress, senderAddress);
|
|
2002
2143
|
}
|
|
2003
2144
|
async function sendAlpha(wallet, toAddress, amountAlpha, fromAddress) {
|
|
2004
2145
|
const plan = await createTransactionPlan(wallet, toAddress, amountAlpha, fromAddress);
|
|
2005
2146
|
if (!plan.success) {
|
|
2006
|
-
throw new
|
|
2147
|
+
throw new SphereError(plan.error || "Transaction planning failed", "TRANSFER_FAILED");
|
|
2007
2148
|
}
|
|
2008
2149
|
const results = [];
|
|
2009
2150
|
for (const tx of plan.transactions) {
|
|
@@ -2070,7 +2211,7 @@ var L1PaymentsModule = class {
|
|
|
2070
2211
|
*/
|
|
2071
2212
|
async ensureConnected() {
|
|
2072
2213
|
if (this._disabled) {
|
|
2073
|
-
throw new
|
|
2214
|
+
throw new SphereError("L1 provider is disabled", "NOT_INITIALIZED");
|
|
2074
2215
|
}
|
|
2075
2216
|
if (!isWebSocketConnected() && this._config.electrumUrl) {
|
|
2076
2217
|
await connect(this._config.electrumUrl);
|
|
@@ -2125,8 +2266,9 @@ var L1PaymentsModule = class {
|
|
|
2125
2266
|
const l1Address = await this.resolveNametagToL1Address(recipient);
|
|
2126
2267
|
return l1Address;
|
|
2127
2268
|
} catch {
|
|
2128
|
-
throw new
|
|
2129
|
-
`Recipient "${recipient}" is not a valid nametag or L1 address. Use @nametag for explicit nametag or a valid alpha1... address
|
|
2269
|
+
throw new SphereError(
|
|
2270
|
+
`Recipient "${recipient}" is not a valid nametag or L1 address. Use @nametag for explicit nametag or a valid alpha1... address.`,
|
|
2271
|
+
"INVALID_RECIPIENT"
|
|
2130
2272
|
);
|
|
2131
2273
|
}
|
|
2132
2274
|
}
|
|
@@ -2135,15 +2277,16 @@ var L1PaymentsModule = class {
|
|
|
2135
2277
|
*/
|
|
2136
2278
|
async resolveNametagToL1Address(nametag) {
|
|
2137
2279
|
if (!this._transport?.resolve) {
|
|
2138
|
-
throw new
|
|
2280
|
+
throw new SphereError("Transport provider does not support resolution", "TRANSPORT_ERROR");
|
|
2139
2281
|
}
|
|
2140
2282
|
const info = await this._transport.resolve(nametag);
|
|
2141
2283
|
if (!info) {
|
|
2142
|
-
throw new
|
|
2284
|
+
throw new SphereError(`Nametag not found: ${nametag}`, "INVALID_RECIPIENT");
|
|
2143
2285
|
}
|
|
2144
2286
|
if (!info.l1Address) {
|
|
2145
|
-
throw new
|
|
2146
|
-
`Nametag @${nametag} does not have L1 address information. The owner needs to update their nametag registration
|
|
2287
|
+
throw new SphereError(
|
|
2288
|
+
`Nametag @${nametag} does not have L1 address information. The owner needs to update their nametag registration.`,
|
|
2289
|
+
"INVALID_RECIPIENT"
|
|
2147
2290
|
);
|
|
2148
2291
|
}
|
|
2149
2292
|
return info.l1Address;
|
|
@@ -2413,7 +2556,7 @@ var L1PaymentsModule = class {
|
|
|
2413
2556
|
}
|
|
2414
2557
|
ensureInitialized() {
|
|
2415
2558
|
if (!this._initialized) {
|
|
2416
|
-
throw new
|
|
2559
|
+
throw new SphereError("L1PaymentsModule not initialized", "NOT_INITIALIZED");
|
|
2417
2560
|
}
|
|
2418
2561
|
}
|
|
2419
2562
|
_getWatchedAddresses() {
|
|
@@ -2438,6 +2581,7 @@ function createL1PaymentsModule(config) {
|
|
|
2438
2581
|
}
|
|
2439
2582
|
|
|
2440
2583
|
// modules/payments/TokenSplitCalculator.ts
|
|
2584
|
+
init_logger();
|
|
2441
2585
|
var import_Token = require("@unicitylabs/state-transition-sdk/lib/token/Token");
|
|
2442
2586
|
var import_CoinId = require("@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId");
|
|
2443
2587
|
var TokenSplitCalculator = class {
|
|
@@ -2460,7 +2604,7 @@ var TokenSplitCalculator = class {
|
|
|
2460
2604
|
const sdkToken = await import_Token.Token.fromJSON(parsed);
|
|
2461
2605
|
const realAmount = this.getTokenBalance(sdkToken, targetCoinIdHex);
|
|
2462
2606
|
if (realAmount <= 0n) {
|
|
2463
|
-
|
|
2607
|
+
logger.warn("TokenSplit", `Token ${t.id} has 0 balance for coinId ${targetCoinIdHex}`);
|
|
2464
2608
|
continue;
|
|
2465
2609
|
}
|
|
2466
2610
|
candidates.push({
|
|
@@ -2469,14 +2613,15 @@ var TokenSplitCalculator = class {
|
|
|
2469
2613
|
uiToken: t
|
|
2470
2614
|
});
|
|
2471
2615
|
} catch (e) {
|
|
2472
|
-
|
|
2616
|
+
logger.warn("TokenSplit", "Failed to parse token", t.id, e);
|
|
2473
2617
|
}
|
|
2474
2618
|
}
|
|
2475
2619
|
candidates.sort((a, b) => a.amount < b.amount ? -1 : 1);
|
|
2476
2620
|
const totalAvailable = candidates.reduce((sum, t) => sum + t.amount, 0n);
|
|
2477
2621
|
if (totalAvailable < targetAmount) {
|
|
2478
|
-
|
|
2479
|
-
|
|
2622
|
+
logger.error(
|
|
2623
|
+
"TokenSplit",
|
|
2624
|
+
`Insufficient funds. Available: ${totalAvailable}, Required: ${targetAmount}`
|
|
2480
2625
|
);
|
|
2481
2626
|
return null;
|
|
2482
2627
|
}
|
|
@@ -2574,6 +2719,8 @@ var TokenSplitCalculator = class {
|
|
|
2574
2719
|
};
|
|
2575
2720
|
|
|
2576
2721
|
// modules/payments/TokenSplitExecutor.ts
|
|
2722
|
+
init_logger();
|
|
2723
|
+
init_errors();
|
|
2577
2724
|
var import_Token2 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
|
|
2578
2725
|
var import_TokenId = require("@unicitylabs/state-transition-sdk/lib/token/TokenId");
|
|
2579
2726
|
var import_TokenState = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
|
|
@@ -2613,7 +2760,7 @@ var TokenSplitExecutor = class {
|
|
|
2613
2760
|
}
|
|
2614
2761
|
async executeSplit(tokenToSplit, splitAmount, remainderAmount, coinIdHex, recipientAddress) {
|
|
2615
2762
|
const tokenIdHex = toHex(tokenToSplit.id.bytes);
|
|
2616
|
-
|
|
2763
|
+
logger.debug("TokenSplit", `Splitting token ${tokenIdHex.slice(0, 8)}...`);
|
|
2617
2764
|
const coinId = new import_CoinId2.CoinId(fromHex(coinIdHex));
|
|
2618
2765
|
const seedString = `${tokenIdHex}_${splitAmount.toString()}_${remainderAmount.toString()}`;
|
|
2619
2766
|
const recipientTokenId = new import_TokenId.TokenId(await sha2562(seedString));
|
|
@@ -2633,23 +2780,23 @@ var TokenSplitExecutor = class {
|
|
|
2633
2780
|
const coinDataB = import_TokenCoinData.TokenCoinData.create([[coinId, remainderAmount]]);
|
|
2634
2781
|
builder.createToken(senderTokenId, tokenToSplit.type, new Uint8Array(0), coinDataB, senderAddress, senderSalt, null);
|
|
2635
2782
|
const split = await builder.build(tokenToSplit);
|
|
2636
|
-
|
|
2783
|
+
logger.debug("TokenSplit", "Step 1: Burning original token...");
|
|
2637
2784
|
const burnSalt = await sha2562(seedString + "_burn_salt");
|
|
2638
2785
|
const burnCommitment = await split.createBurnCommitment(burnSalt, this.signingService);
|
|
2639
2786
|
const burnResponse = await this.client.submitTransferCommitment(burnCommitment);
|
|
2640
2787
|
if (burnResponse.status !== "SUCCESS" && burnResponse.status !== "REQUEST_ID_EXISTS") {
|
|
2641
|
-
throw new
|
|
2788
|
+
throw new SphereError(`Burn failed: ${burnResponse.status}`, "TRANSFER_FAILED");
|
|
2642
2789
|
}
|
|
2643
2790
|
const burnInclusionProof = await (0, import_InclusionProofUtils.waitInclusionProof)(this.trustBase, this.client, burnCommitment);
|
|
2644
2791
|
const burnTransaction = burnCommitment.toTransaction(burnInclusionProof);
|
|
2645
|
-
|
|
2646
|
-
|
|
2792
|
+
logger.debug("TokenSplit", "Original token burned.");
|
|
2793
|
+
logger.debug("TokenSplit", "Step 2: Minting split tokens...");
|
|
2647
2794
|
const mintCommitments = await split.createSplitMintCommitments(this.trustBase, burnTransaction);
|
|
2648
2795
|
const mintedTokensInfo = [];
|
|
2649
2796
|
for (const commitment of mintCommitments) {
|
|
2650
2797
|
const res = await this.client.submitMintCommitment(commitment);
|
|
2651
2798
|
if (res.status !== "SUCCESS" && res.status !== "REQUEST_ID_EXISTS") {
|
|
2652
|
-
throw new
|
|
2799
|
+
throw new SphereError(`Mint split token failed: ${res.status}`, "TRANSFER_FAILED");
|
|
2653
2800
|
}
|
|
2654
2801
|
const proof = await (0, import_InclusionProofUtils.waitInclusionProof)(this.trustBase, this.client, commitment);
|
|
2655
2802
|
const commTokenIdHex = toHex(commitment.transactionData.tokenId.bytes);
|
|
@@ -2662,7 +2809,7 @@ var TokenSplitExecutor = class {
|
|
|
2662
2809
|
salt: commitment.transactionData.salt
|
|
2663
2810
|
});
|
|
2664
2811
|
}
|
|
2665
|
-
|
|
2812
|
+
logger.debug("TokenSplit", "Split tokens minted.");
|
|
2666
2813
|
const recipientInfo = mintedTokensInfo.find((t) => t.isForRecipient);
|
|
2667
2814
|
const senderInfo = mintedTokensInfo.find((t) => !t.isForRecipient);
|
|
2668
2815
|
const createToken = async (info, label) => {
|
|
@@ -2670,12 +2817,12 @@ var TokenSplitExecutor = class {
|
|
|
2670
2817
|
const state = new import_TokenState.TokenState(predicate, null);
|
|
2671
2818
|
const token = await import_Token2.Token.mint(this.trustBase, state, info.commitment.toTransaction(info.inclusionProof));
|
|
2672
2819
|
const verification = await token.verify(this.trustBase);
|
|
2673
|
-
if (!verification.isSuccessful) throw new
|
|
2820
|
+
if (!verification.isSuccessful) throw new SphereError(`Token verification failed: ${label}`, "TRANSFER_FAILED");
|
|
2674
2821
|
return token;
|
|
2675
2822
|
};
|
|
2676
2823
|
const recipientTokenBeforeTransfer = await createToken(recipientInfo, "Recipient");
|
|
2677
2824
|
const senderToken = await createToken(senderInfo, "Sender");
|
|
2678
|
-
|
|
2825
|
+
logger.debug("TokenSplit", "Step 3: Transferring to recipient...");
|
|
2679
2826
|
const transferSalt = await sha2562(seedString + "_transfer_salt");
|
|
2680
2827
|
const transferCommitment = await import_TransferCommitment.TransferCommitment.create(
|
|
2681
2828
|
recipientTokenBeforeTransfer,
|
|
@@ -2687,11 +2834,11 @@ var TokenSplitExecutor = class {
|
|
|
2687
2834
|
);
|
|
2688
2835
|
const transferRes = await this.client.submitTransferCommitment(transferCommitment);
|
|
2689
2836
|
if (transferRes.status !== "SUCCESS" && transferRes.status !== "REQUEST_ID_EXISTS") {
|
|
2690
|
-
throw new
|
|
2837
|
+
throw new SphereError(`Transfer failed: ${transferRes.status}`, "TRANSFER_FAILED");
|
|
2691
2838
|
}
|
|
2692
2839
|
const transferProof = await (0, import_InclusionProofUtils.waitInclusionProof)(this.trustBase, this.client, transferCommitment);
|
|
2693
2840
|
const transferTx = transferCommitment.toTransaction(transferProof);
|
|
2694
|
-
|
|
2841
|
+
logger.debug("TokenSplit", "Split transfer complete!");
|
|
2695
2842
|
return {
|
|
2696
2843
|
tokenForRecipient: recipientTokenBeforeTransfer,
|
|
2697
2844
|
tokenForSender: senderToken,
|
|
@@ -2701,6 +2848,7 @@ var TokenSplitExecutor = class {
|
|
|
2701
2848
|
};
|
|
2702
2849
|
|
|
2703
2850
|
// modules/payments/NametagMinter.ts
|
|
2851
|
+
init_logger();
|
|
2704
2852
|
var import_Token3 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
|
|
2705
2853
|
var import_TokenId2 = require("@unicitylabs/state-transition-sdk/lib/token/TokenId");
|
|
2706
2854
|
var import_TokenType = require("@unicitylabs/state-transition-sdk/lib/token/TokenType");
|
|
@@ -2725,10 +2873,8 @@ var NametagMinter = class {
|
|
|
2725
2873
|
this.skipVerification = config.skipVerification ?? false;
|
|
2726
2874
|
this.debug = config.debug ?? false;
|
|
2727
2875
|
}
|
|
2728
|
-
log(...args) {
|
|
2729
|
-
|
|
2730
|
-
console.log("[NametagMinter]", ...args);
|
|
2731
|
-
}
|
|
2876
|
+
log(message, ...args) {
|
|
2877
|
+
logger.debug("NametagMinter", message, ...args);
|
|
2732
2878
|
}
|
|
2733
2879
|
/**
|
|
2734
2880
|
* Check if a nametag is available (not already minted)
|
|
@@ -2915,6 +3061,7 @@ function isValidTokenId(tokenId) {
|
|
|
2915
3061
|
}
|
|
2916
3062
|
|
|
2917
3063
|
// registry/TokenRegistry.ts
|
|
3064
|
+
init_logger();
|
|
2918
3065
|
init_constants();
|
|
2919
3066
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
2920
3067
|
var TokenRegistry = class _TokenRegistry {
|
|
@@ -3136,14 +3283,12 @@ var TokenRegistry = class _TokenRegistry {
|
|
|
3136
3283
|
clearTimeout(timer);
|
|
3137
3284
|
}
|
|
3138
3285
|
if (!response.ok) {
|
|
3139
|
-
|
|
3140
|
-
`[TokenRegistry] Remote fetch failed: HTTP ${response.status} ${response.statusText}`
|
|
3141
|
-
);
|
|
3286
|
+
logger.warn("TokenRegistry", `Remote fetch failed: HTTP ${response.status} ${response.statusText}`);
|
|
3142
3287
|
return false;
|
|
3143
3288
|
}
|
|
3144
3289
|
const data = await response.json();
|
|
3145
3290
|
if (!this.isValidDefinitionsArray(data)) {
|
|
3146
|
-
|
|
3291
|
+
logger.warn("TokenRegistry", "Remote data is not a valid token definitions array");
|
|
3147
3292
|
return false;
|
|
3148
3293
|
}
|
|
3149
3294
|
const definitions = data;
|
|
@@ -3153,7 +3298,7 @@ var TokenRegistry = class _TokenRegistry {
|
|
|
3153
3298
|
return true;
|
|
3154
3299
|
} catch (error) {
|
|
3155
3300
|
const message = error instanceof Error ? error.message : String(error);
|
|
3156
|
-
|
|
3301
|
+
logger.warn("TokenRegistry", `Remote refresh failed: ${message}`);
|
|
3157
3302
|
return false;
|
|
3158
3303
|
}
|
|
3159
3304
|
}
|
|
@@ -3715,7 +3860,13 @@ function countCommittedTransactions(token) {
|
|
|
3715
3860
|
}
|
|
3716
3861
|
}
|
|
3717
3862
|
|
|
3863
|
+
// modules/payments/PaymentsModule.ts
|
|
3864
|
+
init_logger();
|
|
3865
|
+
init_errors();
|
|
3866
|
+
|
|
3718
3867
|
// modules/payments/InstantSplitExecutor.ts
|
|
3868
|
+
init_logger();
|
|
3869
|
+
init_errors();
|
|
3719
3870
|
var import_Token4 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
|
|
3720
3871
|
var import_TokenId3 = require("@unicitylabs/state-transition-sdk/lib/token/TokenId");
|
|
3721
3872
|
var import_TokenState3 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
|
|
@@ -3771,7 +3922,7 @@ var InstantSplitExecutor = class {
|
|
|
3771
3922
|
async buildSplitBundle(tokenToSplit, splitAmount, remainderAmount, coinIdHex, recipientAddress, options) {
|
|
3772
3923
|
const splitGroupId = crypto.randomUUID();
|
|
3773
3924
|
const tokenIdHex = toHex2(tokenToSplit.id.bytes);
|
|
3774
|
-
|
|
3925
|
+
logger.debug("InstantSplit", `Building V5 bundle for token ${tokenIdHex.slice(0, 8)}...`);
|
|
3775
3926
|
const coinId = new import_CoinId3.CoinId(fromHex2(coinIdHex));
|
|
3776
3927
|
const seedString = `${tokenIdHex}_${splitAmount.toString()}_${remainderAmount.toString()}_${Date.now()}`;
|
|
3777
3928
|
const recipientTokenId = new import_TokenId3.TokenId(await sha2563(seedString));
|
|
@@ -3808,19 +3959,19 @@ var InstantSplitExecutor = class {
|
|
|
3808
3959
|
null
|
|
3809
3960
|
);
|
|
3810
3961
|
const split = await builder.build(tokenToSplit);
|
|
3811
|
-
|
|
3962
|
+
logger.debug("InstantSplit", "Step 1: Creating and submitting burn...");
|
|
3812
3963
|
const burnSalt = await sha2563(seedString + "_burn_salt");
|
|
3813
3964
|
const burnCommitment = await split.createBurnCommitment(burnSalt, this.signingService);
|
|
3814
3965
|
const burnResponse = await this.client.submitTransferCommitment(burnCommitment);
|
|
3815
3966
|
if (burnResponse.status !== "SUCCESS" && burnResponse.status !== "REQUEST_ID_EXISTS") {
|
|
3816
|
-
throw new
|
|
3967
|
+
throw new SphereError(`Burn submission failed: ${burnResponse.status}`, "TRANSFER_FAILED");
|
|
3817
3968
|
}
|
|
3818
|
-
|
|
3969
|
+
logger.debug("InstantSplit", "Step 2: Waiting for burn proof...");
|
|
3819
3970
|
const burnProof = this.devMode ? await this.waitInclusionProofWithDevBypass(burnCommitment, options?.burnProofTimeoutMs) : await (0, import_InclusionProofUtils3.waitInclusionProof)(this.trustBase, this.client, burnCommitment);
|
|
3820
3971
|
const burnTransaction = burnCommitment.toTransaction(burnProof);
|
|
3821
|
-
|
|
3972
|
+
logger.debug("InstantSplit", "Burn proof received");
|
|
3822
3973
|
options?.onBurnCompleted?.(JSON.stringify(burnTransaction.toJSON()));
|
|
3823
|
-
|
|
3974
|
+
logger.debug("InstantSplit", "Step 3: Creating mint commitments...");
|
|
3824
3975
|
const mintCommitments = await split.createSplitMintCommitments(this.trustBase, burnTransaction);
|
|
3825
3976
|
const recipientIdHex = toHex2(recipientTokenId.bytes);
|
|
3826
3977
|
const senderIdHex = toHex2(senderTokenId.bytes);
|
|
@@ -3831,9 +3982,9 @@ var InstantSplitExecutor = class {
|
|
|
3831
3982
|
(c) => toHex2(c.transactionData.tokenId.bytes) === senderIdHex
|
|
3832
3983
|
);
|
|
3833
3984
|
if (!recipientMintCommitment || !senderMintCommitment) {
|
|
3834
|
-
throw new
|
|
3985
|
+
throw new SphereError("Failed to find expected mint commitments", "TRANSFER_FAILED");
|
|
3835
3986
|
}
|
|
3836
|
-
|
|
3987
|
+
logger.debug("InstantSplit", "Step 4: Creating transfer commitment...");
|
|
3837
3988
|
const transferSalt = await sha2563(seedString + "_transfer_salt");
|
|
3838
3989
|
const transferCommitment = await this.createTransferCommitmentFromMintData(
|
|
3839
3990
|
recipientMintCommitment.transactionData,
|
|
@@ -3849,7 +4000,7 @@ var InstantSplitExecutor = class {
|
|
|
3849
4000
|
recipientSalt
|
|
3850
4001
|
);
|
|
3851
4002
|
const mintedState = new import_TokenState3.TokenState(mintedPredicate, null);
|
|
3852
|
-
|
|
4003
|
+
logger.debug("InstantSplit", "Step 5: Packaging V5 bundle...");
|
|
3853
4004
|
const senderPubkey = toHex2(this.signingService.publicKey);
|
|
3854
4005
|
let nametagTokenJson;
|
|
3855
4006
|
const recipientAddressStr = recipientAddress.toString();
|
|
@@ -3921,7 +4072,7 @@ var InstantSplitExecutor = class {
|
|
|
3921
4072
|
recipientAddress,
|
|
3922
4073
|
options
|
|
3923
4074
|
);
|
|
3924
|
-
|
|
4075
|
+
logger.debug("InstantSplit", "Sending via transport...");
|
|
3925
4076
|
const senderPubkey = toHex2(this.signingService.publicKey);
|
|
3926
4077
|
const nostrEventId = await transport.sendTokenTransfer(recipientPubkey, {
|
|
3927
4078
|
token: JSON.stringify(buildResult.bundle),
|
|
@@ -3933,7 +4084,7 @@ var InstantSplitExecutor = class {
|
|
|
3933
4084
|
}
|
|
3934
4085
|
});
|
|
3935
4086
|
const criticalPathDuration = performance.now() - startTime;
|
|
3936
|
-
|
|
4087
|
+
logger.debug("InstantSplit", `V5 complete in ${criticalPathDuration.toFixed(0)}ms`);
|
|
3937
4088
|
options?.onNostrDelivered?.(nostrEventId);
|
|
3938
4089
|
const backgroundPromise = buildResult.startBackground();
|
|
3939
4090
|
return {
|
|
@@ -3947,7 +4098,7 @@ var InstantSplitExecutor = class {
|
|
|
3947
4098
|
} catch (error) {
|
|
3948
4099
|
const duration = performance.now() - startTime;
|
|
3949
4100
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3950
|
-
|
|
4101
|
+
logger.error("InstantSplit", `Failed after ${duration.toFixed(0)}ms:`, error);
|
|
3951
4102
|
return {
|
|
3952
4103
|
success: false,
|
|
3953
4104
|
criticalPathDurationMs: duration,
|
|
@@ -3996,7 +4147,7 @@ var InstantSplitExecutor = class {
|
|
|
3996
4147
|
* Then waits for sender's mint proof, reconstructs change token, and saves it.
|
|
3997
4148
|
*/
|
|
3998
4149
|
submitBackgroundV5(senderMintCommitment, recipientMintCommitment, transferCommitment, context) {
|
|
3999
|
-
|
|
4150
|
+
logger.debug("InstantSplit", "Background: Starting parallel mint submission...");
|
|
4000
4151
|
const startTime = performance.now();
|
|
4001
4152
|
const submissions = Promise.all([
|
|
4002
4153
|
this.client.submitMintCommitment(senderMintCommitment).then((res) => ({ type: "senderMint", status: res.status })).catch((err) => ({ type: "senderMint", status: "ERROR", error: err })),
|
|
@@ -4005,14 +4156,14 @@ var InstantSplitExecutor = class {
|
|
|
4005
4156
|
]);
|
|
4006
4157
|
return submissions.then(async (results) => {
|
|
4007
4158
|
const submitDuration = performance.now() - startTime;
|
|
4008
|
-
|
|
4159
|
+
logger.debug("InstantSplit", `Background: Submissions complete in ${submitDuration.toFixed(0)}ms`);
|
|
4009
4160
|
context.onProgress?.({
|
|
4010
4161
|
stage: "MINTS_SUBMITTED",
|
|
4011
4162
|
message: `All commitments submitted in ${submitDuration.toFixed(0)}ms`
|
|
4012
4163
|
});
|
|
4013
4164
|
const senderMintResult = results.find((r) => r.type === "senderMint");
|
|
4014
4165
|
if (senderMintResult?.status !== "SUCCESS" && senderMintResult?.status !== "REQUEST_ID_EXISTS") {
|
|
4015
|
-
|
|
4166
|
+
logger.error("InstantSplit", "Background: Sender mint failed - cannot save change token");
|
|
4016
4167
|
context.onProgress?.({
|
|
4017
4168
|
stage: "FAILED",
|
|
4018
4169
|
message: "Sender mint submission failed",
|
|
@@ -4020,12 +4171,12 @@ var InstantSplitExecutor = class {
|
|
|
4020
4171
|
});
|
|
4021
4172
|
return;
|
|
4022
4173
|
}
|
|
4023
|
-
|
|
4174
|
+
logger.debug("InstantSplit", "Background: Waiting for sender mint proof...");
|
|
4024
4175
|
const proofStartTime = performance.now();
|
|
4025
4176
|
try {
|
|
4026
4177
|
const senderMintProof = this.devMode ? await this.waitInclusionProofWithDevBypass(senderMintCommitment) : await (0, import_InclusionProofUtils3.waitInclusionProof)(this.trustBase, this.client, senderMintCommitment);
|
|
4027
4178
|
const proofDuration = performance.now() - proofStartTime;
|
|
4028
|
-
|
|
4179
|
+
logger.debug("InstantSplit", `Background: Sender mint proof received in ${proofDuration.toFixed(0)}ms`);
|
|
4029
4180
|
context.onProgress?.({
|
|
4030
4181
|
stage: "MINTS_PROVEN",
|
|
4031
4182
|
message: `Mint proof received in ${proofDuration.toFixed(0)}ms`
|
|
@@ -4043,38 +4194,38 @@ var InstantSplitExecutor = class {
|
|
|
4043
4194
|
if (!this.devMode) {
|
|
4044
4195
|
const verification = await changeToken.verify(this.trustBase);
|
|
4045
4196
|
if (!verification.isSuccessful) {
|
|
4046
|
-
throw new
|
|
4197
|
+
throw new SphereError("Change token verification failed", "TRANSFER_FAILED");
|
|
4047
4198
|
}
|
|
4048
4199
|
}
|
|
4049
|
-
|
|
4200
|
+
logger.debug("InstantSplit", "Background: Change token created");
|
|
4050
4201
|
context.onProgress?.({
|
|
4051
4202
|
stage: "CHANGE_TOKEN_SAVED",
|
|
4052
4203
|
message: "Change token created and verified"
|
|
4053
4204
|
});
|
|
4054
4205
|
if (context.onChangeTokenCreated) {
|
|
4055
4206
|
await context.onChangeTokenCreated(changeToken);
|
|
4056
|
-
|
|
4207
|
+
logger.debug("InstantSplit", "Background: Change token saved");
|
|
4057
4208
|
}
|
|
4058
4209
|
if (context.onStorageSync) {
|
|
4059
4210
|
try {
|
|
4060
4211
|
const syncSuccess = await context.onStorageSync();
|
|
4061
|
-
|
|
4212
|
+
logger.debug("InstantSplit", `Background: Storage sync ${syncSuccess ? "completed" : "deferred"}`);
|
|
4062
4213
|
context.onProgress?.({
|
|
4063
4214
|
stage: "STORAGE_SYNCED",
|
|
4064
4215
|
message: syncSuccess ? "Storage synchronized" : "Sync deferred"
|
|
4065
4216
|
});
|
|
4066
4217
|
} catch (syncError) {
|
|
4067
|
-
|
|
4218
|
+
logger.warn("InstantSplit", "Background: Storage sync error:", syncError);
|
|
4068
4219
|
}
|
|
4069
4220
|
}
|
|
4070
4221
|
const totalDuration = performance.now() - startTime;
|
|
4071
|
-
|
|
4222
|
+
logger.debug("InstantSplit", `Background: Complete in ${totalDuration.toFixed(0)}ms`);
|
|
4072
4223
|
context.onProgress?.({
|
|
4073
4224
|
stage: "COMPLETED",
|
|
4074
4225
|
message: `Background processing complete in ${totalDuration.toFixed(0)}ms`
|
|
4075
4226
|
});
|
|
4076
4227
|
} catch (proofError) {
|
|
4077
|
-
|
|
4228
|
+
logger.error("InstantSplit", "Background: Failed to get sender mint proof:", proofError);
|
|
4078
4229
|
context.onProgress?.({
|
|
4079
4230
|
stage: "FAILED",
|
|
4080
4231
|
message: "Failed to get mint proof",
|
|
@@ -4082,7 +4233,7 @@ var InstantSplitExecutor = class {
|
|
|
4082
4233
|
});
|
|
4083
4234
|
}
|
|
4084
4235
|
}).catch((err) => {
|
|
4085
|
-
|
|
4236
|
+
logger.error("InstantSplit", "Background: Submission batch failed:", err);
|
|
4086
4237
|
context.onProgress?.({
|
|
4087
4238
|
stage: "FAILED",
|
|
4088
4239
|
message: "Background submission failed",
|
|
@@ -4104,7 +4255,7 @@ var InstantSplitExecutor = class {
|
|
|
4104
4255
|
)
|
|
4105
4256
|
]);
|
|
4106
4257
|
} catch {
|
|
4107
|
-
|
|
4258
|
+
logger.debug("InstantSplit", "Dev mode: Using mock proof");
|
|
4108
4259
|
return {
|
|
4109
4260
|
toJSON: () => ({ mock: true })
|
|
4110
4261
|
};
|
|
@@ -4115,6 +4266,8 @@ var InstantSplitExecutor = class {
|
|
|
4115
4266
|
};
|
|
4116
4267
|
|
|
4117
4268
|
// modules/payments/InstantSplitProcessor.ts
|
|
4269
|
+
init_logger();
|
|
4270
|
+
init_errors();
|
|
4118
4271
|
var import_Token5 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
|
|
4119
4272
|
var import_TokenState4 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
|
|
4120
4273
|
var import_TokenType2 = require("@unicitylabs/state-transition-sdk/lib/token/TokenType");
|
|
@@ -4209,27 +4362,27 @@ var InstantSplitProcessor = class {
|
|
|
4209
4362
|
* 5. Create recipient's final state and finalize token
|
|
4210
4363
|
*/
|
|
4211
4364
|
async processV5Bundle(bundle, signingService, senderPubkey, options) {
|
|
4212
|
-
|
|
4365
|
+
logger.debug("InstantSplit", "Processing V5 bundle...");
|
|
4213
4366
|
const startTime = performance.now();
|
|
4214
4367
|
try {
|
|
4215
4368
|
if (bundle.senderPubkey !== senderPubkey) {
|
|
4216
|
-
|
|
4369
|
+
logger.warn("InstantSplit", "Sender pubkey mismatch (non-fatal)");
|
|
4217
4370
|
}
|
|
4218
4371
|
const burnTxJson = JSON.parse(bundle.burnTransaction);
|
|
4219
4372
|
const _burnTransaction = await import_TransferTransaction.TransferTransaction.fromJSON(burnTxJson);
|
|
4220
|
-
|
|
4373
|
+
logger.debug("InstantSplit", "Burn transaction validated");
|
|
4221
4374
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
4222
4375
|
const mintData = await import_MintTransactionData2.MintTransactionData.fromJSON(mintDataJson);
|
|
4223
4376
|
const mintCommitment = await import_MintCommitment2.MintCommitment.create(mintData);
|
|
4224
|
-
|
|
4377
|
+
logger.debug("InstantSplit", "Mint commitment recreated");
|
|
4225
4378
|
const mintResponse = await this.client.submitMintCommitment(mintCommitment);
|
|
4226
4379
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
4227
|
-
throw new
|
|
4380
|
+
throw new SphereError(`Mint submission failed: ${mintResponse.status}`, "TRANSFER_FAILED");
|
|
4228
4381
|
}
|
|
4229
|
-
|
|
4382
|
+
logger.debug("InstantSplit", `Mint submitted: ${mintResponse.status}`);
|
|
4230
4383
|
const mintProof = this.devMode ? await this.waitInclusionProofWithDevBypass(mintCommitment, options?.proofTimeoutMs) : await (0, import_InclusionProofUtils4.waitInclusionProof)(this.trustBase, this.client, mintCommitment);
|
|
4231
4384
|
const mintTransaction = mintCommitment.toTransaction(mintProof);
|
|
4232
|
-
|
|
4385
|
+
logger.debug("InstantSplit", "Mint proof received");
|
|
4233
4386
|
const tokenType = new import_TokenType2.TokenType(fromHex3(bundle.tokenTypeHex));
|
|
4234
4387
|
const senderMintedStateJson = JSON.parse(bundle.mintedTokenStateJson);
|
|
4235
4388
|
const tokenJson = {
|
|
@@ -4240,17 +4393,17 @@ var InstantSplitProcessor = class {
|
|
|
4240
4393
|
nametags: []
|
|
4241
4394
|
};
|
|
4242
4395
|
const mintedToken = await import_Token5.Token.fromJSON(tokenJson);
|
|
4243
|
-
|
|
4396
|
+
logger.debug("InstantSplit", "Minted token reconstructed from sender state");
|
|
4244
4397
|
const transferCommitmentJson = JSON.parse(bundle.transferCommitment);
|
|
4245
4398
|
const transferCommitment = await import_TransferCommitment3.TransferCommitment.fromJSON(transferCommitmentJson);
|
|
4246
4399
|
const transferResponse = await this.client.submitTransferCommitment(transferCommitment);
|
|
4247
4400
|
if (transferResponse.status !== "SUCCESS" && transferResponse.status !== "REQUEST_ID_EXISTS") {
|
|
4248
|
-
throw new
|
|
4401
|
+
throw new SphereError(`Transfer submission failed: ${transferResponse.status}`, "TRANSFER_FAILED");
|
|
4249
4402
|
}
|
|
4250
|
-
|
|
4403
|
+
logger.debug("InstantSplit", `Transfer submitted: ${transferResponse.status}`);
|
|
4251
4404
|
const transferProof = this.devMode ? await this.waitInclusionProofWithDevBypass(transferCommitment, options?.proofTimeoutMs) : await (0, import_InclusionProofUtils4.waitInclusionProof)(this.trustBase, this.client, transferCommitment);
|
|
4252
4405
|
const transferTransaction = transferCommitment.toTransaction(transferProof);
|
|
4253
|
-
|
|
4406
|
+
logger.debug("InstantSplit", "Transfer proof received");
|
|
4254
4407
|
const transferSalt = fromHex3(bundle.transferSaltHex);
|
|
4255
4408
|
const finalRecipientPredicate = await import_UnmaskedPredicate4.UnmaskedPredicate.create(
|
|
4256
4409
|
mintData.tokenId,
|
|
@@ -4260,42 +4413,43 @@ var InstantSplitProcessor = class {
|
|
|
4260
4413
|
transferSalt
|
|
4261
4414
|
);
|
|
4262
4415
|
const finalRecipientState = new import_TokenState4.TokenState(finalRecipientPredicate, null);
|
|
4263
|
-
|
|
4416
|
+
logger.debug("InstantSplit", "Final recipient state created");
|
|
4264
4417
|
let nametagTokens = [];
|
|
4265
4418
|
const recipientAddressStr = bundle.recipientAddressJson;
|
|
4266
4419
|
if (recipientAddressStr.startsWith("PROXY://")) {
|
|
4267
|
-
|
|
4420
|
+
logger.debug("InstantSplit", "PROXY address detected, finding nametag token...");
|
|
4268
4421
|
if (bundle.nametagTokenJson) {
|
|
4269
4422
|
try {
|
|
4270
4423
|
const nametagToken = await import_Token5.Token.fromJSON(JSON.parse(bundle.nametagTokenJson));
|
|
4271
4424
|
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
4272
4425
|
const proxy = await ProxyAddress.fromTokenId(nametagToken.id);
|
|
4273
4426
|
if (proxy.address !== recipientAddressStr) {
|
|
4274
|
-
|
|
4427
|
+
logger.warn("InstantSplit", "Nametag PROXY address mismatch, ignoring bundle token");
|
|
4275
4428
|
} else {
|
|
4276
4429
|
nametagTokens = [nametagToken];
|
|
4277
|
-
|
|
4430
|
+
logger.debug("InstantSplit", "Using nametag token from bundle (address validated)");
|
|
4278
4431
|
}
|
|
4279
4432
|
} catch (err) {
|
|
4280
|
-
|
|
4433
|
+
logger.warn("InstantSplit", "Failed to parse nametag token from bundle:", err);
|
|
4281
4434
|
}
|
|
4282
4435
|
}
|
|
4283
4436
|
if (nametagTokens.length === 0 && options?.findNametagToken) {
|
|
4284
4437
|
const token = await options.findNametagToken(recipientAddressStr);
|
|
4285
4438
|
if (token) {
|
|
4286
4439
|
nametagTokens = [token];
|
|
4287
|
-
|
|
4440
|
+
logger.debug("InstantSplit", "Found nametag token via callback");
|
|
4288
4441
|
}
|
|
4289
4442
|
}
|
|
4290
4443
|
if (nametagTokens.length === 0 && !this.devMode) {
|
|
4291
|
-
throw new
|
|
4292
|
-
`PROXY address transfer requires nametag token for verification. Address: ${recipientAddressStr}
|
|
4444
|
+
throw new SphereError(
|
|
4445
|
+
`PROXY address transfer requires nametag token for verification. Address: ${recipientAddressStr}`,
|
|
4446
|
+
"TRANSFER_FAILED"
|
|
4293
4447
|
);
|
|
4294
4448
|
}
|
|
4295
4449
|
}
|
|
4296
4450
|
let finalToken;
|
|
4297
4451
|
if (this.devMode) {
|
|
4298
|
-
|
|
4452
|
+
logger.debug("InstantSplit", "Dev mode: finalizing without verification");
|
|
4299
4453
|
const tokenJson2 = mintedToken.toJSON();
|
|
4300
4454
|
tokenJson2.state = finalRecipientState.toJSON();
|
|
4301
4455
|
tokenJson2.transactions = [transferTransaction.toJSON()];
|
|
@@ -4309,16 +4463,16 @@ var InstantSplitProcessor = class {
|
|
|
4309
4463
|
nametagTokens
|
|
4310
4464
|
);
|
|
4311
4465
|
}
|
|
4312
|
-
|
|
4466
|
+
logger.debug("InstantSplit", "Token finalized");
|
|
4313
4467
|
if (!this.devMode) {
|
|
4314
4468
|
const verification = await finalToken.verify(this.trustBase);
|
|
4315
4469
|
if (!verification.isSuccessful) {
|
|
4316
|
-
throw new
|
|
4470
|
+
throw new SphereError(`Token verification failed`, "TRANSFER_FAILED");
|
|
4317
4471
|
}
|
|
4318
|
-
|
|
4472
|
+
logger.debug("InstantSplit", "Token verified");
|
|
4319
4473
|
}
|
|
4320
4474
|
const duration = performance.now() - startTime;
|
|
4321
|
-
|
|
4475
|
+
logger.debug("InstantSplit", `V5 bundle processed in ${duration.toFixed(0)}ms`);
|
|
4322
4476
|
return {
|
|
4323
4477
|
success: true,
|
|
4324
4478
|
token: finalToken,
|
|
@@ -4327,7 +4481,7 @@ var InstantSplitProcessor = class {
|
|
|
4327
4481
|
} catch (error) {
|
|
4328
4482
|
const duration = performance.now() - startTime;
|
|
4329
4483
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4330
|
-
|
|
4484
|
+
logger.error("InstantSplit", "V5 processing failed:", error);
|
|
4331
4485
|
return {
|
|
4332
4486
|
success: false,
|
|
4333
4487
|
error: errorMessage,
|
|
@@ -4353,30 +4507,30 @@ var InstantSplitProcessor = class {
|
|
|
4353
4507
|
durationMs: 0
|
|
4354
4508
|
};
|
|
4355
4509
|
}
|
|
4356
|
-
|
|
4510
|
+
logger.debug("InstantSplit", "Processing V4 bundle (dev mode)...");
|
|
4357
4511
|
const startTime = performance.now();
|
|
4358
4512
|
try {
|
|
4359
4513
|
const burnCommitmentJson = JSON.parse(bundle.burnCommitment);
|
|
4360
4514
|
const burnCommitment = await import_TransferCommitment3.TransferCommitment.fromJSON(burnCommitmentJson);
|
|
4361
4515
|
const burnResponse = await this.client.submitTransferCommitment(burnCommitment);
|
|
4362
4516
|
if (burnResponse.status !== "SUCCESS" && burnResponse.status !== "REQUEST_ID_EXISTS") {
|
|
4363
|
-
throw new
|
|
4517
|
+
throw new SphereError(`Burn submission failed: ${burnResponse.status}`, "TRANSFER_FAILED");
|
|
4364
4518
|
}
|
|
4365
4519
|
await this.waitInclusionProofWithDevBypass(burnCommitment, options?.proofTimeoutMs);
|
|
4366
|
-
|
|
4520
|
+
logger.debug("InstantSplit", "V4: Burn proof received");
|
|
4367
4521
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
4368
4522
|
const mintData = await import_MintTransactionData2.MintTransactionData.fromJSON(mintDataJson);
|
|
4369
4523
|
const mintCommitment = await import_MintCommitment2.MintCommitment.create(mintData);
|
|
4370
4524
|
const mintResponse = await this.client.submitMintCommitment(mintCommitment);
|
|
4371
4525
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
4372
|
-
throw new
|
|
4526
|
+
throw new SphereError(`Mint submission failed: ${mintResponse.status}`, "TRANSFER_FAILED");
|
|
4373
4527
|
}
|
|
4374
4528
|
const mintProof = await this.waitInclusionProofWithDevBypass(
|
|
4375
4529
|
mintCommitment,
|
|
4376
4530
|
options?.proofTimeoutMs
|
|
4377
4531
|
);
|
|
4378
4532
|
const mintTransaction = mintCommitment.toTransaction(mintProof);
|
|
4379
|
-
|
|
4533
|
+
logger.debug("InstantSplit", "V4: Mint proof received");
|
|
4380
4534
|
const tokenType = new import_TokenType2.TokenType(fromHex3(bundle.tokenTypeHex));
|
|
4381
4535
|
const recipientSalt = fromHex3(bundle.recipientSaltHex);
|
|
4382
4536
|
const recipientPredicate = await import_UnmaskedPredicate4.UnmaskedPredicate.create(
|
|
@@ -4395,19 +4549,19 @@ var InstantSplitProcessor = class {
|
|
|
4395
4549
|
nametags: []
|
|
4396
4550
|
};
|
|
4397
4551
|
const mintedToken = await import_Token5.Token.fromJSON(tokenJson);
|
|
4398
|
-
|
|
4552
|
+
logger.debug("InstantSplit", "V4: Minted token reconstructed");
|
|
4399
4553
|
const transferCommitmentJson = JSON.parse(bundle.transferCommitment);
|
|
4400
4554
|
const transferCommitment = await import_TransferCommitment3.TransferCommitment.fromJSON(transferCommitmentJson);
|
|
4401
4555
|
const transferResponse = await this.client.submitTransferCommitment(transferCommitment);
|
|
4402
4556
|
if (transferResponse.status !== "SUCCESS" && transferResponse.status !== "REQUEST_ID_EXISTS") {
|
|
4403
|
-
throw new
|
|
4557
|
+
throw new SphereError(`Transfer submission failed: ${transferResponse.status}`, "TRANSFER_FAILED");
|
|
4404
4558
|
}
|
|
4405
4559
|
const transferProof = await this.waitInclusionProofWithDevBypass(
|
|
4406
4560
|
transferCommitment,
|
|
4407
4561
|
options?.proofTimeoutMs
|
|
4408
4562
|
);
|
|
4409
4563
|
const transferTransaction = transferCommitment.toTransaction(transferProof);
|
|
4410
|
-
|
|
4564
|
+
logger.debug("InstantSplit", "V4: Transfer proof received");
|
|
4411
4565
|
const transferSalt = fromHex3(bundle.transferSaltHex);
|
|
4412
4566
|
const finalPredicate = await import_UnmaskedPredicate4.UnmaskedPredicate.create(
|
|
4413
4567
|
mintData.tokenId,
|
|
@@ -4421,9 +4575,9 @@ var InstantSplitProcessor = class {
|
|
|
4421
4575
|
finalTokenJson.state = finalState.toJSON();
|
|
4422
4576
|
finalTokenJson.transactions = [transferTransaction.toJSON()];
|
|
4423
4577
|
const finalToken = await import_Token5.Token.fromJSON(finalTokenJson);
|
|
4424
|
-
|
|
4578
|
+
logger.debug("InstantSplit", "V4: Token finalized");
|
|
4425
4579
|
const duration = performance.now() - startTime;
|
|
4426
|
-
|
|
4580
|
+
logger.debug("InstantSplit", `V4 bundle processed in ${duration.toFixed(0)}ms`);
|
|
4427
4581
|
return {
|
|
4428
4582
|
success: true,
|
|
4429
4583
|
token: finalToken,
|
|
@@ -4432,7 +4586,7 @@ var InstantSplitProcessor = class {
|
|
|
4432
4586
|
} catch (error) {
|
|
4433
4587
|
const duration = performance.now() - startTime;
|
|
4434
4588
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4435
|
-
|
|
4589
|
+
logger.error("InstantSplit", "V4 processing failed:", error);
|
|
4436
4590
|
return {
|
|
4437
4591
|
success: false,
|
|
4438
4592
|
error: errorMessage,
|
|
@@ -4453,7 +4607,7 @@ var InstantSplitProcessor = class {
|
|
|
4453
4607
|
)
|
|
4454
4608
|
]);
|
|
4455
4609
|
} catch {
|
|
4456
|
-
|
|
4610
|
+
logger.debug("InstantSplit", "Dev mode: Using mock proof");
|
|
4457
4611
|
return {
|
|
4458
4612
|
toJSON: () => ({ mock: true })
|
|
4459
4613
|
};
|
|
@@ -4638,7 +4792,7 @@ async function parseTokenInfo(tokenData) {
|
|
|
4638
4792
|
}
|
|
4639
4793
|
}
|
|
4640
4794
|
} catch (error) {
|
|
4641
|
-
|
|
4795
|
+
logger.warn("Payments", "Failed to parse token info:", error);
|
|
4642
4796
|
}
|
|
4643
4797
|
return defaultInfo;
|
|
4644
4798
|
}
|
|
@@ -4841,11 +4995,6 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4841
4995
|
}
|
|
4842
4996
|
/** Price provider (optional) */
|
|
4843
4997
|
priceProvider = null;
|
|
4844
|
-
log(...args) {
|
|
4845
|
-
if (this.moduleConfig.debug) {
|
|
4846
|
-
console.log("[PaymentsModule]", ...args);
|
|
4847
|
-
}
|
|
4848
|
-
}
|
|
4849
4998
|
// ===========================================================================
|
|
4850
4999
|
// Lifecycle
|
|
4851
5000
|
// ===========================================================================
|
|
@@ -4912,11 +5061,11 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4912
5061
|
if (txfData._history && txfData._history.length > 0) {
|
|
4913
5062
|
await this.importRemoteHistoryEntries(txfData._history);
|
|
4914
5063
|
}
|
|
4915
|
-
|
|
5064
|
+
logger.debug("Payments", `Loaded metadata from provider ${id}`);
|
|
4916
5065
|
break;
|
|
4917
5066
|
}
|
|
4918
5067
|
} catch (err) {
|
|
4919
|
-
|
|
5068
|
+
logger.error("Payments", `Failed to load from provider ${id}:`, err);
|
|
4920
5069
|
}
|
|
4921
5070
|
}
|
|
4922
5071
|
for (const [id, token] of this.tokens) {
|
|
@@ -4925,14 +5074,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4925
5074
|
const data = JSON.parse(token.sdkData);
|
|
4926
5075
|
if (data?._placeholder) {
|
|
4927
5076
|
this.tokens.delete(id);
|
|
4928
|
-
|
|
5077
|
+
logger.debug("Payments", `Removed stale placeholder token: ${id}`);
|
|
4929
5078
|
}
|
|
4930
5079
|
}
|
|
4931
5080
|
} catch {
|
|
4932
5081
|
}
|
|
4933
5082
|
}
|
|
4934
5083
|
const loadedTokens = Array.from(this.tokens.values()).map((t) => `${t.id.slice(0, 12)}(${t.status})`);
|
|
4935
|
-
|
|
5084
|
+
logger.debug("Payments", `load(): from TXF providers: ${this.tokens.size} tokens [${loadedTokens.join(", ")}]`);
|
|
4936
5085
|
await this.loadPendingV5Tokens();
|
|
4937
5086
|
await this.loadProcessedSplitGroupIds();
|
|
4938
5087
|
await this.loadProcessedCombinedTransferIds();
|
|
@@ -4948,8 +5097,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4948
5097
|
};
|
|
4949
5098
|
this.loadedPromise = doLoad();
|
|
4950
5099
|
await this.loadedPromise;
|
|
4951
|
-
this.resolveUnconfirmed().catch(() =>
|
|
4952
|
-
});
|
|
5100
|
+
this.resolveUnconfirmed().catch((err) => logger.debug("Payments", "resolveUnconfirmed failed", err));
|
|
4953
5101
|
this.scheduleResolveUnconfirmed();
|
|
4954
5102
|
}
|
|
4955
5103
|
/**
|
|
@@ -5002,11 +5150,11 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5002
5150
|
const signingService = await this.createSigningService();
|
|
5003
5151
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
5004
5152
|
if (!stClient) {
|
|
5005
|
-
throw new
|
|
5153
|
+
throw new SphereError("State transition client not available. Oracle provider must implement getStateTransitionClient()", "AGGREGATOR_ERROR");
|
|
5006
5154
|
}
|
|
5007
5155
|
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
5008
5156
|
if (!trustBase) {
|
|
5009
|
-
throw new
|
|
5157
|
+
throw new SphereError("Trust base not available. Oracle provider must implement getTrustBase()", "AGGREGATOR_ERROR");
|
|
5010
5158
|
}
|
|
5011
5159
|
const calculator = new TokenSplitCalculator();
|
|
5012
5160
|
const availableTokens = Array.from(this.tokens.values());
|
|
@@ -5016,7 +5164,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5016
5164
|
request.coinId
|
|
5017
5165
|
);
|
|
5018
5166
|
if (!splitPlan) {
|
|
5019
|
-
throw new
|
|
5167
|
+
throw new SphereError("Insufficient balance", "INSUFFICIENT_BALANCE");
|
|
5020
5168
|
}
|
|
5021
5169
|
const tokensToSend = splitPlan.tokensToTransferDirectly.map((t) => t.uiToken);
|
|
5022
5170
|
if (splitPlan.tokenToSplit) {
|
|
@@ -5034,7 +5182,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5034
5182
|
const transferMode = request.transferMode ?? "instant";
|
|
5035
5183
|
if (transferMode === "conservative") {
|
|
5036
5184
|
if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
|
|
5037
|
-
|
|
5185
|
+
logger.debug("Payments", "Executing conservative split...");
|
|
5038
5186
|
const splitExecutor = new TokenSplitExecutor({
|
|
5039
5187
|
stateTransitionClient: stClient,
|
|
5040
5188
|
trustBase,
|
|
@@ -5062,7 +5210,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5062
5210
|
sdkData: JSON.stringify(changeTokenData)
|
|
5063
5211
|
};
|
|
5064
5212
|
await this.addToken(changeUiToken);
|
|
5065
|
-
|
|
5213
|
+
logger.debug("Payments", `Conservative split: change token saved: ${changeUiToken.id}`);
|
|
5066
5214
|
await this.deps.transport.sendTokenTransfer(recipientPubkey, {
|
|
5067
5215
|
sourceToken: JSON.stringify(splitResult.tokenForRecipient.toJSON()),
|
|
5068
5216
|
transferTx: JSON.stringify(splitResult.recipientTransferTx.toJSON()),
|
|
@@ -5076,15 +5224,15 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5076
5224
|
method: "split",
|
|
5077
5225
|
requestIdHex: splitRequestIdHex
|
|
5078
5226
|
});
|
|
5079
|
-
|
|
5227
|
+
logger.debug("Payments", "Conservative split transfer completed");
|
|
5080
5228
|
}
|
|
5081
5229
|
for (const tokenWithAmount of splitPlan.tokensToTransferDirectly) {
|
|
5082
5230
|
const token = tokenWithAmount.uiToken;
|
|
5083
5231
|
const commitment = await this.createSdkCommitment(token, recipientAddress, signingService);
|
|
5084
|
-
|
|
5232
|
+
logger.debug("Payments", `CONSERVATIVE: Sending direct token ${token.id.slice(0, 8)}... to ${recipientPubkey.slice(0, 8)}...`);
|
|
5085
5233
|
const submitResponse = await stClient.submitTransferCommitment(commitment);
|
|
5086
5234
|
if (submitResponse.status !== "SUCCESS" && submitResponse.status !== "REQUEST_ID_EXISTS") {
|
|
5087
|
-
throw new
|
|
5235
|
+
throw new SphereError(`Transfer commitment failed: ${submitResponse.status}`, "TRANSFER_FAILED");
|
|
5088
5236
|
}
|
|
5089
5237
|
const inclusionProof = await (0, import_InclusionProofUtils5.waitInclusionProof)(trustBase, stClient, commitment);
|
|
5090
5238
|
const transferTx = commitment.toTransaction(inclusionProof);
|
|
@@ -5093,7 +5241,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5093
5241
|
transferTx: JSON.stringify(transferTx.toJSON()),
|
|
5094
5242
|
memo: request.memo
|
|
5095
5243
|
});
|
|
5096
|
-
|
|
5244
|
+
logger.debug("Payments", "CONSERVATIVE: Direct token sent successfully");
|
|
5097
5245
|
const requestIdBytes = commitment.requestId;
|
|
5098
5246
|
const requestIdHex = requestIdBytes instanceof Uint8Array ? Array.from(requestIdBytes).map((b) => b.toString(16).padStart(2, "0")).join("") : String(requestIdBytes);
|
|
5099
5247
|
result.tokenTransfers.push({
|
|
@@ -5101,7 +5249,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5101
5249
|
method: "direct",
|
|
5102
5250
|
requestIdHex
|
|
5103
5251
|
});
|
|
5104
|
-
|
|
5252
|
+
logger.debug("Payments", `Token ${token.id} sent via CONSERVATIVE, requestId: ${requestIdHex}`);
|
|
5105
5253
|
await this.removeToken(token.id);
|
|
5106
5254
|
}
|
|
5107
5255
|
} else {
|
|
@@ -5110,7 +5258,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5110
5258
|
let changeTokenPlaceholderId = null;
|
|
5111
5259
|
let builtSplit = null;
|
|
5112
5260
|
if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
|
|
5113
|
-
|
|
5261
|
+
logger.debug("Payments", "Building instant split bundle...");
|
|
5114
5262
|
const executor = new InstantSplitExecutor({
|
|
5115
5263
|
stateTransitionClient: stClient,
|
|
5116
5264
|
trustBase,
|
|
@@ -5144,7 +5292,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5144
5292
|
sdkData: JSON.stringify(changeTokenData)
|
|
5145
5293
|
};
|
|
5146
5294
|
await this.addToken(uiToken);
|
|
5147
|
-
|
|
5295
|
+
logger.debug("Payments", `Change token saved via background: ${uiToken.id}`);
|
|
5148
5296
|
},
|
|
5149
5297
|
onStorageSync: async () => {
|
|
5150
5298
|
await this.save();
|
|
@@ -5152,7 +5300,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5152
5300
|
}
|
|
5153
5301
|
}
|
|
5154
5302
|
);
|
|
5155
|
-
|
|
5303
|
+
logger.debug("Payments", `Split bundle built: splitGroupId=${builtSplit.splitGroupId}`);
|
|
5156
5304
|
}
|
|
5157
5305
|
const directCommitments = await Promise.all(
|
|
5158
5306
|
splitPlan.tokensToTransferDirectly.map(
|
|
@@ -5179,8 +5327,9 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5179
5327
|
senderPubkey,
|
|
5180
5328
|
memo: request.memo
|
|
5181
5329
|
};
|
|
5182
|
-
|
|
5183
|
-
|
|
5330
|
+
logger.debug(
|
|
5331
|
+
"Payments",
|
|
5332
|
+
`Sending V6 combined bundle: transfer=${result.id.slice(0, 8)}... split=${!!builtSplit} direct=${directTokenEntries.length}`
|
|
5184
5333
|
);
|
|
5185
5334
|
await this.deps.transport.sendTokenTransfer(recipientPubkey, {
|
|
5186
5335
|
token: JSON.stringify(combinedBundle),
|
|
@@ -5188,7 +5337,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5188
5337
|
memo: request.memo,
|
|
5189
5338
|
sender: { transportPubkey: senderPubkey }
|
|
5190
5339
|
});
|
|
5191
|
-
|
|
5340
|
+
logger.debug("Payments", "V6 combined bundle sent successfully");
|
|
5192
5341
|
if (builtSplit) {
|
|
5193
5342
|
const bgPromise = builtSplit.startBackground();
|
|
5194
5343
|
this.pendingBackgroundTasks.push(bgPromise);
|
|
@@ -5209,11 +5358,11 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5209
5358
|
sdkData: JSON.stringify({ _placeholder: true })
|
|
5210
5359
|
};
|
|
5211
5360
|
this.tokens.set(placeholder.id, placeholder);
|
|
5212
|
-
|
|
5361
|
+
logger.debug("Payments", `Placeholder change token created: ${placeholder.id} (${placeholder.amount})`);
|
|
5213
5362
|
}
|
|
5214
5363
|
for (const commitment of directCommitments) {
|
|
5215
5364
|
stClient.submitTransferCommitment(commitment).catch(
|
|
5216
|
-
(err) =>
|
|
5365
|
+
(err) => logger.error("Payments", "Background commitment submit failed:", err)
|
|
5217
5366
|
);
|
|
5218
5367
|
}
|
|
5219
5368
|
if (splitPlan.requiresSplit && splitPlan.tokenToSplit) {
|
|
@@ -5236,7 +5385,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5236
5385
|
});
|
|
5237
5386
|
await this.removeToken(token.id);
|
|
5238
5387
|
}
|
|
5239
|
-
|
|
5388
|
+
logger.debug("Payments", "V6 combined transfer completed");
|
|
5240
5389
|
}
|
|
5241
5390
|
result.status = "delivered";
|
|
5242
5391
|
await this.save();
|
|
@@ -5327,11 +5476,11 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5327
5476
|
const signingService = await this.createSigningService();
|
|
5328
5477
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
5329
5478
|
if (!stClient) {
|
|
5330
|
-
throw new
|
|
5479
|
+
throw new SphereError("State transition client not available", "AGGREGATOR_ERROR");
|
|
5331
5480
|
}
|
|
5332
5481
|
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
5333
5482
|
if (!trustBase) {
|
|
5334
|
-
throw new
|
|
5483
|
+
throw new SphereError("Trust base not available", "AGGREGATOR_ERROR");
|
|
5335
5484
|
}
|
|
5336
5485
|
const calculator = new TokenSplitCalculator();
|
|
5337
5486
|
const availableTokens = Array.from(this.tokens.values());
|
|
@@ -5341,10 +5490,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5341
5490
|
request.coinId
|
|
5342
5491
|
);
|
|
5343
5492
|
if (!splitPlan) {
|
|
5344
|
-
throw new
|
|
5493
|
+
throw new SphereError("Insufficient balance", "INSUFFICIENT_BALANCE");
|
|
5345
5494
|
}
|
|
5346
5495
|
if (!splitPlan.requiresSplit || !splitPlan.tokenToSplit) {
|
|
5347
|
-
|
|
5496
|
+
logger.debug("Payments", "No split required, falling back to standard send()");
|
|
5348
5497
|
const result2 = await this.send(request);
|
|
5349
5498
|
return {
|
|
5350
5499
|
success: result2.status === "completed",
|
|
@@ -5352,7 +5501,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5352
5501
|
error: result2.error
|
|
5353
5502
|
};
|
|
5354
5503
|
}
|
|
5355
|
-
|
|
5504
|
+
logger.debug("Payments", `InstantSplit: amount=${splitPlan.splitAmount}, remainder=${splitPlan.remainderAmount}`);
|
|
5356
5505
|
const tokenToSplit = splitPlan.tokenToSplit.uiToken;
|
|
5357
5506
|
tokenToSplit.status = "transferring";
|
|
5358
5507
|
this.tokens.set(tokenToSplit.id, tokenToSplit);
|
|
@@ -5390,7 +5539,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5390
5539
|
sdkData: JSON.stringify(changeTokenData)
|
|
5391
5540
|
};
|
|
5392
5541
|
await this.addToken(uiToken);
|
|
5393
|
-
|
|
5542
|
+
logger.debug("Payments", `Change token saved via background: ${uiToken.id}`);
|
|
5394
5543
|
},
|
|
5395
5544
|
onStorageSync: async () => {
|
|
5396
5545
|
await this.save();
|
|
@@ -5445,7 +5594,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5445
5594
|
async saveUnconfirmedV5Token(bundle, senderPubkey, deferPersistence = false) {
|
|
5446
5595
|
const deterministicId = `v5split_${bundle.splitGroupId}`;
|
|
5447
5596
|
if (this.tokens.has(deterministicId) || this.processedSplitGroupIds.has(bundle.splitGroupId)) {
|
|
5448
|
-
|
|
5597
|
+
logger.debug("Payments", `V5 bundle ${bundle.splitGroupId.slice(0, 12)}... already processed, skipping`);
|
|
5449
5598
|
return null;
|
|
5450
5599
|
}
|
|
5451
5600
|
const registry = TokenRegistry.getInstance();
|
|
@@ -5495,7 +5644,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5495
5644
|
const nostrTokenId = extractTokenIdFromSdkData(sdkData);
|
|
5496
5645
|
const nostrStateHash = extractStateHashFromSdkData(sdkData);
|
|
5497
5646
|
if (nostrTokenId && nostrStateHash && this.isStateTombstoned(nostrTokenId, nostrStateHash)) {
|
|
5498
|
-
|
|
5647
|
+
logger.debug("Payments", `NOSTR-FIRST: Rejecting tombstoned token ${nostrTokenId.slice(0, 8)}..._${nostrStateHash.slice(0, 8)}...`);
|
|
5499
5648
|
return null;
|
|
5500
5649
|
}
|
|
5501
5650
|
if (nostrTokenId) {
|
|
@@ -5504,14 +5653,16 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5504
5653
|
if (existingTokenId !== nostrTokenId) continue;
|
|
5505
5654
|
const existingStateHash = extractStateHashFromSdkData(existing.sdkData);
|
|
5506
5655
|
if (nostrStateHash && existingStateHash === nostrStateHash) {
|
|
5507
|
-
|
|
5508
|
-
|
|
5656
|
+
logger.debug(
|
|
5657
|
+
"Payments",
|
|
5658
|
+
`NOSTR-FIRST: Skipping duplicate token state ${nostrTokenId.slice(0, 8)}..._${nostrStateHash.slice(0, 8)}...`
|
|
5509
5659
|
);
|
|
5510
5660
|
return null;
|
|
5511
5661
|
}
|
|
5512
5662
|
if (!skipGenesisDedup) {
|
|
5513
|
-
|
|
5514
|
-
|
|
5663
|
+
logger.debug(
|
|
5664
|
+
"Payments",
|
|
5665
|
+
`NOSTR-FIRST: Skipping replay of finalized token ${nostrTokenId.slice(0, 8)}...`
|
|
5515
5666
|
);
|
|
5516
5667
|
return null;
|
|
5517
5668
|
}
|
|
@@ -5543,7 +5694,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5543
5694
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
5544
5695
|
if (stClient) {
|
|
5545
5696
|
const response = await stClient.submitTransferCommitment(commitment);
|
|
5546
|
-
|
|
5697
|
+
logger.debug("Payments", `NOSTR-FIRST recipient commitment submit: ${response.status}`);
|
|
5547
5698
|
}
|
|
5548
5699
|
}
|
|
5549
5700
|
this.addProofPollingJob({
|
|
@@ -5558,7 +5709,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5558
5709
|
}
|
|
5559
5710
|
});
|
|
5560
5711
|
} catch (err) {
|
|
5561
|
-
|
|
5712
|
+
logger.error("Payments", "Failed to parse commitment for proof polling:", err);
|
|
5562
5713
|
}
|
|
5563
5714
|
return token;
|
|
5564
5715
|
}
|
|
@@ -5580,11 +5731,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5580
5731
|
await this.loadedPromise;
|
|
5581
5732
|
}
|
|
5582
5733
|
if (this.processedCombinedTransferIds.has(bundle.transferId)) {
|
|
5583
|
-
|
|
5734
|
+
logger.debug("Payments", `V6 combined transfer ${bundle.transferId.slice(0, 12)}... already processed, skipping`);
|
|
5584
5735
|
return;
|
|
5585
5736
|
}
|
|
5586
|
-
|
|
5587
|
-
|
|
5737
|
+
logger.debug(
|
|
5738
|
+
"Payments",
|
|
5739
|
+
`Processing V6 combined transfer ${bundle.transferId.slice(0, 12)}... (split=${!!bundle.splitBundle}, direct=${bundle.directTokens.length})`
|
|
5588
5740
|
);
|
|
5589
5741
|
const allTokens = [];
|
|
5590
5742
|
const tokenBreakdown = [];
|
|
@@ -5598,7 +5750,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5598
5750
|
allTokens.push(splitToken);
|
|
5599
5751
|
tokenBreakdown.push({ id: splitToken.id, amount: splitToken.amount, source: "split" });
|
|
5600
5752
|
} else {
|
|
5601
|
-
|
|
5753
|
+
logger.warn("Payments", `V6: split token was deduped/failed \u2014 amount=${bundle.splitBundle.amount}`);
|
|
5602
5754
|
}
|
|
5603
5755
|
}
|
|
5604
5756
|
const directResults = await Promise.all(
|
|
@@ -5613,13 +5765,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5613
5765
|
tokenBreakdown.push({ id: token.id, amount: token.amount, source: "direct" });
|
|
5614
5766
|
} else {
|
|
5615
5767
|
const entry = bundle.directTokens[i];
|
|
5616
|
-
|
|
5617
|
-
|
|
5768
|
+
logger.warn(
|
|
5769
|
+
"Payments",
|
|
5770
|
+
`V6: direct token #${i} dropped (amount=${entry.amount}, tokenId=${entry.tokenId?.slice(0, 12) ?? "N/A"})`
|
|
5618
5771
|
);
|
|
5619
5772
|
}
|
|
5620
5773
|
}
|
|
5621
5774
|
if (allTokens.length === 0) {
|
|
5622
|
-
|
|
5775
|
+
logger.debug("Payments", "V6 combined transfer: all tokens deduped, nothing to save");
|
|
5623
5776
|
return;
|
|
5624
5777
|
}
|
|
5625
5778
|
this.processedCombinedTransferIds.add(bundle.transferId);
|
|
@@ -5635,7 +5788,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5635
5788
|
import_TransferCommitment4.TransferCommitment.fromJSON(commitment).then(
|
|
5636
5789
|
(c) => stClient.submitTransferCommitment(c)
|
|
5637
5790
|
).catch(
|
|
5638
|
-
(err) =>
|
|
5791
|
+
(err) => logger.error("Payments", "V6 background commitment submit failed:", err)
|
|
5639
5792
|
);
|
|
5640
5793
|
}
|
|
5641
5794
|
}
|
|
@@ -5662,8 +5815,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5662
5815
|
tokenIds: tokenBreakdown
|
|
5663
5816
|
});
|
|
5664
5817
|
if (bundle.splitBundle) {
|
|
5665
|
-
this.resolveUnconfirmed().catch(() =>
|
|
5666
|
-
});
|
|
5818
|
+
this.resolveUnconfirmed().catch((err) => logger.debug("Payments", "resolveUnconfirmed failed", err));
|
|
5667
5819
|
this.scheduleResolveUnconfirmed();
|
|
5668
5820
|
}
|
|
5669
5821
|
}
|
|
@@ -5741,8 +5893,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5741
5893
|
receivedAt: Date.now()
|
|
5742
5894
|
});
|
|
5743
5895
|
await this.save();
|
|
5744
|
-
this.resolveUnconfirmed().catch(() =>
|
|
5745
|
-
});
|
|
5896
|
+
this.resolveUnconfirmed().catch((err) => logger.debug("Payments", "resolveUnconfirmed failed", err));
|
|
5746
5897
|
this.scheduleResolveUnconfirmed();
|
|
5747
5898
|
return { success: true, durationMs: 0 };
|
|
5748
5899
|
} catch (error) {
|
|
@@ -5763,11 +5914,11 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5763
5914
|
const signingService = await this.createSigningService();
|
|
5764
5915
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
5765
5916
|
if (!stClient) {
|
|
5766
|
-
throw new
|
|
5917
|
+
throw new SphereError("State transition client not available", "AGGREGATOR_ERROR");
|
|
5767
5918
|
}
|
|
5768
5919
|
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
5769
5920
|
if (!trustBase) {
|
|
5770
|
-
throw new
|
|
5921
|
+
throw new SphereError("Trust base not available", "AGGREGATOR_ERROR");
|
|
5771
5922
|
}
|
|
5772
5923
|
const devMode = this.deps.oracle.isDevMode?.() ?? false;
|
|
5773
5924
|
const processor = new InstantSplitProcessor({
|
|
@@ -5790,10 +5941,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5790
5941
|
if (proxy.address === proxyAddress) {
|
|
5791
5942
|
return nametagToken;
|
|
5792
5943
|
}
|
|
5793
|
-
|
|
5944
|
+
logger.debug("Payments", `Nametag PROXY address mismatch: ${proxy.address} !== ${proxyAddress}`);
|
|
5794
5945
|
return null;
|
|
5795
5946
|
} catch (err) {
|
|
5796
|
-
|
|
5947
|
+
logger.debug("Payments", "Failed to parse nametag token:", err);
|
|
5797
5948
|
return null;
|
|
5798
5949
|
}
|
|
5799
5950
|
}
|
|
@@ -5901,7 +6052,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5901
6052
|
status: "pending"
|
|
5902
6053
|
};
|
|
5903
6054
|
this.outgoingPaymentRequests.set(requestId2, outgoingRequest);
|
|
5904
|
-
|
|
6055
|
+
logger.debug("Payments", `Payment request sent: ${eventId}`);
|
|
5905
6056
|
return {
|
|
5906
6057
|
success: true,
|
|
5907
6058
|
requestId: requestId2,
|
|
@@ -5909,7 +6060,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5909
6060
|
};
|
|
5910
6061
|
} catch (error) {
|
|
5911
6062
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
5912
|
-
|
|
6063
|
+
logger.debug("Payments", `Failed to send payment request: ${errorMsg}`);
|
|
5913
6064
|
return {
|
|
5914
6065
|
success: false,
|
|
5915
6066
|
error: errorMsg
|
|
@@ -5998,10 +6149,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5998
6149
|
async payPaymentRequest(requestId2, memo) {
|
|
5999
6150
|
const request = this.paymentRequests.find((r) => r.id === requestId2);
|
|
6000
6151
|
if (!request) {
|
|
6001
|
-
throw new
|
|
6152
|
+
throw new SphereError(`Payment request not found: ${requestId2}`, "VALIDATION_ERROR");
|
|
6002
6153
|
}
|
|
6003
6154
|
if (request.status !== "pending" && request.status !== "accepted") {
|
|
6004
|
-
throw new
|
|
6155
|
+
throw new SphereError(`Payment request is not pending or accepted: ${request.status}`, "VALIDATION_ERROR");
|
|
6005
6156
|
}
|
|
6006
6157
|
this.updatePaymentRequestStatus(requestId2, "accepted");
|
|
6007
6158
|
try {
|
|
@@ -6056,10 +6207,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6056
6207
|
try {
|
|
6057
6208
|
handler(request);
|
|
6058
6209
|
} catch (error) {
|
|
6059
|
-
|
|
6210
|
+
logger.debug("Payments", "Payment request handler error:", error);
|
|
6060
6211
|
}
|
|
6061
6212
|
}
|
|
6062
|
-
|
|
6213
|
+
logger.debug("Payments", `Incoming payment request: ${request.id} for ${request.amount} ${request.symbol}`);
|
|
6063
6214
|
}
|
|
6064
6215
|
// ===========================================================================
|
|
6065
6216
|
// Public API - Outgoing Payment Requests
|
|
@@ -6178,10 +6329,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6178
6329
|
try {
|
|
6179
6330
|
handler(response);
|
|
6180
6331
|
} catch (error) {
|
|
6181
|
-
|
|
6332
|
+
logger.debug("Payments", "Payment request response handler error:", error);
|
|
6182
6333
|
}
|
|
6183
6334
|
}
|
|
6184
|
-
|
|
6335
|
+
logger.debug("Payments", `Received payment request response: ${response.id} type: ${response.responseType}`);
|
|
6185
6336
|
}
|
|
6186
6337
|
/**
|
|
6187
6338
|
* Send a response to a payment request (used internally by accept/reject/pay methods)
|
|
@@ -6190,7 +6341,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6190
6341
|
const request = this.paymentRequests.find((r) => r.id === requestId2);
|
|
6191
6342
|
if (!request) return;
|
|
6192
6343
|
if (!this.deps?.transport.sendPaymentRequestResponse) {
|
|
6193
|
-
|
|
6344
|
+
logger.debug("Payments", "Transport does not support sendPaymentRequestResponse");
|
|
6194
6345
|
return;
|
|
6195
6346
|
}
|
|
6196
6347
|
try {
|
|
@@ -6201,9 +6352,9 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6201
6352
|
transferId
|
|
6202
6353
|
};
|
|
6203
6354
|
await this.deps.transport.sendPaymentRequestResponse(request.senderPubkey, payload);
|
|
6204
|
-
|
|
6355
|
+
logger.debug("Payments", `Sent payment request response: ${responseType} for ${requestId2}`);
|
|
6205
6356
|
} catch (error) {
|
|
6206
|
-
|
|
6357
|
+
logger.debug("Payments", "Failed to send payment request response:", error);
|
|
6207
6358
|
}
|
|
6208
6359
|
}
|
|
6209
6360
|
// ===========================================================================
|
|
@@ -6227,7 +6378,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6227
6378
|
async receive(options, callback) {
|
|
6228
6379
|
this.ensureInitialized();
|
|
6229
6380
|
if (!this.deps.transport.fetchPendingEvents) {
|
|
6230
|
-
throw new
|
|
6381
|
+
throw new SphereError("Transport provider does not support fetchPendingEvents", "TRANSPORT_ERROR");
|
|
6231
6382
|
}
|
|
6232
6383
|
const opts = options ?? {};
|
|
6233
6384
|
const tokensBefore = new Set(this.tokens.keys());
|
|
@@ -6374,7 +6525,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6374
6525
|
});
|
|
6375
6526
|
}
|
|
6376
6527
|
} catch (error) {
|
|
6377
|
-
|
|
6528
|
+
logger.warn("Payments", "Failed to fetch prices, returning assets without price data:", error);
|
|
6378
6529
|
}
|
|
6379
6530
|
return rawAssets;
|
|
6380
6531
|
}
|
|
@@ -6496,24 +6647,24 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6496
6647
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
6497
6648
|
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
6498
6649
|
if (!stClient || !trustBase) {
|
|
6499
|
-
|
|
6650
|
+
logger.debug("Payments", `[V5-RESOLVE] resolveUnconfirmed: EARLY EXIT \u2014 stClient=${!!stClient} trustBase=${!!trustBase}`);
|
|
6500
6651
|
return result;
|
|
6501
6652
|
}
|
|
6502
6653
|
const signingService = await this.createSigningService();
|
|
6503
6654
|
const submittedCount = Array.from(this.tokens.values()).filter((t) => t.status === "submitted").length;
|
|
6504
|
-
|
|
6655
|
+
logger.debug("Payments", `[V5-RESOLVE] resolveUnconfirmed: ${submittedCount} submitted token(s) to process`);
|
|
6505
6656
|
for (const [tokenId, token] of this.tokens) {
|
|
6506
6657
|
if (token.status !== "submitted") continue;
|
|
6507
6658
|
const pending2 = this.parsePendingFinalization(token.sdkData);
|
|
6508
6659
|
if (!pending2) {
|
|
6509
|
-
|
|
6660
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 16)}: no pending finalization metadata, skipping`);
|
|
6510
6661
|
result.stillPending++;
|
|
6511
6662
|
continue;
|
|
6512
6663
|
}
|
|
6513
6664
|
if (pending2.type === "v5_bundle") {
|
|
6514
|
-
|
|
6665
|
+
logger.debug("Payments", `[V5-RESOLVE] Processing ${tokenId.slice(0, 16)}... stage=${pending2.stage} attempt=${pending2.attemptCount}`);
|
|
6515
6666
|
const progress = await this.resolveV5Token(tokenId, token, pending2, stClient, trustBase, signingService);
|
|
6516
|
-
|
|
6667
|
+
logger.debug("Payments", `[V5-RESOLVE] Result for ${tokenId.slice(0, 16)}...: ${progress} (stage now: ${pending2.stage})`);
|
|
6517
6668
|
result.details.push({ tokenId, stage: pending2.stage, status: progress });
|
|
6518
6669
|
if (progress === "resolved") result.resolved++;
|
|
6519
6670
|
else if (progress === "failed") result.failed++;
|
|
@@ -6521,7 +6672,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6521
6672
|
}
|
|
6522
6673
|
}
|
|
6523
6674
|
if (result.resolved > 0 || result.failed > 0 || result.stillPending > 0) {
|
|
6524
|
-
|
|
6675
|
+
logger.debug("Payments", `[V5-RESOLVE] Saving: resolved=${result.resolved} failed=${result.failed} stillPending=${result.stillPending}`);
|
|
6525
6676
|
await this.save();
|
|
6526
6677
|
}
|
|
6527
6678
|
return result;
|
|
@@ -6537,19 +6688,19 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6537
6688
|
(t) => t.status === "submitted"
|
|
6538
6689
|
);
|
|
6539
6690
|
if (!hasUnconfirmed) {
|
|
6540
|
-
|
|
6691
|
+
logger.debug("Payments", "[V5-RESOLVE] scheduleResolveUnconfirmed: no submitted tokens, not starting timer");
|
|
6541
6692
|
return;
|
|
6542
6693
|
}
|
|
6543
|
-
|
|
6694
|
+
logger.debug("Payments", `[V5-RESOLVE] scheduleResolveUnconfirmed: starting periodic retry (every ${_PaymentsModule.RESOLVE_UNCONFIRMED_INTERVAL_MS}ms)`);
|
|
6544
6695
|
this.resolveUnconfirmedTimer = setInterval(async () => {
|
|
6545
6696
|
try {
|
|
6546
6697
|
const result = await this.resolveUnconfirmed();
|
|
6547
6698
|
if (result.stillPending === 0) {
|
|
6548
|
-
|
|
6699
|
+
logger.debug("Payments", "[V5-RESOLVE] All tokens resolved, stopping periodic retry");
|
|
6549
6700
|
this.stopResolveUnconfirmedPolling();
|
|
6550
6701
|
}
|
|
6551
6702
|
} catch (err) {
|
|
6552
|
-
|
|
6703
|
+
logger.debug("Payments", "[V5-RESOLVE] Periodic retry error:", err);
|
|
6553
6704
|
}
|
|
6554
6705
|
}, _PaymentsModule.RESOLVE_UNCONFIRMED_INTERVAL_MS);
|
|
6555
6706
|
}
|
|
@@ -6571,57 +6722,57 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6571
6722
|
pending2.lastAttemptAt = Date.now();
|
|
6572
6723
|
try {
|
|
6573
6724
|
if (pending2.stage === "RECEIVED") {
|
|
6574
|
-
|
|
6725
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: RECEIVED \u2192 submitting mint commitment...`);
|
|
6575
6726
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
6576
6727
|
const mintData = await import_MintTransactionData3.MintTransactionData.fromJSON(mintDataJson);
|
|
6577
6728
|
const mintCommitment = await import_MintCommitment3.MintCommitment.create(mintData);
|
|
6578
6729
|
const mintResponse = await stClient.submitMintCommitment(mintCommitment);
|
|
6579
|
-
|
|
6730
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint response status=${mintResponse.status}`);
|
|
6580
6731
|
if (mintResponse.status !== "SUCCESS" && mintResponse.status !== "REQUEST_ID_EXISTS") {
|
|
6581
|
-
throw new
|
|
6732
|
+
throw new SphereError(`Mint submission failed: ${mintResponse.status}`, "TRANSFER_FAILED");
|
|
6582
6733
|
}
|
|
6583
6734
|
pending2.stage = "MINT_SUBMITTED";
|
|
6584
6735
|
this.updatePendingFinalization(token, pending2);
|
|
6585
6736
|
}
|
|
6586
6737
|
if (pending2.stage === "MINT_SUBMITTED") {
|
|
6587
|
-
|
|
6738
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: MINT_SUBMITTED \u2192 checking mint proof...`);
|
|
6588
6739
|
const mintDataJson = JSON.parse(bundle.recipientMintData);
|
|
6589
6740
|
const mintData = await import_MintTransactionData3.MintTransactionData.fromJSON(mintDataJson);
|
|
6590
6741
|
const mintCommitment = await import_MintCommitment3.MintCommitment.create(mintData);
|
|
6591
6742
|
const proof = await this.quickProofCheck(stClient, trustBase, mintCommitment);
|
|
6592
6743
|
if (!proof) {
|
|
6593
|
-
|
|
6744
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint proof not yet available, staying MINT_SUBMITTED`);
|
|
6594
6745
|
this.updatePendingFinalization(token, pending2);
|
|
6595
6746
|
return "pending";
|
|
6596
6747
|
}
|
|
6597
|
-
|
|
6748
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: mint proof obtained!`);
|
|
6598
6749
|
pending2.mintProofJson = JSON.stringify(proof);
|
|
6599
6750
|
pending2.stage = "MINT_PROVEN";
|
|
6600
6751
|
this.updatePendingFinalization(token, pending2);
|
|
6601
6752
|
}
|
|
6602
6753
|
if (pending2.stage === "MINT_PROVEN") {
|
|
6603
|
-
|
|
6754
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: MINT_PROVEN \u2192 submitting transfer commitment...`);
|
|
6604
6755
|
const transferCommitmentJson = JSON.parse(bundle.transferCommitment);
|
|
6605
6756
|
const transferCommitment = await import_TransferCommitment4.TransferCommitment.fromJSON(transferCommitmentJson);
|
|
6606
6757
|
const transferResponse = await stClient.submitTransferCommitment(transferCommitment);
|
|
6607
|
-
|
|
6758
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: transfer response status=${transferResponse.status}`);
|
|
6608
6759
|
if (transferResponse.status !== "SUCCESS" && transferResponse.status !== "REQUEST_ID_EXISTS") {
|
|
6609
|
-
throw new
|
|
6760
|
+
throw new SphereError(`Transfer submission failed: ${transferResponse.status}`, "TRANSFER_FAILED");
|
|
6610
6761
|
}
|
|
6611
6762
|
pending2.stage = "TRANSFER_SUBMITTED";
|
|
6612
6763
|
this.updatePendingFinalization(token, pending2);
|
|
6613
6764
|
}
|
|
6614
6765
|
if (pending2.stage === "TRANSFER_SUBMITTED") {
|
|
6615
|
-
|
|
6766
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: TRANSFER_SUBMITTED \u2192 checking transfer proof...`);
|
|
6616
6767
|
const transferCommitmentJson = JSON.parse(bundle.transferCommitment);
|
|
6617
6768
|
const transferCommitment = await import_TransferCommitment4.TransferCommitment.fromJSON(transferCommitmentJson);
|
|
6618
6769
|
const proof = await this.quickProofCheck(stClient, trustBase, transferCommitment);
|
|
6619
6770
|
if (!proof) {
|
|
6620
|
-
|
|
6771
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: transfer proof not yet available, staying TRANSFER_SUBMITTED`);
|
|
6621
6772
|
this.updatePendingFinalization(token, pending2);
|
|
6622
6773
|
return "pending";
|
|
6623
6774
|
}
|
|
6624
|
-
|
|
6775
|
+
logger.debug("Payments", `[V5-RESOLVE] ${tokenId.slice(0, 12)}: transfer proof obtained! Finalizing...`);
|
|
6625
6776
|
const finalizedToken = await this.finalizeFromV5Bundle(bundle, pending2, signingService, stClient, trustBase);
|
|
6626
6777
|
const confirmedToken = {
|
|
6627
6778
|
id: token.id,
|
|
@@ -6643,12 +6794,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6643
6794
|
tokens: [confirmedToken],
|
|
6644
6795
|
tokenTransfers: []
|
|
6645
6796
|
});
|
|
6646
|
-
|
|
6797
|
+
logger.debug("Payments", `V5 token resolved: ${tokenId.slice(0, 8)}...`);
|
|
6647
6798
|
return "resolved";
|
|
6648
6799
|
}
|
|
6649
6800
|
return "pending";
|
|
6650
6801
|
} catch (error) {
|
|
6651
|
-
|
|
6802
|
+
logger.error("Payments", `resolveV5Token failed for ${tokenId.slice(0, 8)}:`, error);
|
|
6652
6803
|
if (pending2.attemptCount > 50) {
|
|
6653
6804
|
token.status = "invalid";
|
|
6654
6805
|
token.updatedAt = Date.now();
|
|
@@ -6785,19 +6936,19 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6785
6936
|
}
|
|
6786
6937
|
if (pendingTokens.length > 0) {
|
|
6787
6938
|
const json = JSON.stringify(pendingTokens);
|
|
6788
|
-
|
|
6939
|
+
logger.debug("Payments", `[V5-PERSIST] Saving ${pendingTokens.length} pending V5 token(s): ${pendingTokens.map((t) => t.id.slice(0, 16)).join(", ")} (${json.length} bytes)`);
|
|
6789
6940
|
await this.deps.storage.set(
|
|
6790
6941
|
STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS,
|
|
6791
6942
|
json
|
|
6792
6943
|
);
|
|
6793
6944
|
const verify = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS);
|
|
6794
6945
|
if (!verify) {
|
|
6795
|
-
|
|
6946
|
+
logger.error("Payments", "[V5-PERSIST] CRITICAL: KV write succeeded but read-back is empty!");
|
|
6796
6947
|
} else {
|
|
6797
|
-
|
|
6948
|
+
logger.debug("Payments", `[V5-PERSIST] Verified: read-back ${verify.length} bytes`);
|
|
6798
6949
|
}
|
|
6799
6950
|
} else {
|
|
6800
|
-
|
|
6951
|
+
logger.debug("Payments", `[V5-PERSIST] No pending V5 tokens to save (total tokens: ${this.tokens.size}), clearing KV`);
|
|
6801
6952
|
await this.deps.storage.set(STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS, "");
|
|
6802
6953
|
}
|
|
6803
6954
|
}
|
|
@@ -6807,21 +6958,21 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6807
6958
|
*/
|
|
6808
6959
|
async loadPendingV5Tokens() {
|
|
6809
6960
|
const data = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PENDING_V5_TOKENS);
|
|
6810
|
-
|
|
6961
|
+
logger.debug("Payments", `[V5-PERSIST] loadPendingV5Tokens: KV data = ${data ? `${data.length} bytes` : "null/empty"}`);
|
|
6811
6962
|
if (!data) return;
|
|
6812
6963
|
try {
|
|
6813
6964
|
const pendingTokens = JSON.parse(data);
|
|
6814
|
-
|
|
6965
|
+
logger.debug("Payments", `[V5-PERSIST] Parsed ${pendingTokens.length} pending V5 token(s): ${pendingTokens.map((t) => t.id.slice(0, 16)).join(", ")}`);
|
|
6815
6966
|
for (const token of pendingTokens) {
|
|
6816
6967
|
if (!this.tokens.has(token.id)) {
|
|
6817
6968
|
this.tokens.set(token.id, token);
|
|
6818
|
-
|
|
6969
|
+
logger.debug("Payments", `[V5-PERSIST] Restored token ${token.id.slice(0, 16)} (status=${token.status})`);
|
|
6819
6970
|
} else {
|
|
6820
|
-
|
|
6971
|
+
logger.debug("Payments", `[V5-PERSIST] Token ${token.id.slice(0, 16)} already in map, skipping`);
|
|
6821
6972
|
}
|
|
6822
6973
|
}
|
|
6823
6974
|
} catch (err) {
|
|
6824
|
-
|
|
6975
|
+
logger.error("Payments", "[V5-PERSIST] Failed to parse pending V5 tokens:", err);
|
|
6825
6976
|
}
|
|
6826
6977
|
}
|
|
6827
6978
|
/**
|
|
@@ -6874,13 +7025,13 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6874
7025
|
const incomingStateHash = extractStateHashFromSdkData(token.sdkData);
|
|
6875
7026
|
const incomingStateKey = incomingTokenId && incomingStateHash ? createTokenStateKey(incomingTokenId, incomingStateHash) : null;
|
|
6876
7027
|
if (incomingTokenId && incomingStateHash && this.isStateTombstoned(incomingTokenId, incomingStateHash)) {
|
|
6877
|
-
|
|
7028
|
+
logger.debug("Payments", `Rejecting tombstoned token: ${incomingTokenId.slice(0, 8)}..._${incomingStateHash.slice(0, 8)}...`);
|
|
6878
7029
|
return false;
|
|
6879
7030
|
}
|
|
6880
7031
|
if (incomingStateKey) {
|
|
6881
7032
|
for (const [_existingId, existing] of this.tokens) {
|
|
6882
7033
|
if (isSameTokenState(existing, token)) {
|
|
6883
|
-
|
|
7034
|
+
logger.debug("Payments", `Duplicate token state ignored: ${incomingTokenId?.slice(0, 8)}..._${incomingStateHash?.slice(0, 8)}...`);
|
|
6884
7035
|
return false;
|
|
6885
7036
|
}
|
|
6886
7037
|
}
|
|
@@ -6892,19 +7043,19 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6892
7043
|
continue;
|
|
6893
7044
|
}
|
|
6894
7045
|
if (existing.status === "spent" || existing.status === "invalid") {
|
|
6895
|
-
|
|
7046
|
+
logger.debug("Payments", `Replacing spent/invalid token ${incomingTokenId?.slice(0, 8)}...`);
|
|
6896
7047
|
this.tokens.delete(existingId);
|
|
6897
7048
|
break;
|
|
6898
7049
|
}
|
|
6899
7050
|
if (incomingStateHash && existingStateHash && incomingStateHash !== existingStateHash) {
|
|
6900
|
-
|
|
7051
|
+
logger.debug("Payments", `Token ${incomingTokenId?.slice(0, 8)}... state updated: ${existingStateHash.slice(0, 8)}... -> ${incomingStateHash.slice(0, 8)}...`);
|
|
6901
7052
|
await this.archiveToken(existing);
|
|
6902
7053
|
this.tokens.delete(existingId);
|
|
6903
7054
|
break;
|
|
6904
7055
|
}
|
|
6905
7056
|
if (!incomingStateHash || !existingStateHash) {
|
|
6906
7057
|
if (existingId !== token.id) {
|
|
6907
|
-
|
|
7058
|
+
logger.debug("Payments", `Token ${incomingTokenId?.slice(0, 8)}... .id changed, replacing`);
|
|
6908
7059
|
await this.archiveToken(existing);
|
|
6909
7060
|
this.tokens.delete(existingId);
|
|
6910
7061
|
break;
|
|
@@ -6915,7 +7066,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6915
7066
|
this.tokens.set(token.id, token);
|
|
6916
7067
|
await this.archiveToken(token);
|
|
6917
7068
|
await this.save();
|
|
6918
|
-
|
|
7069
|
+
logger.debug("Payments", `Added token ${token.id}, total: ${this.tokens.size}`);
|
|
6919
7070
|
return true;
|
|
6920
7071
|
}
|
|
6921
7072
|
/**
|
|
@@ -6945,7 +7096,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6945
7096
|
}
|
|
6946
7097
|
await this.archiveToken(token);
|
|
6947
7098
|
await this.save();
|
|
6948
|
-
|
|
7099
|
+
logger.debug("Payments", `Updated token ${token.id}`);
|
|
6949
7100
|
}
|
|
6950
7101
|
/**
|
|
6951
7102
|
* Remove a token from the wallet.
|
|
@@ -6968,10 +7119,10 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6968
7119
|
);
|
|
6969
7120
|
if (!alreadyTombstoned) {
|
|
6970
7121
|
this.tombstones.push(tombstone);
|
|
6971
|
-
|
|
7122
|
+
logger.debug("Payments", `Created tombstone for ${tombstone.tokenId.slice(0, 8)}..._${tombstone.stateHash.slice(0, 8)}...`);
|
|
6972
7123
|
}
|
|
6973
7124
|
} else {
|
|
6974
|
-
|
|
7125
|
+
logger.debug("Payments", `Warning: Could not create tombstone for token ${tokenId.slice(0, 8)}... (missing tokenId or stateHash)`);
|
|
6975
7126
|
}
|
|
6976
7127
|
this.tokens.delete(tokenId);
|
|
6977
7128
|
await this.save();
|
|
@@ -7028,7 +7179,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7028
7179
|
}
|
|
7029
7180
|
for (const token of tokensToRemove) {
|
|
7030
7181
|
this.tokens.delete(token.id);
|
|
7031
|
-
|
|
7182
|
+
logger.debug("Payments", `Removed tombstoned token ${token.id.slice(0, 8)}...`);
|
|
7032
7183
|
removedCount++;
|
|
7033
7184
|
}
|
|
7034
7185
|
for (const remoteTombstone of remoteTombstones) {
|
|
@@ -7054,7 +7205,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7054
7205
|
this.tombstones = pruneTombstonesByAge(this.tombstones, maxAge);
|
|
7055
7206
|
if (this.tombstones.length < originalCount) {
|
|
7056
7207
|
await this.save();
|
|
7057
|
-
|
|
7208
|
+
logger.debug("Payments", `Pruned tombstones from ${originalCount} to ${this.tombstones.length}`);
|
|
7058
7209
|
}
|
|
7059
7210
|
}
|
|
7060
7211
|
// ===========================================================================
|
|
@@ -7126,7 +7277,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7126
7277
|
const originalCount = this.archivedTokens.size;
|
|
7127
7278
|
this.archivedTokens = pruneMapByCount(this.archivedTokens, maxCount);
|
|
7128
7279
|
await this.save();
|
|
7129
|
-
|
|
7280
|
+
logger.debug("Payments", `Pruned archived tokens from ${originalCount} to ${this.archivedTokens.size}`);
|
|
7130
7281
|
}
|
|
7131
7282
|
// ===========================================================================
|
|
7132
7283
|
// Public API - Forked Tokens
|
|
@@ -7155,7 +7306,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7155
7306
|
const key = `${tokenId}_${stateHash}`;
|
|
7156
7307
|
if (this.forkedTokens.has(key)) return;
|
|
7157
7308
|
this.forkedTokens.set(key, txfToken);
|
|
7158
|
-
|
|
7309
|
+
logger.debug("Payments", `Stored forked token ${tokenId.slice(0, 8)}... state ${stateHash.slice(0, 12)}...`);
|
|
7159
7310
|
await this.save();
|
|
7160
7311
|
}
|
|
7161
7312
|
/**
|
|
@@ -7187,7 +7338,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7187
7338
|
const originalCount = this.forkedTokens.size;
|
|
7188
7339
|
this.forkedTokens = pruneMapByCount(this.forkedTokens, maxCount);
|
|
7189
7340
|
await this.save();
|
|
7190
|
-
|
|
7341
|
+
logger.debug("Payments", `Pruned forked tokens from ${originalCount} to ${this.forkedTokens.size}`);
|
|
7191
7342
|
}
|
|
7192
7343
|
// ===========================================================================
|
|
7193
7344
|
// Public API - Transaction History
|
|
@@ -7267,7 +7418,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7267
7418
|
const imported = await provider.importHistoryEntries?.(records) ?? 0;
|
|
7268
7419
|
if (imported > 0) {
|
|
7269
7420
|
this._historyCache = await provider.getHistoryEntries();
|
|
7270
|
-
|
|
7421
|
+
logger.debug("Payments", `Migrated ${imported} history entries from KV to history store`);
|
|
7271
7422
|
}
|
|
7272
7423
|
await this.deps.storage.remove(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
|
|
7273
7424
|
} catch {
|
|
@@ -7343,7 +7494,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7343
7494
|
this.nametags.push(nametag);
|
|
7344
7495
|
}
|
|
7345
7496
|
await this.save();
|
|
7346
|
-
|
|
7497
|
+
logger.debug("Payments", `Nametag set: ${nametag.name}`);
|
|
7347
7498
|
}
|
|
7348
7499
|
/**
|
|
7349
7500
|
* Get the current (first) nametag data.
|
|
@@ -7392,7 +7543,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7392
7543
|
const parsed = parseTxfStorageData(result.data);
|
|
7393
7544
|
if (parsed.nametags.length > 0) {
|
|
7394
7545
|
this.nametags = parsed.nametags;
|
|
7395
|
-
|
|
7546
|
+
logger.debug("Payments", `Reloaded ${parsed.nametags.length} nametag(s) from storage`);
|
|
7396
7547
|
return;
|
|
7397
7548
|
}
|
|
7398
7549
|
}
|
|
@@ -7445,7 +7596,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7445
7596
|
const result = await minter.mintNametag(nametag, ownerAddress);
|
|
7446
7597
|
if (result.success && result.nametagData) {
|
|
7447
7598
|
await this.setNametag(result.nametagData);
|
|
7448
|
-
|
|
7599
|
+
logger.debug("Payments", `Nametag minted and saved: ${result.nametagData.name}`);
|
|
7449
7600
|
this.deps.emitEvent("nametag:registered", {
|
|
7450
7601
|
nametag: result.nametagData.name,
|
|
7451
7602
|
addressIndex: 0
|
|
@@ -7455,7 +7606,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7455
7606
|
return result;
|
|
7456
7607
|
} catch (error) {
|
|
7457
7608
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
7458
|
-
|
|
7609
|
+
logger.debug("Payments", "mintNametag failed:", errorMsg);
|
|
7459
7610
|
return {
|
|
7460
7611
|
success: false,
|
|
7461
7612
|
error: errorMsg
|
|
@@ -7553,7 +7704,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7553
7704
|
restoredCount++;
|
|
7554
7705
|
}
|
|
7555
7706
|
if (restoredCount > 0) {
|
|
7556
|
-
|
|
7707
|
+
logger.debug("Payments", `Sync: restored ${restoredCount} token(s) lost by loadFromStorageData`);
|
|
7557
7708
|
}
|
|
7558
7709
|
if (this.nametags.length === 0 && savedNametags.length > 0) {
|
|
7559
7710
|
this.nametags = savedNametags;
|
|
@@ -7562,7 +7713,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7562
7713
|
if (txfData._history && txfData._history.length > 0) {
|
|
7563
7714
|
const imported = await this.importRemoteHistoryEntries(txfData._history);
|
|
7564
7715
|
if (imported > 0) {
|
|
7565
|
-
|
|
7716
|
+
logger.debug("Payments", `Imported ${imported} history entries from IPFS sync`);
|
|
7566
7717
|
}
|
|
7567
7718
|
}
|
|
7568
7719
|
totalAdded += result.added;
|
|
@@ -7575,7 +7726,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7575
7726
|
removed: result.removed
|
|
7576
7727
|
});
|
|
7577
7728
|
} catch (providerError) {
|
|
7578
|
-
|
|
7729
|
+
logger.warn("Payments", `Sync failed for provider ${providerId}:`, providerError);
|
|
7579
7730
|
this.deps.emitEvent("sync:provider", {
|
|
7580
7731
|
providerId,
|
|
7581
7732
|
success: false,
|
|
@@ -7613,7 +7764,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7613
7764
|
if (provider.onEvent) {
|
|
7614
7765
|
const unsub = provider.onEvent((event) => {
|
|
7615
7766
|
if (event.type === "storage:remote-updated") {
|
|
7616
|
-
|
|
7767
|
+
logger.debug("Payments", "Remote update detected from provider", providerId, event.data);
|
|
7617
7768
|
this.debouncedSyncFromRemoteUpdate(providerId, event.data);
|
|
7618
7769
|
}
|
|
7619
7770
|
});
|
|
@@ -7655,7 +7806,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7655
7806
|
removed: result.removed
|
|
7656
7807
|
});
|
|
7657
7808
|
}).catch((err) => {
|
|
7658
|
-
|
|
7809
|
+
logger.debug("Payments", "Auto-sync from remote update failed:", err);
|
|
7659
7810
|
});
|
|
7660
7811
|
}, _PaymentsModule.SYNC_DEBOUNCE_MS);
|
|
7661
7812
|
}
|
|
@@ -7760,8 +7911,9 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7760
7911
|
}
|
|
7761
7912
|
return recipient;
|
|
7762
7913
|
}
|
|
7763
|
-
throw new
|
|
7764
|
-
`Cannot resolve transport pubkey for "${recipient}". No binding event found. The recipient must publish their identity first
|
|
7914
|
+
throw new SphereError(
|
|
7915
|
+
`Cannot resolve transport pubkey for "${recipient}". No binding event found. The recipient must publish their identity first.`,
|
|
7916
|
+
"INVALID_RECIPIENT"
|
|
7765
7917
|
);
|
|
7766
7918
|
}
|
|
7767
7919
|
/**
|
|
@@ -7832,32 +7984,33 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7832
7984
|
return AddressFactory.createAddress(recipient);
|
|
7833
7985
|
}
|
|
7834
7986
|
if (recipient.length === 66 && /^[0-9a-fA-F]+$/.test(recipient)) {
|
|
7835
|
-
|
|
7987
|
+
logger.debug("Payments", "Creating DirectAddress from 33-byte compressed pubkey");
|
|
7836
7988
|
return this.createDirectAddressFromPubkey(recipient);
|
|
7837
7989
|
}
|
|
7838
7990
|
const info = peerInfo ?? await this.deps?.transport.resolve?.(recipient) ?? null;
|
|
7839
7991
|
if (!info) {
|
|
7840
|
-
throw new
|
|
7841
|
-
`Recipient "${recipient}" not found. Use @nametag, a valid PROXY:/DIRECT: address, or a 33-byte hex pubkey
|
|
7992
|
+
throw new SphereError(
|
|
7993
|
+
`Recipient "${recipient}" not found. Use @nametag, a valid PROXY:/DIRECT: address, or a 33-byte hex pubkey.`,
|
|
7994
|
+
"INVALID_RECIPIENT"
|
|
7842
7995
|
);
|
|
7843
7996
|
}
|
|
7844
7997
|
const nametag = recipient.startsWith("@") ? recipient.slice(1) : info.nametag || recipient;
|
|
7845
7998
|
if (addressMode === "proxy") {
|
|
7846
|
-
|
|
7999
|
+
logger.debug("Payments", `Using PROXY address for "${nametag}" (forced)`);
|
|
7847
8000
|
return ProxyAddress.fromNameTag(nametag);
|
|
7848
8001
|
}
|
|
7849
8002
|
if (addressMode === "direct") {
|
|
7850
8003
|
if (!info.directAddress) {
|
|
7851
|
-
throw new
|
|
8004
|
+
throw new SphereError(`"${nametag}" has no DirectAddress stored. It may be a legacy registration.`, "INVALID_RECIPIENT");
|
|
7852
8005
|
}
|
|
7853
|
-
|
|
8006
|
+
logger.debug("Payments", `Using DirectAddress for "${nametag}" (forced): ${info.directAddress.slice(0, 30)}...`);
|
|
7854
8007
|
return AddressFactory.createAddress(info.directAddress);
|
|
7855
8008
|
}
|
|
7856
8009
|
if (info.directAddress) {
|
|
7857
|
-
|
|
8010
|
+
logger.debug("Payments", `Using DirectAddress for "${nametag}": ${info.directAddress.slice(0, 30)}...`);
|
|
7858
8011
|
return AddressFactory.createAddress(info.directAddress);
|
|
7859
8012
|
}
|
|
7860
|
-
|
|
8013
|
+
logger.debug("Payments", `Using PROXY address for legacy nametag "${nametag}"`);
|
|
7861
8014
|
return ProxyAddress.fromNameTag(nametag);
|
|
7862
8015
|
}
|
|
7863
8016
|
/**
|
|
@@ -7870,7 +8023,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7870
8023
|
const sourceTokenInput = typeof payload.sourceToken === "string" ? JSON.parse(payload.sourceToken) : payload.sourceToken;
|
|
7871
8024
|
const commitmentInput = typeof payload.commitmentData === "string" ? JSON.parse(payload.commitmentData) : payload.commitmentData;
|
|
7872
8025
|
if (!sourceTokenInput || !commitmentInput) {
|
|
7873
|
-
|
|
8026
|
+
logger.warn("Payments", "Invalid NOSTR-FIRST transfer format");
|
|
7874
8027
|
return;
|
|
7875
8028
|
}
|
|
7876
8029
|
const token = await this.saveCommitmentOnlyToken(
|
|
@@ -7901,7 +8054,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7901
8054
|
tokenId: nostrTokenId || token.id
|
|
7902
8055
|
});
|
|
7903
8056
|
} catch (error) {
|
|
7904
|
-
|
|
8057
|
+
logger.error("Payments", "Failed to process NOSTR-FIRST transfer:", error);
|
|
7905
8058
|
}
|
|
7906
8059
|
}
|
|
7907
8060
|
/**
|
|
@@ -7926,18 +8079,19 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7926
8079
|
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
7927
8080
|
let proxyNametag = this.getNametag();
|
|
7928
8081
|
if (!proxyNametag?.token) {
|
|
7929
|
-
|
|
8082
|
+
logger.debug("Payments", "Nametag missing in memory, attempting reload from storage...");
|
|
7930
8083
|
await this.reloadNametagsFromStorage();
|
|
7931
8084
|
proxyNametag = this.getNametag();
|
|
7932
8085
|
}
|
|
7933
8086
|
if (!proxyNametag?.token) {
|
|
7934
|
-
throw new
|
|
8087
|
+
throw new SphereError("Cannot finalize PROXY transfer - no nametag token", "VALIDATION_ERROR");
|
|
7935
8088
|
}
|
|
7936
8089
|
const nametagToken = await import_Token6.Token.fromJSON(proxyNametag.token);
|
|
7937
8090
|
const proxy = await ProxyAddress.fromTokenId(nametagToken.id);
|
|
7938
8091
|
if (proxy.address !== recipientAddress.address) {
|
|
7939
|
-
throw new
|
|
7940
|
-
`PROXY address mismatch: nametag resolves to ${proxy.address} but transfer targets ${recipientAddress.address}
|
|
8092
|
+
throw new SphereError(
|
|
8093
|
+
`PROXY address mismatch: nametag resolves to ${proxy.address} but transfer targets ${recipientAddress.address}`,
|
|
8094
|
+
"VALIDATION_ERROR"
|
|
7941
8095
|
);
|
|
7942
8096
|
}
|
|
7943
8097
|
nametagTokens = [nametagToken];
|
|
@@ -7957,12 +8111,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7957
8111
|
try {
|
|
7958
8112
|
const token = this.tokens.get(tokenId);
|
|
7959
8113
|
if (!token) {
|
|
7960
|
-
|
|
8114
|
+
logger.debug("Payments", `Token ${tokenId} not found for finalization`);
|
|
7961
8115
|
return;
|
|
7962
8116
|
}
|
|
7963
8117
|
const commitment = await import_TransferCommitment4.TransferCommitment.fromJSON(commitmentInput);
|
|
7964
8118
|
if (!this.deps.oracle.waitForProofSdk) {
|
|
7965
|
-
|
|
8119
|
+
logger.debug("Payments", "Cannot finalize - no waitForProofSdk");
|
|
7966
8120
|
token.status = "confirmed";
|
|
7967
8121
|
token.updatedAt = Date.now();
|
|
7968
8122
|
await this.save();
|
|
@@ -7974,7 +8128,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7974
8128
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
7975
8129
|
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
7976
8130
|
if (!stClient || !trustBase) {
|
|
7977
|
-
|
|
8131
|
+
logger.debug("Payments", "Cannot finalize - missing state transition client or trust base");
|
|
7978
8132
|
token.status = "confirmed";
|
|
7979
8133
|
token.updatedAt = Date.now();
|
|
7980
8134
|
await this.save();
|
|
@@ -7994,7 +8148,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7994
8148
|
};
|
|
7995
8149
|
this.tokens.set(tokenId, finalizedToken);
|
|
7996
8150
|
await this.save();
|
|
7997
|
-
|
|
8151
|
+
logger.debug("Payments", `NOSTR-FIRST: Token ${tokenId.slice(0, 8)}... finalized and confirmed`);
|
|
7998
8152
|
this.deps.emitEvent("transfer:confirmed", {
|
|
7999
8153
|
id: crypto.randomUUID(),
|
|
8000
8154
|
status: "completed",
|
|
@@ -8002,7 +8156,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8002
8156
|
tokenTransfers: []
|
|
8003
8157
|
});
|
|
8004
8158
|
} catch (error) {
|
|
8005
|
-
|
|
8159
|
+
logger.error("Payments", "Failed to finalize received token:", error);
|
|
8006
8160
|
const token = this.tokens.get(tokenId);
|
|
8007
8161
|
if (token && token.status === "submitted") {
|
|
8008
8162
|
token.status = "confirmed";
|
|
@@ -8017,7 +8171,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8017
8171
|
}
|
|
8018
8172
|
try {
|
|
8019
8173
|
const payload = transfer.payload;
|
|
8020
|
-
|
|
8174
|
+
logger.debug("Payments", "handleIncomingTransfer: keys=", Object.keys(payload).join(","));
|
|
8021
8175
|
let combinedBundle = null;
|
|
8022
8176
|
if (isCombinedTransferBundleV6(payload)) {
|
|
8023
8177
|
combinedBundle = payload;
|
|
@@ -8031,12 +8185,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8031
8185
|
}
|
|
8032
8186
|
}
|
|
8033
8187
|
if (combinedBundle) {
|
|
8034
|
-
|
|
8188
|
+
logger.debug("Payments", "Processing COMBINED_TRANSFER V6 bundle...");
|
|
8035
8189
|
try {
|
|
8036
8190
|
await this.processCombinedTransferBundle(combinedBundle, transfer.senderTransportPubkey);
|
|
8037
|
-
|
|
8191
|
+
logger.debug("Payments", "COMBINED_TRANSFER V6 processed successfully");
|
|
8038
8192
|
} catch (err) {
|
|
8039
|
-
|
|
8193
|
+
logger.error("Payments", "COMBINED_TRANSFER V6 processing error:", err);
|
|
8040
8194
|
}
|
|
8041
8195
|
return;
|
|
8042
8196
|
}
|
|
@@ -8053,7 +8207,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8053
8207
|
}
|
|
8054
8208
|
}
|
|
8055
8209
|
if (instantBundle) {
|
|
8056
|
-
|
|
8210
|
+
logger.debug("Payments", "Processing INSTANT_SPLIT bundle...");
|
|
8057
8211
|
try {
|
|
8058
8212
|
const result = await this.processInstantSplitBundle(
|
|
8059
8213
|
instantBundle,
|
|
@@ -8061,28 +8215,28 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8061
8215
|
payload.memo
|
|
8062
8216
|
);
|
|
8063
8217
|
if (result.success) {
|
|
8064
|
-
|
|
8218
|
+
logger.debug("Payments", "INSTANT_SPLIT processed successfully");
|
|
8065
8219
|
} else {
|
|
8066
|
-
|
|
8220
|
+
logger.warn("Payments", "INSTANT_SPLIT processing failed:", result.error);
|
|
8067
8221
|
}
|
|
8068
8222
|
} catch (err) {
|
|
8069
|
-
|
|
8223
|
+
logger.error("Payments", "INSTANT_SPLIT processing error:", err);
|
|
8070
8224
|
}
|
|
8071
8225
|
return;
|
|
8072
8226
|
}
|
|
8073
8227
|
if (payload.sourceToken && payload.commitmentData && !payload.transferTx) {
|
|
8074
|
-
|
|
8228
|
+
logger.debug("Payments", "NOSTR-FIRST commitment-only transfer detected");
|
|
8075
8229
|
await this.handleCommitmentOnlyTransfer(transfer, payload);
|
|
8076
8230
|
return;
|
|
8077
8231
|
}
|
|
8078
8232
|
let tokenData;
|
|
8079
8233
|
let finalizedSdkToken = null;
|
|
8080
8234
|
if (payload.sourceToken && payload.transferTx) {
|
|
8081
|
-
|
|
8235
|
+
logger.debug("Payments", "Processing Sphere wallet format transfer...");
|
|
8082
8236
|
const sourceTokenInput = typeof payload.sourceToken === "string" ? JSON.parse(payload.sourceToken) : payload.sourceToken;
|
|
8083
8237
|
const transferTxInput = typeof payload.transferTx === "string" ? JSON.parse(payload.transferTx) : payload.transferTx;
|
|
8084
8238
|
if (!sourceTokenInput || !transferTxInput) {
|
|
8085
|
-
|
|
8239
|
+
logger.warn("Payments", "Invalid Sphere wallet transfer format");
|
|
8086
8240
|
return;
|
|
8087
8241
|
}
|
|
8088
8242
|
let sourceToken;
|
|
@@ -8090,7 +8244,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8090
8244
|
try {
|
|
8091
8245
|
sourceToken = await import_Token6.Token.fromJSON(sourceTokenInput);
|
|
8092
8246
|
} catch (err) {
|
|
8093
|
-
|
|
8247
|
+
logger.error("Payments", "Failed to parse sourceToken:", err);
|
|
8094
8248
|
return;
|
|
8095
8249
|
}
|
|
8096
8250
|
try {
|
|
@@ -8104,16 +8258,16 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8104
8258
|
const commitment = await import_TransferCommitment4.TransferCommitment.fromJSON(transferTxInput);
|
|
8105
8259
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
8106
8260
|
if (!stClient) {
|
|
8107
|
-
|
|
8261
|
+
logger.error("Payments", "Cannot process commitment - no state transition client");
|
|
8108
8262
|
return;
|
|
8109
8263
|
}
|
|
8110
8264
|
const response = await stClient.submitTransferCommitment(commitment);
|
|
8111
8265
|
if (response.status !== "SUCCESS" && response.status !== "REQUEST_ID_EXISTS") {
|
|
8112
|
-
|
|
8266
|
+
logger.error("Payments", "Transfer commitment submission failed:", response.status);
|
|
8113
8267
|
return;
|
|
8114
8268
|
}
|
|
8115
8269
|
if (!this.deps.oracle.waitForProofSdk) {
|
|
8116
|
-
|
|
8270
|
+
logger.error("Payments", "Cannot wait for proof - missing oracle method");
|
|
8117
8271
|
return;
|
|
8118
8272
|
}
|
|
8119
8273
|
const inclusionProof = await this.deps.oracle.waitForProofSdk(commitment);
|
|
@@ -8125,7 +8279,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8125
8279
|
const commitment = await import_TransferCommitment4.TransferCommitment.fromJSON(transferTxInput);
|
|
8126
8280
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
8127
8281
|
if (!stClient || !this.deps.oracle.waitForProofSdk) {
|
|
8128
|
-
throw new
|
|
8282
|
+
throw new SphereError("Cannot submit commitment - missing oracle methods", "AGGREGATOR_ERROR");
|
|
8129
8283
|
}
|
|
8130
8284
|
await stClient.submitTransferCommitment(commitment);
|
|
8131
8285
|
const inclusionProof = await this.deps.oracle.waitForProofSdk(commitment);
|
|
@@ -8133,33 +8287,33 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8133
8287
|
}
|
|
8134
8288
|
}
|
|
8135
8289
|
} catch (err) {
|
|
8136
|
-
|
|
8290
|
+
logger.error("Payments", "Failed to parse transferTx:", err);
|
|
8137
8291
|
return;
|
|
8138
8292
|
}
|
|
8139
8293
|
try {
|
|
8140
8294
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
8141
8295
|
const trustBase = this.deps.oracle.getTrustBase?.();
|
|
8142
8296
|
if (!stClient || !trustBase) {
|
|
8143
|
-
|
|
8297
|
+
logger.error("Payments", "Cannot finalize - missing state transition client or trust base. Token rejected.");
|
|
8144
8298
|
return;
|
|
8145
8299
|
}
|
|
8146
8300
|
finalizedSdkToken = await this.finalizeTransferToken(sourceToken, transferTx, stClient, trustBase);
|
|
8147
8301
|
tokenData = finalizedSdkToken.toJSON();
|
|
8148
8302
|
const addressScheme = transferTx.data.recipient.scheme;
|
|
8149
|
-
|
|
8303
|
+
logger.debug("Payments", `${addressScheme === import_AddressScheme.AddressScheme.PROXY ? "PROXY" : "DIRECT"} finalization successful`);
|
|
8150
8304
|
} catch (finalizeError) {
|
|
8151
|
-
|
|
8305
|
+
logger.error("Payments", "Finalization FAILED - token rejected:", finalizeError);
|
|
8152
8306
|
return;
|
|
8153
8307
|
}
|
|
8154
8308
|
} else if (payload.token) {
|
|
8155
8309
|
tokenData = payload.token;
|
|
8156
8310
|
} else {
|
|
8157
|
-
|
|
8311
|
+
logger.warn("Payments", "Unknown transfer payload format");
|
|
8158
8312
|
return;
|
|
8159
8313
|
}
|
|
8160
8314
|
const validation = await this.deps.oracle.validateToken(tokenData);
|
|
8161
8315
|
if (!validation.valid) {
|
|
8162
|
-
|
|
8316
|
+
logger.warn("Payments", "Received invalid token");
|
|
8163
8317
|
return;
|
|
8164
8318
|
}
|
|
8165
8319
|
const tokenInfo = await parseTokenInfo(tokenData);
|
|
@@ -8200,12 +8354,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8200
8354
|
receivedAt: transfer.timestamp
|
|
8201
8355
|
};
|
|
8202
8356
|
this.deps.emitEvent("transfer:incoming", incomingTransfer);
|
|
8203
|
-
|
|
8357
|
+
logger.debug("Payments", `Incoming transfer processed: ${token.id}, ${token.amount} ${token.symbol}`);
|
|
8204
8358
|
} else {
|
|
8205
|
-
|
|
8359
|
+
logger.debug("Payments", `Duplicate transfer ignored: ${token.id}, ${token.amount} ${token.symbol}`);
|
|
8206
8360
|
}
|
|
8207
8361
|
} catch (error) {
|
|
8208
|
-
|
|
8362
|
+
logger.error("Payments", "Failed to process incoming transfer:", error);
|
|
8209
8363
|
}
|
|
8210
8364
|
}
|
|
8211
8365
|
// ===========================================================================
|
|
@@ -8220,15 +8374,15 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8220
8374
|
if (existingArchive) {
|
|
8221
8375
|
if (isIncrementalUpdate(existingArchive, txf)) {
|
|
8222
8376
|
this.archivedTokens.set(tokenId, txf);
|
|
8223
|
-
|
|
8377
|
+
logger.debug("Payments", `Updated archived token ${tokenId.slice(0, 8)}...`);
|
|
8224
8378
|
} else {
|
|
8225
8379
|
const stateHash = getCurrentStateHash(txf) || "";
|
|
8226
8380
|
await this.storeForkedToken(tokenId, stateHash, txf);
|
|
8227
|
-
|
|
8381
|
+
logger.debug("Payments", `Archived token ${tokenId.slice(0, 8)}... is a fork`);
|
|
8228
8382
|
}
|
|
8229
8383
|
} else {
|
|
8230
8384
|
this.archivedTokens.set(tokenId, txf);
|
|
8231
|
-
|
|
8385
|
+
logger.debug("Payments", `Archived token ${tokenId.slice(0, 8)}...`);
|
|
8232
8386
|
}
|
|
8233
8387
|
}
|
|
8234
8388
|
// ===========================================================================
|
|
@@ -8240,20 +8394,20 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8240
8394
|
const txf = tokenToTxf(t);
|
|
8241
8395
|
return `${t.id.slice(0, 12)}(${t.status},txf=${!!txf})`;
|
|
8242
8396
|
});
|
|
8243
|
-
|
|
8397
|
+
logger.debug("Payments", `save(): providers=${providers.size}, tokens=[${tokenStats.join(", ")}]`);
|
|
8244
8398
|
if (providers.size > 0) {
|
|
8245
8399
|
const data = await this.createStorageData();
|
|
8246
8400
|
const dataKeys = Object.keys(data).filter((k) => k.startsWith("token-"));
|
|
8247
|
-
|
|
8401
|
+
logger.debug("Payments", `save(): TXF keys=${dataKeys.length} (${dataKeys.join(", ")})`);
|
|
8248
8402
|
for (const [id, provider] of providers) {
|
|
8249
8403
|
try {
|
|
8250
8404
|
await provider.save(data);
|
|
8251
8405
|
} catch (err) {
|
|
8252
|
-
|
|
8406
|
+
logger.error("Payments", `Failed to save to provider ${id}:`, err);
|
|
8253
8407
|
}
|
|
8254
8408
|
}
|
|
8255
8409
|
} else {
|
|
8256
|
-
|
|
8410
|
+
logger.debug("Payments", "save(): No token storage providers - TXF not persisted");
|
|
8257
8411
|
}
|
|
8258
8412
|
await this.savePendingV5Tokens();
|
|
8259
8413
|
}
|
|
@@ -8291,14 +8445,14 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8291
8445
|
}
|
|
8292
8446
|
loadFromStorageData(data) {
|
|
8293
8447
|
const parsed = parseTxfStorageData(data);
|
|
8294
|
-
|
|
8448
|
+
logger.debug("Payments", `loadFromStorageData: parsed ${parsed.tokens.length} tokens, ${parsed.tombstones.length} tombstones, errors=[${parsed.validationErrors.join("; ")}]`);
|
|
8295
8449
|
this.tombstones = parsed.tombstones;
|
|
8296
8450
|
this.tokens.clear();
|
|
8297
8451
|
for (const token of parsed.tokens) {
|
|
8298
8452
|
const sdkTokenId = extractTokenIdFromSdkData(token.sdkData);
|
|
8299
8453
|
const stateHash = extractStateHashFromSdkData(token.sdkData);
|
|
8300
8454
|
if (sdkTokenId && stateHash && this.isStateTombstoned(sdkTokenId, stateHash)) {
|
|
8301
|
-
|
|
8455
|
+
logger.debug("Payments", `Skipping tombstoned token ${sdkTokenId.slice(0, 8)}... during load (exact state match)`);
|
|
8302
8456
|
continue;
|
|
8303
8457
|
}
|
|
8304
8458
|
this.tokens.set(token.id, token);
|
|
@@ -8318,12 +8472,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8318
8472
|
try {
|
|
8319
8473
|
const stClient = this.deps.oracle.getStateTransitionClient?.();
|
|
8320
8474
|
if (!stClient) {
|
|
8321
|
-
|
|
8475
|
+
logger.debug("Payments", "Cannot submit commitment - no state transition client");
|
|
8322
8476
|
return;
|
|
8323
8477
|
}
|
|
8324
8478
|
const response = await stClient.submitTransferCommitment(commitment);
|
|
8325
8479
|
if (response.status !== "SUCCESS" && response.status !== "REQUEST_ID_EXISTS") {
|
|
8326
|
-
|
|
8480
|
+
logger.debug("Payments", `Transfer commitment submission failed: ${response.status}`);
|
|
8327
8481
|
const token = this.tokens.get(tokenId);
|
|
8328
8482
|
if (token) {
|
|
8329
8483
|
token.status = "invalid";
|
|
@@ -8343,7 +8497,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8343
8497
|
onProofReceived
|
|
8344
8498
|
});
|
|
8345
8499
|
} catch (error) {
|
|
8346
|
-
|
|
8500
|
+
logger.debug("Payments", "submitAndPollForProof error:", error);
|
|
8347
8501
|
}
|
|
8348
8502
|
}
|
|
8349
8503
|
/**
|
|
@@ -8351,7 +8505,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8351
8505
|
*/
|
|
8352
8506
|
addProofPollingJob(job) {
|
|
8353
8507
|
this.proofPollingJobs.set(job.tokenId, job);
|
|
8354
|
-
|
|
8508
|
+
logger.debug("Payments", `Added proof polling job for token ${job.tokenId.slice(0, 8)}...`);
|
|
8355
8509
|
this.startProofPolling();
|
|
8356
8510
|
}
|
|
8357
8511
|
/**
|
|
@@ -8360,7 +8514,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8360
8514
|
startProofPolling() {
|
|
8361
8515
|
if (this.proofPollingInterval) return;
|
|
8362
8516
|
if (this.proofPollingJobs.size === 0) return;
|
|
8363
|
-
|
|
8517
|
+
logger.debug("Payments", "Starting proof polling...");
|
|
8364
8518
|
this.proofPollingInterval = setInterval(
|
|
8365
8519
|
() => this.processProofPollingQueue(),
|
|
8366
8520
|
_PaymentsModule.PROOF_POLLING_INTERVAL_MS
|
|
@@ -8373,7 +8527,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8373
8527
|
if (this.proofPollingInterval) {
|
|
8374
8528
|
clearInterval(this.proofPollingInterval);
|
|
8375
8529
|
this.proofPollingInterval = null;
|
|
8376
|
-
|
|
8530
|
+
logger.debug("Payments", "Stopped proof polling");
|
|
8377
8531
|
}
|
|
8378
8532
|
}
|
|
8379
8533
|
/**
|
|
@@ -8390,7 +8544,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8390
8544
|
job.attemptCount++;
|
|
8391
8545
|
job.lastAttemptAt = Date.now();
|
|
8392
8546
|
if (job.attemptCount >= _PaymentsModule.PROOF_POLLING_MAX_ATTEMPTS) {
|
|
8393
|
-
|
|
8547
|
+
logger.debug("Payments", `Proof polling timeout for token ${tokenId.slice(0, 8)}...`);
|
|
8394
8548
|
const token2 = this.tokens.get(tokenId);
|
|
8395
8549
|
if (token2 && token2.status === "submitted") {
|
|
8396
8550
|
token2.status = "invalid";
|
|
@@ -8429,12 +8583,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8429
8583
|
token.updatedAt = Date.now();
|
|
8430
8584
|
this.tokens.set(tokenId, token);
|
|
8431
8585
|
await this.save();
|
|
8432
|
-
|
|
8586
|
+
logger.debug("Payments", `Proof received for token ${tokenId.slice(0, 8)}..., status: spent`);
|
|
8433
8587
|
}
|
|
8434
8588
|
job.onProofReceived?.(tokenId);
|
|
8435
8589
|
completedJobs.push(tokenId);
|
|
8436
8590
|
} catch (error) {
|
|
8437
|
-
|
|
8591
|
+
logger.debug("Payments", `Proof polling attempt ${job.attemptCount} for ${tokenId.slice(0, 8)}...: ${error}`);
|
|
8438
8592
|
}
|
|
8439
8593
|
}
|
|
8440
8594
|
for (const tokenId of completedJobs) {
|
|
@@ -8449,7 +8603,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
8449
8603
|
// ===========================================================================
|
|
8450
8604
|
ensureInitialized() {
|
|
8451
8605
|
if (!this.deps) {
|
|
8452
|
-
throw new
|
|
8606
|
+
throw new SphereError("PaymentsModule not initialized", "NOT_INITIALIZED");
|
|
8453
8607
|
}
|
|
8454
8608
|
}
|
|
8455
8609
|
};
|
|
@@ -8457,7 +8611,12 @@ function createPaymentsModule(config) {
|
|
|
8457
8611
|
return new PaymentsModule(config);
|
|
8458
8612
|
}
|
|
8459
8613
|
|
|
8614
|
+
// modules/payments/BackgroundCommitmentService.ts
|
|
8615
|
+
init_logger();
|
|
8616
|
+
init_errors();
|
|
8617
|
+
|
|
8460
8618
|
// modules/payments/TokenRecoveryService.ts
|
|
8619
|
+
init_logger();
|
|
8461
8620
|
var import_TokenId4 = require("@unicitylabs/state-transition-sdk/lib/token/TokenId");
|
|
8462
8621
|
var import_TokenState6 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
|
|
8463
8622
|
var import_TokenType4 = require("@unicitylabs/state-transition-sdk/lib/token/TokenType");
|
|
@@ -8466,6 +8625,8 @@ var import_HashAlgorithm6 = require("@unicitylabs/state-transition-sdk/lib/hash/
|
|
|
8466
8625
|
var import_UnmaskedPredicate6 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate");
|
|
8467
8626
|
|
|
8468
8627
|
// modules/communications/CommunicationsModule.ts
|
|
8628
|
+
init_logger();
|
|
8629
|
+
init_errors();
|
|
8469
8630
|
init_constants();
|
|
8470
8631
|
var CommunicationsModule = class {
|
|
8471
8632
|
config;
|
|
@@ -8560,7 +8721,7 @@ var CommunicationsModule = class {
|
|
|
8560
8721
|
}
|
|
8561
8722
|
if (myMessages.length > 0) {
|
|
8562
8723
|
await this.save();
|
|
8563
|
-
|
|
8724
|
+
logger.debug("Communications", `Migrated ${myMessages.length} messages to per-address storage`);
|
|
8564
8725
|
}
|
|
8565
8726
|
}
|
|
8566
8727
|
}
|
|
@@ -8646,7 +8807,7 @@ var CommunicationsModule = class {
|
|
|
8646
8807
|
const msg = this.messages.get(id);
|
|
8647
8808
|
if (msg && msg.senderPubkey !== this.deps.identity.chainPubkey) {
|
|
8648
8809
|
this.deps.transport.sendReadReceipt(msg.senderPubkey, id).catch((err) => {
|
|
8649
|
-
|
|
8810
|
+
logger.warn("Communications", "Failed to send read receipt:", err);
|
|
8650
8811
|
});
|
|
8651
8812
|
}
|
|
8652
8813
|
}
|
|
@@ -8846,7 +9007,7 @@ var CommunicationsModule = class {
|
|
|
8846
9007
|
try {
|
|
8847
9008
|
handler(message);
|
|
8848
9009
|
} catch (error) {
|
|
8849
|
-
|
|
9010
|
+
logger.error("Communications", "Handler error:", error);
|
|
8850
9011
|
}
|
|
8851
9012
|
}
|
|
8852
9013
|
if (this.config.autoSave) {
|
|
@@ -8865,7 +9026,7 @@ var CommunicationsModule = class {
|
|
|
8865
9026
|
try {
|
|
8866
9027
|
handler(composing);
|
|
8867
9028
|
} catch (error) {
|
|
8868
|
-
|
|
9029
|
+
logger.error("Communications", "Composing handler error:", error);
|
|
8869
9030
|
}
|
|
8870
9031
|
}
|
|
8871
9032
|
}
|
|
@@ -8883,7 +9044,7 @@ var CommunicationsModule = class {
|
|
|
8883
9044
|
try {
|
|
8884
9045
|
handler(message);
|
|
8885
9046
|
} catch (error) {
|
|
8886
|
-
|
|
9047
|
+
logger.error("Communications", "Handler error:", error);
|
|
8887
9048
|
}
|
|
8888
9049
|
}
|
|
8889
9050
|
}
|
|
@@ -8924,7 +9085,7 @@ var CommunicationsModule = class {
|
|
|
8924
9085
|
const nametag = recipient.slice(1);
|
|
8925
9086
|
const pubkey = await this.deps.transport.resolveNametag?.(nametag);
|
|
8926
9087
|
if (!pubkey) {
|
|
8927
|
-
throw new
|
|
9088
|
+
throw new SphereError(`Nametag not found: ${recipient}`, "INVALID_RECIPIENT");
|
|
8928
9089
|
}
|
|
8929
9090
|
return { pubkey, nametag };
|
|
8930
9091
|
}
|
|
@@ -8932,7 +9093,7 @@ var CommunicationsModule = class {
|
|
|
8932
9093
|
}
|
|
8933
9094
|
ensureInitialized() {
|
|
8934
9095
|
if (!this.deps) {
|
|
8935
|
-
throw new
|
|
9096
|
+
throw new SphereError("CommunicationsModule not initialized", "NOT_INITIALIZED");
|
|
8936
9097
|
}
|
|
8937
9098
|
}
|
|
8938
9099
|
};
|
|
@@ -8942,6 +9103,8 @@ function createCommunicationsModule(config) {
|
|
|
8942
9103
|
|
|
8943
9104
|
// modules/groupchat/GroupChatModule.ts
|
|
8944
9105
|
var import_nostr_js_sdk2 = require("@unicitylabs/nostr-js-sdk");
|
|
9106
|
+
init_logger();
|
|
9107
|
+
init_errors();
|
|
8945
9108
|
init_constants();
|
|
8946
9109
|
|
|
8947
9110
|
// modules/groupchat/types.ts
|
|
@@ -9087,13 +9250,15 @@ var GroupChatModule = class {
|
|
|
9087
9250
|
for (const subId of this.subscriptionIds) {
|
|
9088
9251
|
try {
|
|
9089
9252
|
this.client.unsubscribe(subId);
|
|
9090
|
-
} catch {
|
|
9253
|
+
} catch (err) {
|
|
9254
|
+
logger.debug("GroupChat", "Failed to unsubscribe", err);
|
|
9091
9255
|
}
|
|
9092
9256
|
}
|
|
9093
9257
|
this.subscriptionIds = [];
|
|
9094
9258
|
try {
|
|
9095
9259
|
this.client.disconnect();
|
|
9096
|
-
} catch {
|
|
9260
|
+
} catch (err) {
|
|
9261
|
+
logger.debug("GroupChat", "Failed to disconnect", err);
|
|
9097
9262
|
}
|
|
9098
9263
|
this.client = null;
|
|
9099
9264
|
}
|
|
@@ -9133,7 +9298,8 @@ var GroupChatModule = class {
|
|
|
9133
9298
|
for (const subId of this.subscriptionIds) {
|
|
9134
9299
|
try {
|
|
9135
9300
|
this.client.unsubscribe(subId);
|
|
9136
|
-
} catch {
|
|
9301
|
+
} catch (err) {
|
|
9302
|
+
logger.debug("GroupChat", "Failed to unsubscribe", err);
|
|
9137
9303
|
}
|
|
9138
9304
|
}
|
|
9139
9305
|
this.subscriptionIds = [];
|
|
@@ -9168,21 +9334,21 @@ var GroupChatModule = class {
|
|
|
9168
9334
|
}
|
|
9169
9335
|
this.deps.emitEvent("groupchat:connection", { connected: true });
|
|
9170
9336
|
} catch (error) {
|
|
9171
|
-
|
|
9337
|
+
logger.error("GroupChat", "Failed to connect to relays", error);
|
|
9172
9338
|
this.deps.emitEvent("groupchat:connection", { connected: false });
|
|
9173
9339
|
this.scheduleReconnect();
|
|
9174
9340
|
}
|
|
9175
9341
|
}
|
|
9176
9342
|
scheduleReconnect() {
|
|
9177
9343
|
if (this.reconnectAttempts >= this.config.maxReconnectAttempts) {
|
|
9178
|
-
|
|
9344
|
+
logger.error("GroupChat", "Max reconnection attempts reached");
|
|
9179
9345
|
return;
|
|
9180
9346
|
}
|
|
9181
9347
|
this.reconnectAttempts++;
|
|
9182
9348
|
this.reconnectTimer = setTimeout(() => {
|
|
9183
9349
|
this.reconnectTimer = null;
|
|
9184
9350
|
if (this.deps) {
|
|
9185
|
-
this.connect().catch(
|
|
9351
|
+
this.connect().catch((err) => logger.error("GroupChat", "Reconnect failed:", err));
|
|
9186
9352
|
}
|
|
9187
9353
|
}, this.config.reconnectDelayMs);
|
|
9188
9354
|
}
|
|
@@ -9417,7 +9583,8 @@ var GroupChatModule = class {
|
|
|
9417
9583
|
},
|
|
9418
9584
|
onComplete: () => {
|
|
9419
9585
|
},
|
|
9420
|
-
timeoutMs: 15e3
|
|
9586
|
+
timeoutMs: 15e3,
|
|
9587
|
+
timeoutLabel: "restoreJoinedGroups"
|
|
9421
9588
|
}
|
|
9422
9589
|
);
|
|
9423
9590
|
if (groupIdsWithMembership.size === 0) return [];
|
|
@@ -9434,7 +9601,8 @@ var GroupChatModule = class {
|
|
|
9434
9601
|
this.fetchMessages(groupId)
|
|
9435
9602
|
]);
|
|
9436
9603
|
}
|
|
9437
|
-
} catch {
|
|
9604
|
+
} catch (error) {
|
|
9605
|
+
logger.warn("GroupChat", "Failed to restore group", groupId, error);
|
|
9438
9606
|
}
|
|
9439
9607
|
}
|
|
9440
9608
|
if (restoredGroups.length > 0) {
|
|
@@ -9467,7 +9635,8 @@ var GroupChatModule = class {
|
|
|
9467
9635
|
},
|
|
9468
9636
|
onComplete: () => {
|
|
9469
9637
|
},
|
|
9470
|
-
timeoutMs: 1e4
|
|
9638
|
+
timeoutMs: 1e4,
|
|
9639
|
+
timeoutLabel: "fetchAvailableGroups(metadata)"
|
|
9471
9640
|
}
|
|
9472
9641
|
),
|
|
9473
9642
|
this.oneshotSubscription(
|
|
@@ -9482,7 +9651,8 @@ var GroupChatModule = class {
|
|
|
9482
9651
|
},
|
|
9483
9652
|
onComplete: () => {
|
|
9484
9653
|
},
|
|
9485
|
-
timeoutMs: 1e4
|
|
9654
|
+
timeoutMs: 1e4,
|
|
9655
|
+
timeoutLabel: "fetchAvailableGroups(members)"
|
|
9486
9656
|
}
|
|
9487
9657
|
)
|
|
9488
9658
|
]);
|
|
@@ -9541,7 +9711,7 @@ var GroupChatModule = class {
|
|
|
9541
9711
|
return true;
|
|
9542
9712
|
}
|
|
9543
9713
|
}
|
|
9544
|
-
|
|
9714
|
+
logger.error("GroupChat", "Failed to join group", error);
|
|
9545
9715
|
return false;
|
|
9546
9716
|
}
|
|
9547
9717
|
}
|
|
@@ -9571,7 +9741,7 @@ var GroupChatModule = class {
|
|
|
9571
9741
|
this.persistAll();
|
|
9572
9742
|
return true;
|
|
9573
9743
|
}
|
|
9574
|
-
|
|
9744
|
+
logger.error("GroupChat", "Failed to leave group", error);
|
|
9575
9745
|
return false;
|
|
9576
9746
|
}
|
|
9577
9747
|
}
|
|
@@ -9622,10 +9792,8 @@ var GroupChatModule = class {
|
|
|
9622
9792
|
kind: NIP29_KINDS.JOIN_REQUEST,
|
|
9623
9793
|
tags: [["h", group.id]],
|
|
9624
9794
|
content: ""
|
|
9625
|
-
}).catch(() =>
|
|
9626
|
-
|
|
9627
|
-
await this.fetchAndSaveMembers(group.id).catch(() => {
|
|
9628
|
-
});
|
|
9795
|
+
}).catch((err) => logger.debug("GroupChat", "Background operation failed", err));
|
|
9796
|
+
await this.fetchAndSaveMembers(group.id).catch((err) => logger.debug("GroupChat", "Failed to fetch members", group.id, err));
|
|
9629
9797
|
this.saveMemberToMemory({
|
|
9630
9798
|
pubkey: creatorPubkey,
|
|
9631
9799
|
groupId: group.id,
|
|
@@ -9637,7 +9805,7 @@ var GroupChatModule = class {
|
|
|
9637
9805
|
this.schedulePersist();
|
|
9638
9806
|
return group;
|
|
9639
9807
|
} catch (error) {
|
|
9640
|
-
|
|
9808
|
+
logger.error("GroupChat", "Failed to create group", error);
|
|
9641
9809
|
return null;
|
|
9642
9810
|
}
|
|
9643
9811
|
}
|
|
@@ -9664,7 +9832,7 @@ var GroupChatModule = class {
|
|
|
9664
9832
|
}
|
|
9665
9833
|
return false;
|
|
9666
9834
|
} catch (error) {
|
|
9667
|
-
|
|
9835
|
+
logger.error("GroupChat", "Failed to delete group", error);
|
|
9668
9836
|
return false;
|
|
9669
9837
|
}
|
|
9670
9838
|
}
|
|
@@ -9684,7 +9852,7 @@ var GroupChatModule = class {
|
|
|
9684
9852
|
});
|
|
9685
9853
|
return eventId ? inviteCode : null;
|
|
9686
9854
|
} catch (error) {
|
|
9687
|
-
|
|
9855
|
+
logger.error("GroupChat", "Failed to create invite", error);
|
|
9688
9856
|
return null;
|
|
9689
9857
|
}
|
|
9690
9858
|
}
|
|
@@ -9734,7 +9902,7 @@ var GroupChatModule = class {
|
|
|
9734
9902
|
}
|
|
9735
9903
|
return null;
|
|
9736
9904
|
} catch (error) {
|
|
9737
|
-
|
|
9905
|
+
logger.error("GroupChat", "Failed to send message", error);
|
|
9738
9906
|
return null;
|
|
9739
9907
|
}
|
|
9740
9908
|
}
|
|
@@ -9773,7 +9941,8 @@ var GroupChatModule = class {
|
|
|
9773
9941
|
this.schedulePersist();
|
|
9774
9942
|
return fetchedMessages;
|
|
9775
9943
|
},
|
|
9776
|
-
timeoutMs: 1e4
|
|
9944
|
+
timeoutMs: 1e4,
|
|
9945
|
+
timeoutLabel: `fetchMessages(${groupId})`
|
|
9777
9946
|
});
|
|
9778
9947
|
}
|
|
9779
9948
|
// ===========================================================================
|
|
@@ -9834,7 +10003,7 @@ var GroupChatModule = class {
|
|
|
9834
10003
|
}
|
|
9835
10004
|
return false;
|
|
9836
10005
|
} catch (error) {
|
|
9837
|
-
|
|
10006
|
+
logger.error("GroupChat", "Failed to kick user", error);
|
|
9838
10007
|
return false;
|
|
9839
10008
|
}
|
|
9840
10009
|
}
|
|
@@ -9857,7 +10026,7 @@ var GroupChatModule = class {
|
|
|
9857
10026
|
}
|
|
9858
10027
|
return false;
|
|
9859
10028
|
} catch (error) {
|
|
9860
|
-
|
|
10029
|
+
logger.error("GroupChat", "Failed to delete message", error);
|
|
9861
10030
|
return false;
|
|
9862
10031
|
}
|
|
9863
10032
|
}
|
|
@@ -10142,7 +10311,7 @@ var GroupChatModule = class {
|
|
|
10142
10311
|
this.persistTimer = setTimeout(() => {
|
|
10143
10312
|
this.persistTimer = null;
|
|
10144
10313
|
this.persistPromise = this.doPersistAll().catch((err) => {
|
|
10145
|
-
|
|
10314
|
+
logger.error("GroupChat", "Persistence error:", err);
|
|
10146
10315
|
}).finally(() => {
|
|
10147
10316
|
this.persistPromise = null;
|
|
10148
10317
|
});
|
|
@@ -10322,7 +10491,8 @@ var GroupChatModule = class {
|
|
|
10322
10491
|
if (state.subId) {
|
|
10323
10492
|
try {
|
|
10324
10493
|
this.client.unsubscribe(state.subId);
|
|
10325
|
-
} catch {
|
|
10494
|
+
} catch (err) {
|
|
10495
|
+
logger.debug("GroupChat", "Failed to unsubscribe", err);
|
|
10326
10496
|
}
|
|
10327
10497
|
const idx = this.subscriptionIds.indexOf(state.subId);
|
|
10328
10498
|
if (idx >= 0) this.subscriptionIds.splice(idx, 1);
|
|
@@ -10337,12 +10507,17 @@ var GroupChatModule = class {
|
|
|
10337
10507
|
});
|
|
10338
10508
|
state.subId = subId;
|
|
10339
10509
|
this.subscriptionIds.push(subId);
|
|
10340
|
-
setTimeout(
|
|
10510
|
+
setTimeout(() => {
|
|
10511
|
+
if (!done && opts.timeoutLabel) {
|
|
10512
|
+
logger.warn("GroupChat", `${opts.timeoutLabel} timed out`);
|
|
10513
|
+
}
|
|
10514
|
+
finish();
|
|
10515
|
+
}, opts.timeoutMs ?? 5e3);
|
|
10341
10516
|
});
|
|
10342
10517
|
}
|
|
10343
10518
|
ensureInitialized() {
|
|
10344
10519
|
if (!this.deps) {
|
|
10345
|
-
throw new
|
|
10520
|
+
throw new SphereError("GroupChatModule not initialized", "NOT_INITIALIZED");
|
|
10346
10521
|
}
|
|
10347
10522
|
}
|
|
10348
10523
|
async ensureConnected() {
|
|
@@ -12511,6 +12686,7 @@ var Pointk1 = /* @__PURE__ */ weierstrass(secp256k1_CURVE, {
|
|
|
12511
12686
|
var secp256k1 = /* @__PURE__ */ ecdsa(Pointk1, sha2564);
|
|
12512
12687
|
|
|
12513
12688
|
// modules/market/MarketModule.ts
|
|
12689
|
+
init_errors();
|
|
12514
12690
|
var DEFAULT_MARKET_API_URL = "https://market-api.unicity.network";
|
|
12515
12691
|
function hexToBytes3(hex) {
|
|
12516
12692
|
const len = hex.length >> 1;
|
|
@@ -12700,7 +12876,7 @@ var MarketModule = class {
|
|
|
12700
12876
|
// ---------------------------------------------------------------------------
|
|
12701
12877
|
ensureIdentity() {
|
|
12702
12878
|
if (!this.identity) {
|
|
12703
|
-
throw new
|
|
12879
|
+
throw new SphereError("MarketModule not initialized \u2014 call initialize() first", "NOT_INITIALIZED");
|
|
12704
12880
|
}
|
|
12705
12881
|
}
|
|
12706
12882
|
/** Register the agent's public key with the server (idempotent) */
|
|
@@ -12726,7 +12902,7 @@ var MarketModule = class {
|
|
|
12726
12902
|
data = JSON.parse(text);
|
|
12727
12903
|
} catch {
|
|
12728
12904
|
}
|
|
12729
|
-
throw new
|
|
12905
|
+
throw new SphereError(data?.error ?? `Agent registration failed: HTTP ${res.status}`, "NETWORK_ERROR");
|
|
12730
12906
|
}
|
|
12731
12907
|
async parseResponse(res) {
|
|
12732
12908
|
const text = await res.text();
|
|
@@ -12734,9 +12910,9 @@ var MarketModule = class {
|
|
|
12734
12910
|
try {
|
|
12735
12911
|
data = JSON.parse(text);
|
|
12736
12912
|
} catch {
|
|
12737
|
-
throw new
|
|
12913
|
+
throw new SphereError(`Market API error: HTTP ${res.status} \u2014 unexpected response (not JSON)`, "NETWORK_ERROR");
|
|
12738
12914
|
}
|
|
12739
|
-
if (!res.ok) throw new
|
|
12915
|
+
if (!res.ok) throw new SphereError(data.error ?? `HTTP ${res.status}`, "NETWORK_ERROR");
|
|
12740
12916
|
return data;
|
|
12741
12917
|
}
|
|
12742
12918
|
async apiPost(path, body) {
|
|
@@ -12792,6 +12968,8 @@ init_constants();
|
|
|
12792
12968
|
|
|
12793
12969
|
// core/encryption.ts
|
|
12794
12970
|
var import_crypto_js6 = __toESM(require("crypto-js"), 1);
|
|
12971
|
+
init_errors();
|
|
12972
|
+
init_logger();
|
|
12795
12973
|
function encryptSimple(plaintext, password) {
|
|
12796
12974
|
return import_crypto_js6.default.AES.encrypt(plaintext, password).toString();
|
|
12797
12975
|
}
|
|
@@ -12799,7 +12977,7 @@ function decryptSimple(ciphertext, password) {
|
|
|
12799
12977
|
const decrypted = import_crypto_js6.default.AES.decrypt(ciphertext, password);
|
|
12800
12978
|
const result = decrypted.toString(import_crypto_js6.default.enc.Utf8);
|
|
12801
12979
|
if (!result) {
|
|
12802
|
-
throw new
|
|
12980
|
+
throw new SphereError("Decryption failed: invalid password or corrupted data", "DECRYPTION_ERROR");
|
|
12803
12981
|
}
|
|
12804
12982
|
return result;
|
|
12805
12983
|
}
|
|
@@ -12813,12 +12991,14 @@ function decryptWithSalt(ciphertext, password, salt) {
|
|
|
12813
12991
|
const decrypted = import_crypto_js6.default.AES.decrypt(ciphertext, key);
|
|
12814
12992
|
const result = decrypted.toString(import_crypto_js6.default.enc.Utf8);
|
|
12815
12993
|
return result || null;
|
|
12816
|
-
} catch {
|
|
12994
|
+
} catch (err) {
|
|
12995
|
+
logger.debug("Encryption", "decryptWithSalt failed", err);
|
|
12817
12996
|
return null;
|
|
12818
12997
|
}
|
|
12819
12998
|
}
|
|
12820
12999
|
|
|
12821
13000
|
// core/scan.ts
|
|
13001
|
+
init_logger();
|
|
12822
13002
|
async function scanAddressesImpl(deriveAddress, options = {}) {
|
|
12823
13003
|
const maxAddresses = options.maxAddresses ?? 50;
|
|
12824
13004
|
const gapLimit = options.gapLimit ?? 20;
|
|
@@ -12857,7 +13037,8 @@ async function scanAddressesImpl(deriveAddress, options = {}) {
|
|
|
12857
13037
|
nametag = tag;
|
|
12858
13038
|
nametagsFoundCount++;
|
|
12859
13039
|
}
|
|
12860
|
-
} catch {
|
|
13040
|
+
} catch (err) {
|
|
13041
|
+
logger.debug("Sphere", "Nametag resolution failed during scan", err);
|
|
12861
13042
|
}
|
|
12862
13043
|
}
|
|
12863
13044
|
foundAddresses.push({
|
|
@@ -12874,7 +13055,7 @@ async function scanAddressesImpl(deriveAddress, options = {}) {
|
|
|
12874
13055
|
consecutiveEmpty++;
|
|
12875
13056
|
}
|
|
12876
13057
|
} catch (err) {
|
|
12877
|
-
|
|
13058
|
+
logger.warn("Sphere", `scanAddresses: Error checking ${addrInfo.address}:`, err);
|
|
12878
13059
|
consecutiveEmpty++;
|
|
12879
13060
|
}
|
|
12880
13061
|
if (consecutiveEmpty >= gapLimit) {
|
|
@@ -12966,6 +13147,7 @@ init_network();
|
|
|
12966
13147
|
var import_crypto_js7 = __toESM(require("crypto-js"), 1);
|
|
12967
13148
|
|
|
12968
13149
|
// core/utils.ts
|
|
13150
|
+
init_errors();
|
|
12969
13151
|
var SECP256K1_ORDER = BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
|
|
12970
13152
|
var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
12971
13153
|
function isValidPrivateKey(hex) {
|
|
@@ -13005,7 +13187,7 @@ function base58Decode(str) {
|
|
|
13005
13187
|
for (let i = 0; i < str.length; i++) {
|
|
13006
13188
|
const char = str[i];
|
|
13007
13189
|
if (!(char in ALPHABET_MAP)) {
|
|
13008
|
-
throw new
|
|
13190
|
+
throw new SphereError("Invalid base58 character: " + char, "VALIDATION_ERROR");
|
|
13009
13191
|
}
|
|
13010
13192
|
num = num * BigInt(58) + BigInt(ALPHABET_MAP[char]);
|
|
13011
13193
|
}
|
|
@@ -13288,6 +13470,7 @@ function parseAndDecryptWalletText(content, password) {
|
|
|
13288
13470
|
|
|
13289
13471
|
// serialization/wallet-dat.ts
|
|
13290
13472
|
var import_crypto_js8 = __toESM(require("crypto-js"), 1);
|
|
13473
|
+
init_errors();
|
|
13291
13474
|
function uint8ArrayToWordArray(u8arr) {
|
|
13292
13475
|
const hex = bytesToHex2(u8arr);
|
|
13293
13476
|
return import_crypto_js8.default.enc.Hex.parse(hex);
|
|
@@ -13472,7 +13655,7 @@ async function decryptCMasterKey(cmk, password, onProgress) {
|
|
|
13472
13655
|
);
|
|
13473
13656
|
const result = import_crypto_js8.default.enc.Hex.stringify(decrypted);
|
|
13474
13657
|
if (!result || result.length !== 64) {
|
|
13475
|
-
throw new
|
|
13658
|
+
throw new SphereError("Master key decryption failed - incorrect password", "DECRYPTION_ERROR");
|
|
13476
13659
|
}
|
|
13477
13660
|
return result;
|
|
13478
13661
|
}
|
|
@@ -13767,6 +13950,7 @@ var Sphere = class _Sphere {
|
|
|
13767
13950
|
* ```
|
|
13768
13951
|
*/
|
|
13769
13952
|
static async init(options) {
|
|
13953
|
+
if (options.debug) logger.configure({ debug: true });
|
|
13770
13954
|
_Sphere.configureTokenRegistry(options.storage, options.network);
|
|
13771
13955
|
const groupChat = _Sphere.resolveGroupChatConfig(options.groupChat, options.network);
|
|
13772
13956
|
const market = _Sphere.resolveMarketConfig(options.market);
|
|
@@ -13794,8 +13978,9 @@ var Sphere = class _Sphere {
|
|
|
13794
13978
|
mnemonic = _Sphere.generateMnemonic();
|
|
13795
13979
|
generatedMnemonic = mnemonic;
|
|
13796
13980
|
} else {
|
|
13797
|
-
throw new
|
|
13798
|
-
"No wallet exists and no mnemonic provided. Provide a mnemonic or set autoGenerate: true."
|
|
13981
|
+
throw new SphereError(
|
|
13982
|
+
"No wallet exists and no mnemonic provided. Provide a mnemonic or set autoGenerate: true.",
|
|
13983
|
+
"INVALID_CONFIG"
|
|
13799
13984
|
);
|
|
13800
13985
|
}
|
|
13801
13986
|
}
|
|
@@ -13868,11 +14053,12 @@ var Sphere = class _Sphere {
|
|
|
13868
14053
|
* Create new wallet with mnemonic
|
|
13869
14054
|
*/
|
|
13870
14055
|
static async create(options) {
|
|
14056
|
+
if (options.debug) logger.configure({ debug: true });
|
|
13871
14057
|
if (!options.mnemonic || !_Sphere.validateMnemonic(options.mnemonic)) {
|
|
13872
|
-
throw new
|
|
14058
|
+
throw new SphereError("Invalid mnemonic", "INVALID_IDENTITY");
|
|
13873
14059
|
}
|
|
13874
14060
|
if (await _Sphere.exists(options.storage)) {
|
|
13875
|
-
throw new
|
|
14061
|
+
throw new SphereError("Wallet already exists. Use Sphere.load() or Sphere.clear() first.", "ALREADY_INITIALIZED");
|
|
13876
14062
|
}
|
|
13877
14063
|
const progress = options.onProgress;
|
|
13878
14064
|
if (!options.storage.isConnected()) {
|
|
@@ -13918,10 +14104,10 @@ var Sphere = class _Sphere {
|
|
|
13918
14104
|
const discoverOpts = typeof options.discoverAddresses === "object" ? { ...options.discoverAddresses, autoTrack: options.discoverAddresses.autoTrack ?? true } : { autoTrack: true };
|
|
13919
14105
|
const result = await sphere.discoverAddresses(discoverOpts);
|
|
13920
14106
|
if (result.addresses.length > 0) {
|
|
13921
|
-
|
|
14107
|
+
logger.debug("Sphere", `Address discovery: found ${result.addresses.length} address(es)`);
|
|
13922
14108
|
}
|
|
13923
14109
|
} catch (err) {
|
|
13924
|
-
|
|
14110
|
+
logger.warn("Sphere", "Address discovery failed (non-fatal):", err);
|
|
13925
14111
|
}
|
|
13926
14112
|
}
|
|
13927
14113
|
progress?.({ step: "complete", message: "Wallet created" });
|
|
@@ -13931,8 +14117,9 @@ var Sphere = class _Sphere {
|
|
|
13931
14117
|
* Load existing wallet from storage
|
|
13932
14118
|
*/
|
|
13933
14119
|
static async load(options) {
|
|
14120
|
+
if (options.debug) logger.configure({ debug: true });
|
|
13934
14121
|
if (!await _Sphere.exists(options.storage)) {
|
|
13935
|
-
throw new
|
|
14122
|
+
throw new SphereError("No wallet found. Use Sphere.create() to create a new wallet.", "NOT_INITIALIZED");
|
|
13936
14123
|
}
|
|
13937
14124
|
const progress = options.onProgress;
|
|
13938
14125
|
_Sphere.configureTokenRegistry(options.storage, options.network);
|
|
@@ -13963,16 +14150,16 @@ var Sphere = class _Sphere {
|
|
|
13963
14150
|
_Sphere.instance = sphere;
|
|
13964
14151
|
if (sphere._identity?.nametag && !sphere._payments.hasNametag()) {
|
|
13965
14152
|
progress?.({ step: "registering_nametag", message: "Restoring nametag token..." });
|
|
13966
|
-
|
|
14153
|
+
logger.debug("Sphere", `Nametag @${sphere._identity.nametag} has no token, attempting to mint...`);
|
|
13967
14154
|
try {
|
|
13968
14155
|
const result = await sphere.mintNametag(sphere._identity.nametag);
|
|
13969
14156
|
if (result.success) {
|
|
13970
|
-
|
|
14157
|
+
logger.debug("Sphere", `Nametag token minted successfully on load`);
|
|
13971
14158
|
} else {
|
|
13972
|
-
|
|
14159
|
+
logger.warn("Sphere", `Could not mint nametag token: ${result.error}`);
|
|
13973
14160
|
}
|
|
13974
14161
|
} catch (err) {
|
|
13975
|
-
|
|
14162
|
+
logger.warn("Sphere", `Nametag token mint failed:`, err);
|
|
13976
14163
|
}
|
|
13977
14164
|
}
|
|
13978
14165
|
if (options.discoverAddresses !== false && sphere._transport.discoverAddresses && sphere._masterKey) {
|
|
@@ -13981,10 +14168,10 @@ var Sphere = class _Sphere {
|
|
|
13981
14168
|
const discoverOpts = typeof options.discoverAddresses === "object" ? { ...options.discoverAddresses, autoTrack: options.discoverAddresses.autoTrack ?? true } : { autoTrack: true };
|
|
13982
14169
|
const result = await sphere.discoverAddresses(discoverOpts);
|
|
13983
14170
|
if (result.addresses.length > 0) {
|
|
13984
|
-
|
|
14171
|
+
logger.debug("Sphere", `Address discovery: found ${result.addresses.length} address(es)`);
|
|
13985
14172
|
}
|
|
13986
14173
|
} catch (err) {
|
|
13987
|
-
|
|
14174
|
+
logger.warn("Sphere", "Address discovery failed (non-fatal):", err);
|
|
13988
14175
|
}
|
|
13989
14176
|
}
|
|
13990
14177
|
progress?.({ step: "complete", message: "Wallet loaded" });
|
|
@@ -13994,24 +14181,25 @@ var Sphere = class _Sphere {
|
|
|
13994
14181
|
* Import wallet from mnemonic or master key
|
|
13995
14182
|
*/
|
|
13996
14183
|
static async import(options) {
|
|
14184
|
+
if (options.debug) logger.configure({ debug: true });
|
|
13997
14185
|
if (!options.mnemonic && !options.masterKey) {
|
|
13998
|
-
throw new
|
|
14186
|
+
throw new SphereError("Either mnemonic or masterKey is required", "INVALID_CONFIG");
|
|
13999
14187
|
}
|
|
14000
14188
|
const progress = options.onProgress;
|
|
14001
|
-
|
|
14189
|
+
logger.debug("Sphere", "Starting import...");
|
|
14002
14190
|
const needsClear = _Sphere.instance !== null || await _Sphere.exists(options.storage);
|
|
14003
14191
|
if (needsClear) {
|
|
14004
14192
|
progress?.({ step: "clearing", message: "Clearing previous wallet data..." });
|
|
14005
|
-
|
|
14193
|
+
logger.debug("Sphere", "Clearing existing wallet data...");
|
|
14006
14194
|
await _Sphere.clear({ storage: options.storage, tokenStorage: options.tokenStorage });
|
|
14007
|
-
|
|
14195
|
+
logger.debug("Sphere", "Clear done");
|
|
14008
14196
|
} else {
|
|
14009
|
-
|
|
14197
|
+
logger.debug("Sphere", "No existing wallet \u2014 skipping clear");
|
|
14010
14198
|
}
|
|
14011
14199
|
if (!options.storage.isConnected()) {
|
|
14012
|
-
|
|
14200
|
+
logger.debug("Sphere", "Reconnecting storage...");
|
|
14013
14201
|
await options.storage.connect();
|
|
14014
|
-
|
|
14202
|
+
logger.debug("Sphere", "Storage reconnected");
|
|
14015
14203
|
}
|
|
14016
14204
|
const groupChatConfig = _Sphere.resolveGroupChatConfig(options.groupChat);
|
|
14017
14205
|
const marketConfig = _Sphere.resolveMarketConfig(options.market);
|
|
@@ -14029,14 +14217,14 @@ var Sphere = class _Sphere {
|
|
|
14029
14217
|
progress?.({ step: "storing_keys", message: "Storing wallet keys..." });
|
|
14030
14218
|
if (options.mnemonic) {
|
|
14031
14219
|
if (!_Sphere.validateMnemonic(options.mnemonic)) {
|
|
14032
|
-
throw new
|
|
14220
|
+
throw new SphereError("Invalid mnemonic", "INVALID_IDENTITY");
|
|
14033
14221
|
}
|
|
14034
|
-
|
|
14222
|
+
logger.debug("Sphere", "Storing mnemonic...");
|
|
14035
14223
|
await sphere.storeMnemonic(options.mnemonic, options.derivationPath, options.basePath);
|
|
14036
|
-
|
|
14224
|
+
logger.debug("Sphere", "Initializing identity from mnemonic...");
|
|
14037
14225
|
await sphere.initializeIdentityFromMnemonic(options.mnemonic, options.derivationPath);
|
|
14038
14226
|
} else if (options.masterKey) {
|
|
14039
|
-
|
|
14227
|
+
logger.debug("Sphere", "Storing master key...");
|
|
14040
14228
|
await sphere.storeMasterKey(
|
|
14041
14229
|
options.masterKey,
|
|
14042
14230
|
options.chainCode,
|
|
@@ -14044,7 +14232,7 @@ var Sphere = class _Sphere {
|
|
|
14044
14232
|
options.basePath,
|
|
14045
14233
|
options.derivationMode
|
|
14046
14234
|
);
|
|
14047
|
-
|
|
14235
|
+
logger.debug("Sphere", "Initializing identity from master key...");
|
|
14048
14236
|
await sphere.initializeIdentityFromMasterKey(
|
|
14049
14237
|
options.masterKey,
|
|
14050
14238
|
options.chainCode,
|
|
@@ -14052,38 +14240,38 @@ var Sphere = class _Sphere {
|
|
|
14052
14240
|
);
|
|
14053
14241
|
}
|
|
14054
14242
|
progress?.({ step: "initializing", message: "Initializing wallet..." });
|
|
14055
|
-
|
|
14243
|
+
logger.debug("Sphere", "Initializing providers...");
|
|
14056
14244
|
await sphere.initializeProviders();
|
|
14057
|
-
|
|
14245
|
+
logger.debug("Sphere", "Providers initialized. Initializing modules...");
|
|
14058
14246
|
await sphere.initializeModules();
|
|
14059
|
-
|
|
14247
|
+
logger.debug("Sphere", "Modules initialized");
|
|
14060
14248
|
if (!options.nametag) {
|
|
14061
14249
|
progress?.({ step: "recovering_nametag", message: "Recovering nametag..." });
|
|
14062
|
-
|
|
14250
|
+
logger.debug("Sphere", "Recovering nametag from transport...");
|
|
14063
14251
|
await sphere.recoverNametagFromTransport();
|
|
14064
|
-
|
|
14252
|
+
logger.debug("Sphere", "Nametag recovery done");
|
|
14065
14253
|
progress?.({ step: "syncing_identity", message: "Publishing identity..." });
|
|
14066
14254
|
await sphere.syncIdentityWithTransport();
|
|
14067
14255
|
}
|
|
14068
14256
|
progress?.({ step: "finalizing", message: "Finalizing wallet..." });
|
|
14069
|
-
|
|
14257
|
+
logger.debug("Sphere", "Finalizing wallet creation...");
|
|
14070
14258
|
await sphere.finalizeWalletCreation();
|
|
14071
14259
|
sphere._initialized = true;
|
|
14072
14260
|
_Sphere.instance = sphere;
|
|
14073
|
-
|
|
14261
|
+
logger.debug("Sphere", "Tracking address 0...");
|
|
14074
14262
|
await sphere.ensureAddressTracked(0);
|
|
14075
14263
|
if (options.nametag) {
|
|
14076
14264
|
progress?.({ step: "registering_nametag", message: "Registering nametag..." });
|
|
14077
|
-
|
|
14265
|
+
logger.debug("Sphere", "Registering nametag...");
|
|
14078
14266
|
await sphere.registerNametag(options.nametag);
|
|
14079
14267
|
}
|
|
14080
14268
|
if (sphere._tokenStorageProviders.size > 0) {
|
|
14081
14269
|
progress?.({ step: "syncing_tokens", message: "Syncing tokens..." });
|
|
14082
14270
|
try {
|
|
14083
14271
|
const syncResult = await sphere._payments.sync();
|
|
14084
|
-
|
|
14272
|
+
logger.debug("Sphere", `Auto-sync: +${syncResult.added} -${syncResult.removed}`);
|
|
14085
14273
|
} catch (err) {
|
|
14086
|
-
|
|
14274
|
+
logger.warn("Sphere", "Auto-sync failed (non-fatal):", err);
|
|
14087
14275
|
}
|
|
14088
14276
|
}
|
|
14089
14277
|
if (options.discoverAddresses !== false && sphere._transport.discoverAddresses) {
|
|
@@ -14092,14 +14280,14 @@ var Sphere = class _Sphere {
|
|
|
14092
14280
|
const discoverOpts = typeof options.discoverAddresses === "object" ? { ...options.discoverAddresses, autoTrack: options.discoverAddresses.autoTrack ?? true } : { autoTrack: true };
|
|
14093
14281
|
const result = await sphere.discoverAddresses(discoverOpts);
|
|
14094
14282
|
if (result.addresses.length > 0) {
|
|
14095
|
-
|
|
14283
|
+
logger.debug("Sphere", `Address discovery: found ${result.addresses.length} address(es)`);
|
|
14096
14284
|
}
|
|
14097
14285
|
} catch (err) {
|
|
14098
|
-
|
|
14286
|
+
logger.warn("Sphere", "Address discovery failed (non-fatal):", err);
|
|
14099
14287
|
}
|
|
14100
14288
|
}
|
|
14101
14289
|
progress?.({ step: "complete", message: "Import complete" });
|
|
14102
|
-
|
|
14290
|
+
logger.debug("Sphere", "Import complete");
|
|
14103
14291
|
return sphere;
|
|
14104
14292
|
}
|
|
14105
14293
|
/**
|
|
@@ -14125,26 +14313,26 @@ var Sphere = class _Sphere {
|
|
|
14125
14313
|
const storage = "get" in storageOrOptions ? storageOrOptions : storageOrOptions.storage;
|
|
14126
14314
|
const tokenStorage = "get" in storageOrOptions ? void 0 : storageOrOptions.tokenStorage;
|
|
14127
14315
|
if (_Sphere.instance) {
|
|
14128
|
-
|
|
14316
|
+
logger.debug("Sphere", "Destroying Sphere instance...");
|
|
14129
14317
|
await _Sphere.instance.destroy();
|
|
14130
|
-
|
|
14318
|
+
logger.debug("Sphere", "Sphere instance destroyed");
|
|
14131
14319
|
}
|
|
14132
|
-
|
|
14320
|
+
logger.debug("Sphere", "Clearing L1 vesting cache...");
|
|
14133
14321
|
await vestingClassifier.destroy();
|
|
14134
|
-
|
|
14322
|
+
logger.debug("Sphere", "Yielding 50ms for IDB transaction settlement...");
|
|
14135
14323
|
await new Promise((r) => setTimeout(r, 50));
|
|
14136
14324
|
if (tokenStorage?.clear) {
|
|
14137
|
-
|
|
14325
|
+
logger.debug("Sphere", "Clearing token storage...");
|
|
14138
14326
|
try {
|
|
14139
14327
|
await tokenStorage.clear();
|
|
14140
|
-
|
|
14328
|
+
logger.debug("Sphere", "Token storage cleared");
|
|
14141
14329
|
} catch (err) {
|
|
14142
|
-
|
|
14330
|
+
logger.warn("Sphere", "Token storage clear failed:", err);
|
|
14143
14331
|
}
|
|
14144
14332
|
} else {
|
|
14145
|
-
|
|
14333
|
+
logger.debug("Sphere", "No token storage provider to clear");
|
|
14146
14334
|
}
|
|
14147
|
-
|
|
14335
|
+
logger.debug("Sphere", "Clearing KV storage...");
|
|
14148
14336
|
if (!storage.isConnected()) {
|
|
14149
14337
|
try {
|
|
14150
14338
|
await storage.connect();
|
|
@@ -14153,11 +14341,11 @@ var Sphere = class _Sphere {
|
|
|
14153
14341
|
}
|
|
14154
14342
|
if (storage.isConnected()) {
|
|
14155
14343
|
await storage.clear();
|
|
14156
|
-
|
|
14344
|
+
logger.debug("Sphere", "KV storage cleared");
|
|
14157
14345
|
} else {
|
|
14158
|
-
|
|
14346
|
+
logger.debug("Sphere", "KV storage not connected, skipping");
|
|
14159
14347
|
}
|
|
14160
|
-
|
|
14348
|
+
logger.debug("Sphere", "Done");
|
|
14161
14349
|
}
|
|
14162
14350
|
/**
|
|
14163
14351
|
* Get current instance
|
|
@@ -14249,7 +14437,7 @@ var Sphere = class _Sphere {
|
|
|
14249
14437
|
*/
|
|
14250
14438
|
async addTokenStorageProvider(provider) {
|
|
14251
14439
|
if (this._tokenStorageProviders.has(provider.id)) {
|
|
14252
|
-
throw new
|
|
14440
|
+
throw new SphereError(`Token storage provider '${provider.id}' already exists`, "INVALID_CONFIG");
|
|
14253
14441
|
}
|
|
14254
14442
|
if (this._identity) {
|
|
14255
14443
|
provider.setIdentity(this._identity);
|
|
@@ -14369,7 +14557,7 @@ var Sphere = class _Sphere {
|
|
|
14369
14557
|
exportToJSON(options = {}) {
|
|
14370
14558
|
this.ensureReady();
|
|
14371
14559
|
if (!this._masterKey && !this._identity) {
|
|
14372
|
-
throw new
|
|
14560
|
+
throw new SphereError("Wallet not initialized", "NOT_INITIALIZED");
|
|
14373
14561
|
}
|
|
14374
14562
|
const addressCount = options.addressCount || 1;
|
|
14375
14563
|
const addresses = [];
|
|
@@ -14449,7 +14637,7 @@ var Sphere = class _Sphere {
|
|
|
14449
14637
|
exportToTxt(options = {}) {
|
|
14450
14638
|
this.ensureReady();
|
|
14451
14639
|
if (!this._masterKey && !this._identity) {
|
|
14452
|
-
throw new
|
|
14640
|
+
throw new SphereError("Wallet not initialized", "NOT_INITIALIZED");
|
|
14453
14641
|
}
|
|
14454
14642
|
const addressCount = options.addressCount || 1;
|
|
14455
14643
|
const addresses = [];
|
|
@@ -14888,7 +15076,7 @@ var Sphere = class _Sphere {
|
|
|
14888
15076
|
this.ensureReady();
|
|
14889
15077
|
const entry = this._trackedAddresses.get(index);
|
|
14890
15078
|
if (!entry) {
|
|
14891
|
-
throw new
|
|
15079
|
+
throw new SphereError(`Address at index ${index} is not tracked. Switch to it first.`, "INVALID_CONFIG");
|
|
14892
15080
|
}
|
|
14893
15081
|
if (entry.hidden === hidden) return;
|
|
14894
15082
|
entry.hidden = hidden;
|
|
@@ -14918,14 +15106,14 @@ var Sphere = class _Sphere {
|
|
|
14918
15106
|
async switchToAddress(index, options) {
|
|
14919
15107
|
this.ensureReady();
|
|
14920
15108
|
if (!this._masterKey) {
|
|
14921
|
-
throw new
|
|
15109
|
+
throw new SphereError("HD derivation requires master key with chain code. Cannot switch addresses.", "INVALID_CONFIG");
|
|
14922
15110
|
}
|
|
14923
15111
|
if (index < 0) {
|
|
14924
|
-
throw new
|
|
15112
|
+
throw new SphereError("Address index must be non-negative", "INVALID_CONFIG");
|
|
14925
15113
|
}
|
|
14926
15114
|
const newNametag = options?.nametag ? this.cleanNametag(options.nametag) : void 0;
|
|
14927
15115
|
if (newNametag && !isValidNametag(newNametag)) {
|
|
14928
|
-
throw new
|
|
15116
|
+
throw new SphereError("Invalid nametag format. Use lowercase alphanumeric, underscore, or hyphen (3-20 chars), or a valid phone number.", "VALIDATION_ERROR");
|
|
14929
15117
|
}
|
|
14930
15118
|
const addressInfo = this.deriveAddress(index, false);
|
|
14931
15119
|
const ipnsHash = sha256(addressInfo.publicKey, "hex").slice(0, 40);
|
|
@@ -14935,7 +15123,7 @@ var Sphere = class _Sphere {
|
|
|
14935
15123
|
if (newNametag) {
|
|
14936
15124
|
const existing = await this._transport.resolveNametag?.(newNametag);
|
|
14937
15125
|
if (existing) {
|
|
14938
|
-
throw new
|
|
15126
|
+
throw new SphereError(`Nametag @${newNametag} is already taken`, "VALIDATION_ERROR");
|
|
14939
15127
|
}
|
|
14940
15128
|
let nametags = this._addressNametags.get(addressId);
|
|
14941
15129
|
if (!nametags) {
|
|
@@ -14958,12 +15146,12 @@ var Sphere = class _Sphere {
|
|
|
14958
15146
|
await this._storage.set(STORAGE_KEYS_GLOBAL.CURRENT_ADDRESS_INDEX, index.toString());
|
|
14959
15147
|
this._storage.setIdentity(this._identity);
|
|
14960
15148
|
await this._transport.setIdentity(this._identity);
|
|
14961
|
-
|
|
15149
|
+
logger.debug("Sphere", `switchToAddress(${index}): re-initializing ${this._tokenStorageProviders.size} token storage provider(s)`);
|
|
14962
15150
|
for (const [providerId, provider] of this._tokenStorageProviders.entries()) {
|
|
14963
|
-
|
|
15151
|
+
logger.debug("Sphere", `switchToAddress(${index}): shutdown provider=${providerId}`);
|
|
14964
15152
|
await provider.shutdown();
|
|
14965
15153
|
provider.setIdentity(this._identity);
|
|
14966
|
-
|
|
15154
|
+
logger.debug("Sphere", `switchToAddress(${index}): initialize provider=${providerId}`);
|
|
14967
15155
|
await provider.initialize();
|
|
14968
15156
|
}
|
|
14969
15157
|
await this.reinitializeModulesForNewAddress();
|
|
@@ -14974,9 +15162,9 @@ var Sphere = class _Sphere {
|
|
|
14974
15162
|
nametag: this._identity.nametag,
|
|
14975
15163
|
addressIndex: index
|
|
14976
15164
|
});
|
|
14977
|
-
|
|
15165
|
+
logger.debug("Sphere", `Switched to address ${index}:`, this._identity.l1Address);
|
|
14978
15166
|
this.postSwitchSync(index, newNametag).catch((err) => {
|
|
14979
|
-
|
|
15167
|
+
logger.warn("Sphere", `Post-switch sync failed for address ${index}:`, err);
|
|
14980
15168
|
});
|
|
14981
15169
|
}
|
|
14982
15170
|
/**
|
|
@@ -14990,16 +15178,16 @@ var Sphere = class _Sphere {
|
|
|
14990
15178
|
if (newNametag) {
|
|
14991
15179
|
await this.persistAddressNametags();
|
|
14992
15180
|
if (!this._payments.hasNametag()) {
|
|
14993
|
-
|
|
15181
|
+
logger.debug("Sphere", `Minting nametag token for @${newNametag}...`);
|
|
14994
15182
|
try {
|
|
14995
15183
|
const result = await this.mintNametag(newNametag);
|
|
14996
15184
|
if (result.success) {
|
|
14997
|
-
|
|
15185
|
+
logger.debug("Sphere", `Nametag token minted successfully`);
|
|
14998
15186
|
} else {
|
|
14999
|
-
|
|
15187
|
+
logger.warn("Sphere", `Could not mint nametag token: ${result.error}`);
|
|
15000
15188
|
}
|
|
15001
15189
|
} catch (err) {
|
|
15002
|
-
|
|
15190
|
+
logger.warn("Sphere", `Nametag token mint failed:`, err);
|
|
15003
15191
|
}
|
|
15004
15192
|
}
|
|
15005
15193
|
this.emitEvent("nametag:registered", {
|
|
@@ -15007,16 +15195,16 @@ var Sphere = class _Sphere {
|
|
|
15007
15195
|
addressIndex: index
|
|
15008
15196
|
});
|
|
15009
15197
|
} else if (this._identity?.nametag && !this._payments.hasNametag()) {
|
|
15010
|
-
|
|
15198
|
+
logger.debug("Sphere", `Nametag @${this._identity.nametag} has no token after switch, minting...`);
|
|
15011
15199
|
try {
|
|
15012
15200
|
const result = await this.mintNametag(this._identity.nametag);
|
|
15013
15201
|
if (result.success) {
|
|
15014
|
-
|
|
15202
|
+
logger.debug("Sphere", `Nametag token minted successfully after switch`);
|
|
15015
15203
|
} else {
|
|
15016
|
-
|
|
15204
|
+
logger.warn("Sphere", `Could not mint nametag token after switch: ${result.error}`);
|
|
15017
15205
|
}
|
|
15018
15206
|
} catch (err) {
|
|
15019
|
-
|
|
15207
|
+
logger.warn("Sphere", `Nametag token mint failed after switch:`, err);
|
|
15020
15208
|
}
|
|
15021
15209
|
}
|
|
15022
15210
|
}
|
|
@@ -15055,7 +15243,7 @@ var Sphere = class _Sphere {
|
|
|
15055
15243
|
await this._groupChat?.load();
|
|
15056
15244
|
await this._market?.load();
|
|
15057
15245
|
this._payments.sync().catch((err) => {
|
|
15058
|
-
|
|
15246
|
+
logger.warn("Sphere", "Post-switch sync failed:", err);
|
|
15059
15247
|
});
|
|
15060
15248
|
}
|
|
15061
15249
|
/**
|
|
@@ -15089,7 +15277,7 @@ var Sphere = class _Sphere {
|
|
|
15089
15277
|
*/
|
|
15090
15278
|
_deriveAddressInternal(index, isChange = false) {
|
|
15091
15279
|
if (!this._masterKey) {
|
|
15092
|
-
throw new
|
|
15280
|
+
throw new SphereError("HD derivation requires master key with chain code", "INVALID_CONFIG");
|
|
15093
15281
|
}
|
|
15094
15282
|
if (this._derivationMode === "wif_hmac") {
|
|
15095
15283
|
return generateAddressFromMasterKey(this._masterKey.privateKey, index);
|
|
@@ -15119,7 +15307,7 @@ var Sphere = class _Sphere {
|
|
|
15119
15307
|
deriveAddressAtPath(path) {
|
|
15120
15308
|
this.ensureReady();
|
|
15121
15309
|
if (!this._masterKey) {
|
|
15122
|
-
throw new
|
|
15310
|
+
throw new SphereError("HD derivation requires master key with chain code", "INVALID_CONFIG");
|
|
15123
15311
|
}
|
|
15124
15312
|
const match = path.match(/\/(\d+)$/);
|
|
15125
15313
|
const index = match ? parseInt(match[1], 10) : 0;
|
|
@@ -15186,13 +15374,14 @@ var Sphere = class _Sphere {
|
|
|
15186
15374
|
async scanAddresses(options = {}) {
|
|
15187
15375
|
this.ensureReady();
|
|
15188
15376
|
if (!this._masterKey) {
|
|
15189
|
-
throw new
|
|
15377
|
+
throw new SphereError("Address scanning requires HD master key", "INVALID_CONFIG");
|
|
15190
15378
|
}
|
|
15191
15379
|
const resolveNametag = options.resolveNametag ?? (this._transport.resolveAddressInfo ? async (l1Address) => {
|
|
15192
15380
|
try {
|
|
15193
15381
|
const info = await this._transport.resolveAddressInfo(l1Address);
|
|
15194
15382
|
return info?.nametag ?? null;
|
|
15195
|
-
} catch {
|
|
15383
|
+
} catch (err) {
|
|
15384
|
+
logger.debug("Sphere", "Nametag resolution failed during scan", err);
|
|
15196
15385
|
return null;
|
|
15197
15386
|
}
|
|
15198
15387
|
} : void 0);
|
|
@@ -15243,10 +15432,10 @@ var Sphere = class _Sphere {
|
|
|
15243
15432
|
async discoverAddresses(options = {}) {
|
|
15244
15433
|
this.ensureReady();
|
|
15245
15434
|
if (!this._masterKey) {
|
|
15246
|
-
throw new
|
|
15435
|
+
throw new SphereError("Address discovery requires HD master key", "INVALID_CONFIG");
|
|
15247
15436
|
}
|
|
15248
15437
|
if (!this._transport.discoverAddresses) {
|
|
15249
|
-
throw new
|
|
15438
|
+
throw new SphereError("Transport provider does not support address discovery", "INVALID_CONFIG");
|
|
15250
15439
|
}
|
|
15251
15440
|
const includeL1Scan = options.includeL1Scan ?? true;
|
|
15252
15441
|
const transportResult = await discoverAddressesImpl(
|
|
@@ -15302,7 +15491,7 @@ var Sphere = class _Sphere {
|
|
|
15302
15491
|
}
|
|
15303
15492
|
transportResult.addresses.sort((a, b) => a.index - b.index);
|
|
15304
15493
|
} catch (err) {
|
|
15305
|
-
|
|
15494
|
+
logger.warn("Sphere", "L1 scan failed during discovery (non-fatal):", err);
|
|
15306
15495
|
}
|
|
15307
15496
|
}
|
|
15308
15497
|
if (options.autoTrack && transportResult.addresses.length > 0) {
|
|
@@ -15400,7 +15589,7 @@ var Sphere = class _Sphere {
|
|
|
15400
15589
|
*/
|
|
15401
15590
|
async disableProvider(providerId) {
|
|
15402
15591
|
if (providerId === this._storage.id) {
|
|
15403
|
-
throw new
|
|
15592
|
+
throw new SphereError("Cannot disable the main storage provider", "INVALID_CONFIG");
|
|
15404
15593
|
}
|
|
15405
15594
|
const provider = this.findProviderById(providerId);
|
|
15406
15595
|
if (!provider) return false;
|
|
@@ -15603,10 +15792,10 @@ var Sphere = class _Sphere {
|
|
|
15603
15792
|
this.ensureReady();
|
|
15604
15793
|
const cleanNametag = this.cleanNametag(nametag);
|
|
15605
15794
|
if (!isValidNametag(cleanNametag)) {
|
|
15606
|
-
throw new
|
|
15795
|
+
throw new SphereError("Invalid nametag format. Use lowercase alphanumeric, underscore, or hyphen (3-20 chars), or a valid phone number.", "VALIDATION_ERROR");
|
|
15607
15796
|
}
|
|
15608
15797
|
if (this._identity?.nametag) {
|
|
15609
|
-
throw new
|
|
15798
|
+
throw new SphereError(`Nametag already registered for address ${this._currentAddressIndex}: @${this._identity.nametag}`, "ALREADY_INITIALIZED");
|
|
15610
15799
|
}
|
|
15611
15800
|
if (this._transport.publishIdentityBinding) {
|
|
15612
15801
|
const success = await this._transport.publishIdentityBinding(
|
|
@@ -15616,7 +15805,7 @@ var Sphere = class _Sphere {
|
|
|
15616
15805
|
cleanNametag
|
|
15617
15806
|
);
|
|
15618
15807
|
if (!success) {
|
|
15619
|
-
throw new
|
|
15808
|
+
throw new SphereError("Failed to register nametag. It may already be taken.", "VALIDATION_ERROR");
|
|
15620
15809
|
}
|
|
15621
15810
|
}
|
|
15622
15811
|
this._identity.nametag = cleanNametag;
|
|
@@ -15632,19 +15821,19 @@ var Sphere = class _Sphere {
|
|
|
15632
15821
|
}
|
|
15633
15822
|
await this.persistAddressNametags();
|
|
15634
15823
|
if (!this._payments.hasNametag()) {
|
|
15635
|
-
|
|
15824
|
+
logger.debug("Sphere", `Minting nametag token for @${cleanNametag}...`);
|
|
15636
15825
|
const result = await this.mintNametag(cleanNametag);
|
|
15637
15826
|
if (!result.success) {
|
|
15638
|
-
|
|
15827
|
+
logger.warn("Sphere", `Failed to mint nametag token: ${result.error}`);
|
|
15639
15828
|
} else {
|
|
15640
|
-
|
|
15829
|
+
logger.debug("Sphere", `Nametag token minted successfully`);
|
|
15641
15830
|
}
|
|
15642
15831
|
}
|
|
15643
15832
|
this.emitEvent("nametag:registered", {
|
|
15644
15833
|
nametag: cleanNametag,
|
|
15645
15834
|
addressIndex: this._currentAddressIndex
|
|
15646
15835
|
});
|
|
15647
|
-
|
|
15836
|
+
logger.debug("Sphere", `Nametag registered for address ${this._currentAddressIndex}:`, cleanNametag);
|
|
15648
15837
|
}
|
|
15649
15838
|
/**
|
|
15650
15839
|
* Persist tracked addresses to storage (only minimal fields via StorageProvider)
|
|
@@ -15884,13 +16073,13 @@ var Sphere = class _Sphere {
|
|
|
15884
16073
|
this._identity.directAddress || "",
|
|
15885
16074
|
recoveredNametag
|
|
15886
16075
|
);
|
|
15887
|
-
|
|
16076
|
+
logger.debug("Sphere", `Migrated legacy binding with nametag @${recoveredNametag}`);
|
|
15888
16077
|
return;
|
|
15889
16078
|
}
|
|
15890
16079
|
}
|
|
15891
16080
|
const needsUpdate = !existing.directAddress || !existing.l1Address || !existing.chainPubkey || this._identity?.nametag && !existing.nametag;
|
|
15892
16081
|
if (needsUpdate) {
|
|
15893
|
-
|
|
16082
|
+
logger.debug("Sphere", "Existing binding incomplete, re-publishing with full data");
|
|
15894
16083
|
await this._transport.publishIdentityBinding(
|
|
15895
16084
|
this._identity.chainPubkey,
|
|
15896
16085
|
this._identity.l1Address,
|
|
@@ -15899,11 +16088,11 @@ var Sphere = class _Sphere {
|
|
|
15899
16088
|
);
|
|
15900
16089
|
return;
|
|
15901
16090
|
}
|
|
15902
|
-
|
|
16091
|
+
logger.debug("Sphere", "Existing binding found, skipping re-publish");
|
|
15903
16092
|
return;
|
|
15904
16093
|
}
|
|
15905
16094
|
} catch (e) {
|
|
15906
|
-
|
|
16095
|
+
logger.warn("Sphere", "resolve() failed, skipping publish to avoid overwrite", e);
|
|
15907
16096
|
return;
|
|
15908
16097
|
}
|
|
15909
16098
|
}
|
|
@@ -15915,12 +16104,12 @@ var Sphere = class _Sphere {
|
|
|
15915
16104
|
nametag || void 0
|
|
15916
16105
|
);
|
|
15917
16106
|
if (success) {
|
|
15918
|
-
|
|
16107
|
+
logger.debug("Sphere", `Identity binding published${nametag ? ` with nametag @${nametag}` : ""}`);
|
|
15919
16108
|
} else if (nametag) {
|
|
15920
|
-
|
|
16109
|
+
logger.warn("Sphere", `Nametag @${nametag} is taken by another pubkey`);
|
|
15921
16110
|
}
|
|
15922
16111
|
} catch (error) {
|
|
15923
|
-
|
|
16112
|
+
logger.warn("Sphere", `Identity binding sync failed:`, error);
|
|
15924
16113
|
}
|
|
15925
16114
|
}
|
|
15926
16115
|
/**
|
|
@@ -16073,7 +16262,7 @@ var Sphere = class _Sphere {
|
|
|
16073
16262
|
if (encryptedMnemonic) {
|
|
16074
16263
|
const mnemonic = this.decrypt(encryptedMnemonic);
|
|
16075
16264
|
if (!mnemonic) {
|
|
16076
|
-
throw new
|
|
16265
|
+
throw new SphereError("Failed to decrypt mnemonic", "STORAGE_ERROR");
|
|
16077
16266
|
}
|
|
16078
16267
|
this._mnemonic = mnemonic;
|
|
16079
16268
|
this._source = "mnemonic";
|
|
@@ -16081,7 +16270,7 @@ var Sphere = class _Sphere {
|
|
|
16081
16270
|
} else if (encryptedMasterKey) {
|
|
16082
16271
|
const masterKey = this.decrypt(encryptedMasterKey);
|
|
16083
16272
|
if (!masterKey) {
|
|
16084
|
-
throw new
|
|
16273
|
+
throw new SphereError("Failed to decrypt master key", "STORAGE_ERROR");
|
|
16085
16274
|
}
|
|
16086
16275
|
this._mnemonic = null;
|
|
16087
16276
|
if (this._source === "unknown") {
|
|
@@ -16093,7 +16282,7 @@ var Sphere = class _Sphere {
|
|
|
16093
16282
|
derivationPath ?? void 0
|
|
16094
16283
|
);
|
|
16095
16284
|
} else {
|
|
16096
|
-
throw new
|
|
16285
|
+
throw new SphereError("No wallet data found in storage", "NOT_INITIALIZED");
|
|
16097
16286
|
}
|
|
16098
16287
|
if (this._identity) {
|
|
16099
16288
|
this._storage.setIdentity(this._identity);
|
|
@@ -16115,7 +16304,7 @@ var Sphere = class _Sphere {
|
|
|
16115
16304
|
nametag
|
|
16116
16305
|
};
|
|
16117
16306
|
this._storage.setIdentity(this._identity);
|
|
16118
|
-
|
|
16307
|
+
logger.debug("Sphere", `Restored to address ${this._currentAddressIndex}:`, this._identity.l1Address);
|
|
16119
16308
|
} else if (this._identity && nametag) {
|
|
16120
16309
|
this._identity.nametag = nametag;
|
|
16121
16310
|
}
|
|
@@ -16306,7 +16495,7 @@ var Sphere = class _Sphere {
|
|
|
16306
16495
|
// ===========================================================================
|
|
16307
16496
|
ensureReady() {
|
|
16308
16497
|
if (!this._initialized) {
|
|
16309
|
-
throw new
|
|
16498
|
+
throw new SphereError("Sphere not initialized", "NOT_INITIALIZED");
|
|
16310
16499
|
}
|
|
16311
16500
|
}
|
|
16312
16501
|
emitEvent(type, data) {
|
|
@@ -16316,7 +16505,7 @@ var Sphere = class _Sphere {
|
|
|
16316
16505
|
try {
|
|
16317
16506
|
handler(data);
|
|
16318
16507
|
} catch (error) {
|
|
16319
|
-
|
|
16508
|
+
logger.error("Sphere", "Event handler error:", error);
|
|
16320
16509
|
}
|
|
16321
16510
|
}
|
|
16322
16511
|
}
|
|
@@ -16353,6 +16542,7 @@ var getSphere = Sphere.getInstance.bind(Sphere);
|
|
|
16353
16542
|
var sphereExists = Sphere.exists.bind(Sphere);
|
|
16354
16543
|
|
|
16355
16544
|
// core/currency.ts
|
|
16545
|
+
init_logger();
|
|
16356
16546
|
var DEFAULT_TOKEN_DECIMALS = 18;
|
|
16357
16547
|
function toSmallestUnit(amount, decimals = DEFAULT_TOKEN_DECIMALS) {
|
|
16358
16548
|
if (!amount) return 0n;
|
|
@@ -16361,7 +16551,8 @@ function toSmallestUnit(amount, decimals = DEFAULT_TOKEN_DECIMALS) {
|
|
|
16361
16551
|
const [integer, fraction = ""] = str.split(".");
|
|
16362
16552
|
const paddedFraction = fraction.padEnd(decimals, "0").slice(0, decimals);
|
|
16363
16553
|
return BigInt(integer + paddedFraction);
|
|
16364
|
-
} catch {
|
|
16554
|
+
} catch (err) {
|
|
16555
|
+
logger.debug("Currency", "toSmallestUnit conversion failed", err);
|
|
16365
16556
|
return 0n;
|
|
16366
16557
|
}
|
|
16367
16558
|
}
|
|
@@ -16385,6 +16576,8 @@ function formatAmount(amount, options = {}) {
|
|
|
16385
16576
|
|
|
16386
16577
|
// core/index.ts
|
|
16387
16578
|
init_bech32();
|
|
16579
|
+
init_logger();
|
|
16580
|
+
init_errors();
|
|
16388
16581
|
|
|
16389
16582
|
// core/network-health.ts
|
|
16390
16583
|
init_constants();
|
|
@@ -16576,6 +16769,9 @@ async function runCustomCheck(name, checkFn, timeoutMs) {
|
|
|
16576
16769
|
// index.ts
|
|
16577
16770
|
init_constants();
|
|
16578
16771
|
|
|
16772
|
+
// types/index.ts
|
|
16773
|
+
init_errors();
|
|
16774
|
+
|
|
16579
16775
|
// types/payment-session.ts
|
|
16580
16776
|
function createPaymentSession(params) {
|
|
16581
16777
|
const now = Date.now();
|
|
@@ -16635,22 +16831,11 @@ function createPaymentSessionError(code, message, recoverable = false, details)
|
|
|
16635
16831
|
};
|
|
16636
16832
|
}
|
|
16637
16833
|
|
|
16638
|
-
// types/index.ts
|
|
16639
|
-
var SphereError = class extends Error {
|
|
16640
|
-
code;
|
|
16641
|
-
cause;
|
|
16642
|
-
constructor(message, code, cause) {
|
|
16643
|
-
super(message);
|
|
16644
|
-
this.name = "SphereError";
|
|
16645
|
-
this.code = code;
|
|
16646
|
-
this.cause = cause;
|
|
16647
|
-
}
|
|
16648
|
-
};
|
|
16649
|
-
|
|
16650
16834
|
// index.ts
|
|
16651
16835
|
init_constants();
|
|
16652
16836
|
|
|
16653
16837
|
// validation/token-validator.ts
|
|
16838
|
+
init_logger();
|
|
16654
16839
|
var TokenValidator = class {
|
|
16655
16840
|
aggregatorClient = null;
|
|
16656
16841
|
trustBase = null;
|
|
@@ -16775,7 +16960,7 @@ var TokenValidator = class {
|
|
|
16775
16960
|
};
|
|
16776
16961
|
}
|
|
16777
16962
|
} catch (err) {
|
|
16778
|
-
|
|
16963
|
+
logger.warn("Validation", "SDK verification skipped:", err instanceof Error ? err.message : err);
|
|
16779
16964
|
}
|
|
16780
16965
|
}
|
|
16781
16966
|
return { isValid: true };
|
|
@@ -16820,7 +17005,7 @@ var TokenValidator = class {
|
|
|
16820
17005
|
});
|
|
16821
17006
|
return isSpent;
|
|
16822
17007
|
} catch (err) {
|
|
16823
|
-
|
|
17008
|
+
logger.warn("Validation", "Error checking token state:", err);
|
|
16824
17009
|
return false;
|
|
16825
17010
|
}
|
|
16826
17011
|
}
|
|
@@ -16978,6 +17163,8 @@ function createTokenValidator(options) {
|
|
|
16978
17163
|
var import_nostr_js_sdk4 = require("@unicitylabs/nostr-js-sdk");
|
|
16979
17164
|
|
|
16980
17165
|
// price/CoinGeckoPriceProvider.ts
|
|
17166
|
+
init_logger();
|
|
17167
|
+
init_errors();
|
|
16981
17168
|
init_constants();
|
|
16982
17169
|
var CoinGeckoPriceProvider = class {
|
|
16983
17170
|
platform = "coingecko";
|
|
@@ -17029,7 +17216,7 @@ var CoinGeckoPriceProvider = class {
|
|
|
17029
17216
|
const allCovered = uncachedNames.every((n) => this.fetchNames.has(n));
|
|
17030
17217
|
if (allCovered) {
|
|
17031
17218
|
if (this.debug) {
|
|
17032
|
-
|
|
17219
|
+
logger.debug("CoinGecko", "Deduplicating request, reusing in-flight fetch");
|
|
17033
17220
|
}
|
|
17034
17221
|
const fetched = await this.fetchPromise;
|
|
17035
17222
|
for (const name of uncachedNames) {
|
|
@@ -17068,7 +17255,7 @@ var CoinGeckoPriceProvider = class {
|
|
|
17068
17255
|
headers["x-cg-pro-api-key"] = this.apiKey;
|
|
17069
17256
|
}
|
|
17070
17257
|
if (this.debug) {
|
|
17071
|
-
|
|
17258
|
+
logger.debug("CoinGecko", `Fetching prices for: ${uncachedNames.join(", ")}`);
|
|
17072
17259
|
}
|
|
17073
17260
|
const response = await fetch(url, {
|
|
17074
17261
|
headers,
|
|
@@ -17078,7 +17265,7 @@ var CoinGeckoPriceProvider = class {
|
|
|
17078
17265
|
if (response.status === 429) {
|
|
17079
17266
|
this.extendCacheOnRateLimit(uncachedNames);
|
|
17080
17267
|
}
|
|
17081
|
-
throw new
|
|
17268
|
+
throw new SphereError(`CoinGecko API error: ${response.status} ${response.statusText}`, "NETWORK_ERROR");
|
|
17082
17269
|
}
|
|
17083
17270
|
const data = await response.json();
|
|
17084
17271
|
for (const [name, values] of Object.entries(data)) {
|
|
@@ -17108,12 +17295,12 @@ var CoinGeckoPriceProvider = class {
|
|
|
17108
17295
|
}
|
|
17109
17296
|
}
|
|
17110
17297
|
if (this.debug) {
|
|
17111
|
-
|
|
17298
|
+
logger.debug("CoinGecko", `Fetched ${result.size} prices`);
|
|
17112
17299
|
}
|
|
17113
17300
|
this.saveToStorage();
|
|
17114
17301
|
} catch (error) {
|
|
17115
17302
|
if (this.debug) {
|
|
17116
|
-
|
|
17303
|
+
logger.warn("CoinGecko", "Fetch failed, using stale cache:", error);
|
|
17117
17304
|
}
|
|
17118
17305
|
for (const name of uncachedNames) {
|
|
17119
17306
|
const stale = this.cache.get(name);
|
|
@@ -17163,7 +17350,7 @@ var CoinGeckoPriceProvider = class {
|
|
|
17163
17350
|
}
|
|
17164
17351
|
}
|
|
17165
17352
|
if (this.debug) {
|
|
17166
|
-
|
|
17353
|
+
logger.debug("CoinGecko", `Loaded ${Object.keys(data).length} prices from persistent cache`);
|
|
17167
17354
|
}
|
|
17168
17355
|
} catch {
|
|
17169
17356
|
}
|
|
@@ -17180,8 +17367,7 @@ var CoinGeckoPriceProvider = class {
|
|
|
17180
17367
|
Promise.all([
|
|
17181
17368
|
this.storage.set(STORAGE_KEYS_GLOBAL.PRICE_CACHE, JSON.stringify(data)),
|
|
17182
17369
|
this.storage.set(STORAGE_KEYS_GLOBAL.PRICE_CACHE_TS, String(Date.now()))
|
|
17183
|
-
]).catch(() =>
|
|
17184
|
-
});
|
|
17370
|
+
]).catch((err) => logger.debug("Price", "Cache save failed (non-critical)", err));
|
|
17185
17371
|
}
|
|
17186
17372
|
// ===========================================================================
|
|
17187
17373
|
// Rate-limit handling
|
|
@@ -17200,7 +17386,7 @@ var CoinGeckoPriceProvider = class {
|
|
|
17200
17386
|
}
|
|
17201
17387
|
}
|
|
17202
17388
|
if (this.debug) {
|
|
17203
|
-
|
|
17389
|
+
logger.warn("CoinGecko", `Rate-limited (429), extended cache TTL by ${backoffMs / 1e3}s`);
|
|
17204
17390
|
}
|
|
17205
17391
|
}
|
|
17206
17392
|
async getPrice(tokenName) {
|
|
@@ -17213,12 +17399,13 @@ var CoinGeckoPriceProvider = class {
|
|
|
17213
17399
|
};
|
|
17214
17400
|
|
|
17215
17401
|
// price/index.ts
|
|
17402
|
+
init_errors();
|
|
17216
17403
|
function createPriceProvider(config) {
|
|
17217
17404
|
switch (config.platform) {
|
|
17218
17405
|
case "coingecko":
|
|
17219
17406
|
return new CoinGeckoPriceProvider(config);
|
|
17220
17407
|
default:
|
|
17221
|
-
throw new
|
|
17408
|
+
throw new SphereError(`Unsupported price platform: ${String(config.platform)}`, "INVALID_CONFIG");
|
|
17222
17409
|
}
|
|
17223
17410
|
}
|
|
17224
17411
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -17328,6 +17515,7 @@ function createPriceProvider(config) {
|
|
|
17328
17515
|
isPaymentSessionTimedOut,
|
|
17329
17516
|
isPhoneNumber,
|
|
17330
17517
|
isSQLiteDatabase,
|
|
17518
|
+
isSphereError,
|
|
17331
17519
|
isTextWalletEncrypted,
|
|
17332
17520
|
isTokenKey,
|
|
17333
17521
|
isValidBech32,
|
|
@@ -17338,6 +17526,7 @@ function createPriceProvider(config) {
|
|
|
17338
17526
|
isWalletTextFormat,
|
|
17339
17527
|
keyFromTokenId,
|
|
17340
17528
|
loadSphere,
|
|
17529
|
+
logger,
|
|
17341
17530
|
mnemonicToSeedSync,
|
|
17342
17531
|
normalizeNametag,
|
|
17343
17532
|
normalizeSdkTokenToStorage,
|