@unicitylabs/sphere-sdk 0.2.2 → 0.2.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/README.md +22 -69
- package/dist/core/index.cjs +167 -62
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +16 -5
- package/dist/core/index.d.ts +16 -5
- package/dist/core/index.js +167 -62
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +89 -12
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +89 -12
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/browser/ipfs.cjs +3 -1
- package/dist/impl/browser/ipfs.cjs.map +1 -1
- package/dist/impl/browser/ipfs.js +3 -1
- package/dist/impl/browser/ipfs.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +77 -12
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +21 -0
- package/dist/impl/nodejs/index.d.ts +21 -0
- package/dist/impl/nodejs/index.js +77 -12
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +167 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -5
- package/dist/index.d.ts +18 -5
- package/dist/index.js +167 -62
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -74,7 +74,9 @@ var STORAGE_KEYS_GLOBAL = {
|
|
|
74
74
|
/** Nametag cache per address (separate from tracked addresses registry) */
|
|
75
75
|
ADDRESS_NAMETAGS: "address_nametags",
|
|
76
76
|
/** Active addresses registry (JSON: TrackedAddressesStorage) */
|
|
77
|
-
TRACKED_ADDRESSES: "tracked_addresses"
|
|
77
|
+
TRACKED_ADDRESSES: "tracked_addresses",
|
|
78
|
+
/** Last processed Nostr wallet event timestamp (unix seconds), keyed per pubkey */
|
|
79
|
+
LAST_WALLET_EVENT_TS: "last_wallet_event_ts"
|
|
78
80
|
};
|
|
79
81
|
var STORAGE_KEYS_ADDRESS = {
|
|
80
82
|
/** Pending transfers for this address */
|
|
@@ -1141,6 +1143,9 @@ var NostrTransportProvider = class {
|
|
|
1141
1143
|
type = "p2p";
|
|
1142
1144
|
description = "P2P messaging via Nostr protocol";
|
|
1143
1145
|
config;
|
|
1146
|
+
storage = null;
|
|
1147
|
+
/** In-memory max event timestamp to avoid read-before-write races in updateLastEventTimestamp. */
|
|
1148
|
+
lastEventTs = 0;
|
|
1144
1149
|
identity = null;
|
|
1145
1150
|
keyManager = null;
|
|
1146
1151
|
status = "disconnected";
|
|
@@ -1166,6 +1171,7 @@ var NostrTransportProvider = class {
|
|
|
1166
1171
|
createWebSocket: config.createWebSocket,
|
|
1167
1172
|
generateUUID: config.generateUUID ?? defaultUUIDGenerator
|
|
1168
1173
|
};
|
|
1174
|
+
this.storage = config.storage ?? null;
|
|
1169
1175
|
}
|
|
1170
1176
|
// ===========================================================================
|
|
1171
1177
|
// BaseProvider Implementation
|
|
@@ -1204,7 +1210,14 @@ var NostrTransportProvider = class {
|
|
|
1204
1210
|
this.emitEvent({ type: "transport:connected", timestamp: Date.now() });
|
|
1205
1211
|
}
|
|
1206
1212
|
});
|
|
1207
|
-
await
|
|
1213
|
+
await Promise.race([
|
|
1214
|
+
this.nostrClient.connect(...this.config.relays),
|
|
1215
|
+
new Promise(
|
|
1216
|
+
(_, reject) => setTimeout(() => reject(new Error(
|
|
1217
|
+
`Transport connection timed out after ${this.config.timeout}ms`
|
|
1218
|
+
)), this.config.timeout)
|
|
1219
|
+
)
|
|
1220
|
+
]);
|
|
1208
1221
|
if (!this.nostrClient.isConnected()) {
|
|
1209
1222
|
throw new Error("Failed to connect to any relay");
|
|
1210
1223
|
}
|
|
@@ -1212,7 +1225,7 @@ var NostrTransportProvider = class {
|
|
|
1212
1225
|
this.emitEvent({ type: "transport:connected", timestamp: Date.now() });
|
|
1213
1226
|
this.log("Connected to", this.nostrClient.getConnectedRelays().size, "relays");
|
|
1214
1227
|
if (this.identity) {
|
|
1215
|
-
this.subscribeToEvents();
|
|
1228
|
+
await this.subscribeToEvents();
|
|
1216
1229
|
}
|
|
1217
1230
|
} catch (error) {
|
|
1218
1231
|
this.status = "error";
|
|
@@ -1365,11 +1378,18 @@ var NostrTransportProvider = class {
|
|
|
1365
1378
|
this.log("NostrClient reconnected to relay:", url);
|
|
1366
1379
|
}
|
|
1367
1380
|
});
|
|
1368
|
-
await
|
|
1369
|
-
|
|
1381
|
+
await Promise.race([
|
|
1382
|
+
this.nostrClient.connect(...this.config.relays),
|
|
1383
|
+
new Promise(
|
|
1384
|
+
(_, reject) => setTimeout(() => reject(new Error(
|
|
1385
|
+
`Transport reconnection timed out after ${this.config.timeout}ms`
|
|
1386
|
+
)), this.config.timeout)
|
|
1387
|
+
)
|
|
1388
|
+
]);
|
|
1389
|
+
await this.subscribeToEvents();
|
|
1370
1390
|
oldClient.disconnect();
|
|
1371
1391
|
} else if (this.isConnected()) {
|
|
1372
|
-
this.subscribeToEvents();
|
|
1392
|
+
await this.subscribeToEvents();
|
|
1373
1393
|
}
|
|
1374
1394
|
}
|
|
1375
1395
|
/**
|
|
@@ -1897,10 +1917,31 @@ var NostrTransportProvider = class {
|
|
|
1897
1917
|
this.handleBroadcast(event);
|
|
1898
1918
|
break;
|
|
1899
1919
|
}
|
|
1920
|
+
if (event.created_at && this.storage && this.keyManager) {
|
|
1921
|
+
const kind = event.kind;
|
|
1922
|
+
if (kind === EVENT_KINDS.DIRECT_MESSAGE || kind === EVENT_KINDS.TOKEN_TRANSFER || kind === EVENT_KINDS.PAYMENT_REQUEST || kind === EVENT_KINDS.PAYMENT_REQUEST_RESPONSE) {
|
|
1923
|
+
this.updateLastEventTimestamp(event.created_at);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1900
1926
|
} catch (error) {
|
|
1901
1927
|
this.log("Failed to handle event:", error);
|
|
1902
1928
|
}
|
|
1903
1929
|
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Save the max event timestamp to storage (fire-and-forget, no await needed by caller).
|
|
1932
|
+
* Uses in-memory `lastEventTs` to avoid read-before-write race conditions
|
|
1933
|
+
* when multiple events arrive in quick succession.
|
|
1934
|
+
*/
|
|
1935
|
+
updateLastEventTimestamp(createdAt) {
|
|
1936
|
+
if (!this.storage || !this.keyManager) return;
|
|
1937
|
+
if (createdAt <= this.lastEventTs) return;
|
|
1938
|
+
this.lastEventTs = createdAt;
|
|
1939
|
+
const pubkey = this.keyManager.getPublicKeyHex();
|
|
1940
|
+
const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_WALLET_EVENT_TS}_${pubkey.slice(0, 16)}`;
|
|
1941
|
+
this.storage.set(storageKey, createdAt.toString()).catch((err) => {
|
|
1942
|
+
this.log("Failed to save last event timestamp:", err);
|
|
1943
|
+
});
|
|
1944
|
+
}
|
|
1904
1945
|
async handleDirectMessage(event) {
|
|
1905
1946
|
this.log("Ignoring NIP-04 kind 4 event (DMs use NIP-17):", event.id?.slice(0, 12));
|
|
1906
1947
|
}
|
|
@@ -1979,6 +2020,7 @@ var NostrTransportProvider = class {
|
|
|
1979
2020
|
const request = {
|
|
1980
2021
|
id: event.id,
|
|
1981
2022
|
senderTransportPubkey: event.pubkey,
|
|
2023
|
+
senderNametag: requestData.recipientNametag,
|
|
1982
2024
|
request: {
|
|
1983
2025
|
requestId: requestData.requestId,
|
|
1984
2026
|
amount: requestData.amount,
|
|
@@ -2133,7 +2175,7 @@ var NostrTransportProvider = class {
|
|
|
2133
2175
|
// Track subscription IDs for cleanup
|
|
2134
2176
|
walletSubscriptionId = null;
|
|
2135
2177
|
chatSubscriptionId = null;
|
|
2136
|
-
subscribeToEvents() {
|
|
2178
|
+
async subscribeToEvents() {
|
|
2137
2179
|
this.log("subscribeToEvents called, identity:", !!this.identity, "keyManager:", !!this.keyManager, "nostrClient:", !!this.nostrClient);
|
|
2138
2180
|
if (!this.identity || !this.keyManager || !this.nostrClient) {
|
|
2139
2181
|
this.log("subscribeToEvents: skipped - no identity, keyManager, or nostrClient");
|
|
@@ -2153,6 +2195,27 @@ var NostrTransportProvider = class {
|
|
|
2153
2195
|
}
|
|
2154
2196
|
const nostrPubkey = this.keyManager.getPublicKeyHex();
|
|
2155
2197
|
this.log("Subscribing with Nostr pubkey:", nostrPubkey);
|
|
2198
|
+
let since;
|
|
2199
|
+
if (this.storage) {
|
|
2200
|
+
const storageKey = `${STORAGE_KEYS_GLOBAL.LAST_WALLET_EVENT_TS}_${nostrPubkey.slice(0, 16)}`;
|
|
2201
|
+
try {
|
|
2202
|
+
const stored = await this.storage.get(storageKey);
|
|
2203
|
+
if (stored) {
|
|
2204
|
+
since = parseInt(stored, 10);
|
|
2205
|
+
this.lastEventTs = since;
|
|
2206
|
+
this.log("Resuming from stored event timestamp:", since);
|
|
2207
|
+
} else {
|
|
2208
|
+
since = Math.floor(Date.now() / 1e3);
|
|
2209
|
+
this.log("No stored timestamp, starting from now:", since);
|
|
2210
|
+
}
|
|
2211
|
+
} catch (err) {
|
|
2212
|
+
this.log("Failed to read last event timestamp, falling back to now:", err);
|
|
2213
|
+
since = Math.floor(Date.now() / 1e3);
|
|
2214
|
+
}
|
|
2215
|
+
} else {
|
|
2216
|
+
since = Math.floor(Date.now() / 1e3) - 86400;
|
|
2217
|
+
this.log("No storage adapter, using 24h fallback");
|
|
2218
|
+
}
|
|
2156
2219
|
const walletFilter = new import_nostr_js_sdk.Filter();
|
|
2157
2220
|
walletFilter.kinds = [
|
|
2158
2221
|
EVENT_KINDS.DIRECT_MESSAGE,
|
|
@@ -2161,7 +2224,7 @@ var NostrTransportProvider = class {
|
|
|
2161
2224
|
EVENT_KINDS.PAYMENT_REQUEST_RESPONSE
|
|
2162
2225
|
];
|
|
2163
2226
|
walletFilter["#p"] = [nostrPubkey];
|
|
2164
|
-
walletFilter.since =
|
|
2227
|
+
walletFilter.since = since;
|
|
2165
2228
|
this.walletSubscriptionId = this.nostrClient.subscribe(walletFilter, {
|
|
2166
2229
|
onEvent: (event) => {
|
|
2167
2230
|
this.log("Received wallet event kind:", event.kind, "id:", event.id?.slice(0, 12));
|
|
@@ -2958,10 +3021,11 @@ function createNodeProviders(config) {
|
|
|
2958
3021
|
const oracleConfig = resolveOracleConfig(network, config?.oracle);
|
|
2959
3022
|
const l1Config = resolveL1Config(network, config?.l1);
|
|
2960
3023
|
const priceConfig = resolvePriceConfig(config?.price);
|
|
3024
|
+
const storage = createFileStorageProvider({
|
|
3025
|
+
dataDir: config?.dataDir ?? "./sphere-data"
|
|
3026
|
+
});
|
|
2961
3027
|
return {
|
|
2962
|
-
storage
|
|
2963
|
-
dataDir: config?.dataDir ?? "./sphere-data"
|
|
2964
|
-
}),
|
|
3028
|
+
storage,
|
|
2965
3029
|
tokenStorage: createFileTokenStorageProvider({
|
|
2966
3030
|
tokensDir: config?.tokensDir ?? "./sphere-tokens"
|
|
2967
3031
|
}),
|
|
@@ -2969,7 +3033,8 @@ function createNodeProviders(config) {
|
|
|
2969
3033
|
relays: transportConfig.relays,
|
|
2970
3034
|
timeout: transportConfig.timeout,
|
|
2971
3035
|
autoReconnect: transportConfig.autoReconnect,
|
|
2972
|
-
debug: transportConfig.debug
|
|
3036
|
+
debug: transportConfig.debug,
|
|
3037
|
+
storage
|
|
2973
3038
|
}),
|
|
2974
3039
|
oracle: createUnicityAggregatorProvider({
|
|
2975
3040
|
url: oracleConfig.url,
|