@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/core/index.cjs +82 -2
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +69 -17
- package/dist/core/index.d.ts +69 -17
- package/dist/core/index.js +82 -2
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +82 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +69 -17
- package/dist/index.d.ts +69 -17
- package/dist/index.js +82 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/core/index.d.cts
CHANGED
|
@@ -1950,6 +1950,26 @@ type WalletApiPaymentRequestsPage = {
|
|
|
1950
1950
|
cursor: string | null;
|
|
1951
1951
|
syncEpoch: bigint;
|
|
1952
1952
|
};
|
|
1953
|
+
/**
|
|
1954
|
+
* One §16 history wire record (§10 — the server never writes history rows).
|
|
1955
|
+
* `memo` / `counterpartyNametag` are S6 `enc1.` envelopes, verbatim (§8.3) —
|
|
1956
|
+
* encrypted on POST, returned encrypted on GET, decrypted by the owner only.
|
|
1957
|
+
*/
|
|
1958
|
+
interface WalletApiHistoryRecord {
|
|
1959
|
+
dedupKey: string;
|
|
1960
|
+
id: string;
|
|
1961
|
+
type: string;
|
|
1962
|
+
ts: string;
|
|
1963
|
+
assets: {
|
|
1964
|
+
coinId: string;
|
|
1965
|
+
amount: string;
|
|
1966
|
+
}[];
|
|
1967
|
+
transferId?: string;
|
|
1968
|
+
tokenId?: string;
|
|
1969
|
+
counterpartyPubkey?: string;
|
|
1970
|
+
memo?: string;
|
|
1971
|
+
counterpartyNametag?: string;
|
|
1972
|
+
}
|
|
1953
1973
|
/**
|
|
1954
1974
|
* The narrow, STRUCTURAL slice of the wallet-api client this module needs:
|
|
1955
1975
|
* the E.3 intent lifecycle, blob uploads for spend outputs, and the §10
|
|
@@ -1984,21 +2004,24 @@ interface PaymentsWalletApiPort {
|
|
|
1984
2004
|
/** Upload to a presigned PUT (a 412 = already present = success — §5.2). */
|
|
1985
2005
|
uploadBlob(putUrl: string, bytes: Uint8Array): Promise<void>;
|
|
1986
2006
|
/** §10: client-asserted history records (§16 wire shape), deduped by dedupKey server-side. */
|
|
1987
|
-
postHistoryRecords(records:
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2007
|
+
postHistoryRecords(records: WalletApiHistoryRecord[]): Promise<void>;
|
|
2008
|
+
/**
|
|
2009
|
+
* §10/§16: read the client-written history log back — newest-first keyset
|
|
2010
|
+
* pages (`{records, more, cursor, syncEpoch}`). The READ side of the §10 log:
|
|
2011
|
+
* a reloaded thin wallet rebuilds its history from here (the in-memory cache
|
|
2012
|
+
* is process-lifetime; the durable log lives on the server). `memo` /
|
|
2013
|
+
* `counterpartyNametag` come back as the verbatim S6 `enc1.` envelopes — the
|
|
2014
|
+
* owner decrypts them with its own field key on display (§8.3).
|
|
2015
|
+
*/
|
|
2016
|
+
listHistory(options?: {
|
|
2017
|
+
before?: string;
|
|
2018
|
+
limit?: number;
|
|
2019
|
+
}): Promise<{
|
|
2020
|
+
records: WalletApiHistoryRecord[];
|
|
2021
|
+
more: boolean;
|
|
2022
|
+
cursor: string | null;
|
|
2023
|
+
syncEpoch: bigint;
|
|
2024
|
+
}>;
|
|
2002
2025
|
/** Network name — scopes the persisted payment-request cursor (mirrors the mailbox cursor). */
|
|
2003
2026
|
readonly network?: string;
|
|
2004
2027
|
/** `POST /v1/payment-requests` (§16) — `memo` MUST already be an S6 envelope (§8.3). */
|
|
@@ -2598,10 +2621,39 @@ declare class PaymentsModule {
|
|
|
2598
2621
|
*/
|
|
2599
2622
|
addToHistory(entry: Omit<TransactionHistoryEntry, 'id' | 'dedupKey'>): Promise<void>;
|
|
2600
2623
|
/**
|
|
2601
|
-
* Load history
|
|
2602
|
-
*
|
|
2624
|
+
* Load history into the in-memory cache.
|
|
2625
|
+
*
|
|
2626
|
+
* In the wallet-api composition (the `walletApi` client is present) the
|
|
2627
|
+
* durable §10 history log lives on the SERVER — the thin storage provider
|
|
2628
|
+
* keeps none — so the cache is rebuilt from `walletApi.listHistory()`. The
|
|
2629
|
+
* twin of the #521 inventory reload bug: `_historyCache` is process-lifetime,
|
|
2630
|
+
* so a reload (tab refresh) must re-pull it or render an empty history.
|
|
2631
|
+
* Compositions WITHOUT `walletApi` keep the legacy local path below.
|
|
2603
2632
|
*/
|
|
2604
2633
|
loadHistory(): Promise<void>;
|
|
2634
|
+
/**
|
|
2635
|
+
* Rebuild `_historyCache` from the server's §10 history log (the wallet-api
|
|
2636
|
+
* composition). Pages newest-first via the keyset cursor until `more:false`
|
|
2637
|
+
* or the page cap; dedups by `dedupKey` (a hydrate-then-receive in the same
|
|
2638
|
+
* session must not double-list). The S6 `memo` / `counterpartyNametag`
|
|
2639
|
+
* envelopes are decrypted with the owner's own field key on the way in.
|
|
2640
|
+
*
|
|
2641
|
+
* Best-effort, like the §10 history POST: history is untrusted DISPLAY data,
|
|
2642
|
+
* so a backend outage during hydration must NEVER fail `load()` (the money
|
|
2643
|
+
* path) — the in-session cache is left intact and the pull retries next load.
|
|
2644
|
+
*/
|
|
2645
|
+
private hydrateHistoryFromServer;
|
|
2646
|
+
/**
|
|
2647
|
+
* Map one §16 history wire record onto the display
|
|
2648
|
+
* {@link TransactionHistoryEntry}. `counterpartyNametag` lands on the role-
|
|
2649
|
+
* appropriate field (sender for RECEIVED, recipient otherwise); the S6 memo +
|
|
2650
|
+
* nametag envelopes decrypt under THIS wallet's field key (self-scoped at
|
|
2651
|
+
* rest — §8.3), surfaced as absent if they don't decrypt rather than as
|
|
2652
|
+
* ciphertext (same rule as mailbox/payment-request memos).
|
|
2653
|
+
*/
|
|
2654
|
+
private historyEntryFromWire;
|
|
2655
|
+
/** S6 field decrypt that surfaces an undecryptable envelope as absent (§8.3). */
|
|
2656
|
+
private tryDecryptField;
|
|
2605
2657
|
/**
|
|
2606
2658
|
* Import history entries from remote TXF data into local store.
|
|
2607
2659
|
* Delegates to the local TokenStorageProvider's importHistoryEntries() for
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1950,6 +1950,26 @@ type WalletApiPaymentRequestsPage = {
|
|
|
1950
1950
|
cursor: string | null;
|
|
1951
1951
|
syncEpoch: bigint;
|
|
1952
1952
|
};
|
|
1953
|
+
/**
|
|
1954
|
+
* One §16 history wire record (§10 — the server never writes history rows).
|
|
1955
|
+
* `memo` / `counterpartyNametag` are S6 `enc1.` envelopes, verbatim (§8.3) —
|
|
1956
|
+
* encrypted on POST, returned encrypted on GET, decrypted by the owner only.
|
|
1957
|
+
*/
|
|
1958
|
+
interface WalletApiHistoryRecord {
|
|
1959
|
+
dedupKey: string;
|
|
1960
|
+
id: string;
|
|
1961
|
+
type: string;
|
|
1962
|
+
ts: string;
|
|
1963
|
+
assets: {
|
|
1964
|
+
coinId: string;
|
|
1965
|
+
amount: string;
|
|
1966
|
+
}[];
|
|
1967
|
+
transferId?: string;
|
|
1968
|
+
tokenId?: string;
|
|
1969
|
+
counterpartyPubkey?: string;
|
|
1970
|
+
memo?: string;
|
|
1971
|
+
counterpartyNametag?: string;
|
|
1972
|
+
}
|
|
1953
1973
|
/**
|
|
1954
1974
|
* The narrow, STRUCTURAL slice of the wallet-api client this module needs:
|
|
1955
1975
|
* the E.3 intent lifecycle, blob uploads for spend outputs, and the §10
|
|
@@ -1984,21 +2004,24 @@ interface PaymentsWalletApiPort {
|
|
|
1984
2004
|
/** Upload to a presigned PUT (a 412 = already present = success — §5.2). */
|
|
1985
2005
|
uploadBlob(putUrl: string, bytes: Uint8Array): Promise<void>;
|
|
1986
2006
|
/** §10: client-asserted history records (§16 wire shape), deduped by dedupKey server-side. */
|
|
1987
|
-
postHistoryRecords(records:
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2007
|
+
postHistoryRecords(records: WalletApiHistoryRecord[]): Promise<void>;
|
|
2008
|
+
/**
|
|
2009
|
+
* §10/§16: read the client-written history log back — newest-first keyset
|
|
2010
|
+
* pages (`{records, more, cursor, syncEpoch}`). The READ side of the §10 log:
|
|
2011
|
+
* a reloaded thin wallet rebuilds its history from here (the in-memory cache
|
|
2012
|
+
* is process-lifetime; the durable log lives on the server). `memo` /
|
|
2013
|
+
* `counterpartyNametag` come back as the verbatim S6 `enc1.` envelopes — the
|
|
2014
|
+
* owner decrypts them with its own field key on display (§8.3).
|
|
2015
|
+
*/
|
|
2016
|
+
listHistory(options?: {
|
|
2017
|
+
before?: string;
|
|
2018
|
+
limit?: number;
|
|
2019
|
+
}): Promise<{
|
|
2020
|
+
records: WalletApiHistoryRecord[];
|
|
2021
|
+
more: boolean;
|
|
2022
|
+
cursor: string | null;
|
|
2023
|
+
syncEpoch: bigint;
|
|
2024
|
+
}>;
|
|
2002
2025
|
/** Network name — scopes the persisted payment-request cursor (mirrors the mailbox cursor). */
|
|
2003
2026
|
readonly network?: string;
|
|
2004
2027
|
/** `POST /v1/payment-requests` (§16) — `memo` MUST already be an S6 envelope (§8.3). */
|
|
@@ -2598,10 +2621,39 @@ declare class PaymentsModule {
|
|
|
2598
2621
|
*/
|
|
2599
2622
|
addToHistory(entry: Omit<TransactionHistoryEntry, 'id' | 'dedupKey'>): Promise<void>;
|
|
2600
2623
|
/**
|
|
2601
|
-
* Load history
|
|
2602
|
-
*
|
|
2624
|
+
* Load history into the in-memory cache.
|
|
2625
|
+
*
|
|
2626
|
+
* In the wallet-api composition (the `walletApi` client is present) the
|
|
2627
|
+
* durable §10 history log lives on the SERVER — the thin storage provider
|
|
2628
|
+
* keeps none — so the cache is rebuilt from `walletApi.listHistory()`. The
|
|
2629
|
+
* twin of the #521 inventory reload bug: `_historyCache` is process-lifetime,
|
|
2630
|
+
* so a reload (tab refresh) must re-pull it or render an empty history.
|
|
2631
|
+
* Compositions WITHOUT `walletApi` keep the legacy local path below.
|
|
2603
2632
|
*/
|
|
2604
2633
|
loadHistory(): Promise<void>;
|
|
2634
|
+
/**
|
|
2635
|
+
* Rebuild `_historyCache` from the server's §10 history log (the wallet-api
|
|
2636
|
+
* composition). Pages newest-first via the keyset cursor until `more:false`
|
|
2637
|
+
* or the page cap; dedups by `dedupKey` (a hydrate-then-receive in the same
|
|
2638
|
+
* session must not double-list). The S6 `memo` / `counterpartyNametag`
|
|
2639
|
+
* envelopes are decrypted with the owner's own field key on the way in.
|
|
2640
|
+
*
|
|
2641
|
+
* Best-effort, like the §10 history POST: history is untrusted DISPLAY data,
|
|
2642
|
+
* so a backend outage during hydration must NEVER fail `load()` (the money
|
|
2643
|
+
* path) — the in-session cache is left intact and the pull retries next load.
|
|
2644
|
+
*/
|
|
2645
|
+
private hydrateHistoryFromServer;
|
|
2646
|
+
/**
|
|
2647
|
+
* Map one §16 history wire record onto the display
|
|
2648
|
+
* {@link TransactionHistoryEntry}. `counterpartyNametag` lands on the role-
|
|
2649
|
+
* appropriate field (sender for RECEIVED, recipient otherwise); the S6 memo +
|
|
2650
|
+
* nametag envelopes decrypt under THIS wallet's field key (self-scoped at
|
|
2651
|
+
* rest — §8.3), surfaced as absent if they don't decrypt rather than as
|
|
2652
|
+
* ciphertext (same rule as mailbox/payment-request memos).
|
|
2653
|
+
*/
|
|
2654
|
+
private historyEntryFromWire;
|
|
2655
|
+
/** S6 field decrypt that surfaces an undecryptable envelope as absent (§8.3). */
|
|
2656
|
+
private tryDecryptField;
|
|
2605
2657
|
/**
|
|
2606
2658
|
* Import history entries from remote TXF data into local store.
|
|
2607
2659
|
* Delegates to the local TokenStorageProvider's importHistoryEntries() for
|
package/dist/core/index.js
CHANGED
|
@@ -9550,6 +9550,7 @@ function computeHistoryDedupKey(type, tokenId, transferId) {
|
|
|
9550
9550
|
return `${type}_${crypto.randomUUID()}`;
|
|
9551
9551
|
}
|
|
9552
9552
|
var MAX_SYNCED_HISTORY_ENTRIES = 5e3;
|
|
9553
|
+
var MAX_HISTORY_HYDRATION_PAGES = 100;
|
|
9553
9554
|
var SEND_ENGINE_OP_TIMEOUT_MS = 6e4;
|
|
9554
9555
|
var DELIVERY_POLL_INTERVAL_MS = 3e4;
|
|
9555
9556
|
function enrichWithRegistry(info) {
|
|
@@ -11839,10 +11840,20 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
11839
11840
|
this.deps.emitEvent("history:updated", historyEntry);
|
|
11840
11841
|
}
|
|
11841
11842
|
/**
|
|
11842
|
-
* Load history
|
|
11843
|
-
*
|
|
11843
|
+
* Load history into the in-memory cache.
|
|
11844
|
+
*
|
|
11845
|
+
* In the wallet-api composition (the `walletApi` client is present) the
|
|
11846
|
+
* durable §10 history log lives on the SERVER — the thin storage provider
|
|
11847
|
+
* keeps none — so the cache is rebuilt from `walletApi.listHistory()`. The
|
|
11848
|
+
* twin of the #521 inventory reload bug: `_historyCache` is process-lifetime,
|
|
11849
|
+
* so a reload (tab refresh) must re-pull it or render an empty history.
|
|
11850
|
+
* Compositions WITHOUT `walletApi` keep the legacy local path below.
|
|
11844
11851
|
*/
|
|
11845
11852
|
async loadHistory() {
|
|
11853
|
+
if (this.deps.walletApi?.listHistory) {
|
|
11854
|
+
await this.hydrateHistoryFromServer(this.deps.walletApi);
|
|
11855
|
+
return;
|
|
11856
|
+
}
|
|
11846
11857
|
const provider = this.getLocalTokenStorageProvider();
|
|
11847
11858
|
if (provider?.getHistoryEntries) {
|
|
11848
11859
|
this._historyCache = await provider.getHistoryEntries();
|
|
@@ -11874,6 +11885,75 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
11874
11885
|
}
|
|
11875
11886
|
}
|
|
11876
11887
|
}
|
|
11888
|
+
/**
|
|
11889
|
+
* Rebuild `_historyCache` from the server's §10 history log (the wallet-api
|
|
11890
|
+
* composition). Pages newest-first via the keyset cursor until `more:false`
|
|
11891
|
+
* or the page cap; dedups by `dedupKey` (a hydrate-then-receive in the same
|
|
11892
|
+
* session must not double-list). The S6 `memo` / `counterpartyNametag`
|
|
11893
|
+
* envelopes are decrypted with the owner's own field key on the way in.
|
|
11894
|
+
*
|
|
11895
|
+
* Best-effort, like the §10 history POST: history is untrusted DISPLAY data,
|
|
11896
|
+
* so a backend outage during hydration must NEVER fail `load()` (the money
|
|
11897
|
+
* path) — the in-session cache is left intact and the pull retries next load.
|
|
11898
|
+
*/
|
|
11899
|
+
async hydrateHistoryFromServer(api) {
|
|
11900
|
+
const byDedupKey = /* @__PURE__ */ new Map();
|
|
11901
|
+
try {
|
|
11902
|
+
let before;
|
|
11903
|
+
for (let page = 0; page < MAX_HISTORY_HYDRATION_PAGES; page++) {
|
|
11904
|
+
const result = await api.listHistory(before !== void 0 ? { before } : {});
|
|
11905
|
+
for (const wire of result.records) {
|
|
11906
|
+
if (!byDedupKey.has(wire.dedupKey)) {
|
|
11907
|
+
byDedupKey.set(wire.dedupKey, this.historyEntryFromWire(wire));
|
|
11908
|
+
}
|
|
11909
|
+
}
|
|
11910
|
+
if (!result.more || result.cursor === null) break;
|
|
11911
|
+
before = result.cursor;
|
|
11912
|
+
}
|
|
11913
|
+
} catch (err) {
|
|
11914
|
+
logger.warn("Payments", "history hydration from server failed (kept in-memory; retries next load):", err);
|
|
11915
|
+
return;
|
|
11916
|
+
}
|
|
11917
|
+
this._historyCache = [...byDedupKey.values()];
|
|
11918
|
+
}
|
|
11919
|
+
/**
|
|
11920
|
+
* Map one §16 history wire record onto the display
|
|
11921
|
+
* {@link TransactionHistoryEntry}. `counterpartyNametag` lands on the role-
|
|
11922
|
+
* appropriate field (sender for RECEIVED, recipient otherwise); the S6 memo +
|
|
11923
|
+
* nametag envelopes decrypt under THIS wallet's field key (self-scoped at
|
|
11924
|
+
* rest — §8.3), surfaced as absent if they don't decrypt rather than as
|
|
11925
|
+
* ciphertext (same rule as mailbox/payment-request memos).
|
|
11926
|
+
*/
|
|
11927
|
+
historyEntryFromWire(wire) {
|
|
11928
|
+
const asset = wire.assets[0];
|
|
11929
|
+
const coinId = asset?.coinId ?? "";
|
|
11930
|
+
const received = wire.type === "RECEIVED";
|
|
11931
|
+
const memo = this.tryDecryptField(wire.memo);
|
|
11932
|
+
const nametag = this.tryDecryptField(wire.counterpartyNametag);
|
|
11933
|
+
return {
|
|
11934
|
+
id: wire.id,
|
|
11935
|
+
dedupKey: wire.dedupKey,
|
|
11936
|
+
type: wire.type,
|
|
11937
|
+
amount: asset?.amount ?? "0",
|
|
11938
|
+
coinId,
|
|
11939
|
+
symbol: this.getCoinSymbol(coinId),
|
|
11940
|
+
timestamp: Date.parse(wire.ts),
|
|
11941
|
+
...wire.transferId !== void 0 ? { transferId: wire.transferId } : {},
|
|
11942
|
+
...wire.tokenId !== void 0 ? { tokenId: wire.tokenId } : {},
|
|
11943
|
+
...wire.counterpartyPubkey !== void 0 ? received ? { senderPubkey: wire.counterpartyPubkey } : { recipientPubkey: wire.counterpartyPubkey } : {},
|
|
11944
|
+
...nametag !== void 0 ? received ? { senderNametag: nametag } : { recipientNametag: nametag } : {},
|
|
11945
|
+
...memo !== void 0 ? { memo } : {}
|
|
11946
|
+
};
|
|
11947
|
+
}
|
|
11948
|
+
/** S6 field decrypt that surfaces an undecryptable envelope as absent (§8.3). */
|
|
11949
|
+
tryDecryptField(envelope) {
|
|
11950
|
+
if (envelope === void 0) return void 0;
|
|
11951
|
+
try {
|
|
11952
|
+
return decryptField(this.getFieldEncryptionKey(), envelope);
|
|
11953
|
+
} catch {
|
|
11954
|
+
return void 0;
|
|
11955
|
+
}
|
|
11956
|
+
}
|
|
11877
11957
|
/**
|
|
11878
11958
|
* Import history entries from remote TXF data into local store.
|
|
11879
11959
|
* Delegates to the local TokenStorageProvider's importHistoryEntries() for
|