@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/core/index.cjs +1351 -52
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +342 -3
- package/dist/core/index.d.ts +342 -3
- package/dist/core/index.js +1357 -48
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +109 -11
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +109 -11
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/browser/ipfs.cjs +38 -10
- package/dist/impl/browser/ipfs.cjs.map +1 -1
- package/dist/impl/browser/ipfs.js +38 -10
- package/dist/impl/browser/ipfs.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +105 -11
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +43 -0
- package/dist/impl/nodejs/index.d.ts +43 -0
- package/dist/impl/nodejs/index.js +105 -11
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +1334 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -2
- package/dist/index.d.ts +43 -2
- package/dist/index.js +1333 -50
- package/dist/index.js.map +1 -1
- package/dist/l1/index.d.cts +717 -0
- package/dist/l1/index.d.ts +717 -0
- package/package.json +1 -1
package/dist/core/index.js
CHANGED
|
@@ -145,7 +145,7 @@ function getAddressId(directAddress) {
|
|
|
145
145
|
const last = hash.slice(-6).toLowerCase();
|
|
146
146
|
return `DIRECT_${first}_${last}`;
|
|
147
147
|
}
|
|
148
|
-
var DEFAULT_ENCRYPTION_KEY, STORAGE_KEYS_GLOBAL, STORAGE_KEYS_ADDRESS, STORAGE_KEYS, DEFAULT_NOSTR_RELAYS, NIP29_KINDS, DEFAULT_AGGREGATOR_URL, DEV_AGGREGATOR_URL, TEST_AGGREGATOR_URL, DEFAULT_IPFS_GATEWAYS, DEFAULT_BASE_PATH, DEFAULT_DERIVATION_PATH, DEFAULT_ELECTRUM_URL, TEST_ELECTRUM_URL, TOKEN_REGISTRY_URL, TOKEN_REGISTRY_REFRESH_INTERVAL, TEST_NOSTR_RELAYS, DEFAULT_GROUP_RELAYS, NETWORKS;
|
|
148
|
+
var DEFAULT_ENCRYPTION_KEY, STORAGE_KEYS_GLOBAL, STORAGE_KEYS_ADDRESS, STORAGE_KEYS, DEFAULT_NOSTR_RELAYS, NOSTR_EVENT_KINDS, NIP29_KINDS, DEFAULT_AGGREGATOR_URL, DEV_AGGREGATOR_URL, TEST_AGGREGATOR_URL, DEFAULT_IPFS_GATEWAYS, DEFAULT_BASE_PATH, DEFAULT_DERIVATION_PATH, DEFAULT_ELECTRUM_URL, TEST_ELECTRUM_URL, TOKEN_REGISTRY_URL, TOKEN_REGISTRY_REFRESH_INTERVAL, TEST_NOSTR_RELAYS, DEFAULT_GROUP_RELAYS, NETWORKS, TIMEOUTS;
|
|
149
149
|
var init_constants = __esm({
|
|
150
150
|
"constants.ts"() {
|
|
151
151
|
"use strict";
|
|
@@ -222,6 +222,20 @@ var init_constants = __esm({
|
|
|
222
222
|
"wss://nos.lol",
|
|
223
223
|
"wss://relay.nostr.band"
|
|
224
224
|
];
|
|
225
|
+
NOSTR_EVENT_KINDS = {
|
|
226
|
+
/** NIP-04 encrypted direct message */
|
|
227
|
+
DIRECT_MESSAGE: 4,
|
|
228
|
+
/** Token transfer (Unicity custom - 31113) */
|
|
229
|
+
TOKEN_TRANSFER: 31113,
|
|
230
|
+
/** Payment request (Unicity custom - 31115) */
|
|
231
|
+
PAYMENT_REQUEST: 31115,
|
|
232
|
+
/** Payment request response (Unicity custom - 31116) */
|
|
233
|
+
PAYMENT_REQUEST_RESPONSE: 31116,
|
|
234
|
+
/** Nametag binding (NIP-78 app-specific data) */
|
|
235
|
+
NAMETAG_BINDING: 30078,
|
|
236
|
+
/** Public broadcast */
|
|
237
|
+
BROADCAST: 1
|
|
238
|
+
};
|
|
225
239
|
NIP29_KINDS = {
|
|
226
240
|
/** Chat message sent to group */
|
|
227
241
|
CHAT_MESSAGE: 9,
|
|
@@ -303,6 +317,18 @@ var init_constants = __esm({
|
|
|
303
317
|
tokenRegistryUrl: TOKEN_REGISTRY_URL
|
|
304
318
|
}
|
|
305
319
|
};
|
|
320
|
+
TIMEOUTS = {
|
|
321
|
+
/** WebSocket connection timeout */
|
|
322
|
+
WEBSOCKET_CONNECT: 1e4,
|
|
323
|
+
/** Nostr relay reconnect delay */
|
|
324
|
+
NOSTR_RECONNECT_DELAY: 3e3,
|
|
325
|
+
/** Max reconnect attempts */
|
|
326
|
+
MAX_RECONNECT_ATTEMPTS: 5,
|
|
327
|
+
/** Proof polling interval */
|
|
328
|
+
PROOF_POLL_INTERVAL: 1e3,
|
|
329
|
+
/** Sync interval */
|
|
330
|
+
SYNC_INTERVAL: 6e4
|
|
331
|
+
};
|
|
306
332
|
}
|
|
307
333
|
});
|
|
308
334
|
|
|
@@ -753,6 +779,1112 @@ var init_network = __esm({
|
|
|
753
779
|
init_logger();
|
|
754
780
|
init_errors();
|
|
755
781
|
|
|
782
|
+
// transport/MultiAddressTransportMux.ts
|
|
783
|
+
init_logger();
|
|
784
|
+
init_errors();
|
|
785
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
786
|
+
import {
|
|
787
|
+
NostrKeyManager,
|
|
788
|
+
NIP04,
|
|
789
|
+
NIP17,
|
|
790
|
+
Event as NostrEventClass,
|
|
791
|
+
EventKinds,
|
|
792
|
+
NostrClient,
|
|
793
|
+
Filter,
|
|
794
|
+
isChatMessage,
|
|
795
|
+
isReadReceipt
|
|
796
|
+
} from "@unicitylabs/nostr-js-sdk";
|
|
797
|
+
|
|
798
|
+
// transport/websocket.ts
|
|
799
|
+
function defaultUUIDGenerator() {
|
|
800
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
801
|
+
return crypto.randomUUID();
|
|
802
|
+
}
|
|
803
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
804
|
+
const r = Math.random() * 16 | 0;
|
|
805
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
806
|
+
return v.toString(16);
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// transport/MultiAddressTransportMux.ts
|
|
811
|
+
init_constants();
|
|
812
|
+
var EVENT_KINDS = NOSTR_EVENT_KINDS;
|
|
813
|
+
var COMPOSING_INDICATOR_KIND = 25050;
|
|
814
|
+
var MultiAddressTransportMux = class {
|
|
815
|
+
config;
|
|
816
|
+
storage = null;
|
|
817
|
+
// Single NostrClient — one WebSocket connection for all addresses
|
|
818
|
+
nostrClient = null;
|
|
819
|
+
// KeyManager used for NostrClient creation (uses first address or temp key)
|
|
820
|
+
primaryKeyManager = null;
|
|
821
|
+
status = "disconnected";
|
|
822
|
+
// Per-address entries
|
|
823
|
+
addresses = /* @__PURE__ */ new Map();
|
|
824
|
+
// pubkey → address index (for fast routing)
|
|
825
|
+
pubkeyToIndex = /* @__PURE__ */ new Map();
|
|
826
|
+
// Subscription IDs
|
|
827
|
+
walletSubscriptionId = null;
|
|
828
|
+
chatSubscriptionId = null;
|
|
829
|
+
chatEoseFired = false;
|
|
830
|
+
chatEoseHandlers = [];
|
|
831
|
+
// Dedup
|
|
832
|
+
processedEventIds = /* @__PURE__ */ new Set();
|
|
833
|
+
// Event callbacks (mux-level, forwarded to all adapters)
|
|
834
|
+
eventCallbacks = /* @__PURE__ */ new Set();
|
|
835
|
+
constructor(config) {
|
|
836
|
+
this.config = {
|
|
837
|
+
relays: config.relays ?? [...DEFAULT_NOSTR_RELAYS],
|
|
838
|
+
timeout: config.timeout ?? TIMEOUTS.WEBSOCKET_CONNECT,
|
|
839
|
+
autoReconnect: config.autoReconnect ?? true,
|
|
840
|
+
reconnectDelay: config.reconnectDelay ?? TIMEOUTS.NOSTR_RECONNECT_DELAY,
|
|
841
|
+
maxReconnectAttempts: config.maxReconnectAttempts ?? TIMEOUTS.MAX_RECONNECT_ATTEMPTS,
|
|
842
|
+
createWebSocket: config.createWebSocket,
|
|
843
|
+
generateUUID: config.generateUUID ?? defaultUUIDGenerator
|
|
844
|
+
};
|
|
845
|
+
this.storage = config.storage ?? null;
|
|
846
|
+
}
|
|
847
|
+
// ===========================================================================
|
|
848
|
+
// Address Management
|
|
849
|
+
// ===========================================================================
|
|
850
|
+
/**
|
|
851
|
+
* Add an address to the multiplexer.
|
|
852
|
+
* Creates an AddressTransportAdapter for this address.
|
|
853
|
+
* If already connected, updates subscriptions to include the new pubkey.
|
|
854
|
+
*/
|
|
855
|
+
async addAddress(index, identity, resolveDelegate) {
|
|
856
|
+
const existing = this.addresses.get(index);
|
|
857
|
+
if (existing) {
|
|
858
|
+
existing.identity = identity;
|
|
859
|
+
existing.keyManager = NostrKeyManager.fromPrivateKey(Buffer2.from(identity.privateKey, "hex"));
|
|
860
|
+
existing.nostrPubkey = existing.keyManager.getPublicKeyHex();
|
|
861
|
+
for (const [pk, idx] of this.pubkeyToIndex) {
|
|
862
|
+
if (idx === index) this.pubkeyToIndex.delete(pk);
|
|
863
|
+
}
|
|
864
|
+
this.pubkeyToIndex.set(existing.nostrPubkey, index);
|
|
865
|
+
logger.debug("Mux", `Updated address ${index}, pubkey: ${existing.nostrPubkey.slice(0, 16)}...`);
|
|
866
|
+
await this.updateSubscriptions();
|
|
867
|
+
return existing.adapter;
|
|
868
|
+
}
|
|
869
|
+
const keyManager = NostrKeyManager.fromPrivateKey(Buffer2.from(identity.privateKey, "hex"));
|
|
870
|
+
const nostrPubkey = keyManager.getPublicKeyHex();
|
|
871
|
+
const adapter = new AddressTransportAdapter(this, index, identity, resolveDelegate);
|
|
872
|
+
const entry = {
|
|
873
|
+
index,
|
|
874
|
+
identity,
|
|
875
|
+
keyManager,
|
|
876
|
+
nostrPubkey,
|
|
877
|
+
adapter,
|
|
878
|
+
lastEventTs: 0,
|
|
879
|
+
fallbackSince: null
|
|
880
|
+
};
|
|
881
|
+
this.addresses.set(index, entry);
|
|
882
|
+
this.pubkeyToIndex.set(nostrPubkey, index);
|
|
883
|
+
logger.debug("Mux", `Added address ${index}, pubkey: ${nostrPubkey.slice(0, 16)}..., total: ${this.addresses.size}`);
|
|
884
|
+
if (this.addresses.size === 1) {
|
|
885
|
+
this.primaryKeyManager = keyManager;
|
|
886
|
+
}
|
|
887
|
+
if (this.isConnected()) {
|
|
888
|
+
await this.updateSubscriptions();
|
|
889
|
+
}
|
|
890
|
+
return adapter;
|
|
891
|
+
}
|
|
892
|
+
/**
|
|
893
|
+
* Remove an address from the multiplexer.
|
|
894
|
+
* Stops routing events to this address.
|
|
895
|
+
*/
|
|
896
|
+
async removeAddress(index) {
|
|
897
|
+
const entry = this.addresses.get(index);
|
|
898
|
+
if (!entry) return;
|
|
899
|
+
this.pubkeyToIndex.delete(entry.nostrPubkey);
|
|
900
|
+
this.addresses.delete(index);
|
|
901
|
+
logger.debug("Mux", `Removed address ${index}, remaining: ${this.addresses.size}`);
|
|
902
|
+
if (this.isConnected() && this.addresses.size > 0) {
|
|
903
|
+
await this.updateSubscriptions();
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Get adapter for a specific address index.
|
|
908
|
+
*/
|
|
909
|
+
getAdapter(index) {
|
|
910
|
+
return this.addresses.get(index)?.adapter;
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Set fallback 'since' for an address (consumed once on next subscription setup).
|
|
914
|
+
*/
|
|
915
|
+
setFallbackSince(index, sinceSeconds) {
|
|
916
|
+
const entry = this.addresses.get(index);
|
|
917
|
+
if (entry) {
|
|
918
|
+
entry.fallbackSince = sinceSeconds;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
// ===========================================================================
|
|
922
|
+
// Connection Management (delegated from adapters)
|
|
923
|
+
// ===========================================================================
|
|
924
|
+
async connect() {
|
|
925
|
+
if (this.status === "connected") return;
|
|
926
|
+
this.status = "connecting";
|
|
927
|
+
try {
|
|
928
|
+
if (!this.primaryKeyManager) {
|
|
929
|
+
const tempKey = Buffer2.alloc(32);
|
|
930
|
+
crypto.getRandomValues(tempKey);
|
|
931
|
+
this.primaryKeyManager = NostrKeyManager.fromPrivateKey(tempKey);
|
|
932
|
+
}
|
|
933
|
+
this.nostrClient = new NostrClient(this.primaryKeyManager, {
|
|
934
|
+
autoReconnect: this.config.autoReconnect,
|
|
935
|
+
reconnectIntervalMs: this.config.reconnectDelay,
|
|
936
|
+
maxReconnectIntervalMs: this.config.reconnectDelay * 16,
|
|
937
|
+
pingIntervalMs: 15e3
|
|
938
|
+
});
|
|
939
|
+
this.nostrClient.addConnectionListener({
|
|
940
|
+
onConnect: (url) => {
|
|
941
|
+
logger.debug("Mux", "Connected to relay:", url);
|
|
942
|
+
this.emitEvent({ type: "transport:connected", timestamp: Date.now() });
|
|
943
|
+
},
|
|
944
|
+
onDisconnect: (url, reason) => {
|
|
945
|
+
logger.debug("Mux", "Disconnected from relay:", url, "reason:", reason);
|
|
946
|
+
},
|
|
947
|
+
onReconnecting: (url, attempt) => {
|
|
948
|
+
logger.debug("Mux", "Reconnecting to relay:", url, "attempt:", attempt);
|
|
949
|
+
this.emitEvent({ type: "transport:reconnecting", timestamp: Date.now() });
|
|
950
|
+
},
|
|
951
|
+
onReconnected: (url) => {
|
|
952
|
+
logger.debug("Mux", "Reconnected to relay:", url);
|
|
953
|
+
this.emitEvent({ type: "transport:connected", timestamp: Date.now() });
|
|
954
|
+
}
|
|
955
|
+
});
|
|
956
|
+
await Promise.race([
|
|
957
|
+
this.nostrClient.connect(...this.config.relays),
|
|
958
|
+
new Promise(
|
|
959
|
+
(_, reject) => setTimeout(() => reject(new Error(
|
|
960
|
+
`Transport connection timed out after ${this.config.timeout}ms`
|
|
961
|
+
)), this.config.timeout)
|
|
962
|
+
)
|
|
963
|
+
]);
|
|
964
|
+
if (!this.nostrClient.isConnected()) {
|
|
965
|
+
throw new SphereError("Failed to connect to any relay", "TRANSPORT_ERROR");
|
|
966
|
+
}
|
|
967
|
+
this.status = "connected";
|
|
968
|
+
this.emitEvent({ type: "transport:connected", timestamp: Date.now() });
|
|
969
|
+
if (this.addresses.size > 0) {
|
|
970
|
+
await this.updateSubscriptions();
|
|
971
|
+
}
|
|
972
|
+
} catch (error) {
|
|
973
|
+
this.status = "error";
|
|
974
|
+
throw error;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
async disconnect() {
|
|
978
|
+
if (this.nostrClient) {
|
|
979
|
+
this.nostrClient.disconnect();
|
|
980
|
+
this.nostrClient = null;
|
|
981
|
+
}
|
|
982
|
+
this.walletSubscriptionId = null;
|
|
983
|
+
this.chatSubscriptionId = null;
|
|
984
|
+
this.chatEoseFired = false;
|
|
985
|
+
this.status = "disconnected";
|
|
986
|
+
this.emitEvent({ type: "transport:disconnected", timestamp: Date.now() });
|
|
987
|
+
}
|
|
988
|
+
isConnected() {
|
|
989
|
+
return this.status === "connected" && this.nostrClient?.isConnected() === true;
|
|
990
|
+
}
|
|
991
|
+
getStatus() {
|
|
992
|
+
return this.status;
|
|
993
|
+
}
|
|
994
|
+
// ===========================================================================
|
|
995
|
+
// Relay Management
|
|
996
|
+
// ===========================================================================
|
|
997
|
+
getRelays() {
|
|
998
|
+
return [...this.config.relays];
|
|
999
|
+
}
|
|
1000
|
+
getConnectedRelays() {
|
|
1001
|
+
if (!this.nostrClient) return [];
|
|
1002
|
+
return Array.from(this.nostrClient.getConnectedRelays());
|
|
1003
|
+
}
|
|
1004
|
+
async addRelay(relayUrl) {
|
|
1005
|
+
if (this.config.relays.includes(relayUrl)) return false;
|
|
1006
|
+
this.config.relays.push(relayUrl);
|
|
1007
|
+
if (this.status === "connected" && this.nostrClient) {
|
|
1008
|
+
try {
|
|
1009
|
+
await this.nostrClient.connect(relayUrl);
|
|
1010
|
+
this.emitEvent({ type: "transport:relay_added", timestamp: Date.now(), data: { relay: relayUrl, connected: true } });
|
|
1011
|
+
return true;
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
this.emitEvent({ type: "transport:relay_added", timestamp: Date.now(), data: { relay: relayUrl, connected: false, error: String(error) } });
|
|
1014
|
+
return false;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
return true;
|
|
1018
|
+
}
|
|
1019
|
+
async removeRelay(relayUrl) {
|
|
1020
|
+
const idx = this.config.relays.indexOf(relayUrl);
|
|
1021
|
+
if (idx === -1) return false;
|
|
1022
|
+
this.config.relays.splice(idx, 1);
|
|
1023
|
+
this.emitEvent({ type: "transport:relay_removed", timestamp: Date.now(), data: { relay: relayUrl } });
|
|
1024
|
+
return true;
|
|
1025
|
+
}
|
|
1026
|
+
hasRelay(relayUrl) {
|
|
1027
|
+
return this.config.relays.includes(relayUrl);
|
|
1028
|
+
}
|
|
1029
|
+
isRelayConnected(relayUrl) {
|
|
1030
|
+
if (!this.nostrClient) return false;
|
|
1031
|
+
return this.nostrClient.getConnectedRelays().has(relayUrl);
|
|
1032
|
+
}
|
|
1033
|
+
// ===========================================================================
|
|
1034
|
+
// Subscription Management
|
|
1035
|
+
// ===========================================================================
|
|
1036
|
+
/**
|
|
1037
|
+
* Update Nostr subscriptions to listen for events on ALL registered address pubkeys.
|
|
1038
|
+
* Called whenever addresses are added/removed.
|
|
1039
|
+
*/
|
|
1040
|
+
async updateSubscriptions() {
|
|
1041
|
+
if (!this.nostrClient || this.addresses.size === 0) return;
|
|
1042
|
+
if (this.walletSubscriptionId) {
|
|
1043
|
+
this.nostrClient.unsubscribe(this.walletSubscriptionId);
|
|
1044
|
+
this.walletSubscriptionId = null;
|
|
1045
|
+
}
|
|
1046
|
+
if (this.chatSubscriptionId) {
|
|
1047
|
+
this.nostrClient.unsubscribe(this.chatSubscriptionId);
|
|
1048
|
+
this.chatSubscriptionId = null;
|
|
1049
|
+
}
|
|
1050
|
+
const allPubkeys = [];
|
|
1051
|
+
for (const entry of this.addresses.values()) {
|
|
1052
|
+
allPubkeys.push(entry.nostrPubkey);
|
|
1053
|
+
}
|
|
1054
|
+
logger.debug("Mux", `Subscribing for ${allPubkeys.length} address(es):`, allPubkeys.map((p) => p.slice(0, 12)).join(", "));
|
|
1055
|
+
let globalSince = Math.floor(Date.now() / 1e3);
|
|
1056
|
+
for (const entry of this.addresses.values()) {
|
|
1057
|
+
const since = await this.getAddressSince(entry);
|
|
1058
|
+
if (since < globalSince) {
|
|
1059
|
+
globalSince = since;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
const walletFilter = new Filter();
|
|
1063
|
+
walletFilter.kinds = [
|
|
1064
|
+
EVENT_KINDS.DIRECT_MESSAGE,
|
|
1065
|
+
EVENT_KINDS.TOKEN_TRANSFER,
|
|
1066
|
+
EVENT_KINDS.PAYMENT_REQUEST,
|
|
1067
|
+
EVENT_KINDS.PAYMENT_REQUEST_RESPONSE
|
|
1068
|
+
];
|
|
1069
|
+
walletFilter["#p"] = allPubkeys;
|
|
1070
|
+
walletFilter.since = globalSince;
|
|
1071
|
+
this.walletSubscriptionId = this.nostrClient.subscribe(walletFilter, {
|
|
1072
|
+
onEvent: (event) => {
|
|
1073
|
+
this.handleEvent({
|
|
1074
|
+
id: event.id,
|
|
1075
|
+
kind: event.kind,
|
|
1076
|
+
content: event.content,
|
|
1077
|
+
tags: event.tags,
|
|
1078
|
+
pubkey: event.pubkey,
|
|
1079
|
+
created_at: event.created_at,
|
|
1080
|
+
sig: event.sig
|
|
1081
|
+
});
|
|
1082
|
+
},
|
|
1083
|
+
onEndOfStoredEvents: () => {
|
|
1084
|
+
logger.debug("Mux", "Wallet subscription EOSE");
|
|
1085
|
+
},
|
|
1086
|
+
onError: (_subId, error) => {
|
|
1087
|
+
logger.debug("Mux", "Wallet subscription error:", error);
|
|
1088
|
+
}
|
|
1089
|
+
});
|
|
1090
|
+
const chatFilter = new Filter();
|
|
1091
|
+
chatFilter.kinds = [EventKinds.GIFT_WRAP];
|
|
1092
|
+
chatFilter["#p"] = allPubkeys;
|
|
1093
|
+
this.chatSubscriptionId = this.nostrClient.subscribe(chatFilter, {
|
|
1094
|
+
onEvent: (event) => {
|
|
1095
|
+
this.handleEvent({
|
|
1096
|
+
id: event.id,
|
|
1097
|
+
kind: event.kind,
|
|
1098
|
+
content: event.content,
|
|
1099
|
+
tags: event.tags,
|
|
1100
|
+
pubkey: event.pubkey,
|
|
1101
|
+
created_at: event.created_at,
|
|
1102
|
+
sig: event.sig
|
|
1103
|
+
});
|
|
1104
|
+
},
|
|
1105
|
+
onEndOfStoredEvents: () => {
|
|
1106
|
+
logger.debug("Mux", "Chat subscription EOSE");
|
|
1107
|
+
if (!this.chatEoseFired) {
|
|
1108
|
+
this.chatEoseFired = true;
|
|
1109
|
+
for (const handler of this.chatEoseHandlers) {
|
|
1110
|
+
try {
|
|
1111
|
+
handler();
|
|
1112
|
+
} catch {
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
},
|
|
1117
|
+
onError: (_subId, error) => {
|
|
1118
|
+
logger.debug("Mux", "Chat subscription error:", error);
|
|
1119
|
+
}
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Determine 'since' timestamp for an address entry.
|
|
1124
|
+
*/
|
|
1125
|
+
async getAddressSince(entry) {
|
|
1126
|
+
if (this.storage) {
|
|
1127
|
+
const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_WALLET_EVENT_TS}_${entry.nostrPubkey.slice(0, 16)}`;
|
|
1128
|
+
try {
|
|
1129
|
+
const stored = await this.storage.get(storageKey);
|
|
1130
|
+
if (stored) {
|
|
1131
|
+
const ts = parseInt(stored, 10);
|
|
1132
|
+
entry.lastEventTs = ts;
|
|
1133
|
+
entry.fallbackSince = null;
|
|
1134
|
+
return ts;
|
|
1135
|
+
} else if (entry.fallbackSince !== null) {
|
|
1136
|
+
const ts = entry.fallbackSince;
|
|
1137
|
+
entry.lastEventTs = ts;
|
|
1138
|
+
entry.fallbackSince = null;
|
|
1139
|
+
return ts;
|
|
1140
|
+
}
|
|
1141
|
+
} catch {
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
return Math.floor(Date.now() / 1e3);
|
|
1145
|
+
}
|
|
1146
|
+
// ===========================================================================
|
|
1147
|
+
// Event Routing
|
|
1148
|
+
// ===========================================================================
|
|
1149
|
+
/**
|
|
1150
|
+
* Route an incoming Nostr event to the correct address adapter.
|
|
1151
|
+
*/
|
|
1152
|
+
async handleEvent(event) {
|
|
1153
|
+
if (event.id && this.processedEventIds.has(event.id)) return;
|
|
1154
|
+
if (event.id) this.processedEventIds.add(event.id);
|
|
1155
|
+
try {
|
|
1156
|
+
if (event.kind === EventKinds.GIFT_WRAP) {
|
|
1157
|
+
await this.routeGiftWrap(event);
|
|
1158
|
+
} else {
|
|
1159
|
+
const recipientPubkey = this.extractRecipientPubkey(event);
|
|
1160
|
+
if (!recipientPubkey) {
|
|
1161
|
+
logger.debug("Mux", "Event has no #p tag, dropping:", event.id?.slice(0, 12));
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
const addressIndex = this.pubkeyToIndex.get(recipientPubkey);
|
|
1165
|
+
if (addressIndex === void 0) {
|
|
1166
|
+
logger.debug("Mux", "Event for unknown pubkey:", recipientPubkey.slice(0, 16), "dropping");
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
const entry = this.addresses.get(addressIndex);
|
|
1170
|
+
if (!entry) return;
|
|
1171
|
+
await this.dispatchWalletEvent(entry, event);
|
|
1172
|
+
}
|
|
1173
|
+
} catch (error) {
|
|
1174
|
+
logger.debug("Mux", "Failed to handle event:", event.id?.slice(0, 12), error);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Extract recipient pubkey from event's #p tag.
|
|
1179
|
+
* Returns the first #p value that matches a known address pubkey,
|
|
1180
|
+
* or the first #p value if none match.
|
|
1181
|
+
*/
|
|
1182
|
+
extractRecipientPubkey(event) {
|
|
1183
|
+
const pTags = event.tags?.filter((t) => t[0] === "p");
|
|
1184
|
+
if (!pTags || pTags.length === 0) return null;
|
|
1185
|
+
for (const tag of pTags) {
|
|
1186
|
+
if (tag[1] && this.pubkeyToIndex.has(tag[1])) {
|
|
1187
|
+
return tag[1];
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
return pTags[0]?.[1] ?? null;
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* Route a gift wrap event by trying decryption with each address keyManager.
|
|
1194
|
+
*/
|
|
1195
|
+
async routeGiftWrap(event) {
|
|
1196
|
+
for (const entry of this.addresses.values()) {
|
|
1197
|
+
try {
|
|
1198
|
+
const pm = NIP17.unwrap(event, entry.keyManager);
|
|
1199
|
+
logger.debug("Mux", `Gift wrap decrypted by address ${entry.index}, sender: ${pm.senderPubkey?.slice(0, 16)}`);
|
|
1200
|
+
if (pm.senderPubkey === entry.nostrPubkey) {
|
|
1201
|
+
try {
|
|
1202
|
+
const parsed = JSON.parse(pm.content);
|
|
1203
|
+
if (parsed?.selfWrap && parsed.recipientPubkey) {
|
|
1204
|
+
const message2 = {
|
|
1205
|
+
id: parsed.originalId || pm.eventId,
|
|
1206
|
+
senderTransportPubkey: pm.senderPubkey,
|
|
1207
|
+
senderNametag: parsed.senderNametag,
|
|
1208
|
+
recipientTransportPubkey: parsed.recipientPubkey,
|
|
1209
|
+
content: parsed.text ?? "",
|
|
1210
|
+
timestamp: pm.timestamp * 1e3,
|
|
1211
|
+
encrypted: true,
|
|
1212
|
+
isSelfWrap: true
|
|
1213
|
+
};
|
|
1214
|
+
entry.adapter.dispatchMessage(message2);
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
} catch {
|
|
1218
|
+
}
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
if (isReadReceipt(pm)) {
|
|
1222
|
+
if (pm.replyToEventId) {
|
|
1223
|
+
const receipt = {
|
|
1224
|
+
senderTransportPubkey: pm.senderPubkey,
|
|
1225
|
+
messageEventId: pm.replyToEventId,
|
|
1226
|
+
timestamp: pm.timestamp * 1e3
|
|
1227
|
+
};
|
|
1228
|
+
entry.adapter.dispatchReadReceipt(receipt);
|
|
1229
|
+
}
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
if (pm.kind === COMPOSING_INDICATOR_KIND) {
|
|
1233
|
+
let senderNametag2;
|
|
1234
|
+
let expiresIn = 3e4;
|
|
1235
|
+
try {
|
|
1236
|
+
const parsed = JSON.parse(pm.content);
|
|
1237
|
+
senderNametag2 = parsed.senderNametag || void 0;
|
|
1238
|
+
expiresIn = parsed.expiresIn ?? 3e4;
|
|
1239
|
+
} catch {
|
|
1240
|
+
}
|
|
1241
|
+
entry.adapter.dispatchComposingIndicator({
|
|
1242
|
+
senderPubkey: pm.senderPubkey,
|
|
1243
|
+
senderNametag: senderNametag2,
|
|
1244
|
+
expiresIn
|
|
1245
|
+
});
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
try {
|
|
1249
|
+
const parsed = JSON.parse(pm.content);
|
|
1250
|
+
if (parsed?.type === "typing") {
|
|
1251
|
+
const indicator = {
|
|
1252
|
+
senderTransportPubkey: pm.senderPubkey,
|
|
1253
|
+
senderNametag: parsed.senderNametag,
|
|
1254
|
+
timestamp: pm.timestamp * 1e3
|
|
1255
|
+
};
|
|
1256
|
+
entry.adapter.dispatchTypingIndicator(indicator);
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
} catch {
|
|
1260
|
+
}
|
|
1261
|
+
if (!isChatMessage(pm)) return;
|
|
1262
|
+
let content = pm.content;
|
|
1263
|
+
let senderNametag;
|
|
1264
|
+
try {
|
|
1265
|
+
const parsed = JSON.parse(content);
|
|
1266
|
+
if (typeof parsed === "object" && parsed.text !== void 0) {
|
|
1267
|
+
content = parsed.text;
|
|
1268
|
+
senderNametag = parsed.senderNametag || void 0;
|
|
1269
|
+
}
|
|
1270
|
+
} catch {
|
|
1271
|
+
}
|
|
1272
|
+
const message = {
|
|
1273
|
+
id: event.id,
|
|
1274
|
+
senderTransportPubkey: pm.senderPubkey,
|
|
1275
|
+
senderNametag,
|
|
1276
|
+
content,
|
|
1277
|
+
timestamp: pm.timestamp * 1e3,
|
|
1278
|
+
encrypted: true
|
|
1279
|
+
};
|
|
1280
|
+
entry.adapter.dispatchMessage(message);
|
|
1281
|
+
return;
|
|
1282
|
+
} catch {
|
|
1283
|
+
continue;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
logger.debug("Mux", "Gift wrap could not be decrypted by any address");
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Dispatch a wallet event (non-gift-wrap) to the correct address adapter.
|
|
1290
|
+
*/
|
|
1291
|
+
async dispatchWalletEvent(entry, event) {
|
|
1292
|
+
switch (event.kind) {
|
|
1293
|
+
case EVENT_KINDS.DIRECT_MESSAGE:
|
|
1294
|
+
break;
|
|
1295
|
+
case EVENT_KINDS.TOKEN_TRANSFER:
|
|
1296
|
+
await this.handleTokenTransfer(entry, event);
|
|
1297
|
+
break;
|
|
1298
|
+
case EVENT_KINDS.PAYMENT_REQUEST:
|
|
1299
|
+
await this.handlePaymentRequest(entry, event);
|
|
1300
|
+
break;
|
|
1301
|
+
case EVENT_KINDS.PAYMENT_REQUEST_RESPONSE:
|
|
1302
|
+
await this.handlePaymentRequestResponse(entry, event);
|
|
1303
|
+
break;
|
|
1304
|
+
}
|
|
1305
|
+
if (event.created_at) {
|
|
1306
|
+
this.updateLastEventTimestamp(entry, event.created_at);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
async handleTokenTransfer(entry, event) {
|
|
1310
|
+
try {
|
|
1311
|
+
const content = await this.decryptContent(entry, event.content, event.pubkey);
|
|
1312
|
+
const payload = JSON.parse(content);
|
|
1313
|
+
const transfer = {
|
|
1314
|
+
id: event.id,
|
|
1315
|
+
senderTransportPubkey: event.pubkey,
|
|
1316
|
+
payload,
|
|
1317
|
+
timestamp: event.created_at * 1e3
|
|
1318
|
+
};
|
|
1319
|
+
entry.adapter.dispatchTokenTransfer(transfer);
|
|
1320
|
+
} catch (err) {
|
|
1321
|
+
logger.debug("Mux", `Token transfer decrypt failed for address ${entry.index}:`, err?.message?.slice(0, 50));
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
async handlePaymentRequest(entry, event) {
|
|
1325
|
+
try {
|
|
1326
|
+
const content = await this.decryptContent(entry, event.content, event.pubkey);
|
|
1327
|
+
const requestData = JSON.parse(content);
|
|
1328
|
+
const request = {
|
|
1329
|
+
id: event.id,
|
|
1330
|
+
senderTransportPubkey: event.pubkey,
|
|
1331
|
+
request: {
|
|
1332
|
+
requestId: requestData.requestId,
|
|
1333
|
+
amount: requestData.amount,
|
|
1334
|
+
coinId: requestData.coinId,
|
|
1335
|
+
message: requestData.message,
|
|
1336
|
+
recipientNametag: requestData.recipientNametag,
|
|
1337
|
+
metadata: requestData.metadata
|
|
1338
|
+
},
|
|
1339
|
+
timestamp: event.created_at * 1e3
|
|
1340
|
+
};
|
|
1341
|
+
entry.adapter.dispatchPaymentRequest(request);
|
|
1342
|
+
} catch (err) {
|
|
1343
|
+
logger.debug("Mux", `Payment request decrypt failed for address ${entry.index}:`, err?.message?.slice(0, 50));
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
async handlePaymentRequestResponse(entry, event) {
|
|
1347
|
+
try {
|
|
1348
|
+
const content = await this.decryptContent(entry, event.content, event.pubkey);
|
|
1349
|
+
const responseData = JSON.parse(content);
|
|
1350
|
+
const response = {
|
|
1351
|
+
id: event.id,
|
|
1352
|
+
responderTransportPubkey: event.pubkey,
|
|
1353
|
+
response: {
|
|
1354
|
+
requestId: responseData.requestId,
|
|
1355
|
+
responseType: responseData.responseType,
|
|
1356
|
+
message: responseData.message,
|
|
1357
|
+
transferId: responseData.transferId
|
|
1358
|
+
},
|
|
1359
|
+
timestamp: event.created_at * 1e3
|
|
1360
|
+
};
|
|
1361
|
+
entry.adapter.dispatchPaymentRequestResponse(response);
|
|
1362
|
+
} catch (err) {
|
|
1363
|
+
logger.debug("Mux", `Payment response decrypt failed for address ${entry.index}:`, err?.message?.slice(0, 50));
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
// ===========================================================================
|
|
1367
|
+
// Crypto Helpers
|
|
1368
|
+
// ===========================================================================
|
|
1369
|
+
async decryptContent(entry, content, senderPubkey) {
|
|
1370
|
+
const decrypted = await NIP04.decryptHex(
|
|
1371
|
+
content,
|
|
1372
|
+
entry.keyManager.getPrivateKeyHex(),
|
|
1373
|
+
senderPubkey
|
|
1374
|
+
);
|
|
1375
|
+
return this.stripContentPrefix(decrypted);
|
|
1376
|
+
}
|
|
1377
|
+
stripContentPrefix(content) {
|
|
1378
|
+
const prefixes = ["payment_request:", "token_transfer:", "payment_response:"];
|
|
1379
|
+
for (const prefix of prefixes) {
|
|
1380
|
+
if (content.startsWith(prefix)) return content.slice(prefix.length);
|
|
1381
|
+
}
|
|
1382
|
+
return content;
|
|
1383
|
+
}
|
|
1384
|
+
// ===========================================================================
|
|
1385
|
+
// Sending (called by adapters)
|
|
1386
|
+
// ===========================================================================
|
|
1387
|
+
/**
|
|
1388
|
+
* Create an encrypted event using a specific address's keyManager.
|
|
1389
|
+
* Used by AddressTransportAdapter for sending.
|
|
1390
|
+
*/
|
|
1391
|
+
async createAndPublishEncryptedEvent(addressIndex, kind, content, tags) {
|
|
1392
|
+
const entry = this.addresses.get(addressIndex);
|
|
1393
|
+
if (!entry) throw new SphereError("Address not registered in mux", "NOT_INITIALIZED");
|
|
1394
|
+
if (!this.nostrClient) throw new SphereError("Not connected", "NOT_INITIALIZED");
|
|
1395
|
+
const recipientTag = tags.find((t) => t[0] === "p");
|
|
1396
|
+
if (!recipientTag?.[1]) throw new SphereError("No recipient pubkey in tags", "VALIDATION_ERROR");
|
|
1397
|
+
const encrypted = await NIP04.encryptHex(
|
|
1398
|
+
content,
|
|
1399
|
+
entry.keyManager.getPrivateKeyHex(),
|
|
1400
|
+
recipientTag[1]
|
|
1401
|
+
);
|
|
1402
|
+
const signedEvent = NostrEventClass.create(entry.keyManager, { kind, content: encrypted, tags });
|
|
1403
|
+
const nostrEvent = NostrEventClass.fromJSON({
|
|
1404
|
+
id: signedEvent.id,
|
|
1405
|
+
kind: signedEvent.kind,
|
|
1406
|
+
content: signedEvent.content,
|
|
1407
|
+
tags: signedEvent.tags,
|
|
1408
|
+
pubkey: signedEvent.pubkey,
|
|
1409
|
+
created_at: signedEvent.created_at,
|
|
1410
|
+
sig: signedEvent.sig
|
|
1411
|
+
});
|
|
1412
|
+
await this.nostrClient.publishEvent(nostrEvent);
|
|
1413
|
+
return signedEvent.id;
|
|
1414
|
+
}
|
|
1415
|
+
/**
|
|
1416
|
+
* Create and publish a NIP-17 gift wrap message for a specific address.
|
|
1417
|
+
*/
|
|
1418
|
+
async sendGiftWrap(addressIndex, recipientPubkey, content) {
|
|
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 nostrRecipient = recipientPubkey.length === 66 && (recipientPubkey.startsWith("02") || recipientPubkey.startsWith("03")) ? recipientPubkey.slice(2) : recipientPubkey;
|
|
1423
|
+
const giftWrap = NIP17.createGiftWrap(entry.keyManager, nostrRecipient, content);
|
|
1424
|
+
const giftWrapEvent = NostrEventClass.fromJSON(giftWrap);
|
|
1425
|
+
await this.nostrClient.publishEvent(giftWrapEvent);
|
|
1426
|
+
const selfPubkey = entry.keyManager.getPublicKeyHex();
|
|
1427
|
+
const senderNametag = entry.identity.nametag;
|
|
1428
|
+
const selfWrapContent = JSON.stringify({
|
|
1429
|
+
selfWrap: true,
|
|
1430
|
+
originalId: giftWrap.id,
|
|
1431
|
+
recipientPubkey,
|
|
1432
|
+
senderNametag,
|
|
1433
|
+
text: content
|
|
1434
|
+
});
|
|
1435
|
+
const selfGiftWrap = NIP17.createGiftWrap(entry.keyManager, selfPubkey, selfWrapContent);
|
|
1436
|
+
const selfGiftWrapEvent = NostrEventClass.fromJSON(selfGiftWrap);
|
|
1437
|
+
this.nostrClient.publishEvent(selfGiftWrapEvent).catch((err) => {
|
|
1438
|
+
logger.debug("Mux", "Self-wrap publish failed:", err);
|
|
1439
|
+
});
|
|
1440
|
+
return giftWrap.id;
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Publish a raw event (e.g., identity binding, broadcast).
|
|
1444
|
+
*/
|
|
1445
|
+
async publishRawEvent(addressIndex, kind, content, tags) {
|
|
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 signedEvent = NostrEventClass.create(entry.keyManager, { kind, content, tags });
|
|
1450
|
+
const nostrEvent = NostrEventClass.fromJSON({
|
|
1451
|
+
id: signedEvent.id,
|
|
1452
|
+
kind: signedEvent.kind,
|
|
1453
|
+
content: signedEvent.content,
|
|
1454
|
+
tags: signedEvent.tags,
|
|
1455
|
+
pubkey: signedEvent.pubkey,
|
|
1456
|
+
created_at: signedEvent.created_at,
|
|
1457
|
+
sig: signedEvent.sig
|
|
1458
|
+
});
|
|
1459
|
+
await this.nostrClient.publishEvent(nostrEvent);
|
|
1460
|
+
return signedEvent.id;
|
|
1461
|
+
}
|
|
1462
|
+
// ===========================================================================
|
|
1463
|
+
// Resolve Methods (delegates to inner — these are stateless relay queries)
|
|
1464
|
+
// ===========================================================================
|
|
1465
|
+
/**
|
|
1466
|
+
* Get the NostrClient for resolve operations.
|
|
1467
|
+
* Adapters use this for resolve*, publishIdentityBinding, etc.
|
|
1468
|
+
*/
|
|
1469
|
+
getNostrClient() {
|
|
1470
|
+
return this.nostrClient;
|
|
1471
|
+
}
|
|
1472
|
+
/**
|
|
1473
|
+
* Get keyManager for a specific address (used by adapters for resolve/binding).
|
|
1474
|
+
*/
|
|
1475
|
+
getKeyManager(addressIndex) {
|
|
1476
|
+
return this.addresses.get(addressIndex)?.keyManager ?? null;
|
|
1477
|
+
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Get identity for a specific address.
|
|
1480
|
+
*/
|
|
1481
|
+
getIdentity(addressIndex) {
|
|
1482
|
+
return this.addresses.get(addressIndex)?.identity ?? null;
|
|
1483
|
+
}
|
|
1484
|
+
// ===========================================================================
|
|
1485
|
+
// Event timestamp persistence
|
|
1486
|
+
// ===========================================================================
|
|
1487
|
+
updateLastEventTimestamp(entry, createdAt) {
|
|
1488
|
+
if (!this.storage) return;
|
|
1489
|
+
if (createdAt <= entry.lastEventTs) return;
|
|
1490
|
+
entry.lastEventTs = createdAt;
|
|
1491
|
+
const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_WALLET_EVENT_TS}_${entry.nostrPubkey.slice(0, 16)}`;
|
|
1492
|
+
this.storage.set(storageKey, createdAt.toString()).catch((err) => {
|
|
1493
|
+
logger.debug("Mux", "Failed to save last event timestamp:", err);
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
// ===========================================================================
|
|
1497
|
+
// Mux-level event system
|
|
1498
|
+
// ===========================================================================
|
|
1499
|
+
onTransportEvent(callback) {
|
|
1500
|
+
this.eventCallbacks.add(callback);
|
|
1501
|
+
return () => this.eventCallbacks.delete(callback);
|
|
1502
|
+
}
|
|
1503
|
+
onChatReady(handler) {
|
|
1504
|
+
if (this.chatEoseFired) {
|
|
1505
|
+
try {
|
|
1506
|
+
handler();
|
|
1507
|
+
} catch {
|
|
1508
|
+
}
|
|
1509
|
+
return () => {
|
|
1510
|
+
};
|
|
1511
|
+
}
|
|
1512
|
+
this.chatEoseHandlers.push(handler);
|
|
1513
|
+
return () => {
|
|
1514
|
+
const idx = this.chatEoseHandlers.indexOf(handler);
|
|
1515
|
+
if (idx >= 0) this.chatEoseHandlers.splice(idx, 1);
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
emitEvent(event) {
|
|
1519
|
+
for (const cb of this.eventCallbacks) {
|
|
1520
|
+
try {
|
|
1521
|
+
cb(event);
|
|
1522
|
+
} catch {
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
for (const entry of this.addresses.values()) {
|
|
1526
|
+
entry.adapter.emitTransportEvent(event);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
// ===========================================================================
|
|
1530
|
+
// Dedup Management
|
|
1531
|
+
// ===========================================================================
|
|
1532
|
+
/**
|
|
1533
|
+
* Clear processed event IDs (e.g., on address change or periodic cleanup).
|
|
1534
|
+
*/
|
|
1535
|
+
clearProcessedEvents() {
|
|
1536
|
+
this.processedEventIds.clear();
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* Get the storage adapter (for adapters that need it).
|
|
1540
|
+
*/
|
|
1541
|
+
getStorage() {
|
|
1542
|
+
return this.storage;
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Get the UUID generator.
|
|
1546
|
+
*/
|
|
1547
|
+
getUUIDGenerator() {
|
|
1548
|
+
return this.config.generateUUID;
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
var AddressTransportAdapter = class {
|
|
1552
|
+
id;
|
|
1553
|
+
name;
|
|
1554
|
+
type = "p2p";
|
|
1555
|
+
description;
|
|
1556
|
+
mux;
|
|
1557
|
+
addressIndex;
|
|
1558
|
+
identity;
|
|
1559
|
+
resolveDelegate;
|
|
1560
|
+
// Per-address handler sets
|
|
1561
|
+
messageHandlers = /* @__PURE__ */ new Set();
|
|
1562
|
+
transferHandlers = /* @__PURE__ */ new Set();
|
|
1563
|
+
paymentRequestHandlers = /* @__PURE__ */ new Set();
|
|
1564
|
+
paymentRequestResponseHandlers = /* @__PURE__ */ new Set();
|
|
1565
|
+
readReceiptHandlers = /* @__PURE__ */ new Set();
|
|
1566
|
+
typingIndicatorHandlers = /* @__PURE__ */ new Set();
|
|
1567
|
+
composingHandlers = /* @__PURE__ */ new Set();
|
|
1568
|
+
instantSplitBundleHandlers = /* @__PURE__ */ new Set();
|
|
1569
|
+
broadcastHandlers = /* @__PURE__ */ new Map();
|
|
1570
|
+
eventCallbacks = /* @__PURE__ */ new Set();
|
|
1571
|
+
pendingMessages = [];
|
|
1572
|
+
chatEoseHandlers = [];
|
|
1573
|
+
constructor(mux, addressIndex, identity, resolveDelegate) {
|
|
1574
|
+
this.mux = mux;
|
|
1575
|
+
this.addressIndex = addressIndex;
|
|
1576
|
+
this.identity = identity;
|
|
1577
|
+
this.resolveDelegate = resolveDelegate ?? null;
|
|
1578
|
+
this.id = `nostr-addr-${addressIndex}`;
|
|
1579
|
+
this.name = `Nostr Transport (address ${addressIndex})`;
|
|
1580
|
+
this.description = `P2P messaging for address index ${addressIndex}`;
|
|
1581
|
+
}
|
|
1582
|
+
// ===========================================================================
|
|
1583
|
+
// BaseProvider — delegates to mux
|
|
1584
|
+
// ===========================================================================
|
|
1585
|
+
async connect() {
|
|
1586
|
+
await this.mux.connect();
|
|
1587
|
+
}
|
|
1588
|
+
async disconnect() {
|
|
1589
|
+
}
|
|
1590
|
+
isConnected() {
|
|
1591
|
+
return this.mux.isConnected();
|
|
1592
|
+
}
|
|
1593
|
+
getStatus() {
|
|
1594
|
+
return this.mux.getStatus();
|
|
1595
|
+
}
|
|
1596
|
+
// ===========================================================================
|
|
1597
|
+
// Identity (no-op — mux manages identity via addAddress)
|
|
1598
|
+
// ===========================================================================
|
|
1599
|
+
async setIdentity(identity) {
|
|
1600
|
+
this.identity = identity;
|
|
1601
|
+
await this.mux.addAddress(this.addressIndex, identity);
|
|
1602
|
+
}
|
|
1603
|
+
// ===========================================================================
|
|
1604
|
+
// Sending — delegates to mux with this address's keyManager
|
|
1605
|
+
// ===========================================================================
|
|
1606
|
+
async sendMessage(recipientPubkey, content) {
|
|
1607
|
+
const senderNametag = this.identity.nametag;
|
|
1608
|
+
const wrappedContent = senderNametag ? JSON.stringify({ senderNametag, text: content }) : content;
|
|
1609
|
+
return this.mux.sendGiftWrap(this.addressIndex, recipientPubkey, wrappedContent);
|
|
1610
|
+
}
|
|
1611
|
+
async sendTokenTransfer(recipientPubkey, payload) {
|
|
1612
|
+
const content = "token_transfer:" + JSON.stringify(payload);
|
|
1613
|
+
const uniqueD = `token-transfer-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1614
|
+
return this.mux.createAndPublishEncryptedEvent(
|
|
1615
|
+
this.addressIndex,
|
|
1616
|
+
EVENT_KINDS.TOKEN_TRANSFER,
|
|
1617
|
+
content,
|
|
1618
|
+
[["p", recipientPubkey], ["d", uniqueD], ["type", "token_transfer"]]
|
|
1619
|
+
);
|
|
1620
|
+
}
|
|
1621
|
+
async sendPaymentRequest(recipientPubkey, payload) {
|
|
1622
|
+
const requestId2 = this.mux.getUUIDGenerator()();
|
|
1623
|
+
const amount = typeof payload.amount === "bigint" ? payload.amount.toString() : payload.amount;
|
|
1624
|
+
const requestContent = {
|
|
1625
|
+
requestId: requestId2,
|
|
1626
|
+
amount,
|
|
1627
|
+
coinId: payload.coinId,
|
|
1628
|
+
message: payload.message,
|
|
1629
|
+
recipientNametag: payload.recipientNametag,
|
|
1630
|
+
deadline: Date.now() + 5 * 60 * 1e3
|
|
1631
|
+
};
|
|
1632
|
+
const content = "payment_request:" + JSON.stringify(requestContent);
|
|
1633
|
+
const tags = [
|
|
1634
|
+
["p", recipientPubkey],
|
|
1635
|
+
["type", "payment_request"],
|
|
1636
|
+
["amount", amount]
|
|
1637
|
+
];
|
|
1638
|
+
if (payload.recipientNametag) {
|
|
1639
|
+
tags.push(["recipient", payload.recipientNametag]);
|
|
1640
|
+
}
|
|
1641
|
+
return this.mux.createAndPublishEncryptedEvent(
|
|
1642
|
+
this.addressIndex,
|
|
1643
|
+
EVENT_KINDS.PAYMENT_REQUEST,
|
|
1644
|
+
content,
|
|
1645
|
+
tags
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
async sendPaymentRequestResponse(recipientPubkey, response) {
|
|
1649
|
+
const content = "payment_response:" + JSON.stringify(response);
|
|
1650
|
+
return this.mux.createAndPublishEncryptedEvent(
|
|
1651
|
+
this.addressIndex,
|
|
1652
|
+
EVENT_KINDS.PAYMENT_REQUEST_RESPONSE,
|
|
1653
|
+
content,
|
|
1654
|
+
[["p", recipientPubkey], ["type", "payment_response"]]
|
|
1655
|
+
);
|
|
1656
|
+
}
|
|
1657
|
+
async sendReadReceipt(recipientPubkey, messageEventId) {
|
|
1658
|
+
const content = JSON.stringify({ type: "read_receipt", messageEventId });
|
|
1659
|
+
await this.mux.sendGiftWrap(this.addressIndex, recipientPubkey, content);
|
|
1660
|
+
}
|
|
1661
|
+
async sendTypingIndicator(recipientPubkey) {
|
|
1662
|
+
const content = JSON.stringify({
|
|
1663
|
+
type: "typing",
|
|
1664
|
+
senderNametag: this.identity.nametag
|
|
1665
|
+
});
|
|
1666
|
+
await this.mux.sendGiftWrap(this.addressIndex, recipientPubkey, content);
|
|
1667
|
+
}
|
|
1668
|
+
async sendComposingIndicator(recipientPubkey, content) {
|
|
1669
|
+
await this.mux.sendGiftWrap(this.addressIndex, recipientPubkey, content);
|
|
1670
|
+
}
|
|
1671
|
+
async sendInstantSplitBundle(recipientPubkey, bundle) {
|
|
1672
|
+
const content = "token_transfer:" + JSON.stringify({
|
|
1673
|
+
type: "instant_split",
|
|
1674
|
+
...bundle
|
|
1675
|
+
});
|
|
1676
|
+
const uniqueD = `instant-split-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1677
|
+
return this.mux.createAndPublishEncryptedEvent(
|
|
1678
|
+
this.addressIndex,
|
|
1679
|
+
EVENT_KINDS.TOKEN_TRANSFER,
|
|
1680
|
+
content,
|
|
1681
|
+
[["p", recipientPubkey], ["d", uniqueD], ["type", "instant_split"]]
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
1684
|
+
// ===========================================================================
|
|
1685
|
+
// Subscription handlers — per-address
|
|
1686
|
+
// ===========================================================================
|
|
1687
|
+
onMessage(handler) {
|
|
1688
|
+
this.messageHandlers.add(handler);
|
|
1689
|
+
if (this.pendingMessages.length > 0) {
|
|
1690
|
+
const pending2 = this.pendingMessages;
|
|
1691
|
+
this.pendingMessages = [];
|
|
1692
|
+
for (const msg of pending2) {
|
|
1693
|
+
try {
|
|
1694
|
+
handler(msg);
|
|
1695
|
+
} catch {
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
return () => this.messageHandlers.delete(handler);
|
|
1700
|
+
}
|
|
1701
|
+
onTokenTransfer(handler) {
|
|
1702
|
+
this.transferHandlers.add(handler);
|
|
1703
|
+
return () => this.transferHandlers.delete(handler);
|
|
1704
|
+
}
|
|
1705
|
+
onPaymentRequest(handler) {
|
|
1706
|
+
this.paymentRequestHandlers.add(handler);
|
|
1707
|
+
return () => this.paymentRequestHandlers.delete(handler);
|
|
1708
|
+
}
|
|
1709
|
+
onPaymentRequestResponse(handler) {
|
|
1710
|
+
this.paymentRequestResponseHandlers.add(handler);
|
|
1711
|
+
return () => this.paymentRequestResponseHandlers.delete(handler);
|
|
1712
|
+
}
|
|
1713
|
+
onReadReceipt(handler) {
|
|
1714
|
+
this.readReceiptHandlers.add(handler);
|
|
1715
|
+
return () => this.readReceiptHandlers.delete(handler);
|
|
1716
|
+
}
|
|
1717
|
+
onTypingIndicator(handler) {
|
|
1718
|
+
this.typingIndicatorHandlers.add(handler);
|
|
1719
|
+
return () => this.typingIndicatorHandlers.delete(handler);
|
|
1720
|
+
}
|
|
1721
|
+
onComposing(handler) {
|
|
1722
|
+
this.composingHandlers.add(handler);
|
|
1723
|
+
return () => this.composingHandlers.delete(handler);
|
|
1724
|
+
}
|
|
1725
|
+
onInstantSplitReceived(handler) {
|
|
1726
|
+
this.instantSplitBundleHandlers.add(handler);
|
|
1727
|
+
return () => this.instantSplitBundleHandlers.delete(handler);
|
|
1728
|
+
}
|
|
1729
|
+
subscribeToBroadcast(tags, handler) {
|
|
1730
|
+
const key = tags.sort().join(":");
|
|
1731
|
+
if (!this.broadcastHandlers.has(key)) {
|
|
1732
|
+
this.broadcastHandlers.set(key, /* @__PURE__ */ new Set());
|
|
1733
|
+
}
|
|
1734
|
+
this.broadcastHandlers.get(key).add(handler);
|
|
1735
|
+
return () => this.broadcastHandlers.get(key)?.delete(handler);
|
|
1736
|
+
}
|
|
1737
|
+
async publishBroadcast(content, tags) {
|
|
1738
|
+
const eventTags = tags ? tags.map((t) => ["t", t]) : [];
|
|
1739
|
+
return this.mux.publishRawEvent(this.addressIndex, 30023, content, eventTags);
|
|
1740
|
+
}
|
|
1741
|
+
// ===========================================================================
|
|
1742
|
+
// Resolve methods — delegate to original NostrTransportProvider
|
|
1743
|
+
// These are stateless relay queries, shared across all addresses
|
|
1744
|
+
// ===========================================================================
|
|
1745
|
+
async resolve(identifier) {
|
|
1746
|
+
return this.resolveDelegate?.resolve?.(identifier) ?? null;
|
|
1747
|
+
}
|
|
1748
|
+
async resolveNametag(nametag) {
|
|
1749
|
+
return this.resolveDelegate?.resolveNametag?.(nametag) ?? null;
|
|
1750
|
+
}
|
|
1751
|
+
async resolveNametagInfo(nametag) {
|
|
1752
|
+
return this.resolveDelegate?.resolveNametagInfo?.(nametag) ?? null;
|
|
1753
|
+
}
|
|
1754
|
+
async resolveAddressInfo(address) {
|
|
1755
|
+
return this.resolveDelegate?.resolveAddressInfo?.(address) ?? null;
|
|
1756
|
+
}
|
|
1757
|
+
async resolveTransportPubkeyInfo(transportPubkey) {
|
|
1758
|
+
return this.resolveDelegate?.resolveTransportPubkeyInfo?.(transportPubkey) ?? null;
|
|
1759
|
+
}
|
|
1760
|
+
async discoverAddresses(transportPubkeys) {
|
|
1761
|
+
return this.resolveDelegate?.discoverAddresses?.(transportPubkeys) ?? [];
|
|
1762
|
+
}
|
|
1763
|
+
async recoverNametag() {
|
|
1764
|
+
return this.resolveDelegate?.recoverNametag?.() ?? null;
|
|
1765
|
+
}
|
|
1766
|
+
async publishIdentityBinding(chainPubkey, l1Address, directAddress, nametag) {
|
|
1767
|
+
return this.resolveDelegate?.publishIdentityBinding?.(chainPubkey, l1Address, directAddress, nametag) ?? false;
|
|
1768
|
+
}
|
|
1769
|
+
// ===========================================================================
|
|
1770
|
+
// Relay Management — delegates to mux
|
|
1771
|
+
// ===========================================================================
|
|
1772
|
+
getRelays() {
|
|
1773
|
+
return this.mux.getRelays();
|
|
1774
|
+
}
|
|
1775
|
+
getConnectedRelays() {
|
|
1776
|
+
return this.mux.getConnectedRelays();
|
|
1777
|
+
}
|
|
1778
|
+
async addRelay(relayUrl) {
|
|
1779
|
+
return this.mux.addRelay(relayUrl);
|
|
1780
|
+
}
|
|
1781
|
+
async removeRelay(relayUrl) {
|
|
1782
|
+
return this.mux.removeRelay(relayUrl);
|
|
1783
|
+
}
|
|
1784
|
+
hasRelay(relayUrl) {
|
|
1785
|
+
return this.mux.hasRelay(relayUrl);
|
|
1786
|
+
}
|
|
1787
|
+
isRelayConnected(relayUrl) {
|
|
1788
|
+
return this.mux.isRelayConnected(relayUrl);
|
|
1789
|
+
}
|
|
1790
|
+
setFallbackSince(sinceSeconds) {
|
|
1791
|
+
this.mux.setFallbackSince(this.addressIndex, sinceSeconds);
|
|
1792
|
+
}
|
|
1793
|
+
async fetchPendingEvents() {
|
|
1794
|
+
}
|
|
1795
|
+
onChatReady(handler) {
|
|
1796
|
+
return this.mux.onChatReady(handler);
|
|
1797
|
+
}
|
|
1798
|
+
// ===========================================================================
|
|
1799
|
+
// Dispatch methods — called by MultiAddressTransportMux to route events
|
|
1800
|
+
// ===========================================================================
|
|
1801
|
+
dispatchMessage(message) {
|
|
1802
|
+
if (this.messageHandlers.size === 0) {
|
|
1803
|
+
this.pendingMessages.push(message);
|
|
1804
|
+
return;
|
|
1805
|
+
}
|
|
1806
|
+
for (const handler of this.messageHandlers) {
|
|
1807
|
+
try {
|
|
1808
|
+
handler(message);
|
|
1809
|
+
} catch (e) {
|
|
1810
|
+
logger.debug("MuxAdapter", "Message handler error:", e);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
dispatchTokenTransfer(transfer) {
|
|
1815
|
+
for (const handler of this.transferHandlers) {
|
|
1816
|
+
try {
|
|
1817
|
+
handler(transfer);
|
|
1818
|
+
} catch (e) {
|
|
1819
|
+
logger.debug("MuxAdapter", "Transfer handler error:", e);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
dispatchPaymentRequest(request) {
|
|
1824
|
+
for (const handler of this.paymentRequestHandlers) {
|
|
1825
|
+
try {
|
|
1826
|
+
handler(request);
|
|
1827
|
+
} catch (e) {
|
|
1828
|
+
logger.debug("MuxAdapter", "Payment request handler error:", e);
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
dispatchPaymentRequestResponse(response) {
|
|
1833
|
+
for (const handler of this.paymentRequestResponseHandlers) {
|
|
1834
|
+
try {
|
|
1835
|
+
handler(response);
|
|
1836
|
+
} catch (e) {
|
|
1837
|
+
logger.debug("MuxAdapter", "Payment response handler error:", e);
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
dispatchReadReceipt(receipt) {
|
|
1842
|
+
for (const handler of this.readReceiptHandlers) {
|
|
1843
|
+
try {
|
|
1844
|
+
handler(receipt);
|
|
1845
|
+
} catch (e) {
|
|
1846
|
+
logger.debug("MuxAdapter", "Read receipt handler error:", e);
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
dispatchTypingIndicator(indicator) {
|
|
1851
|
+
for (const handler of this.typingIndicatorHandlers) {
|
|
1852
|
+
try {
|
|
1853
|
+
handler(indicator);
|
|
1854
|
+
} catch (e) {
|
|
1855
|
+
logger.debug("MuxAdapter", "Typing handler error:", e);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1860
|
+
dispatchComposingIndicator(indicator) {
|
|
1861
|
+
for (const handler of this.composingHandlers) {
|
|
1862
|
+
try {
|
|
1863
|
+
handler(indicator);
|
|
1864
|
+
} catch (e) {
|
|
1865
|
+
logger.debug("MuxAdapter", "Composing handler error:", e);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
dispatchInstantSplitBundle(bundle) {
|
|
1870
|
+
for (const handler of this.instantSplitBundleHandlers) {
|
|
1871
|
+
try {
|
|
1872
|
+
handler(bundle);
|
|
1873
|
+
} catch (e) {
|
|
1874
|
+
logger.debug("MuxAdapter", "Instant split handler error:", e);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
emitTransportEvent(event) {
|
|
1879
|
+
for (const cb of this.eventCallbacks) {
|
|
1880
|
+
try {
|
|
1881
|
+
cb(event);
|
|
1882
|
+
} catch {
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
};
|
|
1887
|
+
|
|
756
1888
|
// modules/payments/L1PaymentsModule.ts
|
|
757
1889
|
init_errors();
|
|
758
1890
|
init_constants();
|
|
@@ -4616,6 +5748,15 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4616
5748
|
this.unsubscribePaymentRequests = null;
|
|
4617
5749
|
this.unsubscribePaymentRequestResponses?.();
|
|
4618
5750
|
this.unsubscribePaymentRequestResponses = null;
|
|
5751
|
+
this.stopProofPolling();
|
|
5752
|
+
this.proofPollingJobs.clear();
|
|
5753
|
+
this.stopResolveUnconfirmedPolling();
|
|
5754
|
+
this.unsubscribeStorageEvents();
|
|
5755
|
+
for (const [, resolver] of this.pendingResponseResolvers) {
|
|
5756
|
+
clearTimeout(resolver.timeout);
|
|
5757
|
+
resolver.reject(new Error("Address switched"));
|
|
5758
|
+
}
|
|
5759
|
+
this.pendingResponseResolvers.clear();
|
|
4619
5760
|
this.tokens.clear();
|
|
4620
5761
|
this.pendingTransfers.clear();
|
|
4621
5762
|
this.tombstones = [];
|
|
@@ -4664,6 +5805,13 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4664
5805
|
try {
|
|
4665
5806
|
const result = await provider.load();
|
|
4666
5807
|
if (result.success && result.data) {
|
|
5808
|
+
const loadedMeta = result.data?._meta;
|
|
5809
|
+
const currentL1 = this.deps.identity.l1Address;
|
|
5810
|
+
const currentChain = this.deps.identity.chainPubkey;
|
|
5811
|
+
if (loadedMeta?.address && currentL1 && loadedMeta.address !== currentL1 && loadedMeta.address !== currentChain) {
|
|
5812
|
+
logger.warn("Payments", `Load: rejecting data from provider ${id} \u2014 address mismatch (got=${loadedMeta.address.slice(0, 20)}... expected=${currentL1.slice(0, 20)}...)`);
|
|
5813
|
+
continue;
|
|
5814
|
+
}
|
|
4667
5815
|
this.loadFromStorageData(result.data);
|
|
4668
5816
|
const txfData = result.data;
|
|
4669
5817
|
if (txfData._history && txfData._history.length > 0) {
|
|
@@ -4745,6 +5893,11 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
4745
5893
|
*/
|
|
4746
5894
|
async send(request) {
|
|
4747
5895
|
this.ensureInitialized();
|
|
5896
|
+
let resolveSendTracker;
|
|
5897
|
+
const sendTracker = new Promise((r) => {
|
|
5898
|
+
resolveSendTracker = r;
|
|
5899
|
+
});
|
|
5900
|
+
this.pendingBackgroundTasks.push(sendTracker);
|
|
4748
5901
|
const result = {
|
|
4749
5902
|
id: crypto.randomUUID(),
|
|
4750
5903
|
status: "pending",
|
|
@@ -5032,6 +6185,8 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
5032
6185
|
}
|
|
5033
6186
|
this.deps.emitEvent("transfer:failed", result);
|
|
5034
6187
|
throw error;
|
|
6188
|
+
} finally {
|
|
6189
|
+
resolveSendTracker();
|
|
5035
6190
|
}
|
|
5036
6191
|
}
|
|
5037
6192
|
/**
|
|
@@ -6044,9 +7199,12 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
6044
7199
|
* Call this before process exit to ensure all tokens are saved.
|
|
6045
7200
|
*/
|
|
6046
7201
|
async waitForPendingOperations() {
|
|
7202
|
+
logger.debug("Payments", `waitForPendingOperations: ${this.pendingBackgroundTasks.length} pending tasks`);
|
|
6047
7203
|
if (this.pendingBackgroundTasks.length > 0) {
|
|
7204
|
+
logger.debug("Payments", "waitForPendingOperations: waiting...");
|
|
6048
7205
|
await Promise.allSettled(this.pendingBackgroundTasks);
|
|
6049
7206
|
this.pendingBackgroundTasks = [];
|
|
7207
|
+
logger.debug("Payments", "waitForPendingOperations: all tasks completed");
|
|
6050
7208
|
}
|
|
6051
7209
|
}
|
|
6052
7210
|
/**
|
|
@@ -7288,6 +8446,13 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
7288
8446
|
try {
|
|
7289
8447
|
const result = await provider.sync(localData);
|
|
7290
8448
|
if (result.success && result.merged) {
|
|
8449
|
+
const mergedMeta = result.merged?._meta;
|
|
8450
|
+
const currentL1 = this.deps.identity.l1Address;
|
|
8451
|
+
const currentChain = this.deps.identity.chainPubkey;
|
|
8452
|
+
if (mergedMeta?.address && currentL1 && mergedMeta.address !== currentL1 && mergedMeta.address !== currentChain) {
|
|
8453
|
+
logger.warn("Payments", `Sync: rejecting data from provider ${providerId} \u2014 address mismatch (got=${mergedMeta.address.slice(0, 20)}... expected=${currentL1.slice(0, 20)}...)`);
|
|
8454
|
+
continue;
|
|
8455
|
+
}
|
|
7291
8456
|
const savedTokens = new Map(this.tokens);
|
|
7292
8457
|
this.loadFromStorageData(result.merged);
|
|
7293
8458
|
let restoredCount = 0;
|
|
@@ -8720,9 +9885,9 @@ init_logger();
|
|
|
8720
9885
|
init_errors();
|
|
8721
9886
|
init_constants();
|
|
8722
9887
|
import {
|
|
8723
|
-
NostrClient,
|
|
8724
|
-
NostrKeyManager,
|
|
8725
|
-
Filter
|
|
9888
|
+
NostrClient as NostrClient2,
|
|
9889
|
+
NostrKeyManager as NostrKeyManager2,
|
|
9890
|
+
Filter as Filter2
|
|
8726
9891
|
} from "@unicitylabs/nostr-js-sdk";
|
|
8727
9892
|
|
|
8728
9893
|
// modules/groupchat/types.ts
|
|
@@ -8738,7 +9903,7 @@ var GroupVisibility = {
|
|
|
8738
9903
|
|
|
8739
9904
|
// modules/groupchat/GroupChatModule.ts
|
|
8740
9905
|
function createNip29Filter(data) {
|
|
8741
|
-
return new
|
|
9906
|
+
return new Filter2(data);
|
|
8742
9907
|
}
|
|
8743
9908
|
var GroupChatModule = class {
|
|
8744
9909
|
config;
|
|
@@ -8787,7 +9952,7 @@ var GroupChatModule = class {
|
|
|
8787
9952
|
}
|
|
8788
9953
|
this.deps = deps;
|
|
8789
9954
|
const secretKey = Buffer.from(deps.identity.privateKey, "hex");
|
|
8790
|
-
this.keyManager =
|
|
9955
|
+
this.keyManager = NostrKeyManager2.fromPrivateKey(secretKey);
|
|
8791
9956
|
}
|
|
8792
9957
|
async load() {
|
|
8793
9958
|
this.ensureInitialized();
|
|
@@ -8922,7 +10087,7 @@ var GroupChatModule = class {
|
|
|
8922
10087
|
}
|
|
8923
10088
|
this.subscriptionIds = [];
|
|
8924
10089
|
const secretKey = Buffer.from(this.deps.identity.privateKey, "hex");
|
|
8925
|
-
this.keyManager =
|
|
10090
|
+
this.keyManager = NostrKeyManager2.fromPrivateKey(secretKey);
|
|
8926
10091
|
if (this.groups.size === 0) {
|
|
8927
10092
|
await this.restoreJoinedGroups();
|
|
8928
10093
|
} else {
|
|
@@ -8934,13 +10099,13 @@ var GroupChatModule = class {
|
|
|
8934
10099
|
this.ensureInitialized();
|
|
8935
10100
|
if (!this.keyManager) {
|
|
8936
10101
|
const secretKey = Buffer.from(this.deps.identity.privateKey, "hex");
|
|
8937
|
-
this.keyManager =
|
|
10102
|
+
this.keyManager = NostrKeyManager2.fromPrivateKey(secretKey);
|
|
8938
10103
|
}
|
|
8939
10104
|
const primaryRelay = this.config.relays[0];
|
|
8940
10105
|
if (primaryRelay) {
|
|
8941
10106
|
await this.checkAndClearOnRelayChange(primaryRelay);
|
|
8942
10107
|
}
|
|
8943
|
-
this.client = new
|
|
10108
|
+
this.client = new NostrClient2(this.keyManager);
|
|
8944
10109
|
try {
|
|
8945
10110
|
await this.client.connect(...this.config.relays);
|
|
8946
10111
|
this.connected = true;
|
|
@@ -9199,7 +10364,7 @@ var GroupChatModule = class {
|
|
|
9199
10364
|
if (!myPubkey) return [];
|
|
9200
10365
|
const groupIdsWithMembership = /* @__PURE__ */ new Set();
|
|
9201
10366
|
await this.oneshotSubscription(
|
|
9202
|
-
new
|
|
10367
|
+
new Filter2({ kinds: [NIP29_KINDS.GROUP_MEMBERS] }),
|
|
9203
10368
|
{
|
|
9204
10369
|
onEvent: (event) => {
|
|
9205
10370
|
const groupId = this.getGroupIdFromMetadataEvent(event);
|
|
@@ -9250,7 +10415,7 @@ var GroupChatModule = class {
|
|
|
9250
10415
|
const memberCountsMap = /* @__PURE__ */ new Map();
|
|
9251
10416
|
await Promise.all([
|
|
9252
10417
|
this.oneshotSubscription(
|
|
9253
|
-
new
|
|
10418
|
+
new Filter2({ kinds: [NIP29_KINDS.GROUP_METADATA] }),
|
|
9254
10419
|
{
|
|
9255
10420
|
onEvent: (event) => {
|
|
9256
10421
|
const group = this.parseGroupMetadata(event);
|
|
@@ -9268,7 +10433,7 @@ var GroupChatModule = class {
|
|
|
9268
10433
|
}
|
|
9269
10434
|
),
|
|
9270
10435
|
this.oneshotSubscription(
|
|
9271
|
-
new
|
|
10436
|
+
new Filter2({ kinds: [NIP29_KINDS.GROUP_MEMBERS] }),
|
|
9272
10437
|
{
|
|
9273
10438
|
onEvent: (event) => {
|
|
9274
10439
|
const groupId = this.getGroupIdFromMetadataEvent(event);
|
|
@@ -9771,7 +10936,7 @@ var GroupChatModule = class {
|
|
|
9771
10936
|
if (!this.client) return /* @__PURE__ */ new Set();
|
|
9772
10937
|
const adminPubkeys = /* @__PURE__ */ new Set();
|
|
9773
10938
|
return this.oneshotSubscription(
|
|
9774
|
-
new
|
|
10939
|
+
new Filter2({ kinds: [NIP29_KINDS.GROUP_ADMINS], "#d": ["", "_"] }),
|
|
9775
10940
|
{
|
|
9776
10941
|
onEvent: (event) => {
|
|
9777
10942
|
const pTags = event.tags.filter((t) => t[0] === "p");
|
|
@@ -9793,7 +10958,7 @@ var GroupChatModule = class {
|
|
|
9793
10958
|
if (!this.client) return null;
|
|
9794
10959
|
let result = null;
|
|
9795
10960
|
return this.oneshotSubscription(
|
|
9796
|
-
new
|
|
10961
|
+
new Filter2({ kinds: [NIP29_KINDS.GROUP_METADATA], "#d": [groupId] }),
|
|
9797
10962
|
{
|
|
9798
10963
|
onEvent: (event) => {
|
|
9799
10964
|
if (!result) result = this.parseGroupMetadata(event);
|
|
@@ -9830,7 +10995,7 @@ var GroupChatModule = class {
|
|
|
9830
10995
|
if (!this.client) return [];
|
|
9831
10996
|
const members = [];
|
|
9832
10997
|
return this.oneshotSubscription(
|
|
9833
|
-
new
|
|
10998
|
+
new Filter2({ kinds: [NIP29_KINDS.GROUP_MEMBERS], "#d": [groupId] }),
|
|
9834
10999
|
{
|
|
9835
11000
|
onEvent: (event) => {
|
|
9836
11001
|
const pTags = event.tags.filter((t) => t[0] === "p");
|
|
@@ -9851,7 +11016,7 @@ var GroupChatModule = class {
|
|
|
9851
11016
|
if (!this.client) return [];
|
|
9852
11017
|
const adminPubkeys = [];
|
|
9853
11018
|
return this.oneshotSubscription(
|
|
9854
|
-
new
|
|
11019
|
+
new Filter2({ kinds: [NIP29_KINDS.GROUP_ADMINS], "#d": [groupId] }),
|
|
9855
11020
|
{
|
|
9856
11021
|
onEvent: (event) => {
|
|
9857
11022
|
const pTags = event.tags.filter((t) => t[0] === "p");
|
|
@@ -13651,11 +14816,18 @@ var Sphere = class _Sphere {
|
|
|
13651
14816
|
_transport;
|
|
13652
14817
|
_oracle;
|
|
13653
14818
|
_priceProvider;
|
|
13654
|
-
// Modules
|
|
14819
|
+
// Modules (single-instance — backward compat, delegates to active address)
|
|
13655
14820
|
_payments;
|
|
13656
14821
|
_communications;
|
|
13657
14822
|
_groupChat = null;
|
|
13658
14823
|
_market = null;
|
|
14824
|
+
// Per-address module instances (Phase 2: independent parallel operation)
|
|
14825
|
+
_addressModules = /* @__PURE__ */ new Map();
|
|
14826
|
+
_transportMux = null;
|
|
14827
|
+
// Stored configs for creating per-address modules
|
|
14828
|
+
_l1Config;
|
|
14829
|
+
_groupChatConfig;
|
|
14830
|
+
_marketConfig;
|
|
13659
14831
|
// Events
|
|
13660
14832
|
eventHandlers = /* @__PURE__ */ new Map();
|
|
13661
14833
|
// Provider management
|
|
@@ -13673,6 +14845,9 @@ var Sphere = class _Sphere {
|
|
|
13673
14845
|
if (tokenStorage) {
|
|
13674
14846
|
this._tokenStorageProviders.set(tokenStorage.id, tokenStorage);
|
|
13675
14847
|
}
|
|
14848
|
+
this._l1Config = l1Config;
|
|
14849
|
+
this._groupChatConfig = groupChatConfig;
|
|
14850
|
+
this._marketConfig = marketConfig;
|
|
13676
14851
|
this._payments = createPaymentsModule({ l1: l1Config });
|
|
13677
14852
|
this._communications = createCommunicationsModule();
|
|
13678
14853
|
this._groupChat = groupChatConfig ? createGroupChatModule(groupChatConfig) : null;
|
|
@@ -14931,7 +16106,7 @@ var Sphere = class _Sphere {
|
|
|
14931
16106
|
nametags.set(0, newNametag);
|
|
14932
16107
|
}
|
|
14933
16108
|
const nametag = this._addressNametags.get(addressId)?.get(0);
|
|
14934
|
-
|
|
16109
|
+
const newIdentity = {
|
|
14935
16110
|
privateKey: addressInfo.privateKey,
|
|
14936
16111
|
chainPubkey: addressInfo.publicKey,
|
|
14937
16112
|
l1Address: addressInfo.address,
|
|
@@ -14939,20 +16114,53 @@ var Sphere = class _Sphere {
|
|
|
14939
16114
|
ipnsName: "12D3KooW" + ipnsHash,
|
|
14940
16115
|
nametag
|
|
14941
16116
|
};
|
|
16117
|
+
if (!this._addressModules.has(index)) {
|
|
16118
|
+
logger.debug("Sphere", `switchToAddress(${index}): creating per-address modules (lazy init)`);
|
|
16119
|
+
const addressTokenProviders = /* @__PURE__ */ new Map();
|
|
16120
|
+
for (const [providerId, provider] of this._tokenStorageProviders.entries()) {
|
|
16121
|
+
if (provider.createForAddress) {
|
|
16122
|
+
const newProvider = provider.createForAddress();
|
|
16123
|
+
newProvider.setIdentity(newIdentity);
|
|
16124
|
+
await newProvider.initialize();
|
|
16125
|
+
addressTokenProviders.set(providerId, newProvider);
|
|
16126
|
+
} else {
|
|
16127
|
+
logger.warn("Sphere", `Token storage provider ${providerId} does not support createForAddress, reusing shared instance`);
|
|
16128
|
+
addressTokenProviders.set(providerId, provider);
|
|
16129
|
+
}
|
|
16130
|
+
}
|
|
16131
|
+
await this.initializeAddressModules(index, newIdentity, addressTokenProviders);
|
|
16132
|
+
} else {
|
|
16133
|
+
const moduleSet = this._addressModules.get(index);
|
|
16134
|
+
if (nametag !== moduleSet.identity.nametag) {
|
|
16135
|
+
moduleSet.identity = newIdentity;
|
|
16136
|
+
const addressTransport = moduleSet.transportAdapter ?? this._transport;
|
|
16137
|
+
moduleSet.payments.initialize({
|
|
16138
|
+
identity: newIdentity,
|
|
16139
|
+
storage: this._storage,
|
|
16140
|
+
tokenStorageProviders: moduleSet.tokenStorageProviders,
|
|
16141
|
+
transport: addressTransport,
|
|
16142
|
+
oracle: this._oracle,
|
|
16143
|
+
emitEvent: this.emitEvent.bind(this),
|
|
16144
|
+
chainCode: this._masterKey?.chainCode || void 0,
|
|
16145
|
+
price: this._priceProvider ?? void 0
|
|
16146
|
+
});
|
|
16147
|
+
}
|
|
16148
|
+
}
|
|
16149
|
+
this._identity = newIdentity;
|
|
14942
16150
|
this._currentAddressIndex = index;
|
|
14943
16151
|
await this._updateCachedProxyAddress();
|
|
16152
|
+
const activeModules = this._addressModules.get(index);
|
|
16153
|
+
this._payments = activeModules.payments;
|
|
16154
|
+
this._communications = activeModules.communications;
|
|
16155
|
+
this._groupChat = activeModules.groupChat;
|
|
16156
|
+
this._market = activeModules.market;
|
|
14944
16157
|
await this._storage.set(STORAGE_KEYS_GLOBAL.CURRENT_ADDRESS_INDEX, index.toString());
|
|
14945
16158
|
this._storage.setIdentity(this._identity);
|
|
14946
|
-
|
|
14947
|
-
|
|
14948
|
-
|
|
14949
|
-
logger.debug("Sphere", `switchToAddress(${index}): shutdown provider=${providerId}`);
|
|
14950
|
-
await provider.shutdown();
|
|
14951
|
-
provider.setIdentity(this._identity);
|
|
14952
|
-
logger.debug("Sphere", `switchToAddress(${index}): initialize provider=${providerId}`);
|
|
14953
|
-
await provider.initialize();
|
|
16159
|
+
if (this._transport.setFallbackSince) {
|
|
16160
|
+
const fallbackTs = Math.floor(Date.now() / 1e3) - 86400;
|
|
16161
|
+
this._transport.setFallbackSince(fallbackTs);
|
|
14954
16162
|
}
|
|
14955
|
-
await this.
|
|
16163
|
+
await this._transport.setIdentity(this._identity);
|
|
14956
16164
|
this.emitEvent("identity:changed", {
|
|
14957
16165
|
l1Address: this._identity.l1Address,
|
|
14958
16166
|
directAddress: this._identity.directAddress,
|
|
@@ -15007,42 +16215,104 @@ var Sphere = class _Sphere {
|
|
|
15007
16215
|
}
|
|
15008
16216
|
}
|
|
15009
16217
|
/**
|
|
15010
|
-
*
|
|
16218
|
+
* Create a new set of per-address modules for the given index.
|
|
16219
|
+
* Each address gets its own PaymentsModule, CommunicationsModule, etc.
|
|
16220
|
+
* Modules are fully independent — they have their own token storage,
|
|
16221
|
+
* and can sync/finalize/split in background regardless of active address.
|
|
16222
|
+
*
|
|
16223
|
+
* @param index - HD address index
|
|
16224
|
+
* @param identity - Full identity for this address
|
|
16225
|
+
* @param tokenStorageProviders - Token storage providers for this address
|
|
15011
16226
|
*/
|
|
15012
|
-
async
|
|
16227
|
+
async initializeAddressModules(index, identity, tokenStorageProviders) {
|
|
15013
16228
|
const emitEvent = this.emitEvent.bind(this);
|
|
15014
|
-
this.
|
|
15015
|
-
|
|
16229
|
+
const adapter = await this.ensureTransportMux(index, identity);
|
|
16230
|
+
const addressTransport = adapter ?? this._transport;
|
|
16231
|
+
const payments = createPaymentsModule({ l1: this._l1Config });
|
|
16232
|
+
const communications = createCommunicationsModule();
|
|
16233
|
+
const groupChat = this._groupChatConfig ? createGroupChatModule(this._groupChatConfig) : null;
|
|
16234
|
+
const market = this._marketConfig ? createMarketModule(this._marketConfig) : null;
|
|
16235
|
+
payments.initialize({
|
|
16236
|
+
identity,
|
|
15016
16237
|
storage: this._storage,
|
|
15017
|
-
tokenStorageProviders
|
|
15018
|
-
transport:
|
|
16238
|
+
tokenStorageProviders,
|
|
16239
|
+
transport: addressTransport,
|
|
15019
16240
|
oracle: this._oracle,
|
|
15020
16241
|
emitEvent,
|
|
15021
16242
|
chainCode: this._masterKey?.chainCode || void 0,
|
|
15022
16243
|
price: this._priceProvider ?? void 0
|
|
15023
16244
|
});
|
|
15024
|
-
|
|
15025
|
-
identity
|
|
16245
|
+
communications.initialize({
|
|
16246
|
+
identity,
|
|
15026
16247
|
storage: this._storage,
|
|
15027
|
-
transport:
|
|
16248
|
+
transport: addressTransport,
|
|
15028
16249
|
emitEvent
|
|
15029
16250
|
});
|
|
15030
|
-
|
|
15031
|
-
identity
|
|
16251
|
+
groupChat?.initialize({
|
|
16252
|
+
identity,
|
|
15032
16253
|
storage: this._storage,
|
|
15033
16254
|
emitEvent
|
|
15034
16255
|
});
|
|
15035
|
-
|
|
15036
|
-
identity
|
|
16256
|
+
market?.initialize({
|
|
16257
|
+
identity,
|
|
15037
16258
|
emitEvent
|
|
15038
16259
|
});
|
|
15039
|
-
await
|
|
15040
|
-
await
|
|
15041
|
-
await
|
|
15042
|
-
await
|
|
15043
|
-
|
|
15044
|
-
|
|
16260
|
+
await payments.load();
|
|
16261
|
+
await communications.load();
|
|
16262
|
+
await groupChat?.load();
|
|
16263
|
+
await market?.load();
|
|
16264
|
+
const moduleSet = {
|
|
16265
|
+
index,
|
|
16266
|
+
identity,
|
|
16267
|
+
payments,
|
|
16268
|
+
communications,
|
|
16269
|
+
groupChat,
|
|
16270
|
+
market,
|
|
16271
|
+
transportAdapter: adapter,
|
|
16272
|
+
tokenStorageProviders: new Map(tokenStorageProviders),
|
|
16273
|
+
initialized: true
|
|
16274
|
+
};
|
|
16275
|
+
this._addressModules.set(index, moduleSet);
|
|
16276
|
+
logger.debug("Sphere", `Initialized per-address modules for address ${index} (transport: ${adapter ? "mux adapter" : "primary"})`);
|
|
16277
|
+
payments.sync().catch((err) => {
|
|
16278
|
+
logger.warn("Sphere", `Post-init sync failed for address ${index}:`, err);
|
|
15045
16279
|
});
|
|
16280
|
+
return moduleSet;
|
|
16281
|
+
}
|
|
16282
|
+
/**
|
|
16283
|
+
* Ensure the transport multiplexer exists and register an address.
|
|
16284
|
+
* Creates the mux on first call. Returns an AddressTransportAdapter
|
|
16285
|
+
* that routes events for this address independently.
|
|
16286
|
+
* @returns AddressTransportAdapter or null if transport is not Nostr-based
|
|
16287
|
+
*/
|
|
16288
|
+
async ensureTransportMux(index, identity) {
|
|
16289
|
+
const transport = this._transport;
|
|
16290
|
+
if (typeof transport.getWebSocketFactory !== "function" || typeof transport.getConfiguredRelays !== "function") {
|
|
16291
|
+
logger.debug("Sphere", "Transport does not support mux interface, skipping");
|
|
16292
|
+
return null;
|
|
16293
|
+
}
|
|
16294
|
+
const nostrTransport = transport;
|
|
16295
|
+
if (!this._transportMux) {
|
|
16296
|
+
this._transportMux = new MultiAddressTransportMux({
|
|
16297
|
+
relays: nostrTransport.getConfiguredRelays(),
|
|
16298
|
+
createWebSocket: nostrTransport.getWebSocketFactory(),
|
|
16299
|
+
storage: nostrTransport.getStorageAdapter() ?? void 0
|
|
16300
|
+
});
|
|
16301
|
+
await this._transportMux.connect();
|
|
16302
|
+
if (typeof nostrTransport.suppressSubscriptions === "function") {
|
|
16303
|
+
nostrTransport.suppressSubscriptions();
|
|
16304
|
+
}
|
|
16305
|
+
logger.debug("Sphere", "Transport mux created and connected");
|
|
16306
|
+
}
|
|
16307
|
+
const adapter = await this._transportMux.addAddress(index, identity, this._transport);
|
|
16308
|
+
return adapter;
|
|
16309
|
+
}
|
|
16310
|
+
/**
|
|
16311
|
+
* Get per-address modules for any address index (creates lazily if needed).
|
|
16312
|
+
* This allows accessing any address's modules without switching.
|
|
16313
|
+
*/
|
|
16314
|
+
getAddressPayments(index) {
|
|
16315
|
+
return this._addressModules.get(index)?.payments;
|
|
15046
16316
|
}
|
|
15047
16317
|
/**
|
|
15048
16318
|
* Derive address at a specific index
|
|
@@ -15970,10 +17240,33 @@ var Sphere = class _Sphere {
|
|
|
15970
17240
|
// ===========================================================================
|
|
15971
17241
|
async destroy() {
|
|
15972
17242
|
this.cleanupProviderEventSubscriptions();
|
|
17243
|
+
for (const [idx, moduleSet] of this._addressModules.entries()) {
|
|
17244
|
+
try {
|
|
17245
|
+
moduleSet.payments.destroy();
|
|
17246
|
+
moduleSet.communications.destroy();
|
|
17247
|
+
moduleSet.groupChat?.destroy();
|
|
17248
|
+
moduleSet.market?.destroy();
|
|
17249
|
+
for (const provider of moduleSet.tokenStorageProviders.values()) {
|
|
17250
|
+
try {
|
|
17251
|
+
await provider.shutdown();
|
|
17252
|
+
} catch {
|
|
17253
|
+
}
|
|
17254
|
+
}
|
|
17255
|
+
moduleSet.tokenStorageProviders.clear();
|
|
17256
|
+
logger.debug("Sphere", `Destroyed modules for address ${idx}`);
|
|
17257
|
+
} catch (err) {
|
|
17258
|
+
logger.warn("Sphere", `Error destroying modules for address ${idx}:`, err);
|
|
17259
|
+
}
|
|
17260
|
+
}
|
|
17261
|
+
this._addressModules.clear();
|
|
15973
17262
|
this._payments.destroy();
|
|
15974
17263
|
this._communications.destroy();
|
|
15975
17264
|
this._groupChat?.destroy();
|
|
15976
17265
|
this._market?.destroy();
|
|
17266
|
+
if (this._transportMux) {
|
|
17267
|
+
await this._transportMux.disconnect();
|
|
17268
|
+
this._transportMux = null;
|
|
17269
|
+
}
|
|
15977
17270
|
await this._transport.disconnect();
|
|
15978
17271
|
await this._storage.disconnect();
|
|
15979
17272
|
await this._oracle.disconnect();
|
|
@@ -16168,6 +17461,9 @@ var Sphere = class _Sphere {
|
|
|
16168
17461
|
// ===========================================================================
|
|
16169
17462
|
async initializeProviders() {
|
|
16170
17463
|
this._storage.setIdentity(this._identity);
|
|
17464
|
+
if (this._transport.setFallbackSince) {
|
|
17465
|
+
this._transport.setFallbackSince(Math.floor(Date.now() / 1e3) - 86400);
|
|
17466
|
+
}
|
|
16171
17467
|
await this._transport.setIdentity(this._identity);
|
|
16172
17468
|
for (const provider of this._tokenStorageProviders.values()) {
|
|
16173
17469
|
provider.setIdentity(this._identity);
|
|
@@ -16258,11 +17554,13 @@ var Sphere = class _Sphere {
|
|
|
16258
17554
|
}
|
|
16259
17555
|
async initializeModules() {
|
|
16260
17556
|
const emitEvent = this.emitEvent.bind(this);
|
|
17557
|
+
const adapter = await this.ensureTransportMux(this._currentAddressIndex, this._identity);
|
|
17558
|
+
const moduleTransport = adapter ?? this._transport;
|
|
16261
17559
|
this._payments.initialize({
|
|
16262
17560
|
identity: this._identity,
|
|
16263
17561
|
storage: this._storage,
|
|
16264
17562
|
tokenStorageProviders: this._tokenStorageProviders,
|
|
16265
|
-
transport:
|
|
17563
|
+
transport: moduleTransport,
|
|
16266
17564
|
oracle: this._oracle,
|
|
16267
17565
|
emitEvent,
|
|
16268
17566
|
// Pass chain code for L1 HD derivation
|
|
@@ -16273,7 +17571,7 @@ var Sphere = class _Sphere {
|
|
|
16273
17571
|
this._communications.initialize({
|
|
16274
17572
|
identity: this._identity,
|
|
16275
17573
|
storage: this._storage,
|
|
16276
|
-
transport:
|
|
17574
|
+
transport: moduleTransport,
|
|
16277
17575
|
emitEvent
|
|
16278
17576
|
});
|
|
16279
17577
|
this._groupChat?.initialize({
|
|
@@ -16289,6 +17587,17 @@ var Sphere = class _Sphere {
|
|
|
16289
17587
|
await this._communications.load();
|
|
16290
17588
|
await this._groupChat?.load();
|
|
16291
17589
|
await this._market?.load();
|
|
17590
|
+
this._addressModules.set(this._currentAddressIndex, {
|
|
17591
|
+
index: this._currentAddressIndex,
|
|
17592
|
+
identity: this._identity,
|
|
17593
|
+
payments: this._payments,
|
|
17594
|
+
communications: this._communications,
|
|
17595
|
+
groupChat: this._groupChat,
|
|
17596
|
+
market: this._market,
|
|
17597
|
+
transportAdapter: adapter,
|
|
17598
|
+
tokenStorageProviders: new Map(this._tokenStorageProviders),
|
|
17599
|
+
initialized: true
|
|
17600
|
+
});
|
|
16292
17601
|
}
|
|
16293
17602
|
// ===========================================================================
|
|
16294
17603
|
// Private: Helpers
|