@unicitylabs/sphere-sdk 0.6.1 → 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 +1371 -52
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +369 -4
- package/dist/core/index.d.ts +369 -4
- package/dist/core/index.js +1377 -48
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +137 -11
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +137 -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 +133 -11
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +54 -0
- package/dist/impl/nodejs/index.d.ts +54 -0
- package/dist/impl/nodejs/index.js +133 -11
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +1354 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -3
- package/dist/index.d.ts +70 -3
- package/dist/index.js +1353 -50
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
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;
|
|
@@ -8594,6 +9733,12 @@ var CommunicationsModule = class {
|
|
|
8594
9733
|
this.unsubscribeComposing = deps.transport.onComposing?.((indicator) => {
|
|
8595
9734
|
this.handleComposingIndicator(indicator);
|
|
8596
9735
|
}) ?? null;
|
|
9736
|
+
if (deps.transport.onChatReady) {
|
|
9737
|
+
deps.transport.onChatReady(() => {
|
|
9738
|
+
const conversations = this.getConversations();
|
|
9739
|
+
deps.emitEvent("communications:ready", { conversationCount: conversations.size });
|
|
9740
|
+
});
|
|
9741
|
+
}
|
|
8597
9742
|
}
|
|
8598
9743
|
/**
|
|
8599
9744
|
* Load messages from storage.
|
|
@@ -9009,9 +10154,9 @@ init_logger();
|
|
|
9009
10154
|
init_errors();
|
|
9010
10155
|
init_constants();
|
|
9011
10156
|
import {
|
|
9012
|
-
NostrClient,
|
|
9013
|
-
NostrKeyManager,
|
|
9014
|
-
Filter
|
|
10157
|
+
NostrClient as NostrClient2,
|
|
10158
|
+
NostrKeyManager as NostrKeyManager2,
|
|
10159
|
+
Filter as Filter2
|
|
9015
10160
|
} from "@unicitylabs/nostr-js-sdk";
|
|
9016
10161
|
|
|
9017
10162
|
// modules/groupchat/types.ts
|
|
@@ -9027,7 +10172,7 @@ var GroupVisibility = {
|
|
|
9027
10172
|
|
|
9028
10173
|
// modules/groupchat/GroupChatModule.ts
|
|
9029
10174
|
function createNip29Filter(data) {
|
|
9030
|
-
return new
|
|
10175
|
+
return new Filter2(data);
|
|
9031
10176
|
}
|
|
9032
10177
|
var GroupChatModule = class {
|
|
9033
10178
|
config;
|
|
@@ -9076,7 +10221,7 @@ var GroupChatModule = class {
|
|
|
9076
10221
|
}
|
|
9077
10222
|
this.deps = deps;
|
|
9078
10223
|
const secretKey = Buffer.from(deps.identity.privateKey, "hex");
|
|
9079
|
-
this.keyManager =
|
|
10224
|
+
this.keyManager = NostrKeyManager2.fromPrivateKey(secretKey);
|
|
9080
10225
|
}
|
|
9081
10226
|
async load() {
|
|
9082
10227
|
this.ensureInitialized();
|
|
@@ -9211,7 +10356,7 @@ var GroupChatModule = class {
|
|
|
9211
10356
|
}
|
|
9212
10357
|
this.subscriptionIds = [];
|
|
9213
10358
|
const secretKey = Buffer.from(this.deps.identity.privateKey, "hex");
|
|
9214
|
-
this.keyManager =
|
|
10359
|
+
this.keyManager = NostrKeyManager2.fromPrivateKey(secretKey);
|
|
9215
10360
|
if (this.groups.size === 0) {
|
|
9216
10361
|
await this.restoreJoinedGroups();
|
|
9217
10362
|
} else {
|
|
@@ -9223,13 +10368,13 @@ var GroupChatModule = class {
|
|
|
9223
10368
|
this.ensureInitialized();
|
|
9224
10369
|
if (!this.keyManager) {
|
|
9225
10370
|
const secretKey = Buffer.from(this.deps.identity.privateKey, "hex");
|
|
9226
|
-
this.keyManager =
|
|
10371
|
+
this.keyManager = NostrKeyManager2.fromPrivateKey(secretKey);
|
|
9227
10372
|
}
|
|
9228
10373
|
const primaryRelay = this.config.relays[0];
|
|
9229
10374
|
if (primaryRelay) {
|
|
9230
10375
|
await this.checkAndClearOnRelayChange(primaryRelay);
|
|
9231
10376
|
}
|
|
9232
|
-
this.client = new
|
|
10377
|
+
this.client = new NostrClient2(this.keyManager);
|
|
9233
10378
|
try {
|
|
9234
10379
|
await this.client.connect(...this.config.relays);
|
|
9235
10380
|
this.connected = true;
|
|
@@ -9240,6 +10385,7 @@ var GroupChatModule = class {
|
|
|
9240
10385
|
await this.subscribeToJoinedGroups();
|
|
9241
10386
|
}
|
|
9242
10387
|
this.deps.emitEvent("groupchat:connection", { connected: true });
|
|
10388
|
+
this.deps.emitEvent("groupchat:ready", { groupCount: this.groups.size });
|
|
9243
10389
|
} catch (error) {
|
|
9244
10390
|
logger.error("GroupChat", "Failed to connect to relays", error);
|
|
9245
10391
|
this.deps.emitEvent("groupchat:connection", { connected: false });
|
|
@@ -9487,7 +10633,7 @@ var GroupChatModule = class {
|
|
|
9487
10633
|
if (!myPubkey) return [];
|
|
9488
10634
|
const groupIdsWithMembership = /* @__PURE__ */ new Set();
|
|
9489
10635
|
await this.oneshotSubscription(
|
|
9490
|
-
new
|
|
10636
|
+
new Filter2({ kinds: [NIP29_KINDS.GROUP_MEMBERS] }),
|
|
9491
10637
|
{
|
|
9492
10638
|
onEvent: (event) => {
|
|
9493
10639
|
const groupId = this.getGroupIdFromMetadataEvent(event);
|
|
@@ -9538,7 +10684,7 @@ var GroupChatModule = class {
|
|
|
9538
10684
|
const memberCountsMap = /* @__PURE__ */ new Map();
|
|
9539
10685
|
await Promise.all([
|
|
9540
10686
|
this.oneshotSubscription(
|
|
9541
|
-
new
|
|
10687
|
+
new Filter2({ kinds: [NIP29_KINDS.GROUP_METADATA] }),
|
|
9542
10688
|
{
|
|
9543
10689
|
onEvent: (event) => {
|
|
9544
10690
|
const group = this.parseGroupMetadata(event);
|
|
@@ -9556,7 +10702,7 @@ var GroupChatModule = class {
|
|
|
9556
10702
|
}
|
|
9557
10703
|
),
|
|
9558
10704
|
this.oneshotSubscription(
|
|
9559
|
-
new
|
|
10705
|
+
new Filter2({ kinds: [NIP29_KINDS.GROUP_MEMBERS] }),
|
|
9560
10706
|
{
|
|
9561
10707
|
onEvent: (event) => {
|
|
9562
10708
|
const groupId = this.getGroupIdFromMetadataEvent(event);
|
|
@@ -9874,6 +11020,19 @@ var GroupChatModule = class {
|
|
|
9874
11020
|
getMessages(groupId) {
|
|
9875
11021
|
return (this.messages.get(groupId) || []).sort((a, b) => a.timestamp - b.timestamp);
|
|
9876
11022
|
}
|
|
11023
|
+
getMessagesPage(groupId, options) {
|
|
11024
|
+
const limit = options?.limit ?? 20;
|
|
11025
|
+
const before = options?.before ?? Infinity;
|
|
11026
|
+
const groupMessages = this.messages.get(groupId) ?? [];
|
|
11027
|
+
const filtered = groupMessages.filter((m) => m.timestamp < before).sort((a, b) => b.timestamp - a.timestamp);
|
|
11028
|
+
const page = filtered.slice(0, limit);
|
|
11029
|
+
return {
|
|
11030
|
+
messages: page.reverse(),
|
|
11031
|
+
// chronological order
|
|
11032
|
+
hasMore: filtered.length > limit,
|
|
11033
|
+
oldestTimestamp: page.length > 0 ? page[0].timestamp : null
|
|
11034
|
+
};
|
|
11035
|
+
}
|
|
9877
11036
|
getMembers(groupId) {
|
|
9878
11037
|
return (this.members.get(groupId) || []).sort((a, b) => a.joinedAt - b.joinedAt);
|
|
9879
11038
|
}
|
|
@@ -10046,7 +11205,7 @@ var GroupChatModule = class {
|
|
|
10046
11205
|
if (!this.client) return /* @__PURE__ */ new Set();
|
|
10047
11206
|
const adminPubkeys = /* @__PURE__ */ new Set();
|
|
10048
11207
|
return this.oneshotSubscription(
|
|
10049
|
-
new
|
|
11208
|
+
new Filter2({ kinds: [NIP29_KINDS.GROUP_ADMINS], "#d": ["", "_"] }),
|
|
10050
11209
|
{
|
|
10051
11210
|
onEvent: (event) => {
|
|
10052
11211
|
const pTags = event.tags.filter((t) => t[0] === "p");
|
|
@@ -10068,7 +11227,7 @@ var GroupChatModule = class {
|
|
|
10068
11227
|
if (!this.client) return null;
|
|
10069
11228
|
let result = null;
|
|
10070
11229
|
return this.oneshotSubscription(
|
|
10071
|
-
new
|
|
11230
|
+
new Filter2({ kinds: [NIP29_KINDS.GROUP_METADATA], "#d": [groupId] }),
|
|
10072
11231
|
{
|
|
10073
11232
|
onEvent: (event) => {
|
|
10074
11233
|
if (!result) result = this.parseGroupMetadata(event);
|
|
@@ -10105,7 +11264,7 @@ var GroupChatModule = class {
|
|
|
10105
11264
|
if (!this.client) return [];
|
|
10106
11265
|
const members = [];
|
|
10107
11266
|
return this.oneshotSubscription(
|
|
10108
|
-
new
|
|
11267
|
+
new Filter2({ kinds: [NIP29_KINDS.GROUP_MEMBERS], "#d": [groupId] }),
|
|
10109
11268
|
{
|
|
10110
11269
|
onEvent: (event) => {
|
|
10111
11270
|
const pTags = event.tags.filter((t) => t[0] === "p");
|
|
@@ -10126,7 +11285,7 @@ var GroupChatModule = class {
|
|
|
10126
11285
|
if (!this.client) return [];
|
|
10127
11286
|
const adminPubkeys = [];
|
|
10128
11287
|
return this.oneshotSubscription(
|
|
10129
|
-
new
|
|
11288
|
+
new Filter2({ kinds: [NIP29_KINDS.GROUP_ADMINS], "#d": [groupId] }),
|
|
10130
11289
|
{
|
|
10131
11290
|
onEvent: (event) => {
|
|
10132
11291
|
const pTags = event.tags.filter((t) => t[0] === "p");
|
|
@@ -13841,11 +15000,18 @@ var Sphere = class _Sphere {
|
|
|
13841
15000
|
_transport;
|
|
13842
15001
|
_oracle;
|
|
13843
15002
|
_priceProvider;
|
|
13844
|
-
// Modules
|
|
15003
|
+
// Modules (single-instance — backward compat, delegates to active address)
|
|
13845
15004
|
_payments;
|
|
13846
15005
|
_communications;
|
|
13847
15006
|
_groupChat = null;
|
|
13848
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;
|
|
13849
15015
|
// Events
|
|
13850
15016
|
eventHandlers = /* @__PURE__ */ new Map();
|
|
13851
15017
|
// Provider management
|
|
@@ -13863,6 +15029,9 @@ var Sphere = class _Sphere {
|
|
|
13863
15029
|
if (tokenStorage) {
|
|
13864
15030
|
this._tokenStorageProviders.set(tokenStorage.id, tokenStorage);
|
|
13865
15031
|
}
|
|
15032
|
+
this._l1Config = l1Config;
|
|
15033
|
+
this._groupChatConfig = groupChatConfig;
|
|
15034
|
+
this._marketConfig = marketConfig;
|
|
13866
15035
|
this._payments = createPaymentsModule({ l1: l1Config });
|
|
13867
15036
|
this._communications = createCommunicationsModule();
|
|
13868
15037
|
this._groupChat = groupChatConfig ? createGroupChatModule(groupChatConfig) : null;
|
|
@@ -15121,7 +16290,7 @@ var Sphere = class _Sphere {
|
|
|
15121
16290
|
nametags.set(0, newNametag);
|
|
15122
16291
|
}
|
|
15123
16292
|
const nametag = this._addressNametags.get(addressId)?.get(0);
|
|
15124
|
-
|
|
16293
|
+
const newIdentity = {
|
|
15125
16294
|
privateKey: addressInfo.privateKey,
|
|
15126
16295
|
chainPubkey: addressInfo.publicKey,
|
|
15127
16296
|
l1Address: addressInfo.address,
|
|
@@ -15129,20 +16298,53 @@ var Sphere = class _Sphere {
|
|
|
15129
16298
|
ipnsName: "12D3KooW" + ipnsHash,
|
|
15130
16299
|
nametag
|
|
15131
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;
|
|
15132
16334
|
this._currentAddressIndex = index;
|
|
15133
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;
|
|
15134
16341
|
await this._storage.set(STORAGE_KEYS_GLOBAL.CURRENT_ADDRESS_INDEX, index.toString());
|
|
15135
16342
|
this._storage.setIdentity(this._identity);
|
|
15136
|
-
|
|
15137
|
-
|
|
15138
|
-
|
|
15139
|
-
logger.debug("Sphere", `switchToAddress(${index}): shutdown provider=${providerId}`);
|
|
15140
|
-
await provider.shutdown();
|
|
15141
|
-
provider.setIdentity(this._identity);
|
|
15142
|
-
logger.debug("Sphere", `switchToAddress(${index}): initialize provider=${providerId}`);
|
|
15143
|
-
await provider.initialize();
|
|
16343
|
+
if (this._transport.setFallbackSince) {
|
|
16344
|
+
const fallbackTs = Math.floor(Date.now() / 1e3) - 86400;
|
|
16345
|
+
this._transport.setFallbackSince(fallbackTs);
|
|
15144
16346
|
}
|
|
15145
|
-
await this.
|
|
16347
|
+
await this._transport.setIdentity(this._identity);
|
|
15146
16348
|
this.emitEvent("identity:changed", {
|
|
15147
16349
|
l1Address: this._identity.l1Address,
|
|
15148
16350
|
directAddress: this._identity.directAddress,
|
|
@@ -15197,42 +16399,104 @@ var Sphere = class _Sphere {
|
|
|
15197
16399
|
}
|
|
15198
16400
|
}
|
|
15199
16401
|
/**
|
|
15200
|
-
*
|
|
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
|
|
15201
16410
|
*/
|
|
15202
|
-
async
|
|
16411
|
+
async initializeAddressModules(index, identity, tokenStorageProviders) {
|
|
15203
16412
|
const emitEvent = this.emitEvent.bind(this);
|
|
15204
|
-
this.
|
|
15205
|
-
|
|
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,
|
|
15206
16421
|
storage: this._storage,
|
|
15207
|
-
tokenStorageProviders
|
|
15208
|
-
transport:
|
|
16422
|
+
tokenStorageProviders,
|
|
16423
|
+
transport: addressTransport,
|
|
15209
16424
|
oracle: this._oracle,
|
|
15210
16425
|
emitEvent,
|
|
15211
16426
|
chainCode: this._masterKey?.chainCode || void 0,
|
|
15212
16427
|
price: this._priceProvider ?? void 0
|
|
15213
16428
|
});
|
|
15214
|
-
|
|
15215
|
-
identity
|
|
16429
|
+
communications.initialize({
|
|
16430
|
+
identity,
|
|
15216
16431
|
storage: this._storage,
|
|
15217
|
-
transport:
|
|
16432
|
+
transport: addressTransport,
|
|
15218
16433
|
emitEvent
|
|
15219
16434
|
});
|
|
15220
|
-
|
|
15221
|
-
identity
|
|
16435
|
+
groupChat?.initialize({
|
|
16436
|
+
identity,
|
|
15222
16437
|
storage: this._storage,
|
|
15223
16438
|
emitEvent
|
|
15224
16439
|
});
|
|
15225
|
-
|
|
15226
|
-
identity
|
|
16440
|
+
market?.initialize({
|
|
16441
|
+
identity,
|
|
15227
16442
|
emitEvent
|
|
15228
16443
|
});
|
|
15229
|
-
await
|
|
15230
|
-
await
|
|
15231
|
-
await
|
|
15232
|
-
await
|
|
15233
|
-
|
|
15234
|
-
|
|
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);
|
|
15235
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;
|
|
15236
16500
|
}
|
|
15237
16501
|
/**
|
|
15238
16502
|
* Derive address at a specific index
|
|
@@ -16160,10 +17424,33 @@ var Sphere = class _Sphere {
|
|
|
16160
17424
|
// ===========================================================================
|
|
16161
17425
|
async destroy() {
|
|
16162
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();
|
|
16163
17446
|
this._payments.destroy();
|
|
16164
17447
|
this._communications.destroy();
|
|
16165
17448
|
this._groupChat?.destroy();
|
|
16166
17449
|
this._market?.destroy();
|
|
17450
|
+
if (this._transportMux) {
|
|
17451
|
+
await this._transportMux.disconnect();
|
|
17452
|
+
this._transportMux = null;
|
|
17453
|
+
}
|
|
16167
17454
|
await this._transport.disconnect();
|
|
16168
17455
|
await this._storage.disconnect();
|
|
16169
17456
|
await this._oracle.disconnect();
|
|
@@ -16358,6 +17645,9 @@ var Sphere = class _Sphere {
|
|
|
16358
17645
|
// ===========================================================================
|
|
16359
17646
|
async initializeProviders() {
|
|
16360
17647
|
this._storage.setIdentity(this._identity);
|
|
17648
|
+
if (this._transport.setFallbackSince) {
|
|
17649
|
+
this._transport.setFallbackSince(Math.floor(Date.now() / 1e3) - 86400);
|
|
17650
|
+
}
|
|
16361
17651
|
await this._transport.setIdentity(this._identity);
|
|
16362
17652
|
for (const provider of this._tokenStorageProviders.values()) {
|
|
16363
17653
|
provider.setIdentity(this._identity);
|
|
@@ -16448,11 +17738,13 @@ var Sphere = class _Sphere {
|
|
|
16448
17738
|
}
|
|
16449
17739
|
async initializeModules() {
|
|
16450
17740
|
const emitEvent = this.emitEvent.bind(this);
|
|
17741
|
+
const adapter = await this.ensureTransportMux(this._currentAddressIndex, this._identity);
|
|
17742
|
+
const moduleTransport = adapter ?? this._transport;
|
|
16451
17743
|
this._payments.initialize({
|
|
16452
17744
|
identity: this._identity,
|
|
16453
17745
|
storage: this._storage,
|
|
16454
17746
|
tokenStorageProviders: this._tokenStorageProviders,
|
|
16455
|
-
transport:
|
|
17747
|
+
transport: moduleTransport,
|
|
16456
17748
|
oracle: this._oracle,
|
|
16457
17749
|
emitEvent,
|
|
16458
17750
|
// Pass chain code for L1 HD derivation
|
|
@@ -16463,7 +17755,7 @@ var Sphere = class _Sphere {
|
|
|
16463
17755
|
this._communications.initialize({
|
|
16464
17756
|
identity: this._identity,
|
|
16465
17757
|
storage: this._storage,
|
|
16466
|
-
transport:
|
|
17758
|
+
transport: moduleTransport,
|
|
16467
17759
|
emitEvent
|
|
16468
17760
|
});
|
|
16469
17761
|
this._groupChat?.initialize({
|
|
@@ -16479,6 +17771,17 @@ var Sphere = class _Sphere {
|
|
|
16479
17771
|
await this._communications.load();
|
|
16480
17772
|
await this._groupChat?.load();
|
|
16481
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
|
+
});
|
|
16482
17785
|
}
|
|
16483
17786
|
// ===========================================================================
|
|
16484
17787
|
// Private: Helpers
|
|
@@ -17159,7 +18462,7 @@ import {
|
|
|
17159
18462
|
encryptNametag,
|
|
17160
18463
|
decryptNametag
|
|
17161
18464
|
} from "@unicitylabs/nostr-js-sdk";
|
|
17162
|
-
import { NostrClient as
|
|
18465
|
+
import { NostrClient as NostrClient3, NostrKeyManager as NostrKeyManager3 } from "@unicitylabs/nostr-js-sdk";
|
|
17163
18466
|
|
|
17164
18467
|
// price/CoinGeckoPriceProvider.ts
|
|
17165
18468
|
init_logger();
|
|
@@ -17431,8 +18734,8 @@ export {
|
|
|
17431
18734
|
NETWORKS,
|
|
17432
18735
|
NIP29_KINDS,
|
|
17433
18736
|
NOSTR_EVENT_KINDS,
|
|
17434
|
-
|
|
17435
|
-
|
|
18737
|
+
NostrClient3 as NostrClient,
|
|
18738
|
+
NostrKeyManager3 as NostrKeyManager,
|
|
17436
18739
|
PaymentsModule,
|
|
17437
18740
|
SIGN_MESSAGE_PREFIX,
|
|
17438
18741
|
STORAGE_KEYS,
|