@unicitylabs/sphere-sdk 0.2.1 → 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 +944 -465
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +127 -6
- package/dist/core/index.d.ts +127 -6
- package/dist/core/index.js +833 -351
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +115 -19
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +115 -19
- 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 +85 -17
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +22 -0
- package/dist/impl/nodejs/index.d.ts +22 -0
- package/dist/impl/nodejs/index.js +85 -17
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +1083 -644
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +118 -6
- package/dist/index.d.ts +118 -6
- package/dist/index.js +993 -554
- package/dist/index.js.map +1 -1
- package/dist/l1/index.cjs +18 -0
- package/dist/l1/index.cjs.map +1 -1
- package/dist/l1/index.d.cts +4 -0
- package/dist/l1/index.d.ts +4 -0
- package/dist/l1/index.js +18 -0
- package/dist/l1/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
|
/**
|
|
@@ -1510,7 +1530,7 @@ var NostrTransportProvider = class {
|
|
|
1510
1530
|
return this.resolveNametagInfo(identifier);
|
|
1511
1531
|
}
|
|
1512
1532
|
async resolveNametag(nametag) {
|
|
1513
|
-
this.
|
|
1533
|
+
this.ensureConnected();
|
|
1514
1534
|
const hashedNametag = (0, import_nostr_js_sdk.hashNametag)(nametag);
|
|
1515
1535
|
let events = await this.queryEvents({
|
|
1516
1536
|
kinds: [EVENT_KINDS.NAMETAG_BINDING],
|
|
@@ -1534,7 +1554,7 @@ var NostrTransportProvider = class {
|
|
|
1534
1554
|
return null;
|
|
1535
1555
|
}
|
|
1536
1556
|
async resolveNametagInfo(nametag) {
|
|
1537
|
-
this.
|
|
1557
|
+
this.ensureConnected();
|
|
1538
1558
|
const hashedNametag = (0, import_nostr_js_sdk.hashNametag)(nametag);
|
|
1539
1559
|
let events = await this.queryEvents({
|
|
1540
1560
|
kinds: [EVENT_KINDS.NAMETAG_BINDING],
|
|
@@ -1611,7 +1631,7 @@ var NostrTransportProvider = class {
|
|
|
1611
1631
|
* Works with both new identity binding events and legacy nametag binding events.
|
|
1612
1632
|
*/
|
|
1613
1633
|
async resolveAddressInfo(address) {
|
|
1614
|
-
this.
|
|
1634
|
+
this.ensureConnected();
|
|
1615
1635
|
const addressHash = hashAddressForTag(address);
|
|
1616
1636
|
const events = await this.queryEvents({
|
|
1617
1637
|
kinds: [EVENT_KINDS.NAMETAG_BINDING],
|
|
@@ -1646,7 +1666,7 @@ var NostrTransportProvider = class {
|
|
|
1646
1666
|
* Queries binding events authored by the given pubkey.
|
|
1647
1667
|
*/
|
|
1648
1668
|
async resolveTransportPubkeyInfo(transportPubkey) {
|
|
1649
|
-
this.
|
|
1669
|
+
this.ensureConnected();
|
|
1650
1670
|
const events = await this.queryEvents({
|
|
1651
1671
|
kinds: [EVENT_KINDS.NAMETAG_BINDING],
|
|
1652
1672
|
authors: [transportPubkey],
|
|
@@ -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));
|
|
@@ -2264,10 +2327,13 @@ var NostrTransportProvider = class {
|
|
|
2264
2327
|
// ===========================================================================
|
|
2265
2328
|
// Private: Helpers
|
|
2266
2329
|
// ===========================================================================
|
|
2267
|
-
|
|
2330
|
+
ensureConnected() {
|
|
2268
2331
|
if (!this.isConnected()) {
|
|
2269
2332
|
throw new Error("NostrTransportProvider not connected");
|
|
2270
2333
|
}
|
|
2334
|
+
}
|
|
2335
|
+
ensureReady() {
|
|
2336
|
+
this.ensureConnected();
|
|
2271
2337
|
if (!this.identity) {
|
|
2272
2338
|
throw new Error("Identity not set");
|
|
2273
2339
|
}
|
|
@@ -2955,10 +3021,11 @@ function createNodeProviders(config) {
|
|
|
2955
3021
|
const oracleConfig = resolveOracleConfig(network, config?.oracle);
|
|
2956
3022
|
const l1Config = resolveL1Config(network, config?.l1);
|
|
2957
3023
|
const priceConfig = resolvePriceConfig(config?.price);
|
|
3024
|
+
const storage = createFileStorageProvider({
|
|
3025
|
+
dataDir: config?.dataDir ?? "./sphere-data"
|
|
3026
|
+
});
|
|
2958
3027
|
return {
|
|
2959
|
-
storage
|
|
2960
|
-
dataDir: config?.dataDir ?? "./sphere-data"
|
|
2961
|
-
}),
|
|
3028
|
+
storage,
|
|
2962
3029
|
tokenStorage: createFileTokenStorageProvider({
|
|
2963
3030
|
tokensDir: config?.tokensDir ?? "./sphere-tokens"
|
|
2964
3031
|
}),
|
|
@@ -2966,7 +3033,8 @@ function createNodeProviders(config) {
|
|
|
2966
3033
|
relays: transportConfig.relays,
|
|
2967
3034
|
timeout: transportConfig.timeout,
|
|
2968
3035
|
autoReconnect: transportConfig.autoReconnect,
|
|
2969
|
-
debug: transportConfig.debug
|
|
3036
|
+
debug: transportConfig.debug,
|
|
3037
|
+
storage
|
|
2970
3038
|
}),
|
|
2971
3039
|
oracle: createUnicityAggregatorProvider({
|
|
2972
3040
|
url: oracleConfig.url,
|