@unicitylabs/sphere-sdk 0.4.6 → 0.4.8

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.js CHANGED
@@ -14,6 +14,228 @@ var __export = (target, all) => {
14
14
  __defProp(target, name, { get: all[name], enumerable: true });
15
15
  };
16
16
 
17
+ // constants.ts
18
+ function getAddressStorageKey(addressId, key) {
19
+ return `${addressId}_${key}`;
20
+ }
21
+ function getAddressId(directAddress) {
22
+ let hash = directAddress;
23
+ if (hash.startsWith("DIRECT://")) {
24
+ hash = hash.slice(9);
25
+ } else if (hash.startsWith("DIRECT:")) {
26
+ hash = hash.slice(7);
27
+ }
28
+ const first = hash.slice(0, 6).toLowerCase();
29
+ const last = hash.slice(-6).toLowerCase();
30
+ return `DIRECT_${first}_${last}`;
31
+ }
32
+ var STORAGE_PREFIX, DEFAULT_ENCRYPTION_KEY, STORAGE_KEYS_GLOBAL, STORAGE_KEYS_ADDRESS, STORAGE_KEYS, DEFAULT_NOSTR_RELAYS, NOSTR_EVENT_KINDS, NIP29_KINDS, DEFAULT_AGGREGATOR_URL, DEV_AGGREGATOR_URL, TEST_AGGREGATOR_URL, DEFAULT_AGGREGATOR_TIMEOUT, DEFAULT_IPFS_GATEWAYS, DEFAULT_IPFS_BOOTSTRAP_PEERS, DEFAULT_BASE_PATH, DEFAULT_DERIVATION_PATH, COIN_TYPES, DEFAULT_ELECTRUM_URL, TEST_ELECTRUM_URL, TOKEN_REGISTRY_URL, TOKEN_REGISTRY_REFRESH_INTERVAL, TEST_NOSTR_RELAYS, DEFAULT_GROUP_RELAYS, NETWORKS, TIMEOUTS, LIMITS;
33
+ var init_constants = __esm({
34
+ "constants.ts"() {
35
+ "use strict";
36
+ STORAGE_PREFIX = "sphere_";
37
+ DEFAULT_ENCRYPTION_KEY = "sphere-default-key";
38
+ STORAGE_KEYS_GLOBAL = {
39
+ /** Encrypted BIP39 mnemonic */
40
+ MNEMONIC: "mnemonic",
41
+ /** Encrypted master private key */
42
+ MASTER_KEY: "master_key",
43
+ /** BIP32 chain code */
44
+ CHAIN_CODE: "chain_code",
45
+ /** HD derivation path (full path like m/44'/0'/0'/0/0) */
46
+ DERIVATION_PATH: "derivation_path",
47
+ /** Base derivation path (like m/44'/0'/0' without chain/index) */
48
+ BASE_PATH: "base_path",
49
+ /** Derivation mode: bip32, wif_hmac, legacy_hmac */
50
+ DERIVATION_MODE: "derivation_mode",
51
+ /** Wallet source: mnemonic, file, unknown */
52
+ WALLET_SOURCE: "wallet_source",
53
+ /** Wallet existence flag */
54
+ WALLET_EXISTS: "wallet_exists",
55
+ /** Current active address index */
56
+ CURRENT_ADDRESS_INDEX: "current_address_index",
57
+ /** Nametag cache per address (separate from tracked addresses registry) */
58
+ ADDRESS_NAMETAGS: "address_nametags",
59
+ /** Active addresses registry (JSON: TrackedAddressesStorage) */
60
+ TRACKED_ADDRESSES: "tracked_addresses",
61
+ /** Last processed Nostr wallet event timestamp (unix seconds), keyed per pubkey */
62
+ LAST_WALLET_EVENT_TS: "last_wallet_event_ts",
63
+ /** Group chat: last used relay URL (stale data detection) — global, same relay for all addresses */
64
+ GROUP_CHAT_RELAY_URL: "group_chat_relay_url",
65
+ /** Cached token registry JSON (fetched from remote) */
66
+ TOKEN_REGISTRY_CACHE: "token_registry_cache",
67
+ /** Timestamp of last token registry cache update (ms since epoch) */
68
+ TOKEN_REGISTRY_CACHE_TS: "token_registry_cache_ts",
69
+ /** Cached price data JSON (from CoinGecko or other provider) */
70
+ PRICE_CACHE: "price_cache",
71
+ /** Timestamp of last price cache update (ms since epoch) */
72
+ PRICE_CACHE_TS: "price_cache_ts"
73
+ };
74
+ STORAGE_KEYS_ADDRESS = {
75
+ /** Pending transfers for this address */
76
+ PENDING_TRANSFERS: "pending_transfers",
77
+ /** Transfer outbox for this address */
78
+ OUTBOX: "outbox",
79
+ /** Conversations for this address */
80
+ CONVERSATIONS: "conversations",
81
+ /** Messages for this address */
82
+ MESSAGES: "messages",
83
+ /** Transaction history for this address */
84
+ TRANSACTION_HISTORY: "transaction_history",
85
+ /** Pending V5 finalization tokens (unconfirmed instant split tokens) */
86
+ PENDING_V5_TOKENS: "pending_v5_tokens",
87
+ /** Group chat: joined groups for this address */
88
+ GROUP_CHAT_GROUPS: "group_chat_groups",
89
+ /** Group chat: messages for this address */
90
+ GROUP_CHAT_MESSAGES: "group_chat_messages",
91
+ /** Group chat: members for this address */
92
+ GROUP_CHAT_MEMBERS: "group_chat_members",
93
+ /** Group chat: processed event IDs for deduplication */
94
+ GROUP_CHAT_PROCESSED_EVENTS: "group_chat_processed_events"
95
+ };
96
+ STORAGE_KEYS = {
97
+ ...STORAGE_KEYS_GLOBAL,
98
+ ...STORAGE_KEYS_ADDRESS
99
+ };
100
+ DEFAULT_NOSTR_RELAYS = [
101
+ "wss://relay.unicity.network",
102
+ "wss://relay.damus.io",
103
+ "wss://nos.lol",
104
+ "wss://relay.nostr.band"
105
+ ];
106
+ NOSTR_EVENT_KINDS = {
107
+ /** NIP-04 encrypted direct message */
108
+ DIRECT_MESSAGE: 4,
109
+ /** Token transfer (Unicity custom - 31113) */
110
+ TOKEN_TRANSFER: 31113,
111
+ /** Payment request (Unicity custom - 31115) */
112
+ PAYMENT_REQUEST: 31115,
113
+ /** Payment request response (Unicity custom - 31116) */
114
+ PAYMENT_REQUEST_RESPONSE: 31116,
115
+ /** Nametag binding (NIP-78 app-specific data) */
116
+ NAMETAG_BINDING: 30078,
117
+ /** Public broadcast */
118
+ BROADCAST: 1
119
+ };
120
+ NIP29_KINDS = {
121
+ /** Chat message sent to group */
122
+ CHAT_MESSAGE: 9,
123
+ /** Thread root message */
124
+ THREAD_ROOT: 11,
125
+ /** Thread reply message */
126
+ THREAD_REPLY: 12,
127
+ /** User join request */
128
+ JOIN_REQUEST: 9021,
129
+ /** User leave request */
130
+ LEAVE_REQUEST: 9022,
131
+ /** Admin: add/update user */
132
+ PUT_USER: 9e3,
133
+ /** Admin: remove user */
134
+ REMOVE_USER: 9001,
135
+ /** Admin: edit group metadata */
136
+ EDIT_METADATA: 9002,
137
+ /** Admin: delete event */
138
+ DELETE_EVENT: 9005,
139
+ /** Admin: create group */
140
+ CREATE_GROUP: 9007,
141
+ /** Admin: delete group */
142
+ DELETE_GROUP: 9008,
143
+ /** Admin: create invite code */
144
+ CREATE_INVITE: 9009,
145
+ /** Relay-signed group metadata */
146
+ GROUP_METADATA: 39e3,
147
+ /** Relay-signed group admins */
148
+ GROUP_ADMINS: 39001,
149
+ /** Relay-signed group members */
150
+ GROUP_MEMBERS: 39002,
151
+ /** Relay-signed group roles */
152
+ GROUP_ROLES: 39003
153
+ };
154
+ DEFAULT_AGGREGATOR_URL = "https://aggregator.unicity.network/rpc";
155
+ DEV_AGGREGATOR_URL = "https://dev-aggregator.dyndns.org/rpc";
156
+ TEST_AGGREGATOR_URL = "https://goggregator-test.unicity.network";
157
+ DEFAULT_AGGREGATOR_TIMEOUT = 3e4;
158
+ DEFAULT_IPFS_GATEWAYS = [
159
+ "https://unicity-ipfs1.dyndns.org"
160
+ ];
161
+ DEFAULT_IPFS_BOOTSTRAP_PEERS = [
162
+ "/dns4/unicity-ipfs2.dyndns.org/tcp/4001/p2p/12D3KooWLNi5NDPPHbrfJakAQqwBqymYTTwMQXQKEWuCrJNDdmfh",
163
+ "/dns4/unicity-ipfs3.dyndns.org/tcp/4001/p2p/12D3KooWQ4aujVE4ShLjdusNZBdffq3TbzrwT2DuWZY9H1Gxhwn6",
164
+ "/dns4/unicity-ipfs4.dyndns.org/tcp/4001/p2p/12D3KooWJ1ByPfUzUrpYvgxKU8NZrR8i6PU1tUgMEbQX9Hh2DEn1",
165
+ "/dns4/unicity-ipfs5.dyndns.org/tcp/4001/p2p/12D3KooWB1MdZZGHN5B8TvWXntbycfe7Cjcz7n6eZ9eykZadvmDv"
166
+ ];
167
+ DEFAULT_BASE_PATH = "m/44'/0'/0'";
168
+ DEFAULT_DERIVATION_PATH = `${DEFAULT_BASE_PATH}/0/0`;
169
+ COIN_TYPES = {
170
+ /** ALPHA token (L1 blockchain) */
171
+ ALPHA: "ALPHA",
172
+ /** Test token */
173
+ TEST: "TEST"
174
+ };
175
+ DEFAULT_ELECTRUM_URL = "wss://fulcrum.unicity.network:50004";
176
+ TEST_ELECTRUM_URL = "wss://fulcrum.unicity.network:50004";
177
+ TOKEN_REGISTRY_URL = "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json";
178
+ TOKEN_REGISTRY_REFRESH_INTERVAL = 36e5;
179
+ TEST_NOSTR_RELAYS = [
180
+ "wss://nostr-relay.testnet.unicity.network"
181
+ ];
182
+ DEFAULT_GROUP_RELAYS = [
183
+ "wss://sphere-relay.unicity.network"
184
+ ];
185
+ NETWORKS = {
186
+ mainnet: {
187
+ name: "Mainnet",
188
+ aggregatorUrl: DEFAULT_AGGREGATOR_URL,
189
+ nostrRelays: DEFAULT_NOSTR_RELAYS,
190
+ ipfsGateways: DEFAULT_IPFS_GATEWAYS,
191
+ electrumUrl: DEFAULT_ELECTRUM_URL,
192
+ groupRelays: DEFAULT_GROUP_RELAYS,
193
+ tokenRegistryUrl: TOKEN_REGISTRY_URL
194
+ },
195
+ testnet: {
196
+ name: "Testnet",
197
+ aggregatorUrl: TEST_AGGREGATOR_URL,
198
+ nostrRelays: TEST_NOSTR_RELAYS,
199
+ ipfsGateways: DEFAULT_IPFS_GATEWAYS,
200
+ electrumUrl: TEST_ELECTRUM_URL,
201
+ groupRelays: DEFAULT_GROUP_RELAYS,
202
+ tokenRegistryUrl: TOKEN_REGISTRY_URL
203
+ },
204
+ dev: {
205
+ name: "Development",
206
+ aggregatorUrl: DEV_AGGREGATOR_URL,
207
+ nostrRelays: TEST_NOSTR_RELAYS,
208
+ ipfsGateways: DEFAULT_IPFS_GATEWAYS,
209
+ electrumUrl: TEST_ELECTRUM_URL,
210
+ groupRelays: DEFAULT_GROUP_RELAYS,
211
+ tokenRegistryUrl: TOKEN_REGISTRY_URL
212
+ }
213
+ };
214
+ TIMEOUTS = {
215
+ /** WebSocket connection timeout */
216
+ WEBSOCKET_CONNECT: 1e4,
217
+ /** Nostr relay reconnect delay */
218
+ NOSTR_RECONNECT_DELAY: 3e3,
219
+ /** Max reconnect attempts */
220
+ MAX_RECONNECT_ATTEMPTS: 5,
221
+ /** Proof polling interval */
222
+ PROOF_POLL_INTERVAL: 1e3,
223
+ /** Sync interval */
224
+ SYNC_INTERVAL: 6e4
225
+ };
226
+ LIMITS = {
227
+ /** Min nametag length */
228
+ NAMETAG_MIN_LENGTH: 3,
229
+ /** Max nametag length */
230
+ NAMETAG_MAX_LENGTH: 20,
231
+ /** Max memo length */
232
+ MEMO_MAX_LENGTH: 500,
233
+ /** Max message length */
234
+ MESSAGE_MAX_LENGTH: 1e4
235
+ };
236
+ }
237
+ });
238
+
17
239
  // core/bech32.ts
