@unicitylabs/sphere-sdk 0.9.1-dev.10 → 0.9.1-dev.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -10003,6 +10003,7 @@ function computeHistoryDedupKey(type, tokenId, transferId) {
10003
10003
  return `${type}_${crypto.randomUUID()}`;
10004
10004
  }
10005
10005
  var MAX_SYNCED_HISTORY_ENTRIES = 5e3;
10006
+ var MAX_HISTORY_HYDRATION_PAGES = 100;
10006
10007
  var SEND_ENGINE_OP_TIMEOUT_MS = 6e4;
10007
10008
  var DELIVERY_POLL_INTERVAL_MS = 3e4;
10008
10009
  function enrichWithRegistry(info) {
@@ -12292,10 +12293,20 @@ var PaymentsModule = class _PaymentsModule {
12292
12293
  this.deps.emitEvent("history:updated", historyEntry);
12293
12294
  }
12294
12295
  /**
12295
- * Load history from the local token storage provider into the in-memory cache.
12296
- * Also performs one-time migration from legacy KV storage.
12296
+ * Load history into the in-memory cache.
12297
+ *
12298
+ * In the wallet-api composition (the `walletApi` client is present) the
12299
+ * durable §10 history log lives on the SERVER — the thin storage provider
12300
+ * keeps none — so the cache is rebuilt from `walletApi.listHistory()`. The
12301
+ * twin of the #521 inventory reload bug: `_historyCache` is process-lifetime,
12302
+ * so a reload (tab refresh) must re-pull it or render an empty history.
12303
+ * Compositions WITHOUT `walletApi` keep the legacy local path below.
12297
12304
  */
12298
12305
  async loadHistory() {
12306
+ if (this.deps.walletApi?.listHistory) {
12307
+ await this.hydrateHistoryFromServer(this.deps.walletApi);
12308
+ return;
12309
+ }
12299
12310
  const provider = this.getLocalTokenStorageProvider();
12300
12311
  if (provider?.getHistoryEntries) {
12301
12312
  this._historyCache = await provider.getHistoryEntries();
@@ -12327,6 +12338,75 @@ var PaymentsModule = class _PaymentsModule {
12327
12338
  }
12328
12339
  }
12329
12340
  }
12341
+ /**
12342
+ * Rebuild `_historyCache` from the server's §10 history log (the wallet-api
12343
+ * composition). Pages newest-first via the keyset cursor until `more:false`
12344
+ * or the page cap; dedups by `dedupKey` (a hydrate-then-receive in the same
12345
+ * session must not double-list). The S6 `memo` / `counterpartyNametag`
12346
+ * envelopes are decrypted with the owner's own field key on the way in.
12347
+ *
12348
+ * Best-effort, like the §10 history POST: history is untrusted DISPLAY data,
12349
+ * so a backend outage during hydration must NEVER fail `load()` (the money
12350
+ * path) — the in-session cache is left intact and the pull retries next load.
12351
+ */
12352
+ async hydrateHistoryFromServer(api) {
12353
+ const byDedupKey = /* @__PURE__ */ new Map();
12354
+ try {
12355
+ let before;
12356
+ for (let page = 0; page < MAX_HISTORY_HYDRATION_PAGES; page++) {
12357
+ const result = await api.listHistory(before !== void 0 ? { before } : {});
12358
+ for (const wire of result.records) {
12359
+ if (!byDedupKey.has(wire.dedupKey)) {
12360
+ byDedupKey.set(wire.dedupKey, this.historyEntryFromWire(wire));
12361
+ }
12362
+ }
12363
+ if (!result.more || result.cursor === null) break;
12364
+ before = result.cursor;
12365
+ }
12366
+ } catch (err) {
12367
+ logger.warn("Payments", "history hydration from server failed (kept in-memory; retries next load):", err);
12368
+ return;
12369
+ }
12370
+ this._historyCache = [...byDedupKey.values()];
12371
+ }
12372
+ /**
12373
+ * Map one §16 history wire record onto the display
12374
+ * {@link TransactionHistoryEntry}. `counterpartyNametag` lands on the role-
12375
+ * appropriate field (sender for RECEIVED, recipient otherwise); the S6 memo +
12376
+ * nametag envelopes decrypt under THIS wallet's field key (self-scoped at
12377
+ * rest — §8.3), surfaced as absent if they don't decrypt rather than as
12378
+ * ciphertext (same rule as mailbox/payment-request memos).
12379
+ */
12380
+ historyEntryFromWire(wire) {
12381
+ const asset = wire.assets[0];
12382
+ const coinId = asset?.coinId ?? "";
12383
+ const received = wire.type === "RECEIVED";
12384
+ const memo = this.tryDecryptField(wire.memo);
12385
+ const nametag = this.tryDecryptField(wire.counterpartyNametag);
12386
+ return {
12387
+ id: wire.id,
12388
+ dedupKey: wire.dedupKey,
12389
+ type: wire.type,
12390
+ amount: asset?.amount ?? "0",
12391
+ coinId,
12392
+ symbol: this.getCoinSymbol(coinId),
12393
+ timestamp: Date.parse(wire.ts),
12394
+ ...wire.transferId !== void 0 ? { transferId: wire.transferId } : {},
12395
+ ...wire.tokenId !== void 0 ? { tokenId: wire.tokenId } : {},
12396
+ ...wire.counterpartyPubkey !== void 0 ? received ? { senderPubkey: wire.counterpartyPubkey } : { recipientPubkey: wire.counterpartyPubkey } : {},
12397
+ ...nametag !== void 0 ? received ? { senderNametag: nametag } : { recipientNametag: nametag } : {},
12398
+ ...memo !== void 0 ? { memo } : {}
12399
+ };
12400
+ }
12401
+ /** S6 field decrypt that surfaces an undecryptable envelope as absent (§8.3). */
12402
+ tryDecryptField(envelope) {
12403
+ if (envelope === void 0) return void 0;
12404
+ try {
12405
+ return decryptField(this.getFieldEncryptionKey(), envelope);
12406
+ } catch {
12407
+ return void 0;
12408
+ }
12409
+ }
12330
12410
  /**
12331
12411
  * Import history entries from remote TXF data into local store.
12332
12412
  * Delegates to the local TokenStorageProvider's importHistoryEntries() for