@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.
@@ -14,6 +14,175 @@ var __export = (target, all) => {
14
14
  __defProp(target, name, { get: all[name], enumerable: true });
15
15
  };
16
16
 
17
+ // constants.ts
18
+ function getAddressId(directAddress) {
19
+ let hash = directAddress;
20
+ if (hash.startsWith("DIRECT://")) {
21
+ hash = hash.slice(9);
22
+ } else if (hash.startsWith("DIRECT:")) {
23
+ hash = hash.slice(7);
24
+ }
25
+ const first = hash.slice(0, 6).toLowerCase();
26
+ const last = hash.slice(-6).toLowerCase();
27
+ return `DIRECT_${first}_${last}`;
28
+ }
29
+ var DEFAULT_ENCRYPTION_KEY, STORAGE_KEYS_GLOBAL, STORAGE_KEYS_ADDRESS, STORAGE_KEYS, DEFAULT_NOSTR_RELAYS, NIP29_KINDS, DEFAULT_AGGREGATOR_URL, DEV_AGGREGATOR_URL, TEST_AGGREGATOR_URL, DEFAULT_IPFS_GATEWAYS, DEFAULT_BASE_PATH, DEFAULT_DERIVATION_PATH, DEFAULT_ELECTRUM_URL, TEST_ELECTRUM_URL, TOKEN_REGISTRY_URL, TOKEN_REGISTRY_REFRESH_INTERVAL, TEST_NOSTR_RELAYS, DEFAULT_GROUP_RELAYS, NETWORKS;
30
+ var init_constants = __esm({
31
+ "constants.ts"() {
32
+ "use strict";
33
+ DEFAULT_ENCRYPTION_KEY = "sphere-default-key";
34
+ STORAGE_KEYS_GLOBAL = {
35
+ /** Encrypted BIP39 mnemonic */
36
+ MNEMONIC: "mnemonic",
37
+ /** Encrypted master private key */
38
+ MASTER_KEY: "master_key",
39
+ /** BIP32 chain code */
40
+ CHAIN_CODE: "chain_code",
41
+ /** HD derivation path (full path like m/44'/0'/0'/0/0) */
42
+ DERIVATION_PATH: "derivation_path",
43
+ /** Base derivation path (like m/44'/0'/0' without chain/index) */
44
+ BASE_PATH: "base_path",
45
+ /** Derivation mode: bip32, wif_hmac, legacy_hmac */
46
+ DERIVATION_MODE: "derivation_mode",
47
+ /** Wallet source: mnemonic, file, unknown */
48
+ WALLET_SOURCE: "wallet_source",
49
+ /** Wallet existence flag */
50
+ WALLET_EXISTS: "wallet_exists",
51
+ /** Current active address index */
52
+ CURRENT_ADDRESS_INDEX: "current_address_index",
53
+ /** Nametag cache per address (separate from tracked addresses registry) */
54
+ ADDRESS_NAMETAGS: "address_nametags",
55
+ /** Active addresses registry (JSON: TrackedAddressesStorage) */
56
+ TRACKED_ADDRESSES: "tracked_addresses",
57
+ /** Last processed Nostr wallet event timestamp (unix seconds), keyed per pubkey */
58
+ LAST_WALLET_EVENT_TS: "last_wallet_event_ts",
59
+ /** Group chat: last used relay URL (stale data detection) — global, same relay for all addresses */
60
+ GROUP_CHAT_RELAY_URL: "group_chat_relay_url",
61
+ /** Cached token registry JSON (fetched from remote) */
62
+ TOKEN_REGISTRY_CACHE: "token_registry_cache",
63
+ /** Timestamp of last token registry cache update (ms since epoch) */
64
+ TOKEN_REGISTRY_CACHE_TS: "token_registry_cache_ts",
65
+ /** Cached price data JSON (from CoinGecko or other provider) */
66
+ PRICE_CACHE: "price_cache",
67
+ /** Timestamp of last price cache update (ms since epoch) */
68
+ PRICE_CACHE_TS: "price_cache_ts"
69
+ };
70
+ STORAGE_KEYS_ADDRESS = {
71
+ /** Pending transfers for this address */
72
+ PENDING_TRANSFERS: "pending_transfers",
73
+ /** Transfer outbox for this address */
74
+ OUTBOX: "outbox",
75
+ /** Conversations for this address */
76
+ CONVERSATIONS: "conversations",
77
+ /** Messages for this address */
78
+ MESSAGES: "messages",
79
+ /** Transaction history for this address */
80
+ TRANSACTION_HISTORY: "transaction_history",
81
+ /** Pending V5 finalization tokens (unconfirmed instant split tokens) */
82
+ PENDING_V5_TOKENS: "pending_v5_tokens",
83
+ /** Group chat: joined groups for this address */
84
+ GROUP_CHAT_GROUPS: "group_chat_groups",
85
+ /** Group chat: messages for this address */
86
+ GROUP_CHAT_MESSAGES: "group_chat_messages",
87
+ /** Group chat: members for this address */
88
+ GROUP_CHAT_MEMBERS: "group_chat_members",
89
+ /** Group chat: processed event IDs for deduplication */
90
+ GROUP_CHAT_PROCESSED_EVENTS: "group_chat_processed_events"
91
+ };
92
+ STORAGE_KEYS = {
93
+ ...STORAGE_KEYS_GLOBAL,
94
+ ...STORAGE_KEYS_ADDRESS
95
+ };
96
+ DEFAULT_NOSTR_RELAYS = [
97
+ "wss://relay.unicity.network",
98
+ "wss://relay.damus.io",
99
+ "wss://nos.lol",
100
+ "wss://relay.nostr.band"
101
+ ];
102
+ NIP29_KINDS = {
103
+ /** Chat message sent to group */
104
+ CHAT_MESSAGE: 9,
105
+ /** Thread root message */
106
+ THREAD_ROOT: 11,
107
+ /** Thread reply message */
108
+ THREAD_REPLY: 12,
109
+ /** User join request */
110
+ JOIN_REQUEST: 9021,
111
+ /** User leave request */
112
+ LEAVE_REQUEST: 9022,
113
+ /** Admin: add/update user */
114
+ PUT_USER: 9e3,
115
+ /** Admin: remove user */
116
+ REMOVE_USER: 9001,
117
+ /** Admin: edit group metadata */
118
+ EDIT_METADATA: 9002,
119
+ /** Admin: delete event */
120
+ DELETE_EVENT: 9005,
121
+ /** Admin: create group */
122
+ CREATE_GROUP: 9007,
123
+ /** Admin: delete group */
124
+ DELETE_GROUP: 9008,
125
+ /** Admin: create invite code */
126
+ CREATE_INVITE: 9009,
127
+ /** Relay-signed group metadata */
128
+ GROUP_METADATA: 39e3,
129
+ /** Relay-signed group admins */
130
+ GROUP_ADMINS: 39001,
131
+ /** Relay-signed group members */
132
+ GROUP_MEMBERS: 39002,
133
+ /** Relay-signed group roles */
134
+ GROUP_ROLES: 39003
135
+ };
136
+ DEFAULT_AGGREGATOR_URL = "https://aggregator.unicity.network/rpc";
137
+ DEV_AGGREGATOR_URL = "https://dev-aggregator.dyndns.org/rpc";
138
+ TEST_AGGREGATOR_URL = "https://goggregator-test.unicity.network";
139
+ DEFAULT_IPFS_GATEWAYS = [
140
+ "https://unicity-ipfs1.dyndns.org"
141
+ ];
142
+ DEFAULT_BASE_PATH = "m/44'/0'/0'";
143
+ DEFAULT_DERIVATION_PATH = `${DEFAULT_BASE_PATH}/0/0`;
144
+ DEFAULT_ELECTRUM_URL = "wss://fulcrum.unicity.network:50004";
145
+ TEST_ELECTRUM_URL = "wss://fulcrum.unicity.network:50004";
146
+ TOKEN_REGISTRY_URL = "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json";
147
+ TOKEN_REGISTRY_REFRESH_INTERVAL = 36e5;
148
+ TEST_NOSTR_RELAYS = [
149
+ "wss://nostr-relay.testnet.unicity.network"
150
+ ];
151
+ DEFAULT_GROUP_RELAYS = [
152
+ "wss://sphere-relay.unicity.network"
153
+ ];
154
+ NETWORKS = {
155
+ mainnet: {
156
+ name: "Mainnet",
157
+ aggregatorUrl: DEFAULT_AGGREGATOR_URL,
158
+ nostrRelays: DEFAULT_NOSTR_RELAYS,
159
+ ipfsGateways: DEFAULT_IPFS_GATEWAYS,
160
+ electrumUrl: DEFAULT_ELECTRUM_URL,
161
+ groupRelays: DEFAULT_GROUP_RELAYS,
162
+ tokenRegistryUrl: TOKEN_REGISTRY_URL
163
+ },
164
+ testnet: {
165
+ name: "Testnet",
166
+ aggregatorUrl: TEST_AGGREGATOR_URL,
167
+ nostrRelays: TEST_NOSTR_RELAYS,
168
+ ipfsGateways: DEFAULT_IPFS_GATEWAYS,
169
+ electrumUrl: TEST_ELECTRUM_URL,
170
+ groupRelays: DEFAULT_GROUP_RELAYS,
171
+ tokenRegistryUrl: TOKEN_REGISTRY_URL
172
+ },
173
+ dev: {
174
+ name: "Development",
175
+ aggregatorUrl: DEV_AGGREGATOR_URL,
176
+ nostrRelays: TEST_NOSTR_RELAYS,
177
+ ipfsGateways: DEFAULT_IPFS_GATEWAYS,
178
+ electrumUrl: TEST_ELECTRUM_URL,
179
+ groupRelays: DEFAULT_GROUP_RELAYS,
180
+ tokenRegistryUrl: TOKEN_REGISTRY_URL
181
+ }
182
+ };
183
+ }
184
+ });
185
+
17
186
  // core/bech32.ts
18
187
  function convertBits(data, fromBits, toBits, pad) {
19
188
  let acc = 0;
@@ -430,7 +599,8 @@ var init_network = __esm({
430
599
  "l1/network.ts"() {
431
600
  "use strict";
432
601
  init_addressToScriptHash();
433
- DEFAULT_ENDPOINT = "wss://fulcrum.unicity.network:50004";
602
+ init_constants();
603
+ DEFAULT_ENDPOINT = DEFAULT_ELECTRUM_URL;
434
604
  ws = null;
435
605
  isConnected = false;
436
606
  isConnecting = false;
@@ -450,6 +620,9 @@ var init_network = __esm({
450
620
  }
451
621
  });
452
622
 
623
+ // modules/payments/L1PaymentsModule.ts
624
+ init_constants();
625
+
453
626
  // l1/index.ts
454
627
  init_bech32();
455
628
  init_addressToScriptHash();
@@ -463,7 +636,7 @@ var ec = new elliptic.ec("secp256k1");
463
636
  var CURVE_ORDER = BigInt(
464
637
  "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
465
638
  );
466
- var DEFAULT_DERIVATION_PATH = "m/44'/0'/0'";
639
+ var DEFAULT_DERIVATION_PATH2 = "m/44'/0'/0'";
467
640
  function generateMnemonic2(strength = 128) {
468
641
  return bip39.generateMnemonic(strength);
469
642
  }
@@ -1484,7 +1657,7 @@ var L1PaymentsModule = class {
1484
1657
  _transport;
1485
1658
  constructor(config) {
1486
1659
  this._config = {
1487
- electrumUrl: config?.electrumUrl ?? "wss://fulcrum.unicity.network:50004",
1660
+ electrumUrl: config?.electrumUrl ?? DEFAULT_ELECTRUM_URL,
1488
1661
  network: config?.network ?? "mainnet",
1489
1662
  defaultFeeRate: config?.defaultFeeRate ?? 10,
1490
1663
  enableVesting: config?.enableVesting ?? true
@@ -2321,168 +2494,8 @@ var NametagMinter = class {
2321
2494
  }
2322
2495
  };
2323
2496
 
2324
- // constants.ts
2325
- var DEFAULT_ENCRYPTION_KEY = "sphere-default-key";
2326
- var STORAGE_KEYS_GLOBAL = {
2327
- /** Encrypted BIP39 mnemonic */
2328
- MNEMONIC: "mnemonic",
2329
- /** Encrypted master private key */
2330
- MASTER_KEY: "master_key",
2331
- /** BIP32 chain code */
2332
- CHAIN_CODE: "chain_code",
2333
- /** HD derivation path (full path like m/44'/0'/0'/0/0) */
2334
- DERIVATION_PATH: "derivation_path",
2335
- /** Base derivation path (like m/44'/0'/0' without chain/index) */
2336
- BASE_PATH: "base_path",
2337
- /** Derivation mode: bip32, wif_hmac, legacy_hmac */
2338
- DERIVATION_MODE: "derivation_mode",
2339
- /** Wallet source: mnemonic, file, unknown */
2340
- WALLET_SOURCE: "wallet_source",
2341
- /** Wallet existence flag */
2342
- WALLET_EXISTS: "wallet_exists",
2343
- /** Current active address index */
2344
- CURRENT_ADDRESS_INDEX: "current_address_index",
2345
- /** Nametag cache per address (separate from tracked addresses registry) */
2346
- ADDRESS_NAMETAGS: "address_nametags",
2347
- /** Active addresses registry (JSON: TrackedAddressesStorage) */
2348
- TRACKED_ADDRESSES: "tracked_addresses",
2349
- /** Last processed Nostr wallet event timestamp (unix seconds), keyed per pubkey */
2350
- LAST_WALLET_EVENT_TS: "last_wallet_event_ts",
2351
- /** Group chat: last used relay URL (stale data detection) — global, same relay for all addresses */
2352
- GROUP_CHAT_RELAY_URL: "group_chat_relay_url",
2353
- /** Cached token registry JSON (fetched from remote) */
2354
- TOKEN_REGISTRY_CACHE: "token_registry_cache",
2355
- /** Timestamp of last token registry cache update (ms since epoch) */
2356
- TOKEN_REGISTRY_CACHE_TS: "token_registry_cache_ts",
2357
- /** Cached price data JSON (from CoinGecko or other provider) */
2358
- PRICE_CACHE: "price_cache",
2359
- /** Timestamp of last price cache update (ms since epoch) */
2360
- PRICE_CACHE_TS: "price_cache_ts"
2361
- };
2362
- var STORAGE_KEYS_ADDRESS = {
2363
- /** Pending transfers for this address */
2364
- PENDING_TRANSFERS: "pending_transfers",
2365
- /** Transfer outbox for this address */
2366
- OUTBOX: "outbox",
2367
- /** Conversations for this address */
2368
- CONVERSATIONS: "conversations",
2369
- /** Messages for this address */
2370
- MESSAGES: "messages",
2371
- /** Transaction history for this address */
2372
- TRANSACTION_HISTORY: "transaction_history",
2373
- /** Pending V5 finalization tokens (unconfirmed instant split tokens) */
2374
- PENDING_V5_TOKENS: "pending_v5_tokens",
2375
- /** Group chat: joined groups for this address */
2376
- GROUP_CHAT_GROUPS: "group_chat_groups",
2377
- /** Group chat: messages for this address */
2378
- GROUP_CHAT_MESSAGES: "group_chat_messages",
2379
- /** Group chat: members for this address */
2380
- GROUP_CHAT_MEMBERS: "group_chat_members",
2381
- /** Group chat: processed event IDs for deduplication */
2382
- GROUP_CHAT_PROCESSED_EVENTS: "group_chat_processed_events"
2383
- };
2384
- var STORAGE_KEYS = {
2385
- ...STORAGE_KEYS_GLOBAL,
2386
- ...STORAGE_KEYS_ADDRESS
2387
- };
2388
- function getAddressId(directAddress) {
2389
- let hash = directAddress;
2390
- if (hash.startsWith("DIRECT://")) {
2391
- hash = hash.slice(9);
2392
- } else if (hash.startsWith("DIRECT:")) {
2393
- hash = hash.slice(7);
2394
- }
2395
- const first = hash.slice(0, 6).toLowerCase();
2396
- const last = hash.slice(-6).toLowerCase();
2397
- return `DIRECT_${first}_${last}`;
2398
- }
2399
- var DEFAULT_NOSTR_RELAYS = [
2400
- "wss://relay.unicity.network",
2401
- "wss://relay.damus.io",
2402
- "wss://nos.lol",
2403
- "wss://relay.nostr.band"
2404
- ];
2405
- var NIP29_KINDS = {
2406
- /** Chat message sent to group */
2407
- CHAT_MESSAGE: 9,
2408
- /** Thread root message */
2409
- THREAD_ROOT: 11,
2410
- /** Thread reply message */
2411
- THREAD_REPLY: 12,
2412
- /** User join request */
2413
- JOIN_REQUEST: 9021,
2414
- /** User leave request */
2415
- LEAVE_REQUEST: 9022,
2416
- /** Admin: add/update user */
2417
- PUT_USER: 9e3,
2418
- /** Admin: remove user */
2419
- REMOVE_USER: 9001,
2420
- /** Admin: edit group metadata */
2421
- EDIT_METADATA: 9002,
2422
- /** Admin: delete event */
2423
- DELETE_EVENT: 9005,
2424
- /** Admin: create group */
2425
- CREATE_GROUP: 9007,
2426
- /** Admin: delete group */
2427
- DELETE_GROUP: 9008,
2428
- /** Admin: create invite code */
2429
- CREATE_INVITE: 9009,
2430
- /** Relay-signed group metadata */
2431
- GROUP_METADATA: 39e3,
2432
- /** Relay-signed group admins */
2433
- GROUP_ADMINS: 39001,
2434
- /** Relay-signed group members */
2435
- GROUP_MEMBERS: 39002,
2436
- /** Relay-signed group roles */
2437
- GROUP_ROLES: 39003
2438
- };
2439
- var DEFAULT_AGGREGATOR_URL = "https://aggregator.unicity.network/rpc";
2440
- var DEV_AGGREGATOR_URL = "https://dev-aggregator.dyndns.org/rpc";
2441
- var TEST_AGGREGATOR_URL = "https://goggregator-test.unicity.network";
2442
- var DEFAULT_IPFS_GATEWAYS = [
2443
- "https://unicity-ipfs1.dyndns.org"
2444
- ];
2445
- var DEFAULT_BASE_PATH = "m/44'/0'/0'";
2446
- var DEFAULT_DERIVATION_PATH2 = `${DEFAULT_BASE_PATH}/0/0`;
2447
- var DEFAULT_ELECTRUM_URL = "wss://fulcrum.alpha.unicity.network:50004";
2448
- var TEST_ELECTRUM_URL = "wss://fulcrum.alpha.testnet.unicity.network:50004";
2449
- var TOKEN_REGISTRY_URL = "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json";
2450
- var TOKEN_REGISTRY_REFRESH_INTERVAL = 36e5;
2451
- var TEST_NOSTR_RELAYS = [
2452
- "wss://nostr-relay.testnet.unicity.network"
2453
- ];
2454
- var DEFAULT_GROUP_RELAYS = [
2455
- "wss://sphere-relay.unicity.network"
2456
- ];
2457
- var NETWORKS = {
2458
- mainnet: {
2459
- name: "Mainnet",
2460
- aggregatorUrl: DEFAULT_AGGREGATOR_URL,
2461
- nostrRelays: DEFAULT_NOSTR_RELAYS,
2462
- ipfsGateways: DEFAULT_IPFS_GATEWAYS,
2463
- electrumUrl: DEFAULT_ELECTRUM_URL,
2464
- groupRelays: DEFAULT_GROUP_RELAYS,
2465
- tokenRegistryUrl: TOKEN_REGISTRY_URL
2466
- },
2467
- testnet: {
2468
- name: "Testnet",
2469
- aggregatorUrl: TEST_AGGREGATOR_URL,
2470
- nostrRelays: TEST_NOSTR_RELAYS,
2471
- ipfsGateways: DEFAULT_IPFS_GATEWAYS,
2472
- electrumUrl: TEST_ELECTRUM_URL,
2473
- groupRelays: DEFAULT_GROUP_RELAYS,
2474
- tokenRegistryUrl: TOKEN_REGISTRY_URL
2475
- },
2476
- dev: {
2477
- name: "Development",
2478
- aggregatorUrl: DEV_AGGREGATOR_URL,
2479
- nostrRelays: TEST_NOSTR_RELAYS,
2480
- ipfsGateways: DEFAULT_IPFS_GATEWAYS,
2481
- electrumUrl: TEST_ELECTRUM_URL,
2482
- groupRelays: DEFAULT_GROUP_RELAYS,
2483
- tokenRegistryUrl: TOKEN_REGISTRY_URL
2484
- }
2485
- };
2497
+ // modules/payments/PaymentsModule.ts
2498
+ init_constants();
2486
2499
 
2487
2500
  // types/txf.ts
2488
2501
  var ARCHIVED_PREFIX = "archived-";
@@ -2524,6 +2537,7 @@ function parseForkedKey(key) {
2524
2537
  }
2525
2538
 
2526
2539
  // registry/TokenRegistry.ts
2540
+ init_constants();
2527
2541
  var FETCH_TIMEOUT_MS = 1e4;
2528
2542
  var TokenRegistry = class _TokenRegistry {
2529
2543
  static instance = null;
@@ -7336,6 +7350,7 @@ import { HashAlgorithm as HashAlgorithm6 } from "@unicitylabs/state-transition-s
7336
7350
  import { UnmaskedPredicate as UnmaskedPredicate6 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate";
7337
7351
 
7338
7352
  // modules/communications/CommunicationsModule.ts
7353
+ init_constants();
7339
7354
  var CommunicationsModule = class {
7340
7355
  config;
7341
7356
  deps = null;
@@ -7810,6 +7825,7 @@ function createCommunicationsModule(config) {
7810
7825
  }
7811
7826
 
7812
7827
  // modules/groupchat/GroupChatModule.ts
7828
+ init_constants();
7813
7829
  import {
7814
7830
  NostrClient,
7815
7831
  NostrKeyManager,
@@ -8065,10 +8081,12 @@ var GroupChatModule = class {
8065
8081
  if (!this.client) return;
8066
8082
  const groupIds = Array.from(this.groups.keys());
8067
8083
  if (groupIds.length === 0) return;
8084
+ const latestTimestamp = this.getLatestMessageTimestamp(groupIds);
8068
8085
  this.trackSubscription(
8069
8086
  createNip29Filter({
8070
8087
  kinds: [NIP29_KINDS.CHAT_MESSAGE, NIP29_KINDS.THREAD_ROOT, NIP29_KINDS.THREAD_REPLY],
8071
- "#h": groupIds
8088
+ "#h": groupIds,
8089
+ ...latestTimestamp ? { since: latestTimestamp } : {}
8072
8090
  }),
8073
8091
  { onEvent: (event) => this.handleGroupEvent(event) }
8074
8092
  );
@@ -8089,10 +8107,12 @@ var GroupChatModule = class {
8089
8107
  }
8090
8108
  subscribeToGroup(groupId) {
8091
8109
  if (!this.client) return;
8110
+ const latestTimestamp = this.getLatestMessageTimestamp([groupId]);
8092
8111
  this.trackSubscription(
8093
8112
  createNip29Filter({
8094
8113
  kinds: [NIP29_KINDS.CHAT_MESSAGE, NIP29_KINDS.THREAD_ROOT, NIP29_KINDS.THREAD_REPLY],
8095
- "#h": [groupId]
8114
+ "#h": [groupId],
8115
+ ...latestTimestamp ? { since: latestTimestamp } : {}
8096
8116
  }),
8097
8117
  { onEvent: (event) => this.handleGroupEvent(event) }
8098
8118
  );
@@ -8784,6 +8804,23 @@ var GroupChatModule = class {
8784
8804
  getMyPublicKey() {
8785
8805
  return this.keyManager?.getPublicKeyHex() || null;
8786
8806
  }
8807
+ /**
8808
+ * Returns the latest message timestamp (in Nostr seconds) across the given groups,
8809
+ * or 0 if no messages exist. Used to set `since` on subscriptions so the relay
8810
+ * only sends events we don't already have.
8811
+ */
8812
+ getLatestMessageTimestamp(groupIds) {
8813
+ let latest = 0;
8814
+ for (const gid of groupIds) {
8815
+ const msgs = this.messages.get(gid);
8816
+ if (!msgs) continue;
8817
+ for (const m of msgs) {
8818
+ const ts = Math.floor(m.timestamp / 1e3);
8819
+ if (ts > latest) latest = ts;
8820
+ }
8821
+ }
8822
+ return latest;
8823
+ }
8787
8824
  // ===========================================================================
8788
8825
  // Private — Relay Admin
8789
8826
  // ===========================================================================
@@ -11638,6 +11675,9 @@ function createMarketModule(config) {
11638
11675
  return new MarketModule(config);
11639
11676
  }
11640
11677
 
11678
+ // core/Sphere.ts
11679
+ init_constants();
11680
+
11641
11681
  // core/encryption.ts
11642
11682
  import CryptoJS6 from "crypto-js";
11643
11683
  var DEFAULT_ITERATIONS = 1e5;
@@ -11826,6 +11866,72 @@ async function scanAddressesImpl(deriveAddress, options = {}) {
11826
11866
  };
11827
11867
  }
11828
11868
 
11869
+ // core/discover.ts
11870
+ async function discoverAddressesImpl(deriveTransportPubkey, batchResolve, options = {}) {
11871
+ const maxAddresses = options.maxAddresses ?? 50;
11872
+ const gapLimit = options.gapLimit ?? 20;
11873
+ const batchSize = options.batchSize ?? 20;
11874
+ const { onProgress, signal } = options;
11875
+ const discovered = /* @__PURE__ */ new Map();
11876
+ let consecutiveEmpty = 0;
11877
+ let scanned = 0;
11878
+ const totalBatches = Math.ceil(maxAddresses / batchSize);
11879
+ for (let batchStart = 0; batchStart < maxAddresses; batchStart += batchSize) {
11880
+ if (signal?.aborted) break;
11881
+ if (consecutiveEmpty >= gapLimit) break;
11882
+ const batchEnd = Math.min(batchStart + batchSize, maxAddresses);
11883
+ const batchIndices = [];
11884
+ const pubkeyToIndex = /* @__PURE__ */ new Map();
11885
+ const pubkeyToInfo = /* @__PURE__ */ new Map();
11886
+ for (let i = batchStart; i < batchEnd; i++) {
11887
+ const info = deriveTransportPubkey(i);
11888
+ batchIndices.push(i);
11889
+ pubkeyToIndex.set(info.transportPubkey, i);
11890
+ pubkeyToInfo.set(info.transportPubkey, info);
11891
+ }
11892
+ const batchPubkeys = Array.from(pubkeyToIndex.keys());
11893
+ onProgress?.({
11894
+ currentBatch: Math.floor(batchStart / batchSize) + 1,
11895
+ totalBatches,
11896
+ discoveredCount: discovered.size,
11897
+ currentGap: consecutiveEmpty,
11898
+ phase: "transport"
11899
+ });
11900
+ const peerInfos = await batchResolve(batchPubkeys);
11901
+ const foundInBatch = /* @__PURE__ */ new Set();
11902
+ for (const peer of peerInfos) {
11903
+ const index = pubkeyToIndex.get(peer.transportPubkey);
11904
+ if (index !== void 0) {
11905
+ foundInBatch.add(index);
11906
+ const derived = pubkeyToInfo.get(peer.transportPubkey);
11907
+ discovered.set(index, {
11908
+ index,
11909
+ l1Address: peer.l1Address || derived.l1Address,
11910
+ directAddress: peer.directAddress || derived.directAddress,
11911
+ chainPubkey: peer.chainPubkey || derived.chainPubkey,
11912
+ nametag: peer.nametag,
11913
+ l1Balance: 0,
11914
+ source: "transport"
11915
+ });
11916
+ }
11917
+ }
11918
+ for (const idx of batchIndices) {
11919
+ scanned++;
11920
+ if (foundInBatch.has(idx)) {
11921
+ consecutiveEmpty = 0;
11922
+ } else {
11923
+ consecutiveEmpty++;
11924
+ if (consecutiveEmpty >= gapLimit) break;
11925
+ }
11926
+ }
11927
+ }
11928
+ return {
11929
+ addresses: Array.from(discovered.values()).sort((a, b) => a.index - b.index),
11930
+ scannedCount: scanned,
11931
+ aborted: signal?.aborted ?? false
11932
+ };
11933
+ }
11934
+
11829
11935
  // core/Sphere.ts
11830
11936
  init_network();
11831
11937
 
@@ -12648,7 +12754,9 @@ var Sphere = class _Sphere {
12648
12754
  price: options.price,
12649
12755
  groupChat,
12650
12756
  market,
12651
- password: options.password
12757
+ password: options.password,
12758
+ discoverAddresses: options.discoverAddresses,
12759
+ onProgress: options.onProgress
12652
12760
  });
12653
12761
  return { sphere: sphere2, created: false };
12654
12762
  }
@@ -12676,7 +12784,9 @@ var Sphere = class _Sphere {
12676
12784
  price: options.price,
12677
12785
  groupChat,
12678
12786
  market,
12679
- password: options.password
12787
+ password: options.password,
12788
+ discoverAddresses: options.discoverAddresses,
12789
+ onProgress: options.onProgress
12680
12790
  });
12681
12791
  return { sphere, created: true, generatedMnemonic };
12682
12792
  }
@@ -12737,6 +12847,7 @@ var Sphere = class _Sphere {
12737
12847
  if (await _Sphere.exists(options.storage)) {
12738
12848
  throw new Error("Wallet already exists. Use Sphere.load() or Sphere.clear() first.");
12739
12849
  }
12850
+ const progress = options.onProgress;
12740
12851
  if (!options.storage.isConnected()) {
12741
12852
  await options.storage.connect();
12742
12853
  }
@@ -12754,20 +12865,39 @@ var Sphere = class _Sphere {
12754
12865
  marketConfig
12755
12866
  );
12756
12867
  sphere._password = options.password ?? null;
12868
+ progress?.({ step: "storing_keys", message: "Storing wallet keys..." });
12757
12869
  await sphere.storeMnemonic(options.mnemonic, options.derivationPath);
12758
12870
  await sphere.initializeIdentityFromMnemonic(options.mnemonic, options.derivationPath);
12871
+ progress?.({ step: "initializing", message: "Initializing wallet..." });
12759
12872
  await sphere.initializeProviders();
12760
12873
  await sphere.initializeModules();
12874
+ progress?.({ step: "finalizing", message: "Finalizing wallet..." });
12761
12875
  await sphere.finalizeWalletCreation();
12762
12876
  sphere._initialized = true;
12763
12877
  _Sphere.instance = sphere;
12764
12878
  await sphere.ensureAddressTracked(0);
12765
12879
  if (options.nametag) {
12880
+ progress?.({ step: "registering_nametag", message: "Registering nametag..." });
12766
12881
  await sphere.registerNametag(options.nametag);
12767
12882
  } else {
12883
+ progress?.({ step: "recovering_nametag", message: "Recovering nametag..." });
12768
12884
  await sphere.recoverNametagFromTransport();
12885
+ progress?.({ step: "syncing_identity", message: "Publishing identity..." });
12769
12886
  await sphere.syncIdentityWithTransport();
12770
12887
  }
12888
+ if (options.discoverAddresses !== false && sphere._transport.discoverAddresses) {
12889
+ progress?.({ step: "discovering_addresses", message: "Discovering addresses..." });
12890
+ try {
12891
+ const discoverOpts = typeof options.discoverAddresses === "object" ? { ...options.discoverAddresses, autoTrack: options.discoverAddresses.autoTrack ?? true } : { autoTrack: true };
12892
+ const result = await sphere.discoverAddresses(discoverOpts);
12893
+ if (result.addresses.length > 0) {
12894
+ console.log(`[Sphere.create] Address discovery: found ${result.addresses.length} address(es)`);
12895
+ }
12896
+ } catch (err) {
12897
+ console.warn("[Sphere.create] Address discovery failed (non-fatal):", err);
12898
+ }
12899
+ }
12900
+ progress?.({ step: "complete", message: "Wallet created" });
12771
12901
  return sphere;
12772
12902
  }
12773
12903
  /**
@@ -12777,6 +12907,7 @@ var Sphere = class _Sphere {
12777
12907
  if (!await _Sphere.exists(options.storage)) {
12778
12908
  throw new Error("No wallet found. Use Sphere.create() to create a new wallet.");
12779
12909
  }
12910
+ const progress = options.onProgress;
12780
12911
  _Sphere.configureTokenRegistry(options.storage, options.network);
12781
12912
  const groupChatConfig = _Sphere.resolveGroupChatConfig(options.groupChat, options.network);
12782
12913
  const marketConfig = _Sphere.resolveMarketConfig(options.market);
@@ -12794,13 +12925,17 @@ var Sphere = class _Sphere {
12794
12925
  if (!options.storage.isConnected()) {
12795
12926
  await options.storage.connect();
12796
12927
  }
12928
+ progress?.({ step: "storing_keys", message: "Loading wallet keys..." });
12797
12929
  await sphere.loadIdentityFromStorage();
12930
+ progress?.({ step: "initializing", message: "Initializing wallet..." });
12798
12931
  await sphere.initializeProviders();
12799
12932
  await sphere.initializeModules();
12933
+ progress?.({ step: "syncing_identity", message: "Publishing identity..." });
12800
12934
  await sphere.syncIdentityWithTransport();
12801
12935
  sphere._initialized = true;
12802
12936
  _Sphere.instance = sphere;
12803
12937
  if (sphere._identity?.nametag && !sphere._payments.hasNametag()) {
12938
+ progress?.({ step: "registering_nametag", message: "Restoring nametag token..." });
12804
12939
  console.log(`[Sphere] Nametag @${sphere._identity.nametag} has no token, attempting to mint...`);
12805
12940
  try {
12806
12941
  const result = await sphere.mintNametag(sphere._identity.nametag);
@@ -12813,6 +12948,19 @@ var Sphere = class _Sphere {
12813
12948
  console.warn(`[Sphere] Nametag token mint failed:`, err);
12814
12949
  }
12815
12950
  }
12951
+ if (options.discoverAddresses !== false && sphere._transport.discoverAddresses && sphere._masterKey) {
12952
+ progress?.({ step: "discovering_addresses", message: "Discovering addresses..." });
12953
+ try {
12954
+ const discoverOpts = typeof options.discoverAddresses === "object" ? { ...options.discoverAddresses, autoTrack: options.discoverAddresses.autoTrack ?? true } : { autoTrack: true };
12955
+ const result = await sphere.discoverAddresses(discoverOpts);
12956
+ if (result.addresses.length > 0) {
12957
+ console.log(`[Sphere.load] Address discovery: found ${result.addresses.length} address(es)`);
12958
+ }
12959
+ } catch (err) {
12960
+ console.warn("[Sphere.load] Address discovery failed (non-fatal):", err);
12961
+ }
12962
+ }
12963
+ progress?.({ step: "complete", message: "Wallet loaded" });
12816
12964
  return sphere;
12817
12965
  }
12818
12966
  /**
@@ -12822,9 +12970,11 @@ var Sphere = class _Sphere {
12822
12970
  if (!options.mnemonic && !options.masterKey) {
12823
12971
  throw new Error("Either mnemonic or masterKey is required");
12824
12972
  }
12973
+ const progress = options.onProgress;
12825
12974
  console.log("[Sphere.import] Starting import...");
12826
12975
  const needsClear = _Sphere.instance !== null || await _Sphere.exists(options.storage);
12827
12976
  if (needsClear) {
12977
+ progress?.({ step: "clearing", message: "Clearing previous wallet data..." });
12828
12978
  console.log("[Sphere.import] Clearing existing wallet data...");
12829
12979
  await _Sphere.clear({ storage: options.storage, tokenStorage: options.tokenStorage });
12830
12980
  console.log("[Sphere.import] Clear done");
@@ -12849,6 +12999,7 @@ var Sphere = class _Sphere {
12849
12999
  marketConfig
12850
13000
  );
12851
13001
  sphere._password = options.password ?? null;
13002
+ progress?.({ step: "storing_keys", message: "Storing wallet keys..." });
12852
13003
  if (options.mnemonic) {
12853
13004
  if (!_Sphere.validateMnemonic(options.mnemonic)) {
12854
13005
  throw new Error("Invalid mnemonic");
@@ -12873,17 +13024,21 @@ var Sphere = class _Sphere {
12873
13024
  options.derivationPath
12874
13025
  );
12875
13026
  }
13027
+ progress?.({ step: "initializing", message: "Initializing wallet..." });
12876
13028
  console.log("[Sphere.import] Initializing providers...");
12877
13029
  await sphere.initializeProviders();
12878
13030
  console.log("[Sphere.import] Providers initialized. Initializing modules...");
12879
13031
  await sphere.initializeModules();
12880
13032
  console.log("[Sphere.import] Modules initialized");
12881
13033
  if (!options.nametag) {
13034
+ progress?.({ step: "recovering_nametag", message: "Recovering nametag..." });
12882
13035
  console.log("[Sphere.import] Recovering nametag from transport...");
12883
13036
  await sphere.recoverNametagFromTransport();
12884
13037
  console.log("[Sphere.import] Nametag recovery done");
13038
+ progress?.({ step: "syncing_identity", message: "Publishing identity..." });
12885
13039
  await sphere.syncIdentityWithTransport();
12886
13040
  }
13041
+ progress?.({ step: "finalizing", message: "Finalizing wallet..." });
12887
13042
  console.log("[Sphere.import] Finalizing wallet creation...");
12888
13043
  await sphere.finalizeWalletCreation();
12889
13044
  sphere._initialized = true;
@@ -12891,10 +13046,12 @@ var Sphere = class _Sphere {
12891
13046
  console.log("[Sphere.import] Tracking address 0...");
12892
13047
  await sphere.ensureAddressTracked(0);
12893
13048
  if (options.nametag) {
13049
+ progress?.({ step: "registering_nametag", message: "Registering nametag..." });
12894
13050
  console.log("[Sphere.import] Registering nametag...");
12895
13051
  await sphere.registerNametag(options.nametag);
12896
13052
  }
12897
13053
  if (sphere._tokenStorageProviders.size > 0) {
13054
+ progress?.({ step: "syncing_tokens", message: "Syncing tokens..." });
12898
13055
  try {
12899
13056
  const syncResult = await sphere._payments.sync();
12900
13057
  console.log(`[Sphere.import] Auto-sync: +${syncResult.added} -${syncResult.removed}`);
@@ -12902,6 +13059,19 @@ var Sphere = class _Sphere {
12902
13059
  console.warn("[Sphere.import] Auto-sync failed (non-fatal):", err);
12903
13060
  }
12904
13061
  }
13062
+ if (options.discoverAddresses !== false && sphere._transport.discoverAddresses) {
13063
+ progress?.({ step: "discovering_addresses", message: "Discovering addresses..." });
13064
+ try {
13065
+ const discoverOpts = typeof options.discoverAddresses === "object" ? { ...options.discoverAddresses, autoTrack: options.discoverAddresses.autoTrack ?? true } : { autoTrack: true };
13066
+ const result = await sphere.discoverAddresses(discoverOpts);
13067
+ if (result.addresses.length > 0) {
13068
+ console.log(`[Sphere.import] Address discovery: found ${result.addresses.length} address(es)`);
13069
+ }
13070
+ } catch (err) {
13071
+ console.warn("[Sphere.import] Address discovery failed (non-fatal):", err);
13072
+ }
13073
+ }
13074
+ progress?.({ step: "complete", message: "Import complete" });
12905
13075
  console.log("[Sphere.import] Import complete");
12906
13076
  return sphere;
12907
13077
  }
@@ -13315,55 +13485,44 @@ var Sphere = class _Sphere {
13315
13485
  * ```
13316
13486
  */
13317
13487
  static async importFromJSON(options) {
13488
+ const { jsonContent, password, ...baseOptions } = options;
13318
13489
  try {
13319
- const data = JSON.parse(options.jsonContent);
13490
+ const data = JSON.parse(jsonContent);
13320
13491
  if (data.version !== "1.0" || data.type !== "sphere-wallet") {
13321
13492
  return { success: false, error: "Invalid wallet format" };
13322
13493
  }
13323
13494
  let mnemonic = data.mnemonic;
13324
13495
  let masterKey = data.wallet.masterPrivateKey;
13325
- if (data.encrypted && options.password) {
13496
+ if (data.encrypted && password) {
13326
13497
  if (mnemonic) {
13327
- const decrypted = decryptSimple(mnemonic, options.password);
13498
+ const decrypted = decryptSimple(mnemonic, password);
13328
13499
  if (!decrypted) {
13329
13500
  return { success: false, error: "Failed to decrypt mnemonic - wrong password?" };
13330
13501
  }
13331
13502
  mnemonic = decrypted;
13332
13503
  }
13333
13504
  if (masterKey) {
13334
- const decrypted = decryptSimple(masterKey, options.password);
13505
+ const decrypted = decryptSimple(masterKey, password);
13335
13506
  if (!decrypted) {
13336
13507
  return { success: false, error: "Failed to decrypt master key - wrong password?" };
13337
13508
  }
13338
13509
  masterKey = decrypted;
13339
13510
  }
13340
- } else if (data.encrypted && !options.password) {
13511
+ } else if (data.encrypted && !password) {
13341
13512
  return { success: false, error: "Password required for encrypted wallet" };
13342
13513
  }
13343
13514
  const basePath = data.wallet.descriptorPath ? `m/${data.wallet.descriptorPath}` : DEFAULT_BASE_PATH;
13344
13515
  if (mnemonic) {
13345
- await _Sphere.import({
13346
- mnemonic,
13347
- basePath,
13348
- storage: options.storage,
13349
- transport: options.transport,
13350
- oracle: options.oracle,
13351
- tokenStorage: options.tokenStorage,
13352
- l1: options.l1
13353
- });
13516
+ await _Sphere.import({ ...baseOptions, mnemonic, basePath });
13354
13517
  return { success: true, mnemonic };
13355
13518
  }
13356
13519
  if (masterKey) {
13357
13520
  await _Sphere.import({
13521
+ ...baseOptions,
13358
13522
  masterKey,
13359
13523
  chainCode: data.wallet.chainCode,
13360
13524
  basePath,
13361
- derivationMode: data.derivationMode || (data.wallet.isBIP32 ? "bip32" : "wif_hmac"),
13362
- storage: options.storage,
13363
- transport: options.transport,
13364
- oracle: options.oracle,
13365
- tokenStorage: options.tokenStorage,
13366
- l1: options.l1
13525
+ derivationMode: data.derivationMode || (data.wallet.isBIP32 ? "bip32" : "wif_hmac")
13367
13526
  });
13368
13527
  return { success: true };
13369
13528
  }
@@ -13406,7 +13565,7 @@ var Sphere = class _Sphere {
13406
13565
  * ```
13407
13566
  */
13408
13567
  static async importFromLegacyFile(options) {
13409
- const { fileContent, fileName, password, onDecryptProgress } = options;
13568
+ const { fileContent, fileName, password, onDecryptProgress, ...baseOptions } = options;
13410
13569
  const fileType = _Sphere.detectLegacyFileType(fileName, fileContent);
13411
13570
  if (fileType === "unknown") {
13412
13571
  return { success: false, error: "Unknown file format" };
@@ -13416,15 +13575,7 @@ var Sphere = class _Sphere {
13416
13575
  if (!_Sphere.validateMnemonic(mnemonic)) {
13417
13576
  return { success: false, error: "Invalid mnemonic phrase" };
13418
13577
  }
13419
- const sphere = await _Sphere.import({
13420
- mnemonic,
13421
- storage: options.storage,
13422
- transport: options.transport,
13423
- oracle: options.oracle,
13424
- tokenStorage: options.tokenStorage,
13425
- nametag: options.nametag,
13426
- l1: options.l1
13427
- });
13578
+ const sphere = await _Sphere.import({ ...baseOptions, mnemonic });
13428
13579
  return { success: true, sphere, mnemonic };
13429
13580
  }
13430
13581
  if (fileType === "dat") {
@@ -13444,16 +13595,11 @@ var Sphere = class _Sphere {
13444
13595
  const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;
13445
13596
  const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;
13446
13597
  const sphere = await _Sphere.import({
13598
+ ...baseOptions,
13447
13599
  masterKey,
13448
13600
  chainCode,
13449
13601
  basePath,
13450
- derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac"),
13451
- storage: options.storage,
13452
- transport: options.transport,
13453
- oracle: options.oracle,
13454
- tokenStorage: options.tokenStorage,
13455
- nametag: options.nametag,
13456
- l1: options.l1
13602
+ derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
13457
13603
  });
13458
13604
  return { success: true, sphere };
13459
13605
  }
@@ -13476,16 +13622,11 @@ var Sphere = class _Sphere {
13476
13622
  const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;
13477
13623
  const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;
13478
13624
  const sphere = await _Sphere.import({
13625
+ ...baseOptions,
13479
13626
  masterKey,
13480
13627
  chainCode,
13481
13628
  basePath,
13482
- derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac"),
13483
- storage: options.storage,
13484
- transport: options.transport,
13485
- oracle: options.oracle,
13486
- tokenStorage: options.tokenStorage,
13487
- nametag: options.nametag,
13488
- l1: options.l1
13629
+ derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
13489
13630
  });
13490
13631
  return { success: true, sphere };
13491
13632
  }
@@ -13499,13 +13640,9 @@ var Sphere = class _Sphere {
13499
13640
  }
13500
13641
  if (parsed.type === "sphere-wallet") {
13501
13642
  const result = await _Sphere.importFromJSON({
13643
+ ...baseOptions,
13502
13644
  jsonContent: content,
13503
- password,
13504
- storage: options.storage,
13505
- transport: options.transport,
13506
- oracle: options.oracle,
13507
- tokenStorage: options.tokenStorage,
13508
- l1: options.l1
13645
+ password
13509
13646
  });
13510
13647
  if (result.success) {
13511
13648
  const sphere2 = _Sphere.getInstance();
@@ -13547,29 +13684,15 @@ var Sphere = class _Sphere {
13547
13684
  const isBIP32 = derivationMode === "bip32" || !!chainCode;
13548
13685
  const basePath = descriptorPath ? `m/${descriptorPath}` : isBIP32 ? "m/84'/1'/0'" : DEFAULT_BASE_PATH;
13549
13686
  if (mnemonic) {
13550
- const sphere2 = await _Sphere.import({
13551
- mnemonic,
13552
- basePath,
13553
- storage: options.storage,
13554
- transport: options.transport,
13555
- oracle: options.oracle,
13556
- tokenStorage: options.tokenStorage,
13557
- nametag: options.nametag,
13558
- l1: options.l1
13559
- });
13687
+ const sphere2 = await _Sphere.import({ ...baseOptions, mnemonic, basePath });
13560
13688
  return { success: true, sphere: sphere2, mnemonic };
13561
13689
  }
13562
13690
  const sphere = await _Sphere.import({
13691
+ ...baseOptions,
13563
13692
  masterKey,
13564
13693
  chainCode,
13565
13694
  basePath,
13566
- derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac"),
13567
- storage: options.storage,
13568
- transport: options.transport,
13569
- oracle: options.oracle,
13570
- tokenStorage: options.tokenStorage,
13571
- nametag: options.nametag,
13572
- l1: options.l1
13695
+ derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
13573
13696
  });
13574
13697
  return { success: true, sphere };
13575
13698
  }
@@ -13817,6 +13940,23 @@ var Sphere = class _Sphere {
13817
13940
  await provider.initialize();
13818
13941
  }
13819
13942
  await this.reinitializeModulesForNewAddress();
13943
+ this.emitEvent("identity:changed", {
13944
+ l1Address: this._identity.l1Address,
13945
+ directAddress: this._identity.directAddress,
13946
+ chainPubkey: this._identity.chainPubkey,
13947
+ nametag: this._identity.nametag,
13948
+ addressIndex: index
13949
+ });
13950
+ console.log(`[Sphere] Switched to address ${index}:`, this._identity.l1Address);
13951
+ this.postSwitchSync(index, newNametag).catch((err) => {
13952
+ console.warn(`[Sphere] Post-switch sync failed for address ${index}:`, err);
13953
+ });
13954
+ }
13955
+ /**
13956
+ * Background transport sync and nametag operations after address switch.
13957
+ * Runs after switchToAddress returns so L1/L3 queries can start immediately.
13958
+ */
13959
+ async postSwitchSync(index, newNametag) {
13820
13960
  if (!newNametag) {
13821
13961
  await this.syncIdentityWithTransport();
13822
13962
  }
@@ -13839,7 +13979,7 @@ var Sphere = class _Sphere {
13839
13979
  nametag: newNametag,
13840
13980
  addressIndex: index
13841
13981
  });
13842
- } else if (this._identity.nametag && !this._payments.hasNametag()) {
13982
+ } else if (this._identity?.nametag && !this._payments.hasNametag()) {
13843
13983
  console.log(`[Sphere] Nametag @${this._identity.nametag} has no token after switch, minting...`);
13844
13984
  try {
13845
13985
  const result = await this.mintNametag(this._identity.nametag);
@@ -13852,14 +13992,6 @@ var Sphere = class _Sphere {
13852
13992
  console.warn(`[Sphere] Nametag token mint failed after switch:`, err);
13853
13993
  }
13854
13994
  }
13855
- this.emitEvent("identity:changed", {
13856
- l1Address: this._identity.l1Address,
13857
- directAddress: this._identity.directAddress,
13858
- chainPubkey: this._identity.chainPubkey,
13859
- nametag: this._identity.nametag,
13860
- addressIndex: index
13861
- });
13862
- console.log(`[Sphere] Switched to address ${index}:`, this._identity.l1Address);
13863
13995
  }
13864
13996
  /**
13865
13997
  * Re-initialize modules after address switch
@@ -14066,6 +14198,98 @@ var Sphere = class _Sphere {
14066
14198
  await this.persistTrackedAddresses();
14067
14199
  await this.persistAddressNametags();
14068
14200
  }
14201
+ /**
14202
+ * Discover previously used HD addresses.
14203
+ *
14204
+ * Primary: queries Nostr relay for identity binding events (fast, single batch query).
14205
+ * Secondary: runs L1 balance scan to find legacy addresses with no binding event.
14206
+ *
14207
+ * @example
14208
+ * ```ts
14209
+ * const result = await sphere.discoverAddresses();
14210
+ * console.log(`Found ${result.addresses.length} addresses`);
14211
+ *
14212
+ * // With auto-tracking
14213
+ * await sphere.discoverAddresses({ autoTrack: true });
14214
+ * ```
14215
+ */
14216
+ async discoverAddresses(options = {}) {
14217
+ this.ensureReady();
14218
+ if (!this._masterKey) {
14219
+ throw new Error("Address discovery requires HD master key");
14220
+ }
14221
+ if (!this._transport.discoverAddresses) {
14222
+ throw new Error("Transport provider does not support address discovery");
14223
+ }
14224
+ const includeL1Scan = options.includeL1Scan ?? true;
14225
+ const transportResult = await discoverAddressesImpl(
14226
+ (index) => {
14227
+ const addrInfo = this._deriveAddressInternal(index, false);
14228
+ return {
14229
+ transportPubkey: addrInfo.publicKey.slice(2),
14230
+ // x-only 32 bytes
14231
+ chainPubkey: addrInfo.publicKey,
14232
+ l1Address: addrInfo.address,
14233
+ directAddress: ""
14234
+ // not needed for discovery query
14235
+ };
14236
+ },
14237
+ (pubkeys) => this._transport.discoverAddresses(pubkeys),
14238
+ options
14239
+ );
14240
+ if (includeL1Scan) {
14241
+ try {
14242
+ const l1Result = await this.scanAddresses({
14243
+ maxAddresses: options.maxAddresses,
14244
+ gapLimit: options.gapLimit,
14245
+ signal: options.signal,
14246
+ onProgress: options.onProgress ? (p) => options.onProgress({
14247
+ currentBatch: 0,
14248
+ totalBatches: 0,
14249
+ discoveredCount: p.foundCount,
14250
+ currentGap: p.currentGap,
14251
+ phase: "l1"
14252
+ }) : void 0
14253
+ });
14254
+ for (const l1Addr of l1Result.addresses) {
14255
+ if (l1Addr.isChange) continue;
14256
+ const existing = transportResult.addresses.find((a) => a.index === l1Addr.index);
14257
+ if (existing) {
14258
+ existing.l1Balance = l1Addr.balance;
14259
+ existing.source = "both";
14260
+ if (!existing.nametag && l1Addr.nametag) {
14261
+ existing.nametag = l1Addr.nametag;
14262
+ }
14263
+ } else {
14264
+ const addrInfo = this._deriveAddressInternal(l1Addr.index, false);
14265
+ transportResult.addresses.push({
14266
+ index: l1Addr.index,
14267
+ l1Address: l1Addr.address,
14268
+ directAddress: "",
14269
+ chainPubkey: addrInfo.publicKey,
14270
+ nametag: l1Addr.nametag,
14271
+ l1Balance: l1Addr.balance,
14272
+ source: "l1"
14273
+ });
14274
+ }
14275
+ }
14276
+ transportResult.addresses.sort((a, b) => a.index - b.index);
14277
+ } catch (err) {
14278
+ console.warn("[Sphere] L1 scan failed during discovery (non-fatal):", err);
14279
+ }
14280
+ }
14281
+ if (options.autoTrack && transportResult.addresses.length > 0) {
14282
+ await this.trackScannedAddresses(
14283
+ transportResult.addresses.map((a) => ({
14284
+ index: a.index,
14285
+ // Preserve existing hidden state; default to false for newly discovered
14286
+ hidden: this._trackedAddresses.get(a.index)?.hidden ?? false,
14287
+ nametag: a.nametag
14288
+ }))
14289
+ );
14290
+ }
14291
+ return transportResult;
14292
+ }
14069
14293
  // ===========================================================================
14070
14294
  // Public Methods - Status
14071
14295
  // ===========================================================================
@@ -14637,6 +14861,17 @@ var Sphere = class _Sphere {
14637
14861
  return;
14638
14862
  }
14639
14863
  }
14864
+ const needsUpdate = !existing.directAddress || !existing.l1Address || !existing.chainPubkey || this._identity?.nametag && !existing.nametag;
14865
+ if (needsUpdate) {
14866
+ console.log("[Sphere] Existing binding incomplete, re-publishing with full data");
14867
+ await this._transport.publishIdentityBinding(
14868
+ this._identity.chainPubkey,
14869
+ this._identity.l1Address,
14870
+ this._identity.directAddress || "",
14871
+ this._identity?.nametag || existing.nametag || void 0
14872
+ );
14873
+ return;
14874
+ }
14640
14875
  console.log("[Sphere] Existing binding found, skipping re-publish");
14641
14876
  return;
14642
14877
  }
@@ -15130,6 +15365,7 @@ var CurrencyUtils = {
15130
15365
  init_bech32();
15131
15366
 
15132
15367
  // core/network-health.ts
15368
+ init_constants();
15133
15369
  var DEFAULT_TIMEOUT_MS = 5e3;
15134
15370
  async function checkNetworkHealth(network = "testnet", options) {
15135
15371
  const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
@@ -15317,7 +15553,7 @@ async function runCustomCheck(name, checkFn, timeoutMs) {
15317
15553
  export {
15318
15554
  CHARSET,
15319
15555
  CurrencyUtils,
15320
- DEFAULT_DERIVATION_PATH,
15556
+ DEFAULT_DERIVATION_PATH2 as DEFAULT_DERIVATION_PATH,
15321
15557
  DEFAULT_TOKEN_DECIMALS,
15322
15558
  Sphere,
15323
15559
  base58Decode,
@@ -15340,6 +15576,7 @@ export {
15340
15576
  deriveChildKey,
15341
15577
  deriveKeyAtPath,
15342
15578
  deserializeEncrypted,
15579
+ discoverAddressesImpl,
15343
15580
  doubleSha256,
15344
15581
  ec,
15345
15582
  encodeBech32,