18
240
  function convertBits(data, fromBits, toBits, pad) {
19
241
  let acc = 0;
@@ -430,7 +652,8 @@ var init_network = __esm({
430
652
  "l1/network.ts"() {
431
653
  "use strict";
432
654
  init_addressToScriptHash();
433
- DEFAULT_ENDPOINT = "wss://fulcrum.unicity.network:50004";
655
+ init_constants();
656
+ DEFAULT_ENDPOINT = DEFAULT_ELECTRUM_URL;
434
657
  ws = null;
435
658
  isConnected = false;
436
659
  isConnecting = false;
@@ -450,6 +673,9 @@ var init_network = __esm({
450
673
  }
451
674
  });
452
675
 
676
+ // modules/payments/L1PaymentsModule.ts
677
+ init_constants();
678
+
453
679
  // l1/index.ts
454
680
  var l1_exports = {};
455
681
  __export(l1_exports, {
@@ -1635,7 +1861,7 @@ var L1PaymentsModule = class {
1635
1861
  _transport;
1636
1862
  constructor(config) {
1637
1863
  this._config = {
1638
- electrumUrl: config?.electrumUrl ?? "wss://fulcrum.unicity.network:50004",
1864
+ electrumUrl: config?.electrumUrl ?? DEFAULT_ELECTRUM_URL,
1639
1865
  network: config?.network ?? "mainnet",
1640
1866
  defaultFeeRate: config?.defaultFeeRate ?? 10,
1641
1867
  enableVesting: config?.enableVesting ?? true
@@ -2475,221 +2701,8 @@ var NametagMinter = class {
2475
2701
  }
2476
2702
  };
2477
2703
 
2478
- // constants.ts
2479
- var STORAGE_PREFIX = "sphere_";
2480
- var DEFAULT_ENCRYPTION_KEY = "sphere-default-key";
2481
- var STORAGE_KEYS_GLOBAL = {
2482
- /** Encrypted BIP39 mnemonic */
2483
- MNEMONIC: "mnemonic",
2484
- /** Encrypted master private key */
2485
- MASTER_KEY: "master_key",
2486
- /** BIP32 chain code */
2487
- CHAIN_CODE: "chain_code",
2488
- /** HD derivation path (full path like m/44'/0'/0'/0/0) */
2489
- DERIVATION_PATH: "derivation_path",
2490
- /** Base derivation path (like m/44'/0'/0' without chain/index) */
2491
- BASE_PATH: "base_path",
2492
- /** Derivation mode: bip32, wif_hmac, legacy_hmac */
2493
- DERIVATION_MODE: "derivation_mode",
2494
- /** Wallet source: mnemonic, file, unknown */
2495
- WALLET_SOURCE: "wallet_source",
2496
- /** Wallet existence flag */
2497
- WALLET_EXISTS: "wallet_exists",
2498
- /** Current active address index */
2499
- CURRENT_ADDRESS_INDEX: "current_address_index",
2500
- /** Nametag cache per address (separate from tracked addresses registry) */
2501
- ADDRESS_NAMETAGS: "address_nametags",
2502
- /** Active addresses registry (JSON: TrackedAddressesStorage) */
2503
- TRACKED_ADDRESSES: "tracked_addresses",
2504
- /** Last processed Nostr wallet event timestamp (unix seconds), keyed per pubkey */
2505
- LAST_WALLET_EVENT_TS: "last_wallet_event_ts",
2506
- /** Group chat: last used relay URL (stale data detection) — global, same relay for all addresses */
2507
- GROUP_CHAT_RELAY_URL: "group_chat_relay_url",
2508
- /** Cached token registry JSON (fetched from remote) */
2509
- TOKEN_REGISTRY_CACHE: "token_registry_cache",
2510
- /** Timestamp of last token registry cache update (ms since epoch) */
2511
- TOKEN_REGISTRY_CACHE_TS: "token_registry_cache_ts",
2512
- /** Cached price data JSON (from CoinGecko or other provider) */
2513
- PRICE_CACHE: "price_cache",
2514
- /** Timestamp of last price cache update (ms since epoch) */
2515
- PRICE_CACHE_TS: "price_cache_ts"
2516
- };
2517
- var STORAGE_KEYS_ADDRESS = {
2518
- /** Pending transfers for this address */
2519
- PENDING_TRANSFERS: "pending_transfers",
2520
- /** Transfer outbox for this address */
2521
- OUTBOX: "outbox",
2522
- /** Conversations for this address */
2523
- CONVERSATIONS: "conversations",
2524
- /** Messages for this address */
2525
- MESSAGES: "messages",
2526
- /** Transaction history for this address */
2527
- TRANSACTION_HISTORY: "transaction_history",
2528
- /** Pending V5 finalization tokens (unconfirmed instant split tokens) */
2529
- PENDING_V5_TOKENS: "pending_v5_tokens",
2530
- /** Group chat: joined groups for this address */
2531
- GROUP_CHAT_GROUPS: "group_chat_groups",
2532
- /** Group chat: messages for this address */
2533
- GROUP_CHAT_MESSAGES: "group_chat_messages",
2534
- /** Group chat: members for this address */
2535
- GROUP_CHAT_MEMBERS: "group_chat_members",
2536
- /** Group chat: processed event IDs for deduplication */
2537
- GROUP_CHAT_PROCESSED_EVENTS: "group_chat_processed_events"
2538
- };
2539
- var STORAGE_KEYS = {
2540
- ...STORAGE_KEYS_GLOBAL,
2541
- ...STORAGE_KEYS_ADDRESS
2542
- };
2543
- function getAddressStorageKey(addressId, key) {
2544
- return `${addressId}_${key}`;
2545
- }
2546
- function getAddressId(directAddress) {
2547
- let hash = directAddress;
2548
- if (hash.startsWith("DIRECT://")) {
2549
- hash = hash.slice(9);
2550
- } else if (hash.startsWith("DIRECT:")) {
2551
- hash = hash.slice(7);
2552
- }
2553
- const first = hash.slice(0, 6).toLowerCase();
2554
- const last = hash.slice(-6).toLowerCase();
2555
- return `DIRECT_${first}_${last}`;
2556
- }
2557
- var DEFAULT_NOSTR_RELAYS = [
2558
- "wss://relay.unicity.network",
2559
- "wss://relay.damus.io",
2560
- "wss://nos.lol",
2561
- "wss://relay.nostr.band"
2562
- ];
2563
- var NOSTR_EVENT_KINDS = {
2564
- /** NIP-04 encrypted direct message */
2565
- DIRECT_MESSAGE: 4,
2566
- /** Token transfer (Unicity custom - 31113) */
2567
- TOKEN_TRANSFER: 31113,
2568
- /** Payment request (Unicity custom - 31115) */
2569
- PAYMENT_REQUEST: 31115,
2570
- /** Payment request response (Unicity custom - 31116) */
2571
- PAYMENT_REQUEST_RESPONSE: 31116,
2572
- /** Nametag binding (NIP-78 app-specific data) */
2573
- NAMETAG_BINDING: 30078,
2574
- /** Public broadcast */
2575
- BROADCAST: 1
2576
- };
2577
- var NIP29_KINDS = {
2578
- /** Chat message sent to group */
2579
- CHAT_MESSAGE: 9,
2580
- /** Thread root message */
2581
- THREAD_ROOT: 11,
2582
- /** Thread reply message */
2583
- THREAD_REPLY: 12,
2584
- /** User join request */
2585
- JOIN_REQUEST: 9021,
2586
- /** User leave request */
2587
- LEAVE_REQUEST: 9022,
2588
- /** Admin: add/update user */
2589
- PUT_USER: 9e3,
2590
- /** Admin: remove user */
2591
- REMOVE_USER: 9001,
2592
- /** Admin: edit group metadata */
2593
- EDIT_METADATA: 9002,
2594
- /** Admin: delete event */
2595
- DELETE_EVENT: 9005,
2596
- /** Admin: create group */
2597
- CREATE_GROUP: 9007,
2598
- /** Admin: delete group */
2599
- DELETE_GROUP: 9008,
2600
- /** Admin: create invite code */
2601
- CREATE_INVITE: 9009,
2602
- /** Relay-signed group metadata */
2603
- GROUP_METADATA: 39e3,
2604
- /** Relay-signed group admins */
2605
- GROUP_ADMINS: 39001,
2606
- /** Relay-signed group members */
2607
- GROUP_MEMBERS: 39002,
2608
- /** Relay-signed group roles */
2609
- GROUP_ROLES: 39003
2610
- };
2611
- var DEFAULT_AGGREGATOR_URL = "https://aggregator.unicity.network/rpc";
2612
- var DEV_AGGREGATOR_URL = "https://dev-aggregator.dyndns.org/rpc";
2613
- var TEST_AGGREGATOR_URL = "https://goggregator-test.unicity.network";
2614
- var DEFAULT_AGGREGATOR_TIMEOUT = 3e4;
2615
- var DEFAULT_IPFS_GATEWAYS = [
2616
- "https://unicity-ipfs1.dyndns.org"
2617
- ];
2618
- var DEFAULT_IPFS_BOOTSTRAP_PEERS = [
2619
- "/dns4/unicity-ipfs2.dyndns.org/tcp/4001/p2p/12D3KooWLNi5NDPPHbrfJakAQqwBqymYTTwMQXQKEWuCrJNDdmfh",
2620
- "/dns4/unicity-ipfs3.dyndns.org/tcp/4001/p2p/12D3KooWQ4aujVE4ShLjdusNZBdffq3TbzrwT2DuWZY9H1Gxhwn6",
2621
- "/dns4/unicity-ipfs4.dyndns.org/tcp/4001/p2p/12D3KooWJ1ByPfUzUrpYvgxKU8NZrR8i6PU1tUgMEbQX9Hh2DEn1",
2622
- "/dns4/unicity-ipfs5.dyndns.org/tcp/4001/p2p/12D3KooWB1MdZZGHN5B8TvWXntbycfe7Cjcz7n6eZ9eykZadvmDv"
2623
- ];
2624
- var DEFAULT_BASE_PATH = "m/44'/0'/0'";
2625
- var DEFAULT_DERIVATION_PATH = `${DEFAULT_BASE_PATH}/0/0`;
2626
- var COIN_TYPES = {
2627
- /** ALPHA token (L1 blockchain) */
2628
- ALPHA: "ALPHA",
2629
- /** Test token */
2630
- TEST: "TEST"
2631
- };
2632
- var DEFAULT_ELECTRUM_URL = "wss://fulcrum.alpha.unicity.network:50004";
2633
- var TEST_ELECTRUM_URL = "wss://fulcrum.alpha.testnet.unicity.network:50004";
2634
- var TOKEN_REGISTRY_URL = "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json";
2635
- var TOKEN_REGISTRY_REFRESH_INTERVAL = 36e5;
2636
- var TEST_NOSTR_RELAYS = [
2637
- "wss://nostr-relay.testnet.unicity.network"
2638
- ];
2639
- var DEFAULT_GROUP_RELAYS = [
2640
- "wss://sphere-relay.unicity.network"
2641
- ];
2642
- var NETWORKS = {
2643
- mainnet: {
2644
- name: "Mainnet",
2645
- aggregatorUrl: DEFAULT_AGGREGATOR_URL,
2646
- nostrRelays: DEFAULT_NOSTR_RELAYS,
2647
- ipfsGateways: DEFAULT_IPFS_GATEWAYS,
2648
- electrumUrl: DEFAULT_ELECTRUM_URL,
2649
- groupRelays: DEFAULT_GROUP_RELAYS,
2650
- tokenRegistryUrl: TOKEN_REGISTRY_URL
2651
- },
2652
- testnet: {
2653
- name: "Testnet",
2654
- aggregatorUrl: TEST_AGGREGATOR_URL,
2655
- nostrRelays: TEST_NOSTR_RELAYS,
2656
- ipfsGateways: DEFAULT_IPFS_GATEWAYS,
2657
- electrumUrl: TEST_ELECTRUM_URL,
2658
- groupRelays: DEFAULT_GROUP_RELAYS,
2659
- tokenRegistryUrl: TOKEN_REGISTRY_URL
2660
- },
2661
- dev: {
2662
- name: "Development",
2663
- aggregatorUrl: DEV_AGGREGATOR_URL,
2664
- nostrRelays: TEST_NOSTR_RELAYS,
2665
- ipfsGateways: DEFAULT_IPFS_GATEWAYS,
2666
- electrumUrl: TEST_ELECTRUM_URL,
2667
- groupRelays: DEFAULT_GROUP_RELAYS,
2668
- tokenRegistryUrl: TOKEN_REGISTRY_URL
2669
- }
2670
- };
2671
- var TIMEOUTS = {
2672
- /** WebSocket connection timeout */
2673
- WEBSOCKET_CONNECT: 1e4,
2674
- /** Nostr relay reconnect delay */
2675
- NOSTR_RECONNECT_DELAY: 3e3,
2676
- /** Max reconnect attempts */
2677
- MAX_RECONNECT_ATTEMPTS: 5,
2678
- /** Proof polling interval */
2679
- PROOF_POLL_INTERVAL: 1e3,
2680
- /** Sync interval */
2681
- SYNC_INTERVAL: 6e4
2682
- };
2683
- var LIMITS = {
2684
- /** Min nametag length */
2685
- NAMETAG_MIN_LENGTH: 3,
2686
- /** Max nametag length */
2687
- NAMETAG_MAX_LENGTH: 20,
2688
- /** Max memo length */
2689
- MEMO_MAX_LENGTH: 500,
2690
- /** Max message length */
2691
- MESSAGE_MAX_LENGTH: 1e4
2692
- };
2704
+ // modules/payments/PaymentsModule.ts
2705
+ init_constants();
2693
2706
 
2694
2707
  // types/txf.ts
2695
2708
  var ARCHIVED_PREFIX = "archived-";
@@ -2734,6 +2747,7 @@ function isValidTokenId(tokenId) {
2734
2747
  }
2735
2748
 
2736
2749
  // registry/TokenRegistry.ts
2750
+ init_constants();
2737
2751
  var FETCH_TIMEOUT_MS = 1e4;
2738
2752
  var TokenRegistry = class _TokenRegistry {
2739
2753
  static instance = null;
@@ -7631,6 +7645,7 @@ import { HashAlgorithm as HashAlgorithm6 } from "@unicitylabs/state-transition-s
7631
7645
  import { UnmaskedPredicate as UnmaskedPredicate6 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate";
7632
7646
 
7633
7647
  // modules/communications/CommunicationsModule.ts
7648
+ init_constants();
7634
7649
  var CommunicationsModule = class {
7635
7650
  config;
7636
7651
  deps = null;
@@ -8105,6 +8120,7 @@ function createCommunicationsModule(config) {
8105
8120
  }
8106
8121
 
8107
8122
  // modules/groupchat/GroupChatModule.ts
8123
+ init_constants();
8108
8124
  import {
8109
8125
  NostrClient,
8110
8126
  NostrKeyManager,
@@ -8360,10 +8376,12 @@ var GroupChatModule = class {
8360
8376
  if (!this.client) return;
8361
8377
  const groupIds = Array.from(this.groups.keys());
8362
8378
  if (groupIds.length === 0) return;
8379
+ const latestTimestamp = this.getLatestMessageTimestamp(groupIds);
8363
8380
  this.trackSubscription(
8364
8381
  createNip29Filter({
8365
8382
  kinds: [NIP29_KINDS.CHAT_MESSAGE, NIP29_KINDS.THREAD_ROOT, NIP29_KINDS.THREAD_REPLY],
8366
- "#h": groupIds
8383
+ "#h": groupIds,
8384
+ ...latestTimestamp ? { since: latestTimestamp } : {}
8367
8385
  }),
8368
8386
  { onEvent: (event) => this.handleGroupEvent(event) }
8369
8387
  );
@@ -8384,10 +8402,12 @@ var GroupChatModule = class {
8384
8402
  }
8385
8403
  subscribeToGroup(groupId) {
8386
8404
  if (!this.client) return;
8405
+ const latestTimestamp = this.getLatestMessageTimestamp([groupId]);
8387
8406
  this.trackSubscription(
8388
8407
  createNip29Filter({
8389
8408
  kinds: [NIP29_KINDS.CHAT_MESSAGE, NIP29_KINDS.THREAD_ROOT, NIP29_KINDS.THREAD_REPLY],
8390
- "#h": [groupId]
8409
+ "#h": [groupId],
8410
+ ...latestTimestamp ? { since: latestTimestamp } : {}
8391
8411
  }),
8392
8412
  { onEvent: (event) => this.handleGroupEvent(event) }
8393
8413
  );
@@ -9079,6 +9099,23 @@ var GroupChatModule = class {
9079
9099
  getMyPublicKey() {
9080
9100
  return this.keyManager?.getPublicKeyHex() || null;
9081
9101
  }
9102
+ /**
9103
+ * Returns the latest message timestamp (in Nostr seconds) across the given groups,
9104
+ * or 0 if no messages exist. Used to set `since` on subscriptions so the relay
9105
+ * only sends events we don't already have.
9106
+ */
9107
+ getLatestMessageTimestamp(groupIds) {
9108
+ let latest = 0;
9109
+ for (const gid of groupIds) {
9110
+ const msgs = this.messages.get(gid);
9111
+ if (!msgs) continue;
9112
+ for (const m of msgs) {
9113
+ const ts = Math.floor(m.timestamp / 1e3);
9114
+ if (ts > latest) latest = ts;
9115
+ }
9116
+ }
9117
+ return latest;
9118
+ }
9082
9119
  // ===========================================================================
9083
9120
  // Private — Relay Admin
9084
9121
  // ===========================================================================
@@ -11933,6 +11970,9 @@ function createMarketModule(config) {
11933
11970
  return new MarketModule(config);
11934
11971
  }
11935
11972
 
11973
+ // core/Sphere.ts
11974
+ init_constants();
11975
+
11936
11976
  // core/encryption.ts
11937
11977
  import CryptoJS6 from "crypto-js";
11938
11978
  function encryptSimple(plaintext, password) {
@@ -12036,6 +12076,72 @@ async function scanAddressesImpl(deriveAddress, options = {}) {
12036
12076
  };
12037
12077
  }
12038
12078
 
12079
+ // core/discover.ts
12080
+ async function discoverAddressesImpl(deriveTransportPubkey, batchResolve, options = {}) {
12081
+ const maxAddresses = options.maxAddresses ?? 50;
12082
+ const gapLimit = options.gapLimit ?? 20;
12083
+ const batchSize = options.batchSize ?? 20;
12084
+ const { onProgress, signal } = options;
12085
+ const discovered = /* @__PURE__ */ new Map();
12086
+ let consecutiveEmpty = 0;
12087
+ let scanned = 0;
12088
+ const totalBatches = Math.ceil(maxAddresses / batchSize);
12089
+ for (let batchStart = 0; batchStart < maxAddresses; batchStart += batchSize) {
12090
+ if (signal?.aborted) break;
12091
+ if (consecutiveEmpty >= gapLimit) break;
12092
+ const batchEnd = Math.min(batchStart + batchSize, maxAddresses);
12093
+ const batchIndices = [];
12094
+ const pubkeyToIndex = /* @__PURE__ */ new Map();
12095
+ const pubkeyToInfo = /* @__PURE__ */ new Map();
12096
+ for (let i = batchStart; i < batchEnd; i++) {
12097
+ const info = deriveTransportPubkey(i);
12098
+ batchIndices.push(i);
12099
+ pubkeyToIndex.set(info.transportPubkey, i);
12100
+ pubkeyToInfo.set(info.transportPubkey, info);
12101
+ }
12102
+ const batchPubkeys = Array.from(pubkeyToIndex.keys());
12103
+ onProgress?.({
12104
+ currentBatch: Math.floor(batchStart / batchSize) + 1,
12105
+ totalBatches,
12106
+ discoveredCount: discovered.size,
12107
+ currentGap: consecutiveEmpty,
12108
+ phase: "transport"
12109
+ });
12110
+ const peerInfos = await batchResolve(batchPubkeys);
12111
+ const foundInBatch = /* @__PURE__ */ new Set();
12112
+ for (const peer of peerInfos) {
12113
+ const index = pubkeyToIndex.get(peer.transportPubkey);
12114
+ if (index !== void 0) {
12115
+ foundInBatch.add(index);
12116
+ const derived = pubkeyToInfo.get(peer.transportPubkey);
12117
+ discovered.set(index, {
12118
+ index,
12119
+ l1Address: peer.l1Address || derived.l1Address,
12120
+ directAddress: peer.directAddress || derived.directAddress,
12121
+ chainPubkey: peer.chainPubkey || derived.chainPubkey,
12122
+ nametag: peer.nametag,
12123
+ l1Balance: 0,
12124
+ source: "transport"
12125
+ });
12126
+ }
12127
+ }
12128
+ for (const idx of batchIndices) {
12129
+ scanned++;
12130
+ if (foundInBatch.has(idx)) {
12131
+ consecutiveEmpty = 0;
12132
+ } else {
12133
+ consecutiveEmpty++;
12134
+ if (consecutiveEmpty >= gapLimit) break;
12135
+ }
12136
+ }
12137
+ }
12138
+ return {
12139
+ addresses: Array.from(discovered.values()).sort((a, b) => a.index - b.index),
12140
+ scannedCount: scanned,
12141
+ aborted: signal?.aborted ?? false
12142
+ };
12143
+ }
12144
+
12039
12145
  // core/Sphere.ts
12040
12146
  init_network();
12041
12147
 
@@ -12858,7 +12964,9 @@ var Sphere = class _Sphere {
12858
12964
  price: options.price,
12859
12965
  groupChat,
12860
12966
  market,
12861
- password: options.password
12967
+ password: options.password,
12968
+ discoverAddresses: options.discoverAddresses,
12969
+ onProgress: options.onProgress
12862
12970
  });
12863
12971
  return { sphere: sphere2, created: false };
12864
12972
  }
@@ -12886,7 +12994,9 @@ var Sphere = class _Sphere {
12886
12994
  price: options.price,
12887
12995
  groupChat,
12888
12996
  market,
12889
- password: options.password
12997
+ password: options.password,
12998
+ discoverAddresses: options.discoverAddresses,
12999
+ onProgress: options.onProgress
12890
13000
  });
12891
13001
  return { sphere, created: true, generatedMnemonic };
12892
13002
  }
@@ -12947,6 +13057,7 @@ var Sphere = class _Sphere {
12947
13057
  if (await _Sphere.exists(options.storage)) {
12948
13058
  throw new Error("Wallet already exists. Use Sphere.load() or Sphere.clear() first.");
12949
13059
  }
13060
+ const progress = options.onProgress;
12950
13061
  if (!options.storage.isConnected()) {
12951
13062
  await options.storage.connect();
12952
13063
  }
@@ -12964,20 +13075,39 @@ var Sphere = class _Sphere {
12964
13075
  marketConfig
12965
13076
  );
12966
13077
  sphere._password = options.password ?? null;
13078
+ progress?.({ step: "storing_keys", message: "Storing wallet keys..." });
12967
13079
  await sphere.storeMnemonic(options.mnemonic, options.derivationPath);
12968
13080
  await sphere.initializeIdentityFromMnemonic(options.mnemonic, options.derivationPath);
13081
+ progress?.({ step: "initializing", message: "Initializing wallet..." });
12969
13082
  await sphere.initializeProviders();
12970
13083
  await sphere.initializeModules();
13084
+ progress?.({ step: "finalizing", message: "Finalizing wallet..." });
12971
13085
  await sphere.finalizeWalletCreation();
12972
13086
  sphere._initialized = true;
12973
13087
  _Sphere.instance = sphere;
12974
13088
  await sphere.ensureAddressTracked(0);
12975
13089
  if (options.nametag) {
13090
+ progress?.({ step: "registering_nametag", message: "Registering nametag..." });
12976
13091
  await sphere.registerNametag(options.nametag);
12977
13092
  } else {
13093
+ progress?.({ step: "recovering_nametag", message: "Recovering nametag..." });
12978
13094
  await sphere.recoverNametagFromTransport();
13095
+ progress?.({ step: "syncing_identity", message: "Publishing identity..." });
12979
13096
  await sphere.syncIdentityWithTransport();
12980
13097
  }
13098
+ if (options.discoverAddresses !== false && sphere._transport.discoverAddresses) {
13099
+ progress?.({ step: "discovering_addresses", message: "Discovering addresses..." });
13100
+ try {
13101
+ const discoverOpts = typeof options.discoverAddresses === "object" ? { ...options.discoverAddresses, autoTrack: options.discoverAddresses.autoTrack ?? true } : { autoTrack: true };
13102
+ const result = await sphere.discoverAddresses(discoverOpts);
13103
+ if (result.addresses.length > 0) {
13104
+ console.log(`[Sphere.create] Address discovery: found ${result.addresses.length} address(es)`);
13105
+ }
13106
+ } catch (err) {
13107
+ console.warn("[Sphere.create] Address discovery failed (non-fatal):", err);
13108
+ }
13109
+ }
13110
+ progress?.({ step: "complete", message: "Wallet created" });
12981
13111
  return sphere;
12982
13112
  }
12983
13113
  /**
@@ -12987,6 +13117,7 @@ var Sphere = class _Sphere {
12987
13117
  if (!await _Sphere.exists(options.storage)) {
12988
13118
  throw new Error("No wallet found. Use Sphere.create() to create a new wallet.");
12989
13119
  }
13120
+ const progress = options.onProgress;
12990
13121
  _Sphere.configureTokenRegistry(options.storage, options.network);
12991
13122
  const groupChatConfig = _Sphere.resolveGroupChatConfig(options.groupChat, options.network);
12992
13123
  const marketConfig = _Sphere.resolveMarketConfig(options.market);
@@ -13004,13 +13135,17 @@ var Sphere = class _Sphere {
13004
13135
  if (!options.storage.isConnected()) {
13005
13136
  await options.storage.connect();
13006
13137
  }
13138
+ progress?.({ step: "storing_keys", message: "Loading wallet keys..." });
13007
13139
  await sphere.loadIdentityFromStorage();
13140
+ progress?.({ step: "initializing", message: "Initializing wallet..." });
13008
13141
  await sphere.initializeProviders();
13009
13142
  await sphere.initializeModules();
13143
+ progress?.({ step: "syncing_identity", message: "Publishing identity..." });
13010
13144
  await sphere.syncIdentityWithTransport();
13011
13145
  sphere._initialized = true;
13012
13146
  _Sphere.instance = sphere;
13013
13147
  if (sphere._identity?.nametag && !sphere._payments.hasNametag()) {
13148
+ progress?.({ step: "registering_nametag", message: "Restoring nametag token..." });
13014
13149
  console.log(`[Sphere] Nametag @${sphere._identity.nametag} has no token, attempting to mint...`);
13015
13150
  try {
13016
13151
  const result = await sphere.mintNametag(sphere._identity.nametag);
@@ -13023,6 +13158,19 @@ var Sphere = class _Sphere {
13023
13158
  console.warn(`[Sphere] Nametag token mint failed:`, err);
13024
13159
  }
13025
13160
  }
13161
+ if (options.discoverAddresses !== false && sphere._transport.discoverAddresses && sphere._masterKey) {
13162
+ progress?.({ step: "discovering_addresses", message: "Discovering addresses..." });
13163
+ try {
13164
+ const discoverOpts = typeof options.discoverAddresses === "object" ? { ...options.discoverAddresses, autoTrack: options.discoverAddresses.autoTrack ?? true } : { autoTrack: true };
13165
+ const result = await sphere.discoverAddresses(discoverOpts);
13166
+ if (result.addresses.length > 0) {
13167
+ console.log(`[Sphere.load] Address discovery: found ${result.addresses.length} address(es)`);
13168
+ }
13169
+ } catch (err) {
13170
+ console.warn("[Sphere.load] Address discovery failed (non-fatal):", err);
13171
+ }
13172
+ }
13173
+ progress?.({ step: "complete", message: "Wallet loaded" });
13026
13174
  return sphere;
13027
13175
  }
13028
13176
  /**
@@ -13032,9 +13180,11 @@ var Sphere = class _Sphere {
13032
13180
  if (!options.mnemonic && !options.masterKey) {
13033
13181
  throw new Error("Either mnemonic or masterKey is required");
13034
13182
  }
13183
+ const progress = options.onProgress;
13035
13184
  console.log("[Sphere.import] Starting import...");
13036
13185
  const needsClear = _Sphere.instance !== null || await _Sphere.exists(options.storage);
13037
13186
  if (needsClear) {
13187
+ progress?.({ step: "clearing", message: "Clearing previous wallet data..." });
13038
13188
  console.log("[Sphere.import] Clearing existing wallet data...");
13039
13189
  await _Sphere.clear({ storage: options.storage, tokenStorage: options.tokenStorage });
13040
13190
  console.log("[Sphere.import] Clear done");
@@ -13059,6 +13209,7 @@ var Sphere = class _Sphere {
13059
13209
  marketConfig
13060
13210
  );
13061
13211
  sphere._password = options.password ?? null;
13212
+ progress?.({ step: "storing_keys", message: "Storing wallet keys..." });
13062
13213
  if (options.mnemonic) {
13063
13214
  if (!_Sphere.validateMnemonic(options.mnemonic)) {
13064
13215
  throw new Error("Invalid mnemonic");
@@ -13083,17 +13234,21 @@ var Sphere = class _Sphere {
13083
13234
  options.derivationPath
13084
13235
  );
13085
13236
  }
13237
+ progress?.({ step: "initializing", message: "Initializing wallet..." });
13086
13238
  console.log("[Sphere.import] Initializing providers...");
13087
13239
  await sphere.initializeProviders();
13088
13240
  console.log("[Sphere.import] Providers initialized. Initializing modules...");
13089
13241
  await sphere.initializeModules();
13090
13242
  console.log("[Sphere.import] Modules initialized");
13091
13243
  if (!options.nametag) {
13244
+ progress?.({ step: "recovering_nametag", message: "Recovering nametag..." });
13092
13245
  console.log("[Sphere.import] Recovering nametag from transport...");
13093
13246
  await sphere.recoverNametagFromTransport();
13094
13247
  console.log("[Sphere.import] Nametag recovery done");
13248
+ progress?.({ step: "syncing_identity", message: "Publishing identity..." });
13095
13249
  await sphere.syncIdentityWithTransport();
13096
13250
  }
13251
+ progress?.({ step: "finalizing", message: "Finalizing wallet..." });
13097
13252
  console.log("[Sphere.import] Finalizing wallet creation...");
13098
13253
  await sphere.finalizeWalletCreation();
13099
13254
  sphere._initialized = true;
@@ -13101,10 +13256,12 @@ var Sphere = class _Sphere {
13101
13256
  console.log("[Sphere.import] Tracking address 0...");
13102
13257
  await sphere.ensureAddressTracked(0);
13103
13258
  if (options.nametag) {
13259
+ progress?.({ step: "registering_nametag", message: "Registering nametag..." });
13104
13260
  console.log("[Sphere.import] Registering nametag...");
13105
13261
  await sphere.registerNametag(options.nametag);
13106
13262
  }
13107
13263
  if (sphere._tokenStorageProviders.size > 0) {
13264
+ progress?.({ step: "syncing_tokens", message: "Syncing tokens..." });
13108
13265
  try {
13109
13266
  const syncResult = await sphere._payments.sync();
13110
13267
  console.log(`[Sphere.import] Auto-sync: +${syncResult.added} -${syncResult.removed}`);
@@ -13112,6 +13269,19 @@ var Sphere = class _Sphere {
13112
13269
  console.warn("[Sphere.import] Auto-sync failed (non-fatal):", err);
13113
13270
  }
13114
13271
  }
13272
+ if (options.discoverAddresses !== false && sphere._transport.discoverAddresses) {
13273
+ progress?.({ step: "discovering_addresses", message: "Discovering addresses..." });
13274
+ try {
13275
+ const discoverOpts = typeof options.discoverAddresses === "object" ? { ...options.discoverAddresses, autoTrack: options.discoverAddresses.autoTrack ?? true } : { autoTrack: true };
13276
+ const result = await sphere.discoverAddresses(discoverOpts);
13277
+ if (result.addresses.length > 0) {
13278
+ console.log(`[Sphere.import] Address discovery: found ${result.addresses.length} address(es)`);
13279
+ }
13280
+ } catch (err) {
13281
+ console.warn("[Sphere.import] Address discovery failed (non-fatal):", err);
13282
+ }
13283
+ }
13284
+ progress?.({ step: "complete", message: "Import complete" });
13115
13285
  console.log("[Sphere.import] Import complete");
13116
13286
  return sphere;
13117
13287
  }
@@ -13525,55 +13695,44 @@ var Sphere = class _Sphere {
13525
13695
  * ```
13526
13696
  */
13527
13697
  static async importFromJSON(options) {
13698
+ const { jsonContent, password, ...baseOptions } = options;
13528
13699
  try {
13529
- const data = JSON.parse(options.jsonContent);
13700
+ const data = JSON.parse(jsonContent);
13530
13701
  if (data.version !== "1.0" || data.type !== "sphere-wallet") {
13531
13702
  return { success: false, error: "Invalid wallet format" };
13532
13703
  }
13533
13704
  let mnemonic = data.mnemonic;
13534
13705
  let masterKey = data.wallet.masterPrivateKey;
13535
- if (data.encrypted && options.password) {
13706
+ if (data.encrypted && password) {
13536
13707
  if (mnemonic) {
13537
- const decrypted = decryptSimple(mnemonic, options.password);
13708
+ const decrypted = decryptSimple(mnemonic, password);
13538
13709
  if (!decrypted) {
13539
13710
  return { success: false, error: "Failed to decrypt mnemonic - wrong password?" };
13540
13711
  }
13541
13712
  mnemonic = decrypted;
13542
13713
  }
13543
13714
  if (masterKey) {
13544
- const decrypted = decryptSimple(masterKey, options.password);
13715
+ const decrypted = decryptSimple(masterKey, password);
13545
13716
  if (!decrypted) {
13546
13717
  return { success: false, error: "Failed to decrypt master key - wrong password?" };
13547
13718
  }
13548
13719
  masterKey = decrypted;
13549
13720
  }
13550
- } else if (data.encrypted && !options.password) {
13721
+ } else if (data.encrypted && !password) {
13551
13722
  return { success: false, error: "Password required for encrypted wallet" };
13552
13723
  }
13553
13724
  const basePath = data.wallet.descriptorPath ? `m/${data.wallet.descriptorPath}` : DEFAULT_BASE_PATH;
13554
13725
  if (mnemonic) {
13555
- await _Sphere.import({
13556
- mnemonic,
13557
- basePath,
13558
- storage: options.storage,
13559
- transport: options.transport,
13560
- oracle: options.oracle,
13561
- tokenStorage: options.tokenStorage,
13562
- l1: options.l1
13563
- });
13726
+ await _Sphere.import({ ...baseOptions, mnemonic, basePath });
13564
13727
  return { success: true, mnemonic };
13565
13728
  }
13566
13729
  if (masterKey) {
13567
13730
  await _Sphere.import({
13731
+ ...baseOptions,
13568
13732
  masterKey,
13569
13733
  chainCode: data.wallet.chainCode,
13570
13734
  basePath,
13571
- derivationMode: data.derivationMode || (data.wallet.isBIP32 ? "bip32" : "wif_hmac"),
13572
- storage: options.storage,
13573
- transport: options.transport,
13574
- oracle: options.oracle,
13575
- tokenStorage: options.tokenStorage,
13576
- l1: options.l1
13735
+ derivationMode: data.derivationMode || (data.wallet.isBIP32 ? "bip32" : "wif_hmac")
13577
13736
  });
13578
13737
  return { success: true };
13579
13738
  }
@@ -13616,7 +13775,7 @@ var Sphere = class _Sphere {
13616
13775
  * ```
13617
13776
  */
13618
13777
  static async importFromLegacyFile(options) {
13619
- const { fileContent, fileName, password, onDecryptProgress } = options;
13778
+ const { fileContent, fileName, password, onDecryptProgress, ...baseOptions } = options;
13620
13779
  const fileType = _Sphere.detectLegacyFileType(fileName, fileContent);
13621
13780
  if (fileType === "unknown") {
13622
13781
  return { success: false, error: "Unknown file format" };
@@ -13626,15 +13785,7 @@ var Sphere = class _Sphere {
13626
13785
  if (!_Sphere.validateMnemonic(mnemonic)) {
13627
13786
  return { success: false, error: "Invalid mnemonic phrase" };
13628
13787
  }
13629
- const sphere = await _Sphere.import({
13630
- mnemonic,
13631
- storage: options.storage,
13632
- transport: options.transport,
13633
- oracle: options.oracle,
13634
- tokenStorage: options.tokenStorage,
13635
- nametag: options.nametag,
13636
- l1: options.l1
13637
- });
13788
+ const sphere = await _Sphere.import({ ...baseOptions, mnemonic });
13638
13789
  return { success: true, sphere, mnemonic };
13639
13790
  }
13640
13791
  if (fileType === "dat") {
@@ -13654,16 +13805,11 @@ var Sphere = class _Sphere {
13654
13805
  const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;
13655
13806
  const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;
13656
13807
  const sphere = await _Sphere.import({
13808
+ ...baseOptions,
13657
13809
  masterKey,
13658
13810
  chainCode,
13659
13811
  basePath,
13660
- derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac"),
13661
- storage: options.storage,
13662
- transport: options.transport,
13663
- oracle: options.oracle,
13664
- tokenStorage: options.tokenStorage,
13665
- nametag: options.nametag,
13666
- l1: options.l1
13812
+ derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
13667
13813
  });
13668
13814
  return { success: true, sphere };
13669
13815
  }
@@ -13686,16 +13832,11 @@ var Sphere = class _Sphere {
13686
13832
  const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;
13687
13833
  const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;
13688
13834
  const sphere = await _Sphere.import({
13835
+ ...baseOptions,
13689
13836
  masterKey,
13690
13837
  chainCode,
13691
13838
  basePath,
13692
- derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac"),
13693
- storage: options.storage,
13694
- transport: options.transport,
13695
- oracle: options.oracle,
13696
- tokenStorage: options.tokenStorage,
13697
- nametag: options.nametag,
13698
- l1: options.l1
13839
+ derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
13699
13840
  });
13700
13841
  return { success: true, sphere };
13701
13842
  }
@@ -13709,13 +13850,9 @@ var Sphere = class _Sphere {
13709
13850
  }
13710
13851
  if (parsed.type === "sphere-wallet") {
13711
13852
  const result = await _Sphere.importFromJSON({
13853
+ ...baseOptions,
13712
13854
  jsonContent: content,
13713
- password,
13714
- storage: options.storage,
13715
- transport: options.transport,
13716
- oracle: options.oracle,
13717
- tokenStorage: options.tokenStorage,
13718
- l1: options.l1
13855
+ password
13719
13856
  });
13720
13857
  if (result.success) {
13721
13858
  const sphere2 = _Sphere.getInstance();
@@ -13757,29 +13894,15 @@ var Sphere = class _Sphere {
13757
13894
  const isBIP32 = derivationMode === "bip32" || !!chainCode;
13758
13895
  const basePath = descriptorPath ? `m/${descriptorPath}` : isBIP32 ? "m/84'/1'/0'" : DEFAULT_BASE_PATH;
13759
13896
  if (mnemonic) {
13760
- const sphere2 = await _Sphere.import({
13761
- mnemonic,
13762
- basePath,
13763
- storage: options.storage,
13764
- transport: options.transport,
13765
- oracle: options.oracle,
13766
- tokenStorage: options.tokenStorage,
13767
- nametag: options.nametag,
13768
- l1: options.l1
13769
- });
13897
+ const sphere2 = await _Sphere.import({ ...baseOptions, mnemonic, basePath });
13770
13898
  return { success: true, sphere: sphere2, mnemonic };
13771
13899
  }
13772
13900
  const sphere = await _Sphere.import({
13901
+ ...baseOptions,
13773
13902
  masterKey,
13774
13903
  chainCode,
13775
13904
  basePath,
13776
- derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac"),
13777
- storage: options.storage,
13778
- transport: options.transport,
13779
- oracle: options.oracle,
13780
- tokenStorage: options.tokenStorage,
13781
- nametag: options.nametag,
13782
- l1: options.l1
13905
+ derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
13783
13906
  });
13784
13907
  return { success: true, sphere };
13785
13908
  }
@@ -14027,6 +14150,23 @@ var Sphere = class _Sphere {
14027
14150
  await provider.initialize();
14028
14151
  }
14029
14152
  await this.reinitializeModulesForNewAddress();
14153
+ this.emitEvent("identity:changed", {
14154
+ l1Address: this._identity.l1Address,
14155
+ directAddress: this._identity.directAddress,
14156
+ chainPubkey: this._identity.chainPubkey,
14157
+ nametag: this._identity.nametag,
14158
+ addressIndex: index
14159
+ });
14160
+ console.log(`[Sphere] Switched to address ${index}:`, this._identity.l1Address);
14161
+ this.postSwitchSync(index, newNametag).catch((err) => {
14162
+ console.warn(`[Sphere] Post-switch sync failed for address ${index}:`, err);
14163
+ });
14164
+ }
14165
+ /**
14166
+ * Background transport sync and nametag operations after address switch.
14167
+ * Runs after switchToAddress returns so L1/L3 queries can start immediately.
14168
+ */
14169
+ async postSwitchSync(index, newNametag) {
14030
14170
  if (!newNametag) {
14031
14171
  await this.syncIdentityWithTransport();
14032
14172
  }
@@ -14049,7 +14189,7 @@ var Sphere = class _Sphere {
14049
14189
  nametag: newNametag,
14050
14190
  addressIndex: index
14051
14191
  });
14052
- } else if (this._identity.nametag && !this._payments.hasNametag()) {
14192
+ } else if (this._identity?.nametag && !this._payments.hasNametag()) {
14053
14193
  console.log(`[Sphere] Nametag @${this._identity.nametag} has no token after switch, minting...`);
14054
14194
  try {
14055
14195
  const result = await this.mintNametag(this._identity.nametag);
@@ -14062,14 +14202,6 @@ var Sphere = class _Sphere {
14062
14202
  console.warn(`[Sphere] Nametag token mint failed after switch:`, err);
14063
14203
  }
14064
14204
  }
14065
- this.emitEvent("identity:changed", {
14066
- l1Address: this._identity.l1Address,
14067
- directAddress: this._identity.directAddress,
14068
- chainPubkey: this._identity.chainPubkey,
14069
- nametag: this._identity.nametag,
14070
- addressIndex: index
14071
- });
14072
- console.log(`[Sphere] Switched to address ${index}:`, this._identity.l1Address);
14073
14205
  }
14074
14206
  /**
14075
14207
  * Re-initialize modules after address switch
@@ -14276,6 +14408,98 @@ var Sphere = class _Sphere {
14276
14408
  await this.persistTrackedAddresses();
14277
14409
  await this.persistAddressNametags();
14278
14410
  }
14411
+ /**
14412
+ * Discover previously used HD addresses.
14413
+ *
14414
+ * Primary: queries Nostr relay for identity binding events (fast, single batch query).
14415
+ * Secondary: runs L1 balance scan to find legacy addresses with no binding event.
14416
+ *
14417
+ * @example
14418
+ * ```ts
14419
+ * const result = await sphere.discoverAddresses();
14420
+ * console.log(`Found ${result.addresses.length} addresses`);
14421
+ *
14422
+ * // With auto-tracking
14423
+ * await sphere.discoverAddresses({ autoTrack: true });
14424
+ * ```
14425
+ */
14426
+ async discoverAddresses(options = {}) {
14427
+ this.ensureReady();
14428
+ if (!this._masterKey) {
14429
+ throw new Error("Address discovery requires HD master key");
14430
+ }
14431
+ if (!this._transport.discoverAddresses) {
14432
+ throw new Error("Transport provider does not support address discovery");
14433
+ }
14434
+ const includeL1Scan = options.includeL1Scan ?? true;
14435
+ const transportResult = await discoverAddressesImpl(
14436
+ (index) => {
14437
+ const addrInfo = this._deriveAddressInternal(index, false);
14438
+ return {
14439
+ transportPubkey: addrInfo.publicKey.slice(2),
14440
+ // x-only 32 bytes
14441
+ chainPubkey: addrInfo.publicKey,
14442
+ l1Address: addrInfo.address,
14443
+ directAddress: ""
14444
+ // not needed for discovery query
14445
+ };
14446
+ },
14447
+ (pubkeys) => this._transport.discoverAddresses(pubkeys),
14448
+ options
14449
+ );
14450
+ if (includeL1Scan) {
14451
+ try {
14452
+ const l1Result = await this.scanAddresses({
14453
+ maxAddresses: options.maxAddresses,
14454
+ gapLimit: options.gapLimit,
14455
+ signal: options.signal,
14456
+ onProgress: options.onProgress ? (p) => options.onProgress({
14457
+ currentBatch: 0,
14458
+ totalBatches: 0,
14459
+ discoveredCount: p.foundCount,
14460
+ currentGap: p.currentGap,
14461
+ phase: "l1"
14462
+ }) : void 0
14463
+ });
14464
+ for (const l1Addr of l1Result.addresses) {
14465
+ if (l1Addr.isChange) continue;
14466
+ const existing = transportResult.addresses.find((a) => a.index === l1Addr.index);
14467
+ if (existing) {
14468
+ existing.l1Balance = l1Addr.balance;
14469
+ existing.source = "both";
14470
+ if (!existing.nametag && l1Addr.nametag) {
14471
+ existing.nametag = l1Addr.nametag;
14472
+ }
14473
+ } else {
14474
+ const addrInfo = this._deriveAddressInternal(l1Addr.index, false);
14475
+ transportResult.addresses.push({
14476
+ index: l1Addr.index,
14477
+ l1Address: l1Addr.address,
14478
+ directAddress: "",
14479
+ chainPubkey: addrInfo.publicKey,
14480
+ nametag: l1Addr.nametag,
14481
+ l1Balance: l1Addr.balance,
14482
+ source: "l1"
14483
+ });
14484
+ }
14485
+ }
14486
+ transportResult.addresses.sort((a, b) => a.index - b.index);
14487
+ } catch (err) {
14488
+ console.warn("[Sphere] L1 scan failed during discovery (non-fatal):", err);
14489
+ }
14490
+ }
14491
+ if (options.autoTrack && transportResult.addresses.length > 0) {
14492
+ await this.trackScannedAddresses(
14493
+ transportResult.addresses.map((a) => ({
14494
+ index: a.index,
14495
+ // Preserve existing hidden state; default to false for newly discovered
14496
+ hidden: this._trackedAddresses.get(a.index)?.hidden ?? false,
14497
+ nametag: a.nametag
14498
+ }))
14499
+ );
14500
+ }
14501
+ return transportResult;
14502
+ }
14279
14503
  // ===========================================================================
14280
14504
  // Public Methods - Status
14281
14505
  // ===========================================================================
@@ -14847,6 +15071,17 @@ var Sphere = class _Sphere {
14847
15071
  return;
14848
15072
  }
14849
15073
  }
15074
+ const needsUpdate = !existing.directAddress || !existing.l1Address || !existing.chainPubkey || this._identity?.nametag && !existing.nametag;
15075
+ if (needsUpdate) {
15076
+ console.log("[Sphere] Existing binding incomplete, re-publishing with full data");
15077
+ await this._transport.publishIdentityBinding(
15078
+ this._identity.chainPubkey,
15079
+ this._identity.l1Address,
15080
+ this._identity.directAddress || "",
15081
+ this._identity?.nametag || existing.nametag || void 0
15082
+ );
15083
+ return;
15084
+ }
14850
15085
  console.log("[Sphere] Existing binding found, skipping re-publish");
14851
15086
  return;
14852
15087
  }
@@ -15335,6 +15570,7 @@ function formatAmount(amount, options = {}) {
15335
15570
  init_bech32();
15336
15571
 
15337
15572
  // core/network-health.ts
15573
+ init_constants();
15338
15574
  var DEFAULT_TIMEOUT_MS = 5e3;
15339
15575
  async function checkNetworkHealth(network = "testnet", options) {
15340
15576
  const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
@@ -15520,6 +15756,9 @@ async function runCustomCheck(name, checkFn, timeoutMs) {
15520
15756
  }
15521
15757
  }
15522
15758
 
15759
+ // index.ts
15760
+ init_constants();
15761
+
15523
15762
  // types/payment-session.ts
15524
15763
  function createPaymentSession(params) {
15525
15764
  const now = Date.now();
@@ -15591,6 +15830,9 @@ var SphereError = class extends Error {
15591
15830
  }
15592
15831
  };
15593
15832
 
15833
+ // index.ts
15834
+ init_constants();
15835
+
15594
15836
  // validation/token-validator.ts
15595
15837
  var TokenValidator = class {
15596
15838
  aggregatorClient = null;
@@ -15924,6 +16166,7 @@ import {
15924
16166
  } from "@unicitylabs/nostr-js-sdk";
15925
16167
 
15926
16168
  // price/CoinGeckoPriceProvider.ts
16169
+ init_constants();
15927
16170
  var CoinGeckoPriceProvider = class {
15928
16171
  platform = "coingecko";
15929
16172
  cache = /* @__PURE__ */ new Map();