@unicitylabs/sphere-sdk 0.4.8 → 0.5.0

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.
@@ -362,6 +362,8 @@ interface InstantSplitResult {
362
362
  * Options for instant split send operation
363
363
  */
364
364
  interface InstantSplitOptions {
365
+ /** Optional memo/message to include with the transfer */
366
+ memo?: string;
365
367
  /** Timeout for Nostr delivery in ms (default: 30000) */
366
368
  nostrTimeoutMs?: number;
367
369
  /** Timeout for burn proof wait in ms (default: 60000) */
@@ -690,7 +692,7 @@ interface TrackedAddress extends TrackedAddressEntry {
690
692
  /** Primary nametag (from nametag cache, without @ prefix) */
691
693
  readonly nametag?: string;
692
694
  }
693
- type SphereEventType = 'transfer:incoming' | 'transfer:confirmed' | 'transfer:failed' | 'payment_request:incoming' | 'payment_request:accepted' | 'payment_request:rejected' | 'payment_request:paid' | 'payment_request:response' | 'message:dm' | 'message:read' | 'message:typing' | 'composing:started' | 'message:broadcast' | 'sync:started' | 'sync:completed' | 'sync:provider' | 'sync:error' | 'connection:changed' | 'nametag:registered' | 'nametag:recovered' | 'identity:changed' | 'address:activated' | 'address:hidden' | 'address:unhidden' | 'sync:remote-update' | 'groupchat:message' | 'groupchat:joined' | 'groupchat:left' | 'groupchat:kicked' | 'groupchat:group_deleted' | 'groupchat:updated' | 'groupchat:connection';
695
+ type SphereEventType = 'transfer:incoming' | 'transfer:confirmed' | 'transfer:failed' | 'payment_request:incoming' | 'payment_request:accepted' | 'payment_request:rejected' | 'payment_request:paid' | 'payment_request:response' | 'message:dm' | 'message:read' | 'message:typing' | 'composing:started' | 'message:broadcast' | 'sync:started' | 'sync:completed' | 'sync:provider' | 'sync:error' | 'connection:changed' | 'nametag:registered' | 'nametag:recovered' | 'identity:changed' | 'address:activated' | 'address:hidden' | 'address:unhidden' | 'sync:remote-update' | 'groupchat:message' | 'groupchat:joined' | 'groupchat:left' | 'groupchat:kicked' | 'groupchat:group_deleted' | 'groupchat:updated' | 'groupchat:connection' | 'history:updated';
694
696
  interface SphereEventMap {
695
697
  'transfer:incoming': IncomingTransfer;
696
698
  'transfer:confirmed': TransferResult;
@@ -790,6 +792,7 @@ interface SphereEventMap {
790
792
  'groupchat:connection': {
791
793
  connected: boolean;
792
794
  };
795
+ 'history:updated': TransactionHistoryEntry;
793
796
  }
794
797
  type SphereEventHandler<T extends SphereEventType> = (data: SphereEventMap[T]) => void;
795
798
  /**
@@ -918,6 +921,199 @@ interface SphereStatus {
918
921
  price: ProviderStatusInfo[];
919
922
  }
920
923
 
924
+ /**
925
+ * Storage Provider Interface
926
+ * Platform-independent storage abstraction
927
+ */
928
+
929
+ /**
930
+ * Basic key-value storage provider
931
+ * All operations are async for platform flexibility
932
+ */
933
+ interface StorageProvider extends BaseProvider {
934
+ /**
935
+ * Set identity for scoped storage
936
+ */
937
+ setIdentity(identity: FullIdentity): void;
938
+ /**
939
+ * Get value by key
940
+ */
941
+ get(key: string): Promise<string | null>;
942
+ /**
943
+ * Set value by key
944
+ */
945
+ set(key: string, value: string): Promise<void>;
946
+ /**
947
+ * Remove key
948
+ */
949
+ remove(key: string): Promise<void>;
950
+ /**
951
+ * Check if key exists
952
+ */
953
+ has(key: string): Promise<boolean>;
954
+ /**
955
+ * Get all keys with optional prefix filter
956
+ */
957
+ keys(prefix?: string): Promise<string[]>;
958
+ /**
959
+ * Clear all keys with optional prefix filter
960
+ */
961
+ clear(prefix?: string): Promise<void>;
962
+ /**
963
+ * Save tracked addresses (only user state: index, hidden, timestamps)
964
+ */
965
+ saveTrackedAddresses(entries: TrackedAddressEntry[]): Promise<void>;
966
+ /**
967
+ * Load tracked addresses
968
+ */
969
+ loadTrackedAddresses(): Promise<TrackedAddressEntry[]>;
970
+ }
971
+ interface HistoryRecord {
972
+ /** Composite dedup key (primary key) — e.g. "RECEIVED_v5split_abc123" */
973
+ dedupKey: string;
974
+ /** UUID for public API consumption */
975
+ id: string;
976
+ type: 'SENT' | 'RECEIVED' | 'SPLIT' | 'MINT';
977
+ amount: string;
978
+ coinId: string;
979
+ symbol: string;
980
+ timestamp: number;
981
+ transferId?: string;
982
+ /** Genesis tokenId this entry relates to (used for dedup) */
983
+ tokenId?: string;
984
+ senderPubkey?: string;
985
+ senderAddress?: string;
986
+ senderNametag?: string;
987
+ recipientPubkey?: string;
988
+ recipientAddress?: string;
989
+ recipientNametag?: string;
990
+ /** Optional memo/message attached to the transfer */
991
+ memo?: string;
992
+ }
993
+ /**
994
+ * Storage result types
995
+ */
996
+ interface SaveResult {
997
+ success: boolean;
998
+ cid?: string;
999
+ error?: string;
1000
+ timestamp: number;
1001
+ }
1002
+ interface LoadResult<T = unknown> {
1003
+ success: boolean;
1004
+ data?: T;
1005
+ error?: string;
1006
+ source: 'local' | 'remote' | 'cache';
1007
+ timestamp: number;
1008
+ }
1009
+ interface SyncResult<T = unknown> {
1010
+ success: boolean;
1011
+ merged?: T;
1012
+ added: number;
1013
+ removed: number;
1014
+ conflicts: number;
1015
+ error?: string;
1016
+ }
1017
+ /**
1018
+ * Token-specific storage provider
1019
+ * Handles token persistence with sync capabilities
1020
+ */
1021
+ interface TokenStorageProvider<TData = unknown> extends BaseProvider {
1022
+ /**
1023
+ * Set identity for storage scope
1024
+ */
1025
+ setIdentity(identity: FullIdentity): void;
1026
+ /**
1027
+ * Initialize provider (called once after identity is set)
1028
+ */
1029
+ initialize(): Promise<boolean>;
1030
+ /**
1031
+ * Shutdown provider
1032
+ */
1033
+ shutdown(): Promise<void>;
1034
+ /**
1035
+ * Save token data
1036
+ */
1037
+ save(data: TData): Promise<SaveResult>;
1038
+ /**
1039
+ * Load token data
1040
+ */
1041
+ load(identifier?: string): Promise<LoadResult<TData>>;
1042
+ /**
1043
+ * Sync local data with remote
1044
+ */
1045
+ sync(localData: TData): Promise<SyncResult<TData>>;
1046
+ /**
1047
+ * Check if data exists
1048
+ */
1049
+ exists?(identifier?: string): Promise<boolean>;
1050
+ /**
1051
+ * Clear all data
1052
+ */
1053
+ clear?(): Promise<boolean>;
1054
+ /**
1055
+ * Subscribe to storage events
1056
+ */
1057
+ onEvent?(callback: StorageEventCallback): () => void;
1058
+ /** Store a history entry (upsert by dedupKey) */
1059
+ addHistoryEntry?(entry: HistoryRecord): Promise<void>;
1060
+ /** Get all history entries sorted by timestamp descending */
1061
+ getHistoryEntries?(): Promise<HistoryRecord[]>;
1062
+ /** Check if a history entry exists by dedupKey */
1063
+ hasHistoryEntry?(dedupKey: string): Promise<boolean>;
1064
+ /** Clear all history entries */
1065
+ clearHistory?(): Promise<void>;
1066
+ /** Bulk import history entries (skip existing dedupKeys). Returns count of newly imported. */
1067
+ importHistoryEntries?(entries: HistoryRecord[]): Promise<number>;
1068
+ }
1069
+ type StorageEventType = 'storage:saving' | 'storage:saved' | 'storage:loading' | 'storage:loaded' | 'storage:error' | 'storage:remote-updated' | 'sync:started' | 'sync:completed' | 'sync:conflict' | 'sync:error';
1070
+ interface StorageEvent {
1071
+ type: StorageEventType;
1072
+ timestamp: number;
1073
+ data?: unknown;
1074
+ error?: string;
1075
+ }
1076
+ type StorageEventCallback = (event: StorageEvent) => void;
1077
+ interface TxfStorageDataBase {
1078
+ _meta: TxfMeta;
1079
+ _tombstones?: TxfTombstone[];
1080
+ _outbox?: TxfOutboxEntry[];
1081
+ _sent?: TxfSentEntry[];
1082
+ _invalid?: TxfInvalidEntry[];
1083
+ [key: `_${string}`]: unknown;
1084
+ }
1085
+ interface TxfMeta {
1086
+ version: number;
1087
+ address: string;
1088
+ ipnsName?: string;
1089
+ formatVersion: string;
1090
+ updatedAt: number;
1091
+ }
1092
+ interface TxfTombstone {
1093
+ tokenId: string;
1094
+ stateHash: string;
1095
+ timestamp: number;
1096
+ }
1097
+ interface TxfOutboxEntry {
1098
+ id: string;
1099
+ status: string;
1100
+ tokenId: string;
1101
+ recipient: string;
1102
+ createdAt: number;
1103
+ data: unknown;
1104
+ }
1105
+ interface TxfSentEntry {
1106
+ tokenId: string;
1107
+ recipient: string;
1108
+ txHash: string;
1109
+ sentAt: number;
1110
+ }
1111
+ interface TxfInvalidEntry {
1112
+ tokenId: string;
1113
+ reason: string;
1114
+ detectedAt: number;
1115
+ }
1116
+
921
1117
  /**
922
1118
  * Transport Provider Interface
923
1119
  * Platform-independent P2P messaging abstraction
@@ -1460,167 +1656,6 @@ interface MintNametagResult {
1460
1656
  error?: string;
1461
1657
  }
1462
1658
 
1463
- /**
1464
- * Storage Provider Interface
1465
- * Platform-independent storage abstraction
1466
- */
1467
-
1468
- /**
1469
- * Basic key-value storage provider
1470
- * All operations are async for platform flexibility
1471
- */
1472
- interface StorageProvider extends BaseProvider {
1473
- /**
1474
- * Set identity for scoped storage
1475
- */
1476
- setIdentity(identity: FullIdentity): void;
1477
- /**
1478
- * Get value by key
1479
- */
1480
- get(key: string): Promise<string | null>;
1481
- /**
1482
- * Set value by key
1483
- */
1484
- set(key: string, value: string): Promise<void>;
1485
- /**
1486
- * Remove key
1487
- */
1488
- remove(key: string): Promise<void>;
1489
- /**
1490
- * Check if key exists
1491
- */
1492
- has(key: string): Promise<boolean>;
1493
- /**
1494
- * Get all keys with optional prefix filter
1495
- */
1496
- keys(prefix?: string): Promise<string[]>;
1497
- /**
1498
- * Clear all keys with optional prefix filter
1499
- */
1500
- clear(prefix?: string): Promise<void>;
1501
- /**
1502
- * Save tracked addresses (only user state: index, hidden, timestamps)
1503
- */
1504
- saveTrackedAddresses(entries: TrackedAddressEntry[]): Promise<void>;
1505
- /**
1506
- * Load tracked addresses
1507
- */
1508
- loadTrackedAddresses(): Promise<TrackedAddressEntry[]>;
1509
- }
1510
- /**
1511
- * Storage result types
1512
- */
1513
- interface SaveResult {
1514
- success: boolean;
1515
- cid?: string;
1516
- error?: string;
1517
- timestamp: number;
1518
- }
1519
- interface LoadResult<T = unknown> {
1520
- success: boolean;
1521
- data?: T;
1522
- error?: string;
1523
- source: 'local' | 'remote' | 'cache';
1524
- timestamp: number;
1525
- }
1526
- interface SyncResult<T = unknown> {
1527
- success: boolean;
1528
- merged?: T;
1529
- added: number;
1530
- removed: number;
1531
- conflicts: number;
1532
- error?: string;
1533
- }
1534
- /**
1535
- * Token-specific storage provider
1536
- * Handles token persistence with sync capabilities
1537
- */
1538
- interface TokenStorageProvider<TData = unknown> extends BaseProvider {
1539
- /**
1540
- * Set identity for storage scope
1541
- */
1542
- setIdentity(identity: FullIdentity): void;
1543
- /**
1544
- * Initialize provider (called once after identity is set)
1545
- */
1546
- initialize(): Promise<boolean>;
1547
- /**
1548
- * Shutdown provider
1549
- */
1550
- shutdown(): Promise<void>;
1551
- /**
1552
- * Save token data
1553
- */
1554
- save(data: TData): Promise<SaveResult>;
1555
- /**
1556
- * Load token data
1557
- */
1558
- load(identifier?: string): Promise<LoadResult<TData>>;
1559
- /**
1560
- * Sync local data with remote
1561
- */
1562
- sync(localData: TData): Promise<SyncResult<TData>>;
1563
- /**
1564
- * Check if data exists
1565
- */
1566
- exists?(identifier?: string): Promise<boolean>;
1567
- /**
1568
- * Clear all data
1569
- */
1570
- clear?(): Promise<boolean>;
1571
- /**
1572
- * Subscribe to storage events
1573
- */
1574
- onEvent?(callback: StorageEventCallback): () => void;
1575
- }
1576
- type StorageEventType = 'storage:saving' | 'storage:saved' | 'storage:loading' | 'storage:loaded' | 'storage:error' | 'storage:remote-updated' | 'sync:started' | 'sync:completed' | 'sync:conflict' | 'sync:error';
1577
- interface StorageEvent {
1578
- type: StorageEventType;
1579
- timestamp: number;
1580
- data?: unknown;
1581
- error?: string;
1582
- }
1583
- type StorageEventCallback = (event: StorageEvent) => void;
1584
- interface TxfStorageDataBase {
1585
- _meta: TxfMeta;
1586
- _tombstones?: TxfTombstone[];
1587
- _outbox?: TxfOutboxEntry[];
1588
- _sent?: TxfSentEntry[];
1589
- _invalid?: TxfInvalidEntry[];
1590
- [key: `_${string}`]: unknown;
1591
- }
1592
- interface TxfMeta {
1593
- version: number;
1594
- address: string;
1595
- ipnsName?: string;
1596
- formatVersion: string;
1597
- updatedAt: number;
1598
- }
1599
- interface TxfTombstone {
1600
- tokenId: string;
1601
- stateHash: string;
1602
- timestamp: number;
1603
- }
1604
- interface TxfOutboxEntry {
1605
- id: string;
1606
- status: string;
1607
- tokenId: string;
1608
- recipient: string;
1609
- createdAt: number;
1610
- data: unknown;
1611
- }
1612
- interface TxfSentEntry {
1613
- tokenId: string;
1614
- recipient: string;
1615
- txHash: string;
1616
- sentAt: number;
1617
- }
1618
- interface TxfInvalidEntry {
1619
- tokenId: string;
1620
- reason: string;
1621
- detectedAt: number;
1622
- }
1623
-
1624
1659
  /**
1625
1660
  * Oracle Provider Interface
1626
1661
  * Platform-independent Unicity oracle abstraction
@@ -1809,30 +1844,10 @@ interface PriceProvider {
1809
1844
  }
1810
1845
 
1811
1846
  /**
1812
- * Payments Module
1813
- * Platform-independent token operations with full wallet repository functionality
1814
- *
1815
- * Includes:
1816
- * - Token CRUD operations
1817
- * - Tombstones for sync
1818
- * - Archived tokens (spent history)
1819
- * - Forked tokens (alternative histories)
1820
- * - Transaction history
1821
- * - Nametag storage
1847
+ * Public history entry type — re-exported from the shared storage layer.
1848
+ * Single source of truth: {@link HistoryRecord} in `storage/storage-provider.ts`.
1822
1849
  */
1823
-
1824
- interface TransactionHistoryEntry {
1825
- id: string;
1826
- type: 'SENT' | 'RECEIVED' | 'SPLIT' | 'MINT';
1827
- amount: string;
1828
- coinId: string;
1829
- symbol: string;
1830
- timestamp: number;
1831
- recipientNametag?: string;
1832
- senderPubkey?: string;
1833
- /** TransferResult.id that created this entry (links history to transfer operation) */
1834
- transferId?: string;
1835
- }
1850
+ type TransactionHistoryEntry = HistoryRecord;
1836
1851
  interface ReceiveOptions {
1837
1852
  /** Wait for all unconfirmed tokens to be finalized (default: false).
1838
1853
  * When false, calls resolveUnconfirmed() once to submit pending commitments.
@@ -1899,7 +1914,7 @@ declare class PaymentsModule {
1899
1914
  private tombstones;
1900
1915
  private archivedTokens;
1901
1916
  private forkedTokens;
1902
- private transactionHistory;
1917
+ private _historyCache;
1903
1918
  private nametags;
1904
1919
  private paymentRequests;
1905
1920
  private paymentRequestHandlers;
@@ -2254,10 +2269,9 @@ declare class PaymentsModule {
2254
2269
  * the old state is archived and replaced with the incoming one.
2255
2270
  *
2256
2271
  * @param token - The token to add.
2257
- * @param skipHistory - When `true`, do not create a `RECEIVED` transaction history entry (default `false`).
2258
2272
  * @returns `true` if the token was added, `false` if rejected as duplicate or tombstoned.
2259
2273
  */
2260
- addToken(token: Token, skipHistory?: boolean): Promise<boolean>;
2274
+ addToken(token: Token): Promise<boolean>;
2261
2275
  /**
2262
2276
  * Update an existing token or add it if not found.
2263
2277
  *
@@ -2275,10 +2289,8 @@ declare class PaymentsModule {
2275
2289
  * entry is created unless `skipHistory` is `true`.
2276
2290
  *
2277
2291
  * @param tokenId - Local UUID of the token to remove.
2278
- * @param recipientNametag - Optional nametag of the transfer recipient (for history).
2279
- * @param skipHistory - When `true`, skip creating a transaction history entry (default `false`).
2280
2292
  */
2281
- removeToken(tokenId: string, recipientNametag?: string, skipHistory?: boolean): Promise<void>;
2293
+ removeToken(tokenId: string): Promise<void>;
2282
2294
  /**
2283
2295
  * Get all tombstone entries.
2284
2296
  *
@@ -2389,14 +2401,30 @@ declare class PaymentsModule {
2389
2401
  * @returns Array of {@link TransactionHistoryEntry} objects in descending timestamp order.
2390
2402
  */
2391
2403
  getHistory(): TransactionHistoryEntry[];
2404
+ /**
2405
+ * Best-effort resolve sender's DIRECT address and nametag from their transport pubkey.
2406
+ * Returns empty object if transport doesn't support resolution or lookup fails.
2407
+ */
2408
+ private resolveSenderInfo;
2392
2409
  /**
2393
2410
  * Append an entry to the transaction history.
2394
2411
  *
2395
- * A unique `id` is auto-generated. The entry is immediately persisted to storage.
2412
+ * A unique `id` and `dedupKey` are auto-generated. The entry is persisted to
2413
+ * the local token storage provider's `history` store (IndexedDB / file).
2414
+ * Duplicate entries with the same `dedupKey` are silently ignored (upsert).
2396
2415
  *
2397
- * @param entry - History entry fields (without `id`).
2416
+ * @param entry - History entry fields (without `id` and `dedupKey`).
2417
+ */
2418
+ addToHistory(entry: Omit<TransactionHistoryEntry, 'id' | 'dedupKey'>): Promise<void>;
2419
+ /**
2420
+ * Load history from the local token storage provider into the in-memory cache.
2421
+ * Also performs one-time migration from legacy KV storage.
2422
+ */
2423
+ loadHistory(): Promise<void>;
2424
+ /**
2425
+ * Get the first local token storage provider (for history operations).
2398
2426
  */
2399
- addToHistory(entry: Omit<TransactionHistoryEntry, 'id'>): Promise<void>;
2427
+ private getLocalTokenStorageProvider;
2400
2428
  /**
2401
2429
  * Set the nametag data for the current identity.
2402
2430
  *