@unicitylabs/sphere-sdk 0.6.2 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -806,6 +806,1112 @@ var init_network = __esm({
806
806
  init_logger();
807
807
  init_errors();
808
808
 
809
+ // transport/MultiAddressTransportMux.ts
810
+ init_logger();
811
+ init_errors();
812
+ import { Buffer as Buffer2 } from "buffer";
813
+ import {
814
+ NostrKeyManager,
815
+ NIP04,
816
+ NIP17,
817
+ Event as NostrEventClass,
818
+ EventKinds,
819
+ NostrClient,
820
+ Filter,
821
+ isChatMessage,
822
+ isReadReceipt
823
+ } from "@unicitylabs/nostr-js-sdk";
824
+
825
+ // transport/websocket.ts
826
+ function defaultUUIDGenerator() {
827
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
828
+ return crypto.randomUUID();
829
+ }
830
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
831
+ const r = Math.random() * 16 | 0;
832
+ const v = c === "x" ? r : r & 3 | 8;
833
+ return v.toString(16);
834
+ });
835
+ }
836
+
837
+ // transport/MultiAddressTransportMux.ts
838
+ init_constants();
839
+ var EVENT_KINDS = NOSTR_EVENT_KINDS;
840
+ var COMPOSING_INDICATOR_KIND = 25050;
841
+ var MultiAddressTransportMux = class {
842
+ config;
843
+ storage = null;
844
+ // Single NostrClient — one WebSocket connection for all addresses
845
+ nostrClient = null;
846
+ // KeyManager used for NostrClient creation (uses first address or temp key)
847
+ primaryKeyManager = null;
848
+ status = "disconnected";
849
+ // Per-address entries
850
+ addresses = /* @__PURE__ */ new Map();
851
+ // pubkey → address index (for fast routing)
852
+ pubkeyToIndex = /* @__PURE__ */ new Map();
853
+ // Subscription IDs
854
+ walletSubscriptionId = null;
855
+ chatSubscriptionId = null;
856
+ chatEoseFired = false;
857
+ chatEoseHandlers = [];
858
+ // Dedup
859
+ processedEventIds = /* @__PURE__ */ new Set();
860
+ // Event callbacks (mux-level, forwarded to all adapters)
861
+ eventCallbacks = /* @__PURE__ */ new Set();
862
+ constructor(config) {
863
+ this.config = {
864
+ relays: config.relays ?? [...DEFAULT_NOSTR_RELAYS],
865
+ timeout: config.timeout ?? TIMEOUTS.WEBSOCKET_CONNECT,
866
+ autoReconnect: config.autoReconnect ?? true,
867
+ reconnectDelay: config.reconnectDelay ?? TIMEOUTS.NOSTR_RECONNECT_DELAY,
868
+ maxReconnectAttempts: config.maxReconnectAttempts ?? TIMEOUTS.MAX_RECONNECT_ATTEMPTS,
869
+ createWebSocket: config.createWebSocket,
870
+ generateUUID: config.generateUUID ?? defaultUUIDGenerator
871
+ };
872
+ this.storage = config.storage ?? null;
873
+ }
874
+ // ===========================================================================
875
+ // Address Management
876
+ // ===========================================================================
877
+ /**
878
+ * Add an address to the multiplexer.
879
+ * Creates an AddressTransportAdapter for this address.
880
+ * If already connected, updates subscriptions to include the new pubkey.
881
+ */
882
+ async addAddress(index, identity, resolveDelegate) {
883
+ const existing = this.addresses.get(index);
884
+ if (existing) {
885
+ existing.identity = identity;
886
+ existing.keyManager = NostrKeyManager.fromPrivateKey(Buffer2.from(identity.privateKey, "hex"));
887
+ existing.nostrPubkey = existing.keyManager.getPublicKeyHex();
888
+ for (const [pk, idx] of this.pubkeyToIndex) {
889
+ if (idx === index) this.pubkeyToIndex.delete(pk);
890
+ }
891
+ this.pubkeyToIndex.set(existing.nostrPubkey, index);
892
+ logger.debug("Mux", `Updated address ${index}, pubkey: ${existing.nostrPubkey.slice(0, 16)}...`);
893
+ await this.updateSubscriptions();
894
+ return existing.adapter;
895
+ }
896
+ const keyManager = NostrKeyManager.fromPrivateKey(Buffer2.from(identity.privateKey, "hex"));
897
+ const nostrPubkey = keyManager.getPublicKeyHex();
898
+ const adapter = new AddressTransportAdapter(this, index, identity, resolveDelegate);
899
+ const entry = {
900
+ index,
901
+ identity,
902
+ keyManager,
903
+ nostrPubkey,
904
+ adapter,
905
+ lastEventTs: 0,
906
+ fallbackSince: null
907
+ };
908
+ this.addresses.set(index, entry);
909
+ this.pubkeyToIndex.set(nostrPubkey, index);
910
+ logger.debug("Mux", `Added address ${index}, pubkey: ${nostrPubkey.slice(0, 16)}..., total: ${this.addresses.size}`);
911
+ if (this.addresses.size === 1) {
912
+ this.primaryKeyManager = keyManager;
913
+ }
914
+ if (this.isConnected()) {
915
+ await this.updateSubscriptions();
916
+ }
917
+ return adapter;
918
+ }
919
+ /**
920
+ * Remove an address from the multiplexer.
921
+ * Stops routing events to this address.
922
+ */
923
+ async removeAddress(index) {
924
+ const entry = this.addresses.get(index);
925
+ if (!entry) return;
926
+ this.pubkeyToIndex.delete(entry.nostrPubkey);
927
+ this.addresses.delete(index);
928
+ logger.debug("Mux", `Removed address ${index}, remaining: ${this.addresses.size}`);
929
+ if (this.isConnected() && this.addresses.size > 0) {
930
+ await this.updateSubscriptions();
931
+ }
932
+ }
933
+ /**
934
+ * Get adapter for a specific address index.
935
+ */
936
+ getAdapter(index) {
937
+ return this.addresses.get(index)?.adapter;
938
+ }
939
+ /**
940
+ * Set fallback 'since' for an address (consumed once on next subscription setup).
941
+ */
942
+ setFallbackSince(index, sinceSeconds) {
943
+ const entry = this.addresses.get(index);
944
+ if (entry) {
945
+ entry.fallbackSince = sinceSeconds;
946
+ }
947
+ }
948
+ // ===========================================================================
949
+ // Connection Management (delegated from adapters)
950
+ // ===========================================================================
951
+ async connect() {
952
+ if (this.status === "connected") return;
953
+ this.status = "connecting";
954
+ try {
955
+ if (!this.primaryKeyManager) {
956
+ const tempKey = Buffer2.alloc(32);
957
+ crypto.getRandomValues(tempKey);
958
+ this.primaryKeyManager = NostrKeyManager.fromPrivateKey(tempKey);
959
+ }
960
+ this.nostrClient = new NostrClient(this.primaryKeyManager, {
961
+ autoReconnect: this.config.autoReconnect,
962
+ reconnectIntervalMs: this.config.reconnectDelay,
963
+ maxReconnectIntervalMs: this.config.reconnectDelay * 16,
964
+ pingIntervalMs: 15e3
965
+ });
966
+ this.nostrClient.addConnectionListener({
967
+ onConnect: (url) => {
968
+ logger.debug("Mux", "Connected to relay:", url);
969
+ this.emitEvent({ type: "transport:connected", timestamp: Date.now() });
970
+ },
971
+ onDisconnect: (url, reason) => {
972
+ logger.debug("Mux", "Disconnected from relay:", url, "reason:", reason);
973
+ },
974
+ onReconnecting: (url, attempt) => {
975
+ logger.debug("Mux", "Reconnecting to relay:", url, "attempt:", attempt);
976
+ this.emitEvent({ type: "transport:reconnecting", timestamp: Date.now() });
977
+ },
978
+ onReconnected: (url) => {
979
+ logger.debug("Mux", "Reconnected to relay:", url);
980
+ this.emitEvent({ type: "transport:connected", timestamp: Date.now() });
981
+ }
982
+ });
983
+ await Promise.race([
984
+ this.nostrClient.connect(...this.config.relays),
985
+ new Promise(
986
+ (_, reject) => setTimeout(() => reject(new Error(
987
+ `Transport connection timed out after ${this.config.timeout}ms`
988
+ )), this.config.timeout)
989
+ )
990
+ ]);
991
+ if (!this.nostrClient.isConnected()) {
992
+ throw new SphereError("Failed to connect to any relay", "TRANSPORT_ERROR");
993
+ }
994
+ this.status = "connected";
995
+ this.emitEvent({ type: "transport:connected", timestamp: Date.now() });
996
+ if (this.addresses.size > 0) {
997
+ await this.updateSubscriptions();
998
+ }
999
+ } catch (error) {
1000
+ this.status = "error";
1001
+ throw error;
1002
+ }
1003
+ }
1004
+ async disconnect() {
1005
+ if (this.nostrClient) {
1006
+ this.nostrClient.disconnect();
1007
+ this.nostrClient = null;
1008
+ }
1009
+ this.walletSubscriptionId = null;
1010
+ this.chatSubscriptionId = null;
1011
+ this.chatEoseFired = false;
1012
+ this.status = "disconnected";
1013
+ this.emitEvent({ type: "transport:disconnected", timestamp: Date.now() });
1014
+ }
1015
+ isConnected() {
1016
+ return this.status === "connected" && this.nostrClient?.isConnected() === true;
1017
+ }
1018
+ getStatus() {
1019
+ return this.status;
1020
+ }
1021
+ // ===========================================================================
1022
+ // Relay Management
1023
+ // ===========================================================================
1024
+ getRelays() {
1025
+ return [...this.config.relays];
1026
+ }
1027
+ getConnectedRelays() {
1028
+ if (!this.nostrClient) return [];
1029
+ return Array.from(this.nostrClient.getConnectedRelays());
1030
+ }
1031
+ async addRelay(relayUrl) {
1032
+ if (this.config.relays.includes(relayUrl)) return false;
1033
+ this.config.relays.push(relayUrl);
1034
+ if (this.status === "connected" && this.nostrClient) {
1035
+ try {
1036
+ await this.nostrClient.connect(relayUrl);
1037
+ this.emitEvent({ type: "transport:relay_added", timestamp: Date.now(), data: { relay: relayUrl, connected: true } });
1038
+ return true;
1039
+ } catch (error) {
1040
+ this.emitEvent({ type: "transport:relay_added", timestamp: Date.now(), data: { relay: relayUrl, connected: false, error: String(error) } });
1041
+ return false;
1042
+ }
1043
+ }
1044
+ return true;
1045
+ }
1046
+ async removeRelay(relayUrl) {
1047
+ const idx = this.config.relays.indexOf(relayUrl);
1048
+ if (idx === -1) return false;
1049
+ this.config.relays.splice(idx, 1);
1050
+ this.emitEvent({ type: "transport:relay_removed", timestamp: Date.now(), data: { relay: relayUrl } });
1051
+ return true;
1052
+ }
1053
+ hasRelay(relayUrl) {
1054
+ return this.config.relays.includes(relayUrl);
1055
+ }
1056
+ isRelayConnected(relayUrl) {
1057
+ if (!this.nostrClient) return false;
1058
+ return this.nostrClient.getConnectedRelays().has(relayUrl);
1059
+ }
1060
+ // ===========================================================================
1061
+ // Subscription Management
1062
+ // ===========================================================================
1063
+ /**
1064
+ * Update Nostr subscriptions to listen for events on ALL registered address pubkeys.
1065
+ * Called whenever addresses are added/removed.
1066
+ */
1067
+ async updateSubscriptions() {
1068
+ if (!this.nostrClient || this.addresses.size === 0) return;
1069
+ if (this.walletSubscriptionId) {
1070
+ this.nostrClient.unsubscribe(this.walletSubscriptionId);
1071
+ this.walletSubscriptionId = null;
1072
+ }
1073
+ if (this.chatSubscriptionId) {
1074
+ this.nostrClient.unsubscribe(this.chatSubscriptionId);
1075
+ this.chatSubscriptionId = null;
1076
+ }
1077
+ const allPubkeys = [];
1078
+ for (const entry of this.addresses.values()) {
1079
+ allPubkeys.push(entry.nostrPubkey);
1080
+ }
1081
+ logger.debug("Mux", `Subscribing for ${allPubkeys.length} address(es):`, allPubkeys.map((p) => p.slice(0, 12)).join(", "));
1082
+ let globalSince = Math.floor(Date.now() / 1e3);
1083
+ for (const entry of this.addresses.values()) {
1084
+ const since = await this.getAddressSince(entry);
1085
+ if (since < globalSince) {
1086
+ globalSince = since;
1087
+ }
1088
+ }
1089
+ const walletFilter = new Filter();
1090
+ walletFilter.kinds = [
1091
+ EVENT_KINDS.DIRECT_MESSAGE,
1092
+ EVENT_KINDS.TOKEN_TRANSFER,
1093
+ EVENT_KINDS.PAYMENT_REQUEST,
1094
+ EVENT_KINDS.PAYMENT_REQUEST_RESPONSE
1095
+ ];
1096
+ walletFilter["#p"] = allPubkeys;
1097
+ walletFilter.since = globalSince;
1098
+ this.walletSubscriptionId = this.nostrClient.subscribe(walletFilter, {
1099
+ onEvent: (event) => {
1100
+ this.handleEvent({
1101
+ id: event.id,
1102
+ kind: event.kind,
1103
+ content: event.content,
1104
+ tags: event.tags,
1105
+ pubkey: event.pubkey,
1106
+ created_at: event.created_at,
1107
+ sig: event.sig
1108
+ });
1109
+ },
1110
+ onEndOfStoredEvents: () => {
1111
+ logger.debug("Mux", "Wallet subscription EOSE");
1112
+ },
1113
+ onError: (_subId, error) => {
1114
+ logger.debug("Mux", "Wallet subscription error:", error);
1115
+ }
1116
+ });
1117
+ const chatFilter = new Filter();
1118
+ chatFilter.kinds = [EventKinds.GIFT_WRAP];
1119
+ chatFilter["#p"] = allPubkeys;
1120
+ this.chatSubscriptionId = this.nostrClient.subscribe(chatFilter, {
1121
+ onEvent: (event) => {
1122
+ this.handleEvent({
1123
+ id: event.id,
1124
+ kind: event.kind,
1125
+ content: event.content,
1126
+ tags: event.tags,
1127
+ pubkey: event.pubkey,
1128
+ created_at: event.created_at,
1129
+ sig: event.sig
1130
+ });
1131
+ },
1132
+ onEndOfStoredEvents: () => {
1133
+ logger.debug("Mux", "Chat subscription EOSE");
1134
+ if (!this.chatEoseFired) {
1135
+ this.chatEoseFired = true;
1136
+ for (const handler of this.chatEoseHandlers) {
1137
+ try {
1138
+ handler();
1139
+ } catch {
1140
+ }
1141
+ }
1142
+ }
1143
+ },
1144
+ onError: (_subId, error) => {
1145
+ logger.debug("Mux", "Chat subscription error:", error);
1146
+ }
1147
+ });
1148
+ }
1149
+ /**
1150
+ * Determine 'since' timestamp for an address entry.
1151
+ */
1152
+ async getAddressSince(entry) {
1153
+ if (this.storage) {
1154
+ const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_WALLET_EVENT_TS}_${entry.nostrPubkey.slice(0, 16)}`;
1155
+ try {
1156
+ const stored = await this.storage.get(storageKey);
1157
+ if (stored) {
1158
+ const ts = parseInt(stored, 10);
1159
+ entry.lastEventTs = ts;
1160
+ entry.fallbackSince = null;
1161
+ return ts;
1162
+ } else if (entry.fallbackSince !== null) {
1163
+ const ts = entry.fallbackSince;
1164
+ entry.lastEventTs = ts;
1165
+ entry.fallbackSince = null;
1166
+ return ts;
1167
+ }
1168
+ } catch {
1169
+ }
1170
+ }
1171
+ return Math.floor(Date.now() / 1e3);
1172
+ }
1173
+ // ===========================================================================
1174
+ // Event Routing
1175
+ // ===========================================================================
1176
+ /**
1177
+ * Route an incoming Nostr event to the correct address adapter.
1178
+ */
1179
+ async handleEvent(event) {
1180
+ if (event.id && this.processedEventIds.has(event.id)) return;
1181
+ if (event.id) this.processedEventIds.add(event.id);
1182
+ try {
1183
+ if (event.kind === EventKinds.GIFT_WRAP) {
1184
+ await this.routeGiftWrap(event);
1185
+ } else {
1186
+ const recipientPubkey = this.extractRecipientPubkey(event);
1187
+ if (!recipientPubkey) {
1188
+ logger.debug("Mux", "Event has no #p tag, dropping:", event.id?.slice(0, 12));
1189
+ return;
1190
+ }
1191
+ const addressIndex = this.pubkeyToIndex.get(recipientPubkey);
1192
+ if (addressIndex === void 0) {
1193
+ logger.debug("Mux", "Event for unknown pubkey:", recipientPubkey.slice(0, 16), "dropping");
1194
+ return;
1195
+ }
1196
+ const entry = this.addresses.get(addressIndex);
1197
+ if (!entry) return;
1198
+ await this.dispatchWalletEvent(entry, event);
1199
+ }
1200
+ } catch (error) {
1201
+ logger.debug("Mux", "Failed to handle event:", event.id?.slice(0, 12), error);
1202
+ }
1203
+ }
1204
+ /**
1205
+ * Extract recipient pubkey from event's #p tag.
1206
+ * Returns the first #p value that matches a known address pubkey,
1207
+ * or the first #p value if none match.
1208
+ */
1209
+ extractRecipientPubkey(event) {
1210
+ const pTags = event.tags?.filter((t) => t[0] === "p");
1211
+ if (!pTags || pTags.length === 0) return null;
1212
+ for (const tag of pTags) {
1213
+ if (tag[1] && this.pubkeyToIndex.has(tag[1])) {
1214
+ return tag[1];
1215
+ }
1216
+ }
1217
+ return pTags[0]?.[1] ?? null;
1218
+ }
1219
+ /**
1220
+ * Route a gift wrap event by trying decryption with each address keyManager.
1221
+ */
1222
+ async routeGiftWrap(event) {
1223
+ for (const entry of this.addresses.values()) {
1224
+ try {
1225
+ const pm = NIP17.unwrap(event, entry.keyManager);
1226
+ logger.debug("Mux", `Gift wrap decrypted by address ${entry.index}, sender: ${pm.senderPubkey?.slice(0, 16)}`);
1227
+ if (pm.senderPubkey === entry.nostrPubkey) {
1228
+ try {
1229
+ const parsed = JSON.parse(pm.content);
1230
+ if (parsed?.selfWrap && parsed.recipientPubkey) {
1231
+ const message2 = {
1232
+ id: parsed.originalId || pm.eventId,
1233
+ senderTransportPubkey: pm.senderPubkey,
1234
+ senderNametag: parsed.senderNametag,
1235
+ recipientTransportPubkey: parsed.recipientPubkey,
1236
+ content: parsed.text ?? "",
1237
+ timestamp: pm.timestamp * 1e3,
1238
+ encrypted: true,
1239
+ isSelfWrap: true
1240
+ };
1241
+ entry.adapter.dispatchMessage(message2);
1242
+ return;
1243
+ }
1244
+ } catch {
1245
+ }
1246
+ return;
1247
+ }
1248
+ if (isReadReceipt(pm)) {
1249
+ if (pm.replyToEventId) {
1250
+ const receipt = {
1251
+ senderTransportPubkey: pm.senderPubkey,
1252
+ messageEventId: pm.replyToEventId,
1253
+ timestamp: pm.timestamp * 1e3
1254
+ };
1255
+ entry.adapter.dispatchReadReceipt(receipt);
1256
+ }
1257
+ return;
1258
+ }
1259
+ if (pm.kind === COMPOSING_INDICATOR_KIND) {
1260
+ let senderNametag2;
1261
+ let expiresIn = 3e4;
1262
+ try {
1263
+ const parsed = JSON.parse(pm.content);
1264
+ senderNametag2 = parsed.senderNametag || void 0;
1265
+ expiresIn = parsed.expiresIn ?? 3e4;
1266
+ } catch {
1267
+ }
1268
+ entry.adapter.dispatchComposingIndicator({
1269
+ senderPubkey: pm.senderPubkey,
1270
+ senderNametag: senderNametag2,
1271
+ expiresIn
1272
+ });
1273
+ return;
1274
+ }
1275
+ try {
1276
+ const parsed = JSON.parse(pm.content);
1277
+ if (parsed?.type === "typing") {
1278
+ const indicator = {
1279
+ senderTransportPubkey: pm.senderPubkey,
1280
+ senderNametag: parsed.senderNametag,
1281
+ timestamp: pm.timestamp * 1e3
1282
+ };
1283
+ entry.adapter.dispatchTypingIndicator(indicator);
1284
+ return;
1285
+ }
1286
+ } catch {
1287
+ }
1288
+ if (!isChatMessage(pm)) return;
1289
+ let content = pm.content;
1290
+ let senderNametag;
1291
+ try {
1292
+ const parsed = JSON.parse(content);
1293
+ if (typeof parsed === "object" && parsed.text !== void 0) {
1294
+ content = parsed.text;
1295
+ senderNametag = parsed.senderNametag || void 0;
1296
+ }
1297
+ } catch {
1298
+ }
1299
+ const message = {
1300
+ id: event.id,
1301
+ senderTransportPubkey: pm.senderPubkey,
1302
+ senderNametag,
1303
+ content,
1304
+ timestamp: pm.timestamp * 1e3,
1305
+ encrypted: true
1306
+ };
1307
+ entry.adapter.dispatchMessage(message);
1308
+ return;
1309
+ } catch {
1310
+ continue;
1311
+ }
1312
+ }
1313
+ logger.debug("Mux", "Gift wrap could not be decrypted by any address");
1314
+ }
1315
+ /**
1316
+ * Dispatch a wallet event (non-gift-wrap) to the correct address adapter.
1317
+ */
1318
+ async dispatchWalletEvent(entry, event) {
1319
+ switch (event.kind) {
1320
+ case EVENT_KINDS.DIRECT_MESSAGE:
1321
+ break;
1322
+ case EVENT_KINDS.TOKEN_TRANSFER:
1323
+ await this.handleTokenTransfer(entry, event);
1324
+ break;
1325
+ case EVENT_KINDS.PAYMENT_REQUEST:
1326
+ await this.handlePaymentRequest(entry, event);
1327
+ break;
1328
+ case EVENT_KINDS.PAYMENT_REQUEST_RESPONSE:
1329
+ await this.handlePaymentRequestResponse(entry, event);
1330
+ break;
1331
+ }
1332
+ if (event.created_at) {
1333
+ this.updateLastEventTimestamp(entry, event.created_at);
1334
+ }
1335
+ }
1336
+ async handleTokenTransfer(entry, event) {
1337
+ try {
1338
+ const content = await this.decryptContent(entry, event.content, event.pubkey);
1339
+ const payload = JSON.parse(content);
1340
+ const transfer = {
1341
+ id: event.id,
1342
+ senderTransportPubkey: event.pubkey,
1343
+ payload,
1344
+ timestamp: event.created_at * 1e3
1345
+ };
1346
+ entry.adapter.dispatchTokenTransfer(transfer);
1347
+ } catch (err) {
1348
+ logger.debug("Mux", `Token transfer decrypt failed for address ${entry.index}:`, err?.message?.slice(0, 50));
1349
+ }
1350
+ }
1351
+ async handlePaymentRequest(entry, event) {
1352
+ try {
1353
+ const content = await this.decryptContent(entry, event.content, event.pubkey);
1354
+ const requestData = JSON.parse(content);
1355
+ const request = {
1356
+ id: event.id,
1357
+ senderTransportPubkey: event.pubkey,
1358
+ request: {
1359
+ requestId: requestData.requestId,
1360
+ amount: requestData.amount,
1361
+ coinId: requestData.coinId,
1362
+ message: requestData.message,
1363
+ recipientNametag: requestData.recipientNametag,
1364
+ metadata: requestData.metadata
1365
+ },
1366
+ timestamp: event.created_at * 1e3
1367
+ };
1368
+ entry.adapter.dispatchPaymentRequest(request);
1369
+ } catch (err) {
1370
+ logger.debug("Mux", `Payment request decrypt failed for address ${entry.index}:`, err?.message?.slice(0, 50));
1371
+ }
1372
+ }
1373
+ async handlePaymentRequestResponse(entry, event) {
1374
+ try {
1375
+ const content = await this.decryptContent(entry, event.content, event.pubkey);
1376
+ const responseData = JSON.parse(content);
1377
+ const response = {
1378
+ id: event.id,
1379
+ responderTransportPubkey: event.pubkey,
1380
+ response: {
1381
+ requestId: responseData.requestId,
1382
+ responseType: responseData.responseType,
1383
+ message: responseData.message,
1384
+ transferId: responseData.transferId
1385
+ },
1386
+ timestamp: event.created_at * 1e3
1387
+ };
1388
+ entry.adapter.dispatchPaymentRequestResponse(response);
1389
+ } catch (err) {
1390
+ logger.debug("Mux", `Payment response decrypt failed for address ${entry.index}:`, err?.message?.slice(0, 50));
1391
+ }
1392
+ }
1393
+ // ===========================================================================
1394
+ // Crypto Helpers
1395
+ // ===========================================================================
1396
+ async decryptContent(entry, content, senderPubkey) {
1397
+ const decrypted = await NIP04.decryptHex(
1398
+ content,
1399
+ entry.keyManager.getPrivateKeyHex(),
1400
+ senderPubkey
1401
+ );
1402
+ return this.stripContentPrefix(decrypted);
1403
+ }
1404
+ stripContentPrefix(content) {
1405
+ const prefixes = ["payment_request:", "token_transfer:", "payment_response:"];
1406
+ for (const prefix of prefixes) {
1407
+ if (content.startsWith(prefix)) return content.slice(prefix.length);
1408
+ }
1409
+ return content;
1410
+ }
1411
+ // ===========================================================================
1412
+ // Sending (called by adapters)
1413
+ // ===========================================================================
1414
+ /**
1415
+ * Create an encrypted event using a specific address's keyManager.
1416
+ * Used by AddressTransportAdapter for sending.
1417
+ */
1418
+ async createAndPublishEncryptedEvent(addressIndex, kind, content, tags) {
1419
+ const entry = this.addresses.get(addressIndex);
1420
+ if (!entry) throw new SphereError("Address not registered in mux", "NOT_INITIALIZED");
1421
+ if (!this.nostrClient) throw new SphereError("Not connected", "NOT_INITIALIZED");
1422
+ const recipientTag = tags.find((t) => t[0] === "p");
1423
+ if (!recipientTag?.[1]) throw new SphereError("No recipient pubkey in tags", "VALIDATION_ERROR");
1424
+ const encrypted = await NIP04.encryptHex(
1425
+ content,
1426
+ entry.keyManager.getPrivateKeyHex(),
1427
+ recipientTag[1]
1428
+ );
1429
+ const signedEvent = NostrEventClass.create(entry.keyManager, { kind, content: encrypted, tags });
1430
+ const nostrEvent = NostrEventClass.fromJSON({
1431
+ id: signedEvent.id,
1432
+ kind: signedEvent.kind,
1433
+ content: signedEvent.content,
1434
+ tags: signedEvent.tags,
1435
+ pubkey: signedEvent.pubkey,
1436
+ created_at: signedEvent.created_at,
1437
+ sig: signedEvent.sig
1438
+ });
1439
+ await this.nostrClient.publishEvent(nostrEvent);
1440
+ return signedEvent.id;
1441
+ }
1442
+ /**
1443
+ * Create and publish a NIP-17 gift wrap message for a specific address.
1444
+ */
1445
+ async sendGiftWrap(addressIndex, recipientPubkey, content) {
1446
+ const entry = this.addresses.get(addressIndex);
1447
+ if (!entry) throw new SphereError("Address not registered in mux", "NOT_INITIALIZED");
1448
+ if (!this.nostrClient) throw new SphereError("Not connected", "NOT_INITIALIZED");
1449
+ const nostrRecipient = recipientPubkey.length === 66 && (recipientPubkey.startsWith("02") || recipientPubkey.startsWith("03")) ? recipientPubkey.slice(2) : recipientPubkey;
1450
+ const giftWrap = NIP17.createGiftWrap(entry.keyManager, nostrRecipient, content);
1451
+ const giftWrapEvent = NostrEventClass.fromJSON(giftWrap);
1452
+ await this.nostrClient.publishEvent(giftWrapEvent);
1453
+ const selfPubkey = entry.keyManager.getPublicKeyHex();
1454
+ const senderNametag = entry.identity.nametag;
1455
+ const selfWrapContent = JSON.stringify({
1456
+ selfWrap: true,
1457
+ originalId: giftWrap.id,
1458
+ recipientPubkey,
1459
+ senderNametag,
1460
+ text: content
1461
+ });
1462
+ const selfGiftWrap = NIP17.createGiftWrap(entry.keyManager, selfPubkey, selfWrapContent);
1463
+ const selfGiftWrapEvent = NostrEventClass.fromJSON(selfGiftWrap);
1464
+ this.nostrClient.publishEvent(selfGiftWrapEvent).catch((err) => {
1465
+ logger.debug("Mux", "Self-wrap publish failed:", err);
1466
+ });
1467
+ return giftWrap.id;
1468
+ }
1469
+ /**
1470
+ * Publish a raw event (e.g., identity binding, broadcast).
1471
+ */
1472
+ async publishRawEvent(addressIndex, kind, content, tags) {
1473
+ const entry = this.addresses.get(addressIndex);
1474
+ if (!entry) throw new SphereError("Address not registered in mux", "NOT_INITIALIZED");
1475
+ if (!this.nostrClient) throw new SphereError("Not connected", "NOT_INITIALIZED");
1476
+ const signedEvent = NostrEventClass.create(entry.keyManager, { kind, content, tags });
1477
+ const nostrEvent = NostrEventClass.fromJSON({
1478
+ id: signedEvent.id,
1479
+ kind: signedEvent.kind,
1480
+ content: signedEvent.content,
1481
+ tags: signedEvent.tags,
1482
+ pubkey: signedEvent.pubkey,
1483
+ created_at: signedEvent.created_at,
1484
+ sig: signedEvent.sig
1485
+ });
1486
+ await this.nostrClient.publishEvent(nostrEvent);
1487
+ return signedEvent.id;
1488
+ }
1489
+ // ===========================================================================
1490
+ // Resolve Methods (delegates to inner — these are stateless relay queries)
1491
+ // ===========================================================================
1492
+ /**
1493
+ * Get the NostrClient for resolve operations.
1494
+ * Adapters use this for resolve*, publishIdentityBinding, etc.
1495
+ */
1496
+ getNostrClient() {
1497
+ return this.nostrClient;
1498
+ }
1499
+ /**
1500
+ * Get keyManager for a specific address (used by adapters for resolve/binding).
1501
+ */
1502
+ getKeyManager(addressIndex) {
1503
+ return this.addresses.get(addressIndex)?.keyManager ?? null;
1504
+ }
1505
+ /**
1506
+ * Get identity for a specific address.
1507
+ */
1508
+ getIdentity(addressIndex) {
1509
+ return this.addresses.get(addressIndex)?.identity ?? null;
1510
+ }
1511
+ // ===========================================================================
1512
+ // Event timestamp persistence
1513
+ // ===========================================================================
1514
+ updateLastEventTimestamp(entry, createdAt) {
1515
+ if (!this.storage) return;
1516
+ if (createdAt <= entry.lastEventTs) return;
1517
+ entry.lastEventTs = createdAt;
1518
+ const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_WALLET_EVENT_TS}_${entry.nostrPubkey.slice(0, 16)}`;
1519
+ this.storage.set(storageKey, createdAt.toString()).catch((err) => {
1520
+ logger.debug("Mux", "Failed to save last event timestamp:", err);
1521
+ });
1522
+ }
1523
+ // ===========================================================================
1524
+ // Mux-level event system
1525
+ // ===========================================================================
1526
+ onTransportEvent(callback) {
1527
+ this.eventCallbacks.add(callback);
1528
+ return () => this.eventCallbacks.delete(callback);
1529
+ }
1530
+ onChatReady(handler) {
1531
+ if (this.chatEoseFired) {
1532
+ try {
1533
+ handler();
1534
+ } catch {
1535
+ }
1536
+ return () => {
1537
+ };
1538
+ }
1539
+ this.chatEoseHandlers.push(handler);
1540
+ return () => {
1541
+ const idx = this.chatEoseHandlers.indexOf(handler);
1542
+ if (idx >= 0) this.chatEoseHandlers.splice(idx, 1);
1543
+ };
1544
+ }
1545
+ emitEvent(event) {
1546
+ for (const cb of this.eventCallbacks) {
1547
+ try {
1548
+ cb(event);
1549
+ } catch {
1550
+ }
1551
+ }
1552
+ for (const entry of this.addresses.values()) {
1553
+ entry.adapter.emitTransportEvent(event);
1554
+ }
1555
+ }
1556
+ // ===========================================================================
1557
+ // Dedup Management
1558
+ // ===========================================================================
1559
+ /**
1560
+ * Clear processed event IDs (e.g., on address change or periodic cleanup).
1561
+ */
1562
+ clearProcessedEvents() {
1563
+ this.processedEventIds.clear();
1564
+ }
1565
+ /**
1566
+ * Get the storage adapter (for adapters that need it).
1567
+ */
1568
+ getStorage() {
1569
+ return this.storage;
1570
+ }
1571
+ /**
1572
+ * Get the UUID generator.
1573
+ */
1574
+ getUUIDGenerator() {
1575
+ return this.config.generateUUID;
1576
+ }
1577
+ };
1578
+ var AddressTransportAdapter = class {
1579
+ id;
1580
+ name;
1581
+ type = "p2p";
1582
+ description;
1583
+ mux;
1584
+ addressIndex;
1585
+ identity;
1586
+ resolveDelegate;
1587
+ // Per-address handler sets
1588
+ messageHandlers = /* @__PURE__ */ new Set();
1589
+ transferHandlers = /* @__PURE__ */ new Set();
1590
+ paymentRequestHandlers = /* @__PURE__ */ new Set();
1591
+ paymentRequestResponseHandlers = /* @__PURE__ */ new Set();
1592
+ readReceiptHandlers = /* @__PURE__ */ new Set();
1593
+ typingIndicatorHandlers = /* @__PURE__ */ new Set();
1594
+ composingHandlers = /* @__PURE__ */ new Set();
1595
+ instantSplitBundleHandlers = /* @__PURE__ */ new Set();
1596
+ broadcastHandlers = /* @__PURE__ */ new Map();
1597
+ eventCallbacks = /* @__PURE__ */ new Set();
1598
+ pendingMessages = [];
1599
+ chatEoseHandlers = [];
1600
+ constructor(mux, addressIndex, identity, resolveDelegate) {
1601
+ this.mux = mux;
1602
+ this.addressIndex = addressIndex;
1603
+ this.identity = identity;
1604
+ this.resolveDelegate = resolveDelegate ?? null;
1605
+ this.id = `nostr-addr-${addressIndex}`;
1606
+ this.name = `Nostr Transport (address ${addressIndex})`;
1607
+ this.description = `P2P messaging for address index ${addressIndex}`;
1608
+ }
1609
+ // ===========================================================================
1610
+ // BaseProvider — delegates to mux
1611
+ // ===========================================================================
1612
+ async connect() {
1613
+ await this.mux.connect();
1614
+ }
1615
+ async disconnect() {
1616
+ }
1617
+ isConnected() {
1618
+ return this.mux.isConnected();
1619
+ }
1620
+ getStatus() {
1621
+ return this.mux.getStatus();
1622
+ }
1623
+ // ===========================================================================
1624
+ // Identity (no-op — mux manages identity via addAddress)
1625
+ // ===========================================================================
1626
+ async setIdentity(identity) {
1627
+ this.identity = identity;
1628
+ await this.mux.addAddress(this.addressIndex, identity);
1629
+ }
1630
+ // ===========================================================================
1631
+ // Sending — delegates to mux with this address's keyManager
1632
+ // ===========================================================================
1633
+ async sendMessage(recipientPubkey, content) {
1634
+ const senderNametag = this.identity.nametag;
1635
+ const wrappedContent = senderNametag ? JSON.stringify({ senderNametag, text: content }) : content;
1636
+ return this.mux.sendGiftWrap(this.addressIndex, recipientPubkey, wrappedContent);
1637
+ }
1638
+ async sendTokenTransfer(recipientPubkey, payload) {
1639
+ const content = "token_transfer:" + JSON.stringify(payload);
1640
+ const uniqueD = `token-transfer-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1641
+ return this.mux.createAndPublishEncryptedEvent(
1642
+ this.addressIndex,
1643
+ EVENT_KINDS.TOKEN_TRANSFER,
1644
+ content,
1645
+ [["p", recipientPubkey], ["d", uniqueD], ["type", "token_transfer"]]
1646
+ );
1647
+ }
1648
+ async sendPaymentRequest(recipientPubkey, payload) {
1649
+ const requestId2 = this.mux.getUUIDGenerator()();
1650
+ const amount = typeof payload.amount === "bigint" ? payload.amount.toString() : payload.amount;
1651
+ const requestContent = {
1652
+ requestId: requestId2,
1653
+ amount,
1654
+ coinId: payload.coinId,
1655
+ message: payload.message,
1656
+ recipientNametag: payload.recipientNametag,
1657
+ deadline: Date.now() + 5 * 60 * 1e3
1658
+ };
1659
+ const content = "payment_request:" + JSON.stringify(requestContent);
1660
+ const tags = [
1661
+ ["p", recipientPubkey],
1662
+ ["type", "payment_request"],
1663
+ ["amount", amount]
1664
+ ];
1665
+ if (payload.recipientNametag) {
1666
+ tags.push(["recipient", payload.recipientNametag]);
1667
+ }
1668
+ return this.mux.createAndPublishEncryptedEvent(
1669
+ this.addressIndex,
1670
+ EVENT_KINDS.PAYMENT_REQUEST,
1671
+ content,
1672
+ tags
1673
+ );
1674
+ }
1675
+ async sendPaymentRequestResponse(recipientPubkey, response) {
1676
+ const content = "payment_response:" + JSON.stringify(response);
1677
+ return this.mux.createAndPublishEncryptedEvent(
1678
+ this.addressIndex,
1679
+ EVENT_KINDS.PAYMENT_REQUEST_RESPONSE,
1680
+ content,
1681
+ [["p", recipientPubkey], ["type", "payment_response"]]
1682
+ );
1683
+ }
1684
+ async sendReadReceipt(recipientPubkey, messageEventId) {
1685
+ const content = JSON.stringify({ type: "read_receipt", messageEventId });
1686
+ await this.mux.sendGiftWrap(this.addressIndex, recipientPubkey, content);
1687
+ }
1688
+ async sendTypingIndicator(recipientPubkey) {
1689
+ const content = JSON.stringify({
1690
+ type: "typing",
1691
+ senderNametag: this.identity.nametag
1692
+ });
1693
+ await this.mux.sendGiftWrap(this.addressIndex, recipientPubkey, content);
1694
+ }
1695
+ async sendComposingIndicator(recipientPubkey, content) {
1696
+ await this.mux.sendGiftWrap(this.addressIndex, recipientPubkey, content);
1697
+ }
1698
+ async sendInstantSplitBundle(recipientPubkey, bundle) {
1699
+ const content = "token_transfer:" + JSON.stringify({
1700
+ type: "instant_split",
1701
+ ...bundle
1702
+ });
1703
+ const uniqueD = `instant-split-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1704
+ return this.mux.createAndPublishEncryptedEvent(
1705
+ this.addressIndex,
1706
+ EVENT_KINDS.TOKEN_TRANSFER,
1707
+ content,
1708
+ [["p", recipientPubkey], ["d", uniqueD], ["type", "instant_split"]]
1709
+ );
1710
+ }
1711
+ // ===========================================================================
1712
+ // Subscription handlers — per-address
1713
+ // ===========================================================================
1714
+ onMessage(handler) {
1715
+ this.messageHandlers.add(handler);
1716
+ if (this.pendingMessages.length > 0) {
1717
+ const pending2 = this.pendingMessages;
1718
+ this.pendingMessages = [];
1719
+ for (const msg of pending2) {
1720
+ try {
1721
+ handler(msg);
1722
+ } catch {
1723
+ }
1724
+ }
1725
+ }
1726
+ return () => this.messageHandlers.delete(handler);
1727
+ }
1728
+ onTokenTransfer(handler) {
1729
+ this.transferHandlers.add(handler);
1730
+ return () => this.transferHandlers.delete(handler);
1731
+ }
1732
+ onPaymentRequest(handler) {
1733
+ this.paymentRequestHandlers.add(handler);
1734
+ return () => this.paymentRequestHandlers.delete(handler);
1735
+ }
1736
+ onPaymentRequestResponse(handler) {
1737
+ this.paymentRequestResponseHandlers.add(handler);
1738
+ return () => this.paymentRequestResponseHandlers.delete(handler);
1739
+ }
1740
+ onReadReceipt(handler) {
1741
+ this.readReceiptHandlers.add(handler);
1742
+ return () => this.readReceiptHandlers.delete(handler);
1743
+ }
1744
+ onTypingIndicator(handler) {
1745
+ this.typingIndicatorHandlers.add(handler);
1746
+ return () => this.typingIndicatorHandlers.delete(handler);
1747
+ }
1748
+ onComposing(handler) {
1749
+ this.composingHandlers.add(handler);
1750
+ return () => this.composingHandlers.delete(handler);
1751
+ }
1752
+ onInstantSplitReceived(handler) {
1753
+ this.instantSplitBundleHandlers.add(handler);
1754
+ return () => this.instantSplitBundleHandlers.delete(handler);
1755
+ }
1756
+ subscribeToBroadcast(tags, handler) {
1757
+ const key = tags.sort().join(":");
1758
+ if (!this.broadcastHandlers.has(key)) {
1759
+ this.broadcastHandlers.set(key, /* @__PURE__ */ new Set());
1760
+ }
1761
+ this.broadcastHandlers.get(key).add(handler);
1762
+ return () => this.broadcastHandlers.get(key)?.delete(handler);
1763
+ }
1764
+ async publishBroadcast(content, tags) {
1765
+ const eventTags = tags ? tags.map((t) => ["t", t]) : [];
1766
+ return this.mux.publishRawEvent(this.addressIndex, 30023, content, eventTags);
1767
+ }
1768
+ // ===========================================================================
1769
+ // Resolve methods — delegate to original NostrTransportProvider
1770
+ // These are stateless relay queries, shared across all addresses
1771
+ // ===========================================================================
1772
+ async resolve(identifier) {
1773
+ return this.resolveDelegate?.resolve?.(identifier) ?? null;
1774
+ }
1775
+ async resolveNametag(nametag) {
1776
+ return this.resolveDelegate?.resolveNametag?.(nametag) ?? null;
1777
+ }
1778
+ async resolveNametagInfo(nametag) {
1779
+ return this.resolveDelegate?.resolveNametagInfo?.(nametag) ?? null;
1780
+ }
1781
+ async resolveAddressInfo(address) {
1782
+ return this.resolveDelegate?.resolveAddressInfo?.(address) ?? null;
1783
+ }
1784
+ async resolveTransportPubkeyInfo(transportPubkey) {
1785
+ return this.resolveDelegate?.resolveTransportPubkeyInfo?.(transportPubkey) ?? null;
1786
+ }
1787
+ async discoverAddresses(transportPubkeys) {
1788
+ return this.resolveDelegate?.discoverAddresses?.(transportPubkeys) ?? [];
1789
+ }
1790
+ async recoverNametag() {
1791
+ return this.resolveDelegate?.recoverNametag?.() ?? null;
1792
+ }
1793
+ async publishIdentityBinding(chainPubkey, l1Address, directAddress, nametag) {
1794
+ return this.resolveDelegate?.publishIdentityBinding?.(chainPubkey, l1Address, directAddress, nametag) ?? false;
1795
+ }
1796
+ // ===========================================================================
1797
+ // Relay Management — delegates to mux
1798
+ // ===========================================================================
1799
+ getRelays() {
1800
+ return this.mux.getRelays();
1801
+ }
1802
+ getConnectedRelays() {
1803
+ return this.mux.getConnectedRelays();
1804
+ }
1805
+ async addRelay(relayUrl) {
1806
+ return this.mux.addRelay(relayUrl);
1807
+ }
1808
+ async removeRelay(relayUrl) {
1809
+ return this.mux.removeRelay(relayUrl);
1810
+ }
1811
+ hasRelay(relayUrl) {
1812
+ return this.mux.hasRelay(relayUrl);
1813
+ }
1814
+ isRelayConnected(relayUrl) {
1815
+ return this.mux.isRelayConnected(relayUrl);
1816
+ }
1817
+ setFallbackSince(sinceSeconds) {
1818
+ this.mux.setFallbackSince(this.addressIndex, sinceSeconds);
1819
+ }
1820
+ async fetchPendingEvents() {
1821
+ }
1822
+ onChatReady(handler) {
1823
+ return this.mux.onChatReady(handler);
1824
+ }
1825
+ // ===========================================================================
1826
+ // Dispatch methods — called by MultiAddressTransportMux to route events
1827
+ // ===========================================================================
1828
+ dispatchMessage(message) {
1829
+ if (this.messageHandlers.size === 0) {
1830
+ this.pendingMessages.push(message);
1831
+ return;
1832
+ }
1833
+ for (const handler of this.messageHandlers) {
1834
+ try {
1835
+ handler(message);
1836
+ } catch (e) {
1837
+ logger.debug("MuxAdapter", "Message handler error:", e);
1838
+ }
1839
+ }
1840
+ }
1841
+ dispatchTokenTransfer(transfer) {
1842
+ for (const handler of this.transferHandlers) {
1843
+ try {
1844
+ handler(transfer);
1845
+ } catch (e) {
1846
+ logger.debug("MuxAdapter", "Transfer handler error:", e);
1847
+ }
1848
+ }
1849
+ }
1850
+ dispatchPaymentRequest(request) {
1851
+ for (const handler of this.paymentRequestHandlers) {
1852
+ try {
1853
+ handler(request);
1854
+ } catch (e) {
1855
+ logger.debug("MuxAdapter", "Payment request handler error:", e);
1856
+ }
1857
+ }
1858
+ }
1859
+ dispatchPaymentRequestResponse(response) {
1860
+ for (const handler of this.paymentRequestResponseHandlers) {
1861
+ try {
1862
+ handler(response);
1863
+ } catch (e) {
1864
+ logger.debug("MuxAdapter", "Payment response handler error:", e);
1865
+ }
1866
+ }
1867
+ }
1868
+ dispatchReadReceipt(receipt) {
1869
+ for (const handler of this.readReceiptHandlers) {
1870
+ try {
1871
+ handler(receipt);
1872
+ } catch (e) {
1873
+ logger.debug("MuxAdapter", "Read receipt handler error:", e);
1874
+ }
1875
+ }
1876
+ }
1877
+ dispatchTypingIndicator(indicator) {
1878
+ for (const handler of this.typingIndicatorHandlers) {
1879
+ try {
1880
+ handler(indicator);
1881
+ } catch (e) {
1882
+ logger.debug("MuxAdapter", "Typing handler error:", e);
1883
+ }
1884
+ }
1885
+ }
1886
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1887
+ dispatchComposingIndicator(indicator) {
1888
+ for (const handler of this.composingHandlers) {
1889
+ try {
1890
+ handler(indicator);
1891
+ } catch (e) {
1892
+ logger.debug("MuxAdapter", "Composing handler error:", e);
1893
+ }
1894
+ }
1895
+ }
1896
+ dispatchInstantSplitBundle(bundle) {
1897
+ for (const handler of this.instantSplitBundleHandlers) {
1898
+ try {
1899
+ handler(bundle);
1900
+ } catch (e) {
1901
+ logger.debug("MuxAdapter", "Instant split handler error:", e);
1902
+ }
1903
+ }
1904
+ }
1905
+ emitTransportEvent(event) {
1906
+ for (const cb of this.eventCallbacks) {
1907
+ try {
1908
+ cb(event);
1909
+ } catch {
1910
+ }
1911
+ }
1912
+ }
1913
+ };
1914
+
809
1915
  // modules/payments/L1PaymentsModule.ts
810
1916
  init_errors();
811
1917
  init_constants();
@@ -4911,6 +6017,15 @@ var PaymentsModule = class _PaymentsModule {
4911
6017
  this.unsubscribePaymentRequests = null;
4912
6018
  this.unsubscribePaymentRequestResponses?.();
4913
6019
  this.unsubscribePaymentRequestResponses = null;
6020
+ this.stopProofPolling();
6021
+ this.proofPollingJobs.clear();
6022
+ this.stopResolveUnconfirmedPolling();
6023
+ this.unsubscribeStorageEvents();
6024
+ for (const [, resolver] of this.pendingResponseResolvers) {
6025
+ clearTimeout(resolver.timeout);
6026
+ resolver.reject(new Error("Address switched"));
6027
+ }
6028
+ this.pendingResponseResolvers.clear();
4914
6029
  this.tokens.clear();
4915
6030
  this.pendingTransfers.clear();
4916
6031
  this.tombstones = [];
@@ -4959,6 +6074,13 @@ var PaymentsModule = class _PaymentsModule {
4959
6074
  try {
4960
6075
  const result = await provider.load();
4961
6076
  if (result.success && result.data) {
6077
+ const loadedMeta = result.data?._meta;
6078
+ const currentL1 = this.deps.identity.l1Address;
6079
+ const currentChain = this.deps.identity.chainPubkey;
6080
+ if (loadedMeta?.address && currentL1 && loadedMeta.address !== currentL1 && loadedMeta.address !== currentChain) {
6081
+ logger.warn("Payments", `Load: rejecting data from provider ${id} \u2014 address mismatch (got=${loadedMeta.address.slice(0, 20)}... expected=${currentL1.slice(0, 20)}...)`);
6082
+ continue;
6083
+ }
4962
6084
  this.loadFromStorageData(result.data);
4963
6085
  const txfData = result.data;
4964
6086
  if (txfData._history && txfData._history.length > 0) {
@@ -5040,6 +6162,11 @@ var PaymentsModule = class _PaymentsModule {
5040
6162
  */
5041
6163
  async send(request) {
5042
6164
  this.ensureInitialized();
6165
+ let resolveSendTracker;
6166
+ const sendTracker = new Promise((r) => {
6167
+ resolveSendTracker = r;
6168
+ });
6169
+ this.pendingBackgroundTasks.push(sendTracker);
5043
6170
  const result = {
5044
6171
  id: crypto.randomUUID(),
5045
6172
  status: "pending",
@@ -5327,6 +6454,8 @@ var PaymentsModule = class _PaymentsModule {
5327
6454
  }
5328
6455
  this.deps.emitEvent("transfer:failed", result);
5329
6456
  throw error;
6457
+ } finally {
6458
+ resolveSendTracker();
5330
6459
  }
5331
6460
  }
5332
6461
  /**
@@ -6339,9 +7468,12 @@ var PaymentsModule = class _PaymentsModule {
6339
7468
  * Call this before process exit to ensure all tokens are saved.
6340
7469
  */
6341
7470
  async waitForPendingOperations() {
7471
+ logger.debug("Payments", `waitForPendingOperations: ${this.pendingBackgroundTasks.length} pending tasks`);
6342
7472
  if (this.pendingBackgroundTasks.length > 0) {
7473
+ logger.debug("Payments", "waitForPendingOperations: waiting...");
6343
7474
  await Promise.allSettled(this.pendingBackgroundTasks);
6344
7475
  this.pendingBackgroundTasks = [];
7476
+ logger.debug("Payments", "waitForPendingOperations: all tasks completed");
6345
7477
  }
6346
7478
  }
6347
7479
  /**
@@ -7583,6 +8715,13 @@ var PaymentsModule = class _PaymentsModule {
7583
8715
  try {
7584
8716
  const result = await provider.sync(localData);
7585
8717
  if (result.success && result.merged) {
8718
+ const mergedMeta = result.merged?._meta;
8719
+ const currentL1 = this.deps.identity.l1Address;
8720
+ const currentChain = this.deps.identity.chainPubkey;
8721
+ if (mergedMeta?.address && currentL1 && mergedMeta.address !== currentL1 && mergedMeta.address !== currentChain) {
8722
+ logger.warn("Payments", `Sync: rejecting data from provider ${providerId} \u2014 address mismatch (got=${mergedMeta.address.slice(0, 20)}... expected=${currentL1.slice(0, 20)}...)`);
8723
+ continue;
8724
+ }
7586
8725
  const savedTokens = new Map(this.tokens);
7587
8726
  this.loadFromStorageData(result.merged);
7588
8727
  let restoredCount = 0;
@@ -9015,9 +10154,9 @@ init_logger();
9015
10154
  init_errors();
9016
10155
  init_constants();
9017
10156
  import {
9018
- NostrClient,
9019
- NostrKeyManager,
9020
- Filter
10157
+ NostrClient as NostrClient2,
10158
+ NostrKeyManager as NostrKeyManager2,
10159
+ Filter as Filter2
9021
10160
  } from "@unicitylabs/nostr-js-sdk";
9022
10161
 
9023
10162
  // modules/groupchat/types.ts
@@ -9033,7 +10172,7 @@ var GroupVisibility = {
9033
10172
 
9034
10173
  // modules/groupchat/GroupChatModule.ts
9035
10174
  function createNip29Filter(data) {
9036
- return new Filter(data);
10175
+ return new Filter2(data);
9037
10176
  }
9038
10177
  var GroupChatModule = class {
9039
10178
  config;
@@ -9082,7 +10221,7 @@ var GroupChatModule = class {
9082
10221
  }
9083
10222
  this.deps = deps;
9084
10223
  const secretKey = Buffer.from(deps.identity.privateKey, "hex");
9085
- this.keyManager = NostrKeyManager.fromPrivateKey(secretKey);
10224
+ this.keyManager = NostrKeyManager2.fromPrivateKey(secretKey);
9086
10225
  }
9087
10226
  async load() {
9088
10227
  this.ensureInitialized();
@@ -9217,7 +10356,7 @@ var GroupChatModule = class {
9217
10356
  }
9218
10357
  this.subscriptionIds = [];
9219
10358
  const secretKey = Buffer.from(this.deps.identity.privateKey, "hex");
9220
- this.keyManager = NostrKeyManager.fromPrivateKey(secretKey);
10359
+ this.keyManager = NostrKeyManager2.fromPrivateKey(secretKey);
9221
10360
  if (this.groups.size === 0) {
9222
10361
  await this.restoreJoinedGroups();
9223
10362
  } else {
@@ -9229,13 +10368,13 @@ var GroupChatModule = class {
9229
10368
  this.ensureInitialized();
9230
10369
  if (!this.keyManager) {
9231
10370
  const secretKey = Buffer.from(this.deps.identity.privateKey, "hex");
9232
- this.keyManager = NostrKeyManager.fromPrivateKey(secretKey);
10371
+ this.keyManager = NostrKeyManager2.fromPrivateKey(secretKey);
9233
10372
  }
9234
10373
  const primaryRelay = this.config.relays[0];
9235
10374
  if (primaryRelay) {
9236
10375
  await this.checkAndClearOnRelayChange(primaryRelay);
9237
10376
  }
9238
- this.client = new NostrClient(this.keyManager);
10377
+ this.client = new NostrClient2(this.keyManager);
9239
10378
  try {
9240
10379
  await this.client.connect(...this.config.relays);
9241
10380
  this.connected = true;
@@ -9494,7 +10633,7 @@ var GroupChatModule = class {
9494
10633
  if (!myPubkey) return [];
9495
10634
  const groupIdsWithMembership = /* @__PURE__ */ new Set();
9496
10635
  await this.oneshotSubscription(
9497
- new Filter({ kinds: [NIP29_KINDS.GROUP_MEMBERS] }),
10636
+ new Filter2({ kinds: [NIP29_KINDS.GROUP_MEMBERS] }),
9498
10637
  {
9499
10638
  onEvent: (event) => {
9500
10639
  const groupId = this.getGroupIdFromMetadataEvent(event);
@@ -9545,7 +10684,7 @@ var GroupChatModule = class {
9545
10684
  const memberCountsMap = /* @__PURE__ */ new Map();
9546
10685
  await Promise.all([
9547
10686
  this.oneshotSubscription(
9548
- new Filter({ kinds: [NIP29_KINDS.GROUP_METADATA] }),
10687
+ new Filter2({ kinds: [NIP29_KINDS.GROUP_METADATA] }),
9549
10688
  {
9550
10689
  onEvent: (event) => {
9551
10690
  const group = this.parseGroupMetadata(event);
@@ -9563,7 +10702,7 @@ var GroupChatModule = class {
9563
10702
  }
9564
10703
  ),
9565
10704
  this.oneshotSubscription(
9566
- new Filter({ kinds: [NIP29_KINDS.GROUP_MEMBERS] }),
10705
+ new Filter2({ kinds: [NIP29_KINDS.GROUP_MEMBERS] }),
9567
10706
  {
9568
10707
  onEvent: (event) => {
9569
10708
  const groupId = this.getGroupIdFromMetadataEvent(event);
@@ -10066,7 +11205,7 @@ var GroupChatModule = class {
10066
11205
  if (!this.client) return /* @__PURE__ */ new Set();
10067
11206
  const adminPubkeys = /* @__PURE__ */ new Set();
10068
11207
  return this.oneshotSubscription(
10069
- new Filter({ kinds: [NIP29_KINDS.GROUP_ADMINS], "#d": ["", "_"] }),
11208
+ new Filter2({ kinds: [NIP29_KINDS.GROUP_ADMINS], "#d": ["", "_"] }),
10070
11209
  {
10071
11210
  onEvent: (event) => {
10072
11211
  const pTags = event.tags.filter((t) => t[0] === "p");
@@ -10088,7 +11227,7 @@ var GroupChatModule = class {
10088
11227
  if (!this.client) return null;
10089
11228
  let result = null;
10090
11229
  return this.oneshotSubscription(
10091
- new Filter({ kinds: [NIP29_KINDS.GROUP_METADATA], "#d": [groupId] }),
11230
+ new Filter2({ kinds: [NIP29_KINDS.GROUP_METADATA], "#d": [groupId] }),
10092
11231
  {
10093
11232
  onEvent: (event) => {
10094
11233
  if (!result) result = this.parseGroupMetadata(event);
@@ -10125,7 +11264,7 @@ var GroupChatModule = class {
10125
11264
  if (!this.client) return [];
10126
11265
  const members = [];
10127
11266
  return this.oneshotSubscription(
10128
- new Filter({ kinds: [NIP29_KINDS.GROUP_MEMBERS], "#d": [groupId] }),
11267
+ new Filter2({ kinds: [NIP29_KINDS.GROUP_MEMBERS], "#d": [groupId] }),
10129
11268
  {
10130
11269
  onEvent: (event) => {
10131
11270
  const pTags = event.tags.filter((t) => t[0] === "p");
@@ -10146,7 +11285,7 @@ var GroupChatModule = class {
10146
11285
  if (!this.client) return [];
10147
11286
  const adminPubkeys = [];
10148
11287
  return this.oneshotSubscription(
10149
- new Filter({ kinds: [NIP29_KINDS.GROUP_ADMINS], "#d": [groupId] }),
11288
+ new Filter2({ kinds: [NIP29_KINDS.GROUP_ADMINS], "#d": [groupId] }),
10150
11289
  {
10151
11290
  onEvent: (event) => {
10152
11291
  const pTags = event.tags.filter((t) => t[0] === "p");
@@ -13861,11 +15000,18 @@ var Sphere = class _Sphere {
13861
15000
  _transport;
13862
15001
  _oracle;
13863
15002
  _priceProvider;
13864
- // Modules
15003
+ // Modules (single-instance — backward compat, delegates to active address)
13865
15004
  _payments;
13866
15005
  _communications;
13867
15006
  _groupChat = null;
13868
15007
  _market = null;
15008
+ // Per-address module instances (Phase 2: independent parallel operation)
15009
+ _addressModules = /* @__PURE__ */ new Map();
15010
+ _transportMux = null;
15011
+ // Stored configs for creating per-address modules
15012
+ _l1Config;
15013
+ _groupChatConfig;
15014
+ _marketConfig;
13869
15015
  // Events
13870
15016
  eventHandlers = /* @__PURE__ */ new Map();
13871
15017
  // Provider management
@@ -13883,6 +15029,9 @@ var Sphere = class _Sphere {
13883
15029
  if (tokenStorage) {
13884
15030
  this._tokenStorageProviders.set(tokenStorage.id, tokenStorage);
13885
15031
  }
15032
+ this._l1Config = l1Config;
15033
+ this._groupChatConfig = groupChatConfig;
15034
+ this._marketConfig = marketConfig;
13886
15035
  this._payments = createPaymentsModule({ l1: l1Config });
13887
15036
  this._communications = createCommunicationsModule();
13888
15037
  this._groupChat = groupChatConfig ? createGroupChatModule(groupChatConfig) : null;
@@ -15141,7 +16290,7 @@ var Sphere = class _Sphere {
15141
16290
  nametags.set(0, newNametag);
15142
16291
  }
15143
16292
  const nametag = this._addressNametags.get(addressId)?.get(0);
15144
- this._identity = {
16293
+ const newIdentity = {
15145
16294
  privateKey: addressInfo.privateKey,
15146
16295
  chainPubkey: addressInfo.publicKey,
15147
16296
  l1Address: addressInfo.address,
@@ -15149,20 +16298,53 @@ var Sphere = class _Sphere {
15149
16298
  ipnsName: "12D3KooW" + ipnsHash,
15150
16299
  nametag
15151
16300
  };
16301
+ if (!this._addressModules.has(index)) {
16302
+ logger.debug("Sphere", `switchToAddress(${index}): creating per-address modules (lazy init)`);
16303
+ const addressTokenProviders = /* @__PURE__ */ new Map();
16304
+ for (const [providerId, provider] of this._tokenStorageProviders.entries()) {
16305
+ if (provider.createForAddress) {
16306
+ const newProvider = provider.createForAddress();
16307
+ newProvider.setIdentity(newIdentity);
16308
+ await newProvider.initialize();
16309
+ addressTokenProviders.set(providerId, newProvider);
16310
+ } else {
16311
+ logger.warn("Sphere", `Token storage provider ${providerId} does not support createForAddress, reusing shared instance`);
16312
+ addressTokenProviders.set(providerId, provider);
16313
+ }
16314
+ }
16315
+ await this.initializeAddressModules(index, newIdentity, addressTokenProviders);
16316
+ } else {
16317
+ const moduleSet = this._addressModules.get(index);
16318
+ if (nametag !== moduleSet.identity.nametag) {
16319
+ moduleSet.identity = newIdentity;
16320
+ const addressTransport = moduleSet.transportAdapter ?? this._transport;
16321
+ moduleSet.payments.initialize({
16322
+ identity: newIdentity,
16323
+ storage: this._storage,
16324
+ tokenStorageProviders: moduleSet.tokenStorageProviders,
16325
+ transport: addressTransport,
16326
+ oracle: this._oracle,
16327
+ emitEvent: this.emitEvent.bind(this),
16328
+ chainCode: this._masterKey?.chainCode || void 0,
16329
+ price: this._priceProvider ?? void 0
16330
+ });
16331
+ }
16332
+ }
16333
+ this._identity = newIdentity;
15152
16334
  this._currentAddressIndex = index;
15153
16335
  await this._updateCachedProxyAddress();
16336
+ const activeModules = this._addressModules.get(index);
16337
+ this._payments = activeModules.payments;
16338
+ this._communications = activeModules.communications;
16339
+ this._groupChat = activeModules.groupChat;
16340
+ this._market = activeModules.market;
15154
16341
  await this._storage.set(STORAGE_KEYS_GLOBAL.CURRENT_ADDRESS_INDEX, index.toString());
15155
16342
  this._storage.setIdentity(this._identity);
15156
- await this._transport.setIdentity(this._identity);
15157
- logger.debug("Sphere", `switchToAddress(${index}): re-initializing ${this._tokenStorageProviders.size} token storage provider(s)`);
15158
- for (const [providerId, provider] of this._tokenStorageProviders.entries()) {
15159
- logger.debug("Sphere", `switchToAddress(${index}): shutdown provider=${providerId}`);
15160
- await provider.shutdown();
15161
- provider.setIdentity(this._identity);
15162
- logger.debug("Sphere", `switchToAddress(${index}): initialize provider=${providerId}`);
15163
- await provider.initialize();
16343
+ if (this._transport.setFallbackSince) {
16344
+ const fallbackTs = Math.floor(Date.now() / 1e3) - 86400;
16345
+ this._transport.setFallbackSince(fallbackTs);
15164
16346
  }
15165
- await this.reinitializeModulesForNewAddress();
16347
+ await this._transport.setIdentity(this._identity);
15166
16348
  this.emitEvent("identity:changed", {
15167
16349
  l1Address: this._identity.l1Address,
15168
16350
  directAddress: this._identity.directAddress,
@@ -15217,42 +16399,104 @@ var Sphere = class _Sphere {
15217
16399
  }
15218
16400
  }
15219
16401
  /**
15220
- * Re-initialize modules after address switch
16402
+ * Create a new set of per-address modules for the given index.
16403
+ * Each address gets its own PaymentsModule, CommunicationsModule, etc.
16404
+ * Modules are fully independent — they have their own token storage,
16405
+ * and can sync/finalize/split in background regardless of active address.
16406
+ *
16407
+ * @param index - HD address index
16408
+ * @param identity - Full identity for this address
16409
+ * @param tokenStorageProviders - Token storage providers for this address
15221
16410
  */
15222
- async reinitializeModulesForNewAddress() {
16411
+ async initializeAddressModules(index, identity, tokenStorageProviders) {
15223
16412
  const emitEvent = this.emitEvent.bind(this);
15224
- this._payments.initialize({
15225
- identity: this._identity,
16413
+ const adapter = await this.ensureTransportMux(index, identity);
16414
+ const addressTransport = adapter ?? this._transport;
16415
+ const payments = createPaymentsModule({ l1: this._l1Config });
16416
+ const communications = createCommunicationsModule();
16417
+ const groupChat = this._groupChatConfig ? createGroupChatModule(this._groupChatConfig) : null;
16418
+ const market = this._marketConfig ? createMarketModule(this._marketConfig) : null;
16419
+ payments.initialize({
16420
+ identity,
15226
16421
  storage: this._storage,
15227
- tokenStorageProviders: this._tokenStorageProviders,
15228
- transport: this._transport,
16422
+ tokenStorageProviders,
16423
+ transport: addressTransport,
15229
16424
  oracle: this._oracle,
15230
16425
  emitEvent,
15231
16426
  chainCode: this._masterKey?.chainCode || void 0,
15232
16427
  price: this._priceProvider ?? void 0
15233
16428
  });
15234
- this._communications.initialize({
15235
- identity: this._identity,
16429
+ communications.initialize({
16430
+ identity,
15236
16431
  storage: this._storage,
15237
- transport: this._transport,
16432
+ transport: addressTransport,
15238
16433
  emitEvent
15239
16434
  });
15240
- this._groupChat?.initialize({
15241
- identity: this._identity,
16435
+ groupChat?.initialize({
16436
+ identity,
15242
16437
  storage: this._storage,
15243
16438
  emitEvent
15244
16439
  });
15245
- this._market?.initialize({
15246
- identity: this._identity,
16440
+ market?.initialize({
16441
+ identity,
15247
16442
  emitEvent
15248
16443
  });
15249
- await this._payments.load();
15250
- await this._communications.load();
15251
- await this._groupChat?.load();
15252
- await this._market?.load();
15253
- this._payments.sync().catch((err) => {
15254
- logger.warn("Sphere", "Post-switch sync failed:", err);
16444
+ await payments.load();
16445
+ await communications.load();
16446
+ await groupChat?.load();
16447
+ await market?.load();
16448
+ const moduleSet = {
16449
+ index,
16450
+ identity,
16451
+ payments,
16452
+ communications,
16453
+ groupChat,
16454
+ market,
16455
+ transportAdapter: adapter,
16456
+ tokenStorageProviders: new Map(tokenStorageProviders),
16457
+ initialized: true
16458
+ };
16459
+ this._addressModules.set(index, moduleSet);
16460
+ logger.debug("Sphere", `Initialized per-address modules for address ${index} (transport: ${adapter ? "mux adapter" : "primary"})`);
16461
+ payments.sync().catch((err) => {
16462
+ logger.warn("Sphere", `Post-init sync failed for address ${index}:`, err);
15255
16463
  });
16464
+ return moduleSet;
16465
+ }
16466
+ /**
16467
+ * Ensure the transport multiplexer exists and register an address.
16468
+ * Creates the mux on first call. Returns an AddressTransportAdapter
16469
+ * that routes events for this address independently.
16470
+ * @returns AddressTransportAdapter or null if transport is not Nostr-based
16471
+ */
16472
+ async ensureTransportMux(index, identity) {
16473
+ const transport = this._transport;
16474
+ if (typeof transport.getWebSocketFactory !== "function" || typeof transport.getConfiguredRelays !== "function") {
16475
+ logger.debug("Sphere", "Transport does not support mux interface, skipping");
16476
+ return null;
16477
+ }
16478
+ const nostrTransport = transport;
16479
+ if (!this._transportMux) {
16480
+ this._transportMux = new MultiAddressTransportMux({
16481
+ relays: nostrTransport.getConfiguredRelays(),
16482
+ createWebSocket: nostrTransport.getWebSocketFactory(),
16483
+ storage: nostrTransport.getStorageAdapter() ?? void 0
16484
+ });
16485
+ await this._transportMux.connect();
16486
+ if (typeof nostrTransport.suppressSubscriptions === "function") {
16487
+ nostrTransport.suppressSubscriptions();
16488
+ }
16489
+ logger.debug("Sphere", "Transport mux created and connected");
16490
+ }
16491
+ const adapter = await this._transportMux.addAddress(index, identity, this._transport);
16492
+ return adapter;
16493
+ }
16494
+ /**
16495
+ * Get per-address modules for any address index (creates lazily if needed).
16496
+ * This allows accessing any address's modules without switching.
16497
+ */
16498
+ getAddressPayments(index) {
16499
+ return this._addressModules.get(index)?.payments;
15256
16500
  }
15257
16501
  /**
15258
16502
  * Derive address at a specific index
@@ -16180,10 +17424,33 @@ var Sphere = class _Sphere {
16180
17424
  // ===========================================================================
16181
17425
  async destroy() {
16182
17426
  this.cleanupProviderEventSubscriptions();
17427
+ for (const [idx, moduleSet] of this._addressModules.entries()) {
17428
+ try {
17429
+ moduleSet.payments.destroy();
17430
+ moduleSet.communications.destroy();
17431
+ moduleSet.groupChat?.destroy();
17432
+ moduleSet.market?.destroy();
17433
+ for (const provider of moduleSet.tokenStorageProviders.values()) {
17434
+ try {
17435
+ await provider.shutdown();
17436
+ } catch {
17437
+ }
17438
+ }
17439
+ moduleSet.tokenStorageProviders.clear();
17440
+ logger.debug("Sphere", `Destroyed modules for address ${idx}`);
17441
+ } catch (err) {
17442
+ logger.warn("Sphere", `Error destroying modules for address ${idx}:`, err);
17443
+ }
17444
+ }
17445
+ this._addressModules.clear();
16183
17446
  this._payments.destroy();
16184
17447
  this._communications.destroy();
16185
17448
  this._groupChat?.destroy();
16186
17449
  this._market?.destroy();
17450
+ if (this._transportMux) {
17451
+ await this._transportMux.disconnect();
17452
+ this._transportMux = null;
17453
+ }
16187
17454
  await this._transport.disconnect();
16188
17455
  await this._storage.disconnect();
16189
17456
  await this._oracle.disconnect();
@@ -16378,6 +17645,9 @@ var Sphere = class _Sphere {
16378
17645
  // ===========================================================================
16379
17646
  async initializeProviders() {
16380
17647
  this._storage.setIdentity(this._identity);
17648
+ if (this._transport.setFallbackSince) {
17649
+ this._transport.setFallbackSince(Math.floor(Date.now() / 1e3) - 86400);
17650
+ }
16381
17651
  await this._transport.setIdentity(this._identity);
16382
17652
  for (const provider of this._tokenStorageProviders.values()) {
16383
17653
  provider.setIdentity(this._identity);
@@ -16468,11 +17738,13 @@ var Sphere = class _Sphere {
16468
17738
  }
16469
17739
  async initializeModules() {
16470
17740
  const emitEvent = this.emitEvent.bind(this);
17741
+ const adapter = await this.ensureTransportMux(this._currentAddressIndex, this._identity);
17742
+ const moduleTransport = adapter ?? this._transport;
16471
17743
  this._payments.initialize({
16472
17744
  identity: this._identity,
16473
17745
  storage: this._storage,
16474
17746
  tokenStorageProviders: this._tokenStorageProviders,
16475
- transport: this._transport,
17747
+ transport: moduleTransport,
16476
17748
  oracle: this._oracle,
16477
17749
  emitEvent,
16478
17750
  // Pass chain code for L1 HD derivation
@@ -16483,7 +17755,7 @@ var Sphere = class _Sphere {
16483
17755
  this._communications.initialize({
16484
17756
  identity: this._identity,
16485
17757
  storage: this._storage,
16486
- transport: this._transport,
17758
+ transport: moduleTransport,
16487
17759
  emitEvent
16488
17760
  });
16489
17761
  this._groupChat?.initialize({
@@ -16499,6 +17771,17 @@ var Sphere = class _Sphere {
16499
17771
  await this._communications.load();
16500
17772
  await this._groupChat?.load();
16501
17773
  await this._market?.load();
17774
+ this._addressModules.set(this._currentAddressIndex, {
17775
+ index: this._currentAddressIndex,
17776
+ identity: this._identity,
17777
+ payments: this._payments,
17778
+ communications: this._communications,
17779
+ groupChat: this._groupChat,
17780
+ market: this._market,
17781
+ transportAdapter: adapter,
17782
+ tokenStorageProviders: new Map(this._tokenStorageProviders),
17783
+ initialized: true
17784
+ });
16502
17785
  }
16503
17786
  // ===========================================================================
16504
17787
  // Private: Helpers
@@ -17179,7 +18462,7 @@ import {
17179
18462
  encryptNametag,
17180
18463
  decryptNametag
17181
18464
  } from "@unicitylabs/nostr-js-sdk";
17182
- import { NostrClient as NostrClient2, NostrKeyManager as NostrKeyManager2 } from "@unicitylabs/nostr-js-sdk";
18465
+ import { NostrClient as NostrClient3, NostrKeyManager as NostrKeyManager3 } from "@unicitylabs/nostr-js-sdk";
17183
18466
 
17184
18467
  // price/CoinGeckoPriceProvider.ts
17185
18468
  init_logger();
@@ -17451,8 +18734,8 @@ export {
17451
18734
  NETWORKS,
17452
18735
  NIP29_KINDS,
17453
18736
  NOSTR_EVENT_KINDS,
17454
- NostrClient2 as NostrClient,
17455
- NostrKeyManager2 as NostrKeyManager,
18737
+ NostrClient3 as NostrClient,
18738
+ NostrKeyManager3 as NostrKeyManager,
17456
18739
  PaymentsModule,
17457
18740
  SIGN_MESSAGE_PREFIX,
17458
18741
  STORAGE_KEYS,