@unicitylabs/sphere-sdk 0.1.3 → 0.1.5

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.
@@ -1445,6 +1445,7 @@ var L1PaymentsModule = class {
1445
1445
  _chainCode;
1446
1446
  _addresses = [];
1447
1447
  _wallet;
1448
+ _transport;
1448
1449
  constructor(config) {
1449
1450
  this._config = {
1450
1451
  electrumUrl: config?.electrumUrl ?? "wss://fulcrum.alpha.unicity.network:50004",
@@ -1457,13 +1458,14 @@ var L1PaymentsModule = class {
1457
1458
  this._identity = deps.identity;
1458
1459
  this._chainCode = deps.chainCode;
1459
1460
  this._addresses = deps.addresses ?? [];
1461
+ this._transport = deps.transport;
1460
1462
  this._wallet = {
1461
1463
  masterPrivateKey: deps.identity.privateKey,
1462
1464
  chainCode: deps.chainCode,
1463
1465
  addresses: [
1464
1466
  {
1465
- address: deps.identity.address,
1466
- publicKey: deps.identity.publicKey,
1467
+ address: deps.identity.l1Address,
1468
+ publicKey: deps.identity.chainPubkey,
1467
1469
  privateKey: deps.identity.privateKey,
1468
1470
  path: "m/0",
1469
1471
  index: 0
@@ -1471,7 +1473,7 @@ var L1PaymentsModule = class {
1471
1473
  ]
1472
1474
  };
1473
1475
  for (const addr of this._addresses) {
1474
- if (addr !== deps.identity.address) {
1476
+ if (addr !== deps.identity.l1Address) {
1475
1477
  this._wallet.addresses.push({
1476
1478
  address: addr,
1477
1479
  path: null,
@@ -1494,18 +1496,64 @@ var L1PaymentsModule = class {
1494
1496
  this._addresses = [];
1495
1497
  this._wallet = void 0;
1496
1498
  }
1499
+ /**
1500
+ * Check if a string looks like an L1 address (alpha1... or alphat1...)
1501
+ */
1502
+ isL1Address(value) {
1503
+ return value.startsWith("alpha1") || value.startsWith("alphat1");
1504
+ }
1505
+ /**
1506
+ * Resolve recipient to L1 address
1507
+ * Supports: L1 address (alpha1...), nametag (with or without @)
1508
+ */
1509
+ async resolveL1Address(recipient) {
1510
+ if (recipient.startsWith("@")) {
1511
+ const nametag = recipient.slice(1);
1512
+ return this.resolveNametagToL1Address(nametag);
1513
+ }
1514
+ if (this.isL1Address(recipient)) {
1515
+ return recipient;
1516
+ }
1517
+ try {
1518
+ const l1Address = await this.resolveNametagToL1Address(recipient);
1519
+ return l1Address;
1520
+ } catch {
1521
+ throw new Error(
1522
+ `Recipient "${recipient}" is not a valid nametag or L1 address. Use @nametag for explicit nametag or a valid alpha1... address.`
1523
+ );
1524
+ }
1525
+ }
1526
+ /**
1527
+ * Resolve nametag to L1 address using transport provider
1528
+ */
1529
+ async resolveNametagToL1Address(nametag) {
1530
+ if (!this._transport?.resolveNametagInfo) {
1531
+ throw new Error("Transport provider does not support nametag resolution");
1532
+ }
1533
+ const info = await this._transport.resolveNametagInfo(nametag);
1534
+ if (!info) {
1535
+ throw new Error(`Nametag not found: ${nametag}`);
1536
+ }
1537
+ if (!info.l1Address) {
1538
+ throw new Error(
1539
+ `Nametag @${nametag} does not have L1 address information. The owner needs to update their nametag registration.`
1540
+ );
1541
+ }
1542
+ return info.l1Address;
1543
+ }
1497
1544
  async send(request) {
1498
1545
  this.ensureInitialized();
1499
1546
  if (!this._wallet || !this._identity) {
1500
1547
  return { success: false, error: "No wallet available" };
1501
1548
  }
1502
1549
  try {
1550
+ const recipientAddress = await this.resolveL1Address(request.to);
1503
1551
  const amountAlpha = parseInt(request.amount, 10) / 1e8;
1504
1552
  const results = await sendAlpha(
1505
1553
  this._wallet,
1506
- request.to,
1554
+ recipientAddress,
1507
1555
  amountAlpha,
1508
- this._identity.address
1556
+ this._identity.l1Address
1509
1557
  );
1510
1558
  if (results && results.length > 0) {
1511
1559
  const txids = results.map((r) => r.txid);
@@ -1727,8 +1775,8 @@ var L1PaymentsModule = class {
1727
1775
  }
1728
1776
  _getWatchedAddresses() {
1729
1777
  const addresses = [...this._addresses];
1730
- if (this._identity?.address && !addresses.includes(this._identity.address)) {
1731
- addresses.unshift(this._identity.address);
1778
+ if (this._identity?.l1Address && !addresses.includes(this._identity.l1Address)) {
1779
+ addresses.unshift(this._identity.l1Address);
1732
1780
  }
1733
1781
  return addresses;
1734
1782
  }
@@ -2200,50 +2248,56 @@ var NametagMinter = class {
2200
2248
  };
2201
2249
 
2202
2250
  // constants.ts
2203
- var STORAGE_PREFIX = "sphere_";
2204
2251
  var DEFAULT_ENCRYPTION_KEY = "sphere-default-key";
2205
- var STORAGE_KEYS = {
2252
+ var STORAGE_KEYS_GLOBAL = {
2206
2253
  /** Encrypted BIP39 mnemonic */
2207
- MNEMONIC: `${STORAGE_PREFIX}mnemonic`,
2254
+ MNEMONIC: "mnemonic",
2208
2255
  /** Encrypted master private key */
2209
- MASTER_KEY: `${STORAGE_PREFIX}master_key`,
2256
+ MASTER_KEY: "master_key",
2210
2257
  /** BIP32 chain code */
2211
- CHAIN_CODE: `${STORAGE_PREFIX}chain_code`,
2258
+ CHAIN_CODE: "chain_code",
2212
2259
  /** HD derivation path (full path like m/44'/0'/0'/0/0) */
2213
- DERIVATION_PATH: `${STORAGE_PREFIX}derivation_path`,
2260
+ DERIVATION_PATH: "derivation_path",
2214
2261
  /** Base derivation path (like m/44'/0'/0' without chain/index) */
2215
- BASE_PATH: `${STORAGE_PREFIX}base_path`,
2262
+ BASE_PATH: "base_path",
2216
2263
  /** Derivation mode: bip32, wif_hmac, legacy_hmac */
2217
- DERIVATION_MODE: `${STORAGE_PREFIX}derivation_mode`,
2264
+ DERIVATION_MODE: "derivation_mode",
2218
2265
  /** Wallet source: mnemonic, file, unknown */
2219
- WALLET_SOURCE: `${STORAGE_PREFIX}wallet_source`,
2266
+ WALLET_SOURCE: "wallet_source",
2220
2267
  /** Wallet existence flag */
2221
- WALLET_EXISTS: `${STORAGE_PREFIX}wallet_exists`,
2222
- /** Registered nametag (legacy - single address) */
2223
- NAMETAG: `${STORAGE_PREFIX}nametag`,
2268
+ WALLET_EXISTS: "wallet_exists",
2224
2269
  /** Current active address index */
2225
- CURRENT_ADDRESS_INDEX: `${STORAGE_PREFIX}current_address_index`,
2226
- /** Address nametags map (JSON: { "0": "alice", "1": "bob" }) */
2227
- ADDRESS_NAMETAGS: `${STORAGE_PREFIX}address_nametags`,
2228
- /** Token data */
2229
- TOKENS: `${STORAGE_PREFIX}tokens`,
2230
- /** Pending transfers */
2231
- PENDING_TRANSFERS: `${STORAGE_PREFIX}pending_transfers`,
2232
- /** Transfer outbox */
2233
- OUTBOX: `${STORAGE_PREFIX}outbox`,
2234
- /** Conversations */
2235
- CONVERSATIONS: `${STORAGE_PREFIX}conversations`,
2236
- /** Messages */
2237
- MESSAGES: `${STORAGE_PREFIX}messages`,
2238
- /** Transaction history */
2239
- TRANSACTION_HISTORY: `${STORAGE_PREFIX}transaction_history`,
2240
- /** Archived tokens (spent token history) */
2241
- ARCHIVED_TOKENS: `${STORAGE_PREFIX}archived_tokens`,
2242
- /** Tombstones (records of deleted/spent tokens) */
2243
- TOMBSTONES: `${STORAGE_PREFIX}tombstones`,
2244
- /** Forked tokens (alternative histories) */
2245
- FORKED_TOKENS: `${STORAGE_PREFIX}forked_tokens`
2270
+ CURRENT_ADDRESS_INDEX: "current_address_index",
2271
+ /** Index of address nametags (JSON: { "0": "alice", "1": "bob" }) - for discovery */
2272
+ ADDRESS_NAMETAGS: "address_nametags"
2273
+ };
2274
+ var STORAGE_KEYS_ADDRESS = {
2275
+ /** Pending transfers for this address */
2276
+ PENDING_TRANSFERS: "pending_transfers",
2277
+ /** Transfer outbox for this address */
2278
+ OUTBOX: "outbox",
2279
+ /** Conversations for this address */
2280
+ CONVERSATIONS: "conversations",
2281
+ /** Messages for this address */
2282
+ MESSAGES: "messages",
2283
+ /** Transaction history for this address */
2284
+ TRANSACTION_HISTORY: "transaction_history"
2246
2285
  };
2286
+ var STORAGE_KEYS = {
2287
+ ...STORAGE_KEYS_GLOBAL,
2288
+ ...STORAGE_KEYS_ADDRESS
2289
+ };
2290
+ function getAddressId(directAddress) {
2291
+ let hash = directAddress;
2292
+ if (hash.startsWith("DIRECT://")) {
2293
+ hash = hash.slice(9);
2294
+ } else if (hash.startsWith("DIRECT:")) {
2295
+ hash = hash.slice(7);
2296
+ }
2297
+ const first = hash.slice(0, 6).toLowerCase();
2298
+ const last = hash.slice(-6).toLowerCase();
2299
+ return `DIRECT_${first}_${last}`;
2300
+ }
2247
2301
  var DEFAULT_BASE_PATH = "m/44'/0'/0'";
2248
2302
  var DEFAULT_DERIVATION_PATH2 = `${DEFAULT_BASE_PATH}/0/0`;
2249
2303
  var LIMITS = {
@@ -2412,6 +2466,7 @@ function txfToToken(tokenId, txf) {
2412
2466
  coinId,
2413
2467
  symbol: isNft ? "NFT" : "UCT",
2414
2468
  name: isNft ? "NFT" : "Token",
2469
+ decimals: isNft ? 0 : 8,
2415
2470
  amount: totalAmount.toString(),
2416
2471
  status: determineTokenStatus(txf),
2417
2472
  createdAt: now,
@@ -2426,9 +2481,6 @@ async function buildTxfStorageData(tokens, meta, options) {
2426
2481
  formatVersion: "2.0"
2427
2482
  }
2428
2483
  };
2429
- if (options?.nametag) {
2430
- storageData._nametag = options.nametag;
2431
- }
2432
2484
  if (options?.tombstones && options.tombstones.length > 0) {
2433
2485
  storageData._tombstones = options.tombstones;
2434
2486
  }
@@ -2568,23 +2620,330 @@ function getCurrentStateHash(txf) {
2568
2620
  return void 0;
2569
2621
  }
2570
2622
 
2623
+ // registry/token-registry.testnet.json
2624
+ var token_registry_testnet_default = [
2625
+ {
2626
+ network: "unicity:testnet",
2627
+ assetKind: "non-fungible",
2628
+ name: "unicity",
2629
+ description: "Unicity testnet token type",
2630
+ id: "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509"
2631
+ },
2632
+ {
2633
+ network: "unicity:testnet",
2634
+ assetKind: "fungible",
2635
+ name: "unicity",
2636
+ symbol: "UCT",
2637
+ decimals: 18,
2638
+ description: "Unicity testnet native coin",
2639
+ icons: [
2640
+ { url: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity_logo_32.png" }
2641
+ ],
2642
+ id: "455ad8720656b08e8dbd5bac1f3c73eeea5431565f6c1c3af742b1aa12d41d89"
2643
+ },
2644
+ {
2645
+ network: "unicity:testnet",
2646
+ assetKind: "fungible",
2647
+ name: "unicity-usd",
2648
+ symbol: "USDU",
2649
+ decimals: 6,
2650
+ description: "Unicity testnet USD stablecoin",
2651
+ icons: [
2652
+ { url: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/usdu_logo_32.png" }
2653
+ ],
2654
+ id: "8f0f3d7a5e7297be0ee98c63b81bcebb2740f43f616566fc290f9823a54f52d7"
2655
+ },
2656
+ {
2657
+ network: "unicity:testnet",
2658
+ assetKind: "fungible",
2659
+ name: "unicity-eur",
2660
+ symbol: "EURU",
2661
+ decimals: 6,
2662
+ description: "Unicity testnet EUR stablecoin",
2663
+ icons: [
2664
+ { url: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/euru_logo_32.png" }
2665
+ ],
2666
+ id: "5e160d5e9fdbb03b553fb9c3f6e6c30efa41fa807be39fb4f18e43776e492925"
2667
+ },
2668
+ {
2669
+ network: "unicity:testnet",
2670
+ assetKind: "fungible",
2671
+ name: "solana",
2672
+ symbol: "SOL",
2673
+ decimals: 9,
2674
+ description: "Solana testnet coin on Unicity",
2675
+ icons: [
2676
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/sol.svg" },
2677
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/sol.png" }
2678
+ ],
2679
+ id: "dee5f8ce778562eec90e9c38a91296a023210ccc76ff4c29d527ac3eb64ade93"
2680
+ },
2681
+ {
2682
+ network: "unicity:testnet",
2683
+ assetKind: "fungible",
2684
+ name: "bitcoin",
2685
+ symbol: "BTC",
2686
+ decimals: 8,
2687
+ description: "Bitcoin testnet coin on Unicity",
2688
+ icons: [
2689
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/btc.svg" },
2690
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/btc.png" }
2691
+ ],
2692
+ id: "86bc190fcf7b2d07c6078de93db803578760148b16d4431aa2f42a3241ff0daa"
2693
+ },
2694
+ {
2695
+ network: "unicity:testnet",
2696
+ assetKind: "fungible",
2697
+ name: "ethereum",
2698
+ symbol: "ETH",
2699
+ decimals: 18,
2700
+ description: "Ethereum testnet coin on Unicity",
2701
+ icons: [
2702
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/eth.svg" },
2703
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/eth.png" }
2704
+ ],
2705
+ id: "3c2450f2fd867e7bb60c6a69d7ad0e53ce967078c201a3ecaa6074ed4c0deafb"
2706
+ },
2707
+ {
2708
+ network: "unicity:testnet",
2709
+ assetKind: "fungible",
2710
+ name: "alpha_test",
2711
+ symbol: "ALPHT",
2712
+ decimals: 8,
2713
+ description: "ALPHA testnet coin on Unicity",
2714
+ icons: [
2715
+ { url: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/alpha_coin.png" }
2716
+ ],
2717
+ id: "cde78ded16ef65818a51f43138031c4284e519300ab0cb60c30a8f9078080e5f"
2718
+ },
2719
+ {
2720
+ network: "unicity:testnet",
2721
+ assetKind: "fungible",
2722
+ name: "tether",
2723
+ symbol: "USDT",
2724
+ decimals: 6,
2725
+ description: "Tether (Ethereum) testnet coin on Unicity",
2726
+ icons: [
2727
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/usdt.svg" },
2728
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/usdt.png" }
2729
+ ],
2730
+ id: "40d25444648418fe7efd433e147187a3a6adf049ac62bc46038bda5b960bf690"
2731
+ },
2732
+ {
2733
+ network: "unicity:testnet",
2734
+ assetKind: "fungible",
2735
+ name: "usd-coin",
2736
+ symbol: "USDC",
2737
+ decimals: 6,
2738
+ description: "USDC (Ethereum) testnet coin on Unicity",
2739
+ icons: [
2740
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/usdc.svg" },
2741
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/usdc.png" }
2742
+ ],
2743
+ id: "2265121770fa6f41131dd9a6cc571e28679263d09a53eb2642e145b5b9a5b0a2"
2744
+ }
2745
+ ];
2746
+
2747
+ // registry/TokenRegistry.ts
2748
+ var TokenRegistry = class _TokenRegistry {
2749
+ static instance = null;
2750
+ definitionsById;
2751
+ definitionsBySymbol;
2752
+ definitionsByName;
2753
+ constructor() {
2754
+ this.definitionsById = /* @__PURE__ */ new Map();
2755
+ this.definitionsBySymbol = /* @__PURE__ */ new Map();
2756
+ this.definitionsByName = /* @__PURE__ */ new Map();
2757
+ this.loadRegistry();
2758
+ }
2759
+ /**
2760
+ * Get singleton instance of TokenRegistry
2761
+ */
2762
+ static getInstance() {
2763
+ if (!_TokenRegistry.instance) {
2764
+ _TokenRegistry.instance = new _TokenRegistry();
2765
+ }
2766
+ return _TokenRegistry.instance;
2767
+ }
2768
+ /**
2769
+ * Reset the singleton instance (useful for testing)
2770
+ */
2771
+ static resetInstance() {
2772
+ _TokenRegistry.instance = null;
2773
+ }
2774
+ /**
2775
+ * Load registry data from bundled JSON
2776
+ */
2777
+ loadRegistry() {
2778
+ const definitions = token_registry_testnet_default;
2779
+ for (const def of definitions) {
2780
+ const idLower = def.id.toLowerCase();
2781
+ this.definitionsById.set(idLower, def);
2782
+ if (def.symbol) {
2783
+ this.definitionsBySymbol.set(def.symbol.toUpperCase(), def);
2784
+ }
2785
+ this.definitionsByName.set(def.name.toLowerCase(), def);
2786
+ }
2787
+ }
2788
+ // ===========================================================================
2789
+ // Lookup Methods
2790
+ // ===========================================================================
2791
+ /**
2792
+ * Get token definition by hex coin ID
2793
+ * @param coinId - 64-character hex string
2794
+ * @returns Token definition or undefined if not found
2795
+ */
2796
+ getDefinition(coinId) {
2797
+ if (!coinId) return void 0;
2798
+ return this.definitionsById.get(coinId.toLowerCase());
2799
+ }
2800
+ /**
2801
+ * Get token definition by symbol (e.g., "UCT", "BTC")
2802
+ * @param symbol - Token symbol (case-insensitive)
2803
+ * @returns Token definition or undefined if not found
2804
+ */
2805
+ getDefinitionBySymbol(symbol) {
2806
+ if (!symbol) return void 0;
2807
+ return this.definitionsBySymbol.get(symbol.toUpperCase());
2808
+ }
2809
+ /**
2810
+ * Get token definition by name (e.g., "bitcoin", "ethereum")
2811
+ * @param name - Token name (case-insensitive)
2812
+ * @returns Token definition or undefined if not found
2813
+ */
2814
+ getDefinitionByName(name) {
2815
+ if (!name) return void 0;
2816
+ return this.definitionsByName.get(name.toLowerCase());
2817
+ }
2818
+ /**
2819
+ * Get token symbol for a coin ID
2820
+ * @param coinId - 64-character hex string
2821
+ * @returns Symbol (e.g., "UCT") or truncated ID if not found
2822
+ */
2823
+ getSymbol(coinId) {
2824
+ const def = this.getDefinition(coinId);
2825
+ if (def?.symbol) {
2826
+ return def.symbol;
2827
+ }
2828
+ return coinId.slice(0, 6).toUpperCase();
2829
+ }
2830
+ /**
2831
+ * Get token name for a coin ID
2832
+ * @param coinId - 64-character hex string
2833
+ * @returns Name (e.g., "Bitcoin") or coin ID if not found
2834
+ */
2835
+ getName(coinId) {
2836
+ const def = this.getDefinition(coinId);
2837
+ if (def?.name) {
2838
+ return def.name.charAt(0).toUpperCase() + def.name.slice(1);
2839
+ }
2840
+ return coinId;
2841
+ }
2842
+ /**
2843
+ * Get decimal places for a coin ID
2844
+ * @param coinId - 64-character hex string
2845
+ * @returns Decimals or 0 if not found
2846
+ */
2847
+ getDecimals(coinId) {
2848
+ const def = this.getDefinition(coinId);
2849
+ return def?.decimals ?? 0;
2850
+ }
2851
+ /**
2852
+ * Get icon URL for a coin ID
2853
+ * @param coinId - 64-character hex string
2854
+ * @param preferPng - Prefer PNG format over SVG
2855
+ * @returns Icon URL or null if not found
2856
+ */
2857
+ getIconUrl(coinId, preferPng = true) {
2858
+ const def = this.getDefinition(coinId);
2859
+ if (!def?.icons || def.icons.length === 0) {
2860
+ return null;
2861
+ }
2862
+ if (preferPng) {
2863
+ const pngIcon = def.icons.find((i) => i.url.toLowerCase().includes(".png"));
2864
+ if (pngIcon) return pngIcon.url;
2865
+ }
2866
+ return def.icons[0].url;
2867
+ }
2868
+ /**
2869
+ * Check if a coin ID is known in the registry
2870
+ * @param coinId - 64-character hex string
2871
+ * @returns true if the coin is in the registry
2872
+ */
2873
+ isKnown(coinId) {
2874
+ return this.definitionsById.has(coinId.toLowerCase());
2875
+ }
2876
+ /**
2877
+ * Get all token definitions
2878
+ * @returns Array of all token definitions
2879
+ */
2880
+ getAllDefinitions() {
2881
+ return Array.from(this.definitionsById.values());
2882
+ }
2883
+ /**
2884
+ * Get all fungible token definitions
2885
+ * @returns Array of fungible token definitions
2886
+ */
2887
+ getFungibleTokens() {
2888
+ return this.getAllDefinitions().filter((def) => def.assetKind === "fungible");
2889
+ }
2890
+ /**
2891
+ * Get all non-fungible token definitions
2892
+ * @returns Array of non-fungible token definitions
2893
+ */
2894
+ getNonFungibleTokens() {
2895
+ return this.getAllDefinitions().filter((def) => def.assetKind === "non-fungible");
2896
+ }
2897
+ /**
2898
+ * Get coin ID by symbol
2899
+ * @param symbol - Token symbol (e.g., "UCT")
2900
+ * @returns Coin ID hex string or undefined if not found
2901
+ */
2902
+ getCoinIdBySymbol(symbol) {
2903
+ const def = this.getDefinitionBySymbol(symbol);
2904
+ return def?.id;
2905
+ }
2906
+ /**
2907
+ * Get coin ID by name
2908
+ * @param name - Token name (e.g., "bitcoin")
2909
+ * @returns Coin ID hex string or undefined if not found
2910
+ */
2911
+ getCoinIdByName(name) {
2912
+ const def = this.getDefinitionByName(name);
2913
+ return def?.id;
2914
+ }
2915
+ };
2916
+
2571
2917
  // modules/payments/PaymentsModule.ts
2572
2918
  var import_Token4 = require("@unicitylabs/state-transition-sdk/lib/token/Token");
2573
- var import_TokenId3 = require("@unicitylabs/state-transition-sdk/lib/token/TokenId");
2574
2919
  var import_CoinId3 = require("@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId");
2575
2920
  var import_TransferCommitment2 = require("@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment");
2576
2921
  var import_TransferTransaction = require("@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction");
2577
2922
  var import_SigningService = require("@unicitylabs/state-transition-sdk/lib/sign/SigningService");
2578
- var import_ProxyAddress = require("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
2579
2923
  var import_AddressScheme = require("@unicitylabs/state-transition-sdk/lib/address/AddressScheme");
2580
2924
  var import_UnmaskedPredicate3 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate");
2581
2925
  var import_TokenState3 = require("@unicitylabs/state-transition-sdk/lib/token/TokenState");
2582
2926
  var import_HashAlgorithm3 = require("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm");
2927
+ function enrichWithRegistry(info) {
2928
+ const registry = TokenRegistry.getInstance();
2929
+ const def = registry.getDefinition(info.coinId);
2930
+ if (def) {
2931
+ return {
2932
+ ...info,
2933
+ symbol: def.symbol || info.symbol,
2934
+ name: def.name.charAt(0).toUpperCase() + def.name.slice(1),
2935
+ decimals: def.decimals ?? 0,
2936
+ iconUrl: registry.getIconUrl(info.coinId) ?? void 0
2937
+ };
2938
+ }
2939
+ return info;
2940
+ }
2583
2941
  async function parseTokenInfo(tokenData) {
2584
2942
  const defaultInfo = {
2585
2943
  coinId: "ALPHA",
2586
2944
  symbol: "ALPHA",
2587
2945
  name: "Alpha Token",
2946
+ decimals: 0,
2588
2947
  amount: "0"
2589
2948
  };
2590
2949
  try {
@@ -2605,23 +2964,25 @@ async function parseTokenInfo(tokenData) {
2605
2964
  }
2606
2965
  if (coinIdObj instanceof import_CoinId3.CoinId) {
2607
2966
  const coinIdHex = coinIdObj.toJSON();
2608
- return {
2967
+ return enrichWithRegistry({
2609
2968
  coinId: coinIdHex,
2610
2969
  symbol: coinIdHex.slice(0, 8),
2611
2970
  name: `Token ${coinIdHex.slice(0, 8)}`,
2971
+ decimals: 0,
2612
2972
  amount: String(amount ?? "0"),
2613
2973
  tokenId: defaultInfo.tokenId
2614
- };
2974
+ });
2615
2975
  } else if (coinIdObj && typeof coinIdObj === "object" && "bytes" in coinIdObj) {
2616
2976
  const bytes = coinIdObj.bytes;
2617
2977
  const coinIdHex = Buffer.isBuffer(bytes) ? bytes.toString("hex") : Array.isArray(bytes) ? Buffer.from(bytes).toString("hex") : String(bytes);
2618
- return {
2978
+ return enrichWithRegistry({
2619
2979
  coinId: coinIdHex,
2620
2980
  symbol: coinIdHex.slice(0, 8),
2621
2981
  name: `Token ${coinIdHex.slice(0, 8)}`,
2982
+ decimals: 0,
2622
2983
  amount: String(amount ?? "0"),
2623
2984
  tokenId: defaultInfo.tokenId
2624
- };
2985
+ });
2625
2986
  }
2626
2987
  }
2627
2988
  }
@@ -2636,13 +2997,14 @@ async function parseTokenInfo(tokenData) {
2636
2997
  if (Array.isArray(firstEntry) && firstEntry.length === 2) {
2637
2998
  const [coinIdHex, amount] = firstEntry;
2638
2999
  const coinIdStr = typeof coinIdHex === "string" ? coinIdHex : String(coinIdHex);
2639
- return {
3000
+ return enrichWithRegistry({
2640
3001
  coinId: coinIdStr,
2641
3002
  symbol: coinIdStr.slice(0, 8),
2642
3003
  name: `Token ${coinIdStr.slice(0, 8)}`,
3004
+ decimals: 0,
2643
3005
  amount: String(amount),
2644
3006
  tokenId: defaultInfo.tokenId
2645
- };
3007
+ });
2646
3008
  }
2647
3009
  }
2648
3010
  }
@@ -2657,25 +3019,27 @@ async function parseTokenInfo(tokenData) {
2657
3019
  const firstEntry = coinData[0];
2658
3020
  if (Array.isArray(firstEntry) && firstEntry.length === 2) {
2659
3021
  const [coinIdHex, amount] = firstEntry;
2660
- return {
3022
+ return enrichWithRegistry({
2661
3023
  coinId: String(coinIdHex),
2662
3024
  symbol: String(coinIdHex).slice(0, 8),
2663
3025
  name: `Token ${String(coinIdHex).slice(0, 8)}`,
3026
+ decimals: 0,
2664
3027
  amount: String(amount),
2665
3028
  tokenId: genesis.tokenId
2666
- };
3029
+ });
2667
3030
  }
2668
3031
  } else if (typeof coinData === "object") {
2669
3032
  const coinEntries = Object.entries(coinData);
2670
3033
  if (coinEntries.length > 0) {
2671
3034
  const [coinId, amount] = coinEntries[0];
2672
- return {
3035
+ return enrichWithRegistry({
2673
3036
  coinId,
2674
3037
  symbol: coinId.slice(0, 8),
2675
3038
  name: `Token ${coinId.slice(0, 8)}`,
3039
+ decimals: 0,
2676
3040
  amount: String(amount),
2677
3041
  tokenId: genesis.tokenId
2678
- };
3042
+ });
2679
3043
  }
2680
3044
  }
2681
3045
  }
@@ -2689,25 +3053,27 @@ async function parseTokenInfo(tokenData) {
2689
3053
  const firstEntry = coinData[0];
2690
3054
  if (Array.isArray(firstEntry) && firstEntry.length === 2) {
2691
3055
  const [coinIdHex, amount] = firstEntry;
2692
- return {
3056
+ return enrichWithRegistry({
2693
3057
  coinId: String(coinIdHex),
2694
3058
  symbol: String(coinIdHex).slice(0, 8),
2695
3059
  name: `Token ${String(coinIdHex).slice(0, 8)}`,
3060
+ decimals: 0,
2696
3061
  amount: String(amount),
2697
3062
  tokenId: defaultInfo.tokenId
2698
- };
3063
+ });
2699
3064
  }
2700
3065
  } else if (typeof coinData === "object") {
2701
3066
  const coinEntries = Object.entries(coinData);
2702
3067
  if (coinEntries.length > 0) {
2703
3068
  const [coinId, amount] = coinEntries[0];
2704
- return {
3069
+ return enrichWithRegistry({
2705
3070
  coinId,
2706
3071
  symbol: coinId.slice(0, 8),
2707
3072
  name: `Token ${coinId.slice(0, 8)}`,
3073
+ decimals: 0,
2708
3074
  amount: String(amount),
2709
3075
  tokenId: defaultInfo.tokenId
2710
- };
3076
+ });
2711
3077
  }
2712
3078
  }
2713
3079
  }
@@ -2866,7 +3232,8 @@ var PaymentsModule = class {
2866
3232
  this.l1.initialize({
2867
3233
  identity: deps.identity,
2868
3234
  chainCode: deps.chainCode,
2869
- addresses: deps.l1Addresses
3235
+ addresses: deps.l1Addresses,
3236
+ transport: deps.transport
2870
3237
  });
2871
3238
  }
2872
3239
  this.unsubscribeTransfers = deps.transport.onTokenTransfer((transfer) => {
@@ -2888,37 +3255,24 @@ var PaymentsModule = class {
2888
3255
  */
2889
3256
  async load() {
2890
3257
  this.ensureInitialized();
2891
- const data = await this.deps.storage.get(STORAGE_KEYS.TOKENS);
2892
- if (data) {
3258
+ const providers = this.getTokenStorageProviders();
3259
+ for (const [id, provider] of providers) {
2893
3260
  try {
2894
- const parsed = JSON.parse(data);
2895
- const tokens = parsed.tokens || [];
2896
- this.tokens.clear();
2897
- for (const token of tokens) {
2898
- this.tokens.set(token.id, token);
2899
- }
2900
- if (Array.isArray(parsed.tombstones)) {
2901
- this.tombstones = parsed.tombstones.filter(
2902
- (t) => typeof t === "object" && t !== null && typeof t.tokenId === "string" && typeof t.stateHash === "string"
2903
- );
2904
- }
2905
- if (parsed.archivedTokens && typeof parsed.archivedTokens === "object") {
2906
- this.archivedTokens = new Map(Object.entries(parsed.archivedTokens));
2907
- }
2908
- if (parsed.forkedTokens && typeof parsed.forkedTokens === "object") {
2909
- this.forkedTokens = new Map(Object.entries(parsed.forkedTokens));
2910
- }
2911
- if (parsed.nametag) {
2912
- this.nametag = parsed.nametag;
3261
+ const result = await provider.load();
3262
+ if (result.success && result.data) {
3263
+ this.loadFromStorageData(result.data);
3264
+ this.log(`Loaded from provider ${id}: ${this.tokens.size} tokens`);
3265
+ break;
2913
3266
  }
2914
- this.log(`Loaded ${this.tokens.size} tokens, ${this.tombstones.length} tombstones, ${this.archivedTokens.size} archived`);
2915
3267
  } catch (err) {
2916
- console.error("[Payments] Failed to parse stored data:", err);
3268
+ console.error(`[Payments] Failed to load from provider ${id}:`, err);
2917
3269
  }
2918
3270
  }
2919
- await this.loadTokensFromFileStorage();
3271
+ if (this.tokens.size === 0) {
3272
+ await this.loadTokensFromFileStorage();
3273
+ }
2920
3274
  await this.loadNametagFromFileStorage();
2921
- const historyData = await this.deps.storage.get(STORAGE_KEYS.TRANSACTION_HISTORY);
3275
+ const historyData = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
2922
3276
  if (historyData) {
2923
3277
  try {
2924
3278
  this.transactionHistory = JSON.parse(historyData);
@@ -2926,7 +3280,7 @@ var PaymentsModule = class {
2926
3280
  this.transactionHistory = [];
2927
3281
  }
2928
3282
  }
2929
- const pending2 = await this.deps.storage.get(STORAGE_KEYS.PENDING_TRANSFERS);
3283
+ const pending2 = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS);
2930
3284
  if (pending2) {
2931
3285
  const transfers = JSON.parse(pending2);
2932
3286
  for (const transfer of transfers) {
@@ -3024,6 +3378,8 @@ var PaymentsModule = class {
3024
3378
  coinId: request.coinId,
3025
3379
  symbol: this.getCoinSymbol(request.coinId),
3026
3380
  name: this.getCoinName(request.coinId),
3381
+ decimals: this.getCoinDecimals(request.coinId),
3382
+ iconUrl: this.getCoinIconUrl(request.coinId),
3027
3383
  amount: splitPlan.remainderAmount.toString(),
3028
3384
  status: "confirmed",
3029
3385
  createdAt: Date.now(),
@@ -3092,20 +3448,25 @@ var PaymentsModule = class {
3092
3448
  * Get coin symbol from coinId
3093
3449
  */
3094
3450
  getCoinSymbol(coinId) {
3095
- const symbols = {
3096
- "UCT": "UCT"
3097
- // Add more as needed
3098
- };
3099
- return symbols[coinId] || coinId.slice(0, 6).toUpperCase();
3451
+ return TokenRegistry.getInstance().getSymbol(coinId);
3100
3452
  }
3101
3453
  /**
3102
3454
  * Get coin name from coinId
3103
3455
  */
3104
3456
  getCoinName(coinId) {
3105
- const names = {
3106
- "UCT": "Unicity Token"
3107
- };
3108
- return names[coinId] || coinId;
3457
+ return TokenRegistry.getInstance().getName(coinId);
3458
+ }
3459
+ /**
3460
+ * Get coin decimals from coinId
3461
+ */
3462
+ getCoinDecimals(coinId) {
3463
+ return TokenRegistry.getInstance().getDecimals(coinId);
3464
+ }
3465
+ /**
3466
+ * Get coin icon URL from coinId
3467
+ */
3468
+ getCoinIconUrl(coinId) {
3469
+ return TokenRegistry.getInstance().getIconUrl(coinId) ?? void 0;
3109
3470
  }
3110
3471
  // ===========================================================================
3111
3472
  // Public API - Payment Requests
@@ -3263,7 +3624,7 @@ var PaymentsModule = class {
3263
3624
  }
3264
3625
  const request = {
3265
3626
  id: transportRequest.id,
3266
- senderPubkey: transportRequest.senderPubkey,
3627
+ senderPubkey: transportRequest.senderTransportPubkey,
3267
3628
  amount: transportRequest.request.amount,
3268
3629
  coinId: transportRequest.request.coinId,
3269
3630
  symbol: transportRequest.request.coinId,
@@ -3375,7 +3736,7 @@ var PaymentsModule = class {
3375
3736
  }
3376
3737
  const response = {
3377
3738
  id: transportResponse.id,
3378
- responderPubkey: transportResponse.responderPubkey,
3739
+ responderPubkey: transportResponse.responderTransportPubkey,
3379
3740
  requestId: transportResponse.response.requestId,
3380
3741
  responseType: transportResponse.response.responseType,
3381
3742
  message: transportResponse.response.message,
@@ -3580,6 +3941,8 @@ var PaymentsModule = class {
3580
3941
  coinId: tokenInfo.coinId,
3581
3942
  symbol: tokenInfo.symbol,
3582
3943
  name: tokenInfo.name,
3944
+ decimals: tokenInfo.decimals,
3945
+ iconUrl: tokenInfo.iconUrl,
3583
3946
  amount: tokenInfo.amount,
3584
3947
  status: "confirmed",
3585
3948
  createdAt: data.receivedAt || Date.now(),
@@ -3835,7 +4198,7 @@ var PaymentsModule = class {
3835
4198
  };
3836
4199
  this.transactionHistory.push(historyEntry);
3837
4200
  await this.deps.storage.set(
3838
- STORAGE_KEYS.TRANSACTION_HISTORY,
4201
+ STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY,
3839
4202
  JSON.stringify(this.transactionHistory)
3840
4203
  );
3841
4204
  }
@@ -4129,6 +4492,23 @@ var PaymentsModule = class {
4129
4492
  // ===========================================================================
4130
4493
  // Private: Transfer Operations
4131
4494
  // ===========================================================================
4495
+ /**
4496
+ * Detect if a string is an L3 address (not a nametag)
4497
+ * Returns true for: hex pubkeys (64+ chars), PROXY:, DIRECT: prefixed addresses
4498
+ */
4499
+ isL3Address(value) {
4500
+ if (value.startsWith("PROXY:") || value.startsWith("DIRECT:")) {
4501
+ return true;
4502
+ }
4503
+ if (value.length >= 64 && /^[0-9a-fA-F]+$/.test(value)) {
4504
+ return true;
4505
+ }
4506
+ return false;
4507
+ }
4508
+ /**
4509
+ * Resolve recipient to Nostr pubkey for messaging
4510
+ * Supports: nametag (with or without @), hex pubkey
4511
+ */
4132
4512
  async resolveRecipient(recipient) {
4133
4513
  if (recipient.startsWith("@")) {
4134
4514
  const nametag = recipient.slice(1);
@@ -4138,7 +4518,19 @@ var PaymentsModule = class {
4138
4518
  }
4139
4519
  return pubkey;
4140
4520
  }
4141
- return recipient;
4521
+ if (this.isL3Address(recipient)) {
4522
+ return recipient;
4523
+ }
4524
+ if (this.deps?.transport.resolveNametag) {
4525
+ const pubkey = await this.deps.transport.resolveNametag(recipient);
4526
+ if (pubkey) {
4527
+ this.log(`Resolved "${recipient}" as nametag to pubkey`);
4528
+ return pubkey;
4529
+ }
4530
+ }
4531
+ throw new Error(
4532
+ `Recipient "${recipient}" is not a valid nametag or address. Use @nametag for explicit nametag or a valid hex pubkey/PROXY:/DIRECT: address.`
4533
+ );
4142
4534
  }
4143
4535
  /**
4144
4536
  * Create SDK TransferCommitment for a token transfer
@@ -4170,19 +4562,74 @@ var PaymentsModule = class {
4170
4562
  return import_SigningService.SigningService.createFromSecret(privateKeyBytes);
4171
4563
  }
4172
4564
  /**
4173
- * Resolve recipient to IAddress
4565
+ * Create DirectAddress from a public key using UnmaskedPredicateReference
4566
+ */
4567
+ async createDirectAddressFromPubkey(pubkeyHex) {
4568
+ const { UnmaskedPredicateReference: UnmaskedPredicateReference3 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
4569
+ const { TokenType: TokenType3 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType");
4570
+ const UNICITY_TOKEN_TYPE_HEX3 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
4571
+ const tokenType = new TokenType3(Buffer.from(UNICITY_TOKEN_TYPE_HEX3, "hex"));
4572
+ const pubkeyBytes = new Uint8Array(
4573
+ pubkeyHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
4574
+ );
4575
+ const addressRef = await UnmaskedPredicateReference3.create(
4576
+ tokenType,
4577
+ "secp256k1",
4578
+ pubkeyBytes,
4579
+ import_HashAlgorithm3.HashAlgorithm.SHA256
4580
+ );
4581
+ return addressRef.toAddress();
4582
+ }
4583
+ /**
4584
+ * Resolve nametag to 33-byte compressed public key using resolveNametagInfo
4585
+ * Returns null if nametag not found or publicKey not available
4586
+ */
4587
+ async resolveNametagToPublicKey(nametag) {
4588
+ if (!this.deps?.transport.resolveNametagInfo) {
4589
+ this.log("resolveNametagInfo not available on transport");
4590
+ return null;
4591
+ }
4592
+ const info = await this.deps.transport.resolveNametagInfo(nametag);
4593
+ if (!info) {
4594
+ this.log(`Nametag "${nametag}" not found`);
4595
+ return null;
4596
+ }
4597
+ if (!info.chainPubkey) {
4598
+ this.log(`Nametag "${nametag}" has no 33-byte chainPubkey (legacy event)`);
4599
+ return null;
4600
+ }
4601
+ return info.chainPubkey;
4602
+ }
4603
+ /**
4604
+ * Resolve recipient to IAddress for L3 transfers
4605
+ * Supports: nametag (with or without @), PROXY:, DIRECT:, hex pubkey
4174
4606
  */
4175
4607
  async resolveRecipientAddress(recipient) {
4608
+ const { AddressFactory } = await import("@unicitylabs/state-transition-sdk/lib/address/AddressFactory");
4176
4609
  if (recipient.startsWith("@")) {
4177
4610
  const nametag = recipient.slice(1);
4178
- const tokenId2 = await import_TokenId3.TokenId.fromNameTag(nametag);
4179
- return import_ProxyAddress.ProxyAddress.fromTokenId(tokenId2);
4611
+ const publicKey2 = await this.resolveNametagToPublicKey(nametag);
4612
+ if (publicKey2) {
4613
+ this.log(`Resolved @${nametag} to 33-byte publicKey for DirectAddress`);
4614
+ return this.createDirectAddressFromPubkey(publicKey2);
4615
+ }
4616
+ throw new Error(`Nametag "${nametag}" not found or missing publicKey`);
4180
4617
  }
4181
- const pubkeyBytes = new Uint8Array(
4182
- recipient.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
4618
+ if (recipient.startsWith("PROXY:") || recipient.startsWith("DIRECT:")) {
4619
+ return AddressFactory.createAddress(recipient);
4620
+ }
4621
+ if (recipient.length === 66 && /^[0-9a-fA-F]+$/.test(recipient)) {
4622
+ this.log(`Creating DirectAddress from 33-byte compressed pubkey`);
4623
+ return this.createDirectAddressFromPubkey(recipient);
4624
+ }
4625
+ const publicKey = await this.resolveNametagToPublicKey(recipient);
4626
+ if (publicKey) {
4627
+ this.log(`Resolved "${recipient}" as nametag to 33-byte publicKey for DirectAddress`);
4628
+ return this.createDirectAddressFromPubkey(publicKey);
4629
+ }
4630
+ throw new Error(
4631
+ `Recipient "${recipient}" is not a valid nametag or L3 address. Use @nametag for explicit nametag or a valid 33-byte hex pubkey/PROXY:/DIRECT: address.`
4183
4632
  );
4184
- const tokenId = new import_TokenId3.TokenId(pubkeyBytes.slice(0, 32));
4185
- return import_ProxyAddress.ProxyAddress.fromTokenId(tokenId);
4186
4633
  }
4187
4634
  async handleIncomingTransfer(transfer) {
4188
4635
  try {
@@ -4240,7 +4687,39 @@ var PaymentsModule = class {
4240
4687
  }
4241
4688
  }
4242
4689
  } else {
4243
- tokenData = sourceTokenInput;
4690
+ this.log("Finalizing DIRECT address transfer for state tracking...");
4691
+ try {
4692
+ const signingService = await this.createSigningService();
4693
+ const transferSalt = transferTx.data.salt;
4694
+ const recipientPredicate = await import_UnmaskedPredicate3.UnmaskedPredicate.create(
4695
+ sourceToken.id,
4696
+ sourceToken.type,
4697
+ signingService,
4698
+ import_HashAlgorithm3.HashAlgorithm.SHA256,
4699
+ transferSalt
4700
+ );
4701
+ const recipientState = new import_TokenState3.TokenState(recipientPredicate, null);
4702
+ const stClient = this.deps.oracle.getStateTransitionClient?.();
4703
+ const trustBase = this.deps.oracle.getTrustBase?.();
4704
+ if (!stClient || !trustBase) {
4705
+ this.log("Cannot finalize DIRECT transfer - missing client, using source token");
4706
+ tokenData = sourceTokenInput;
4707
+ } else {
4708
+ finalizedSdkToken = await stClient.finalizeTransaction(
4709
+ trustBase,
4710
+ sourceToken,
4711
+ recipientState,
4712
+ transferTx,
4713
+ []
4714
+ // No nametag tokens needed for DIRECT
4715
+ );
4716
+ tokenData = finalizedSdkToken.toJSON();
4717
+ this.log("DIRECT transfer finalized successfully");
4718
+ }
4719
+ } catch (finalizeError) {
4720
+ this.log("DIRECT finalization failed, using source token:", finalizeError);
4721
+ tokenData = sourceTokenInput;
4722
+ }
4244
4723
  }
4245
4724
  } else if (payload.token) {
4246
4725
  tokenData = payload.token;
@@ -4259,6 +4738,8 @@ var PaymentsModule = class {
4259
4738
  coinId: tokenInfo.coinId,
4260
4739
  symbol: tokenInfo.symbol,
4261
4740
  name: tokenInfo.name,
4741
+ decimals: tokenInfo.decimals,
4742
+ iconUrl: tokenInfo.iconUrl,
4262
4743
  amount: tokenInfo.amount,
4263
4744
  status: "confirmed",
4264
4745
  createdAt: Date.now(),
@@ -4274,7 +4755,7 @@ var PaymentsModule = class {
4274
4755
  await this.addToken(token);
4275
4756
  const incomingTransfer = {
4276
4757
  id: transfer.id,
4277
- senderPubkey: transfer.senderPubkey,
4758
+ senderPubkey: transfer.senderTransportPubkey,
4278
4759
  tokens: [token],
4279
4760
  memo: payload.memo,
4280
4761
  receivedAt: transfer.timestamp
@@ -4312,28 +4793,32 @@ var PaymentsModule = class {
4312
4793
  // Private: Storage
4313
4794
  // ===========================================================================
4314
4795
  async save() {
4315
- const tokens = Array.from(this.tokens.values());
4316
- const data = {
4317
- tokens,
4318
- tombstones: this.tombstones.length > 0 ? this.tombstones : void 0,
4319
- archivedTokens: this.archivedTokens.size > 0 ? Object.fromEntries(this.archivedTokens) : void 0,
4320
- forkedTokens: this.forkedTokens.size > 0 ? Object.fromEntries(this.forkedTokens) : void 0,
4321
- nametag: this.nametag || void 0
4322
- };
4323
- await this.deps.storage.set(STORAGE_KEYS.TOKENS, JSON.stringify(data));
4796
+ const providers = this.getTokenStorageProviders();
4797
+ if (providers.size === 0) {
4798
+ this.log("No token storage providers - tokens not persisted");
4799
+ return;
4800
+ }
4801
+ const data = await this.createStorageData();
4802
+ for (const [id, provider] of providers) {
4803
+ try {
4804
+ await provider.save(data);
4805
+ } catch (err) {
4806
+ console.error(`[Payments] Failed to save to provider ${id}:`, err);
4807
+ }
4808
+ }
4324
4809
  }
4325
4810
  async saveToOutbox(transfer, recipient) {
4326
4811
  const outbox = await this.loadOutbox();
4327
4812
  outbox.push({ transfer, recipient, createdAt: Date.now() });
4328
- await this.deps.storage.set(STORAGE_KEYS.OUTBOX, JSON.stringify(outbox));
4813
+ await this.deps.storage.set(STORAGE_KEYS_ADDRESS.OUTBOX, JSON.stringify(outbox));
4329
4814
  }
4330
4815
  async removeFromOutbox(transferId) {
4331
4816
  const outbox = await this.loadOutbox();
4332
4817
  const filtered = outbox.filter((e) => e.transfer.id !== transferId);
4333
- await this.deps.storage.set(STORAGE_KEYS.OUTBOX, JSON.stringify(filtered));
4818
+ await this.deps.storage.set(STORAGE_KEYS_ADDRESS.OUTBOX, JSON.stringify(filtered));
4334
4819
  }
4335
4820
  async loadOutbox() {
4336
- const data = await this.deps.storage.get(STORAGE_KEYS.OUTBOX);
4821
+ const data = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.OUTBOX);
4337
4822
  return data ? JSON.parse(data) : [];
4338
4823
  }
4339
4824
  async createStorageData() {
@@ -4342,11 +4827,10 @@ var PaymentsModule = class {
4342
4827
  tokens,
4343
4828
  {
4344
4829
  version: 1,
4345
- address: this.deps.identity.address,
4830
+ address: this.deps.identity.l1Address,
4346
4831
  ipnsName: this.deps.identity.ipnsName ?? ""
4347
4832
  },
4348
4833
  {
4349
- nametag: this.nametag || void 0,
4350
4834
  tombstones: this.tombstones,
4351
4835
  archivedTokens: this.archivedTokens,
4352
4836
  forkedTokens: this.forkedTokens
@@ -4446,7 +4930,7 @@ var CommunicationsModule = class {
4446
4930
  const eventId = await this.deps.transport.sendMessage(recipientPubkey, content);
4447
4931
  const message = {
4448
4932
  id: eventId,
4449
- senderPubkey: this.deps.identity.publicKey,
4933
+ senderPubkey: this.deps.identity.chainPubkey,
4450
4934
  senderNametag: this.deps.identity.nametag,
4451
4935
  recipientPubkey,
4452
4936
  content,
@@ -4473,7 +4957,7 @@ var CommunicationsModule = class {
4473
4957
  getConversations() {
4474
4958
  const conversations = /* @__PURE__ */ new Map();
4475
4959
  for (const message of this.messages.values()) {
4476
- const peer = message.senderPubkey === this.deps?.identity.publicKey ? message.recipientPubkey : message.senderPubkey;
4960
+ const peer = message.senderPubkey === this.deps?.identity.chainPubkey ? message.recipientPubkey : message.senderPubkey;
4477
4961
  if (!conversations.has(peer)) {
4478
4962
  conversations.set(peer, []);
4479
4963
  }
@@ -4503,7 +4987,7 @@ var CommunicationsModule = class {
4503
4987
  */
4504
4988
  getUnreadCount(peerPubkey) {
4505
4989
  let messages = Array.from(this.messages.values()).filter(
4506
- (m) => !m.isRead && m.senderPubkey !== this.deps?.identity.publicKey
4990
+ (m) => !m.isRead && m.senderPubkey !== this.deps?.identity.chainPubkey
4507
4991
  );
4508
4992
  if (peerPubkey) {
4509
4993
  messages = messages.filter((m) => m.senderPubkey === peerPubkey);
@@ -4528,7 +5012,7 @@ var CommunicationsModule = class {
4528
5012
  const eventId = await this.deps.transport.publishBroadcast?.(content, tags);
4529
5013
  const message = {
4530
5014
  id: eventId ?? crypto.randomUUID(),
4531
- authorPubkey: this.deps.identity.publicKey,
5015
+ authorPubkey: this.deps.identity.chainPubkey,
4532
5016
  authorNametag: this.deps.identity.nametag,
4533
5017
  content,
4534
5018
  timestamp: Date.now(),
@@ -4579,11 +5063,12 @@ var CommunicationsModule = class {
4579
5063
  // Private: Message Handling
4580
5064
  // ===========================================================================
4581
5065
  handleIncomingMessage(msg) {
4582
- if (msg.senderPubkey === this.deps?.identity.publicKey) return;
5066
+ if (msg.senderTransportPubkey === this.deps?.identity.chainPubkey) return;
4583
5067
  const message = {
4584
5068
  id: msg.id,
4585
- senderPubkey: msg.senderPubkey,
4586
- recipientPubkey: this.deps.identity.publicKey,
5069
+ senderPubkey: msg.senderTransportPubkey,
5070
+ senderNametag: msg.senderNametag,
5071
+ recipientPubkey: this.deps.identity.chainPubkey,
4587
5072
  content: msg.content,
4588
5073
  timestamp: msg.timestamp,
4589
5074
  isRead: false
@@ -4605,7 +5090,7 @@ var CommunicationsModule = class {
4605
5090
  handleIncomingBroadcast(incoming) {
4606
5091
  const message = {
4607
5092
  id: incoming.id,
4608
- authorPubkey: incoming.authorPubkey,
5093
+ authorPubkey: incoming.authorTransportPubkey,
4609
5094
  content: incoming.content,
4610
5095
  timestamp: incoming.timestamp,
4611
5096
  tags: incoming.tags
@@ -5432,6 +5917,7 @@ var import_SigningService2 = require("@unicitylabs/state-transition-sdk/lib/sign
5432
5917
  var import_TokenType2 = require("@unicitylabs/state-transition-sdk/lib/token/TokenType");
5433
5918
  var import_HashAlgorithm4 = require("@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm");
5434
5919
  var import_UnmaskedPredicateReference2 = require("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
5920
+ var import_nostr_js_sdk = require("@unicitylabs/nostr-js-sdk");
5435
5921
  var UNICITY_TOKEN_TYPE_HEX2 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
5436
5922
  async function deriveL3PredicateAddress(privateKey) {
5437
5923
  const secret = Buffer.from(privateKey, "hex");
@@ -5458,6 +5944,7 @@ var Sphere = class _Sphere {
5458
5944
  _derivationMode = "bip32";
5459
5945
  _basePath = DEFAULT_BASE_PATH;
5460
5946
  _currentAddressIndex = 0;
5947
+ /** Map of addressId -> (nametagIndex -> nametag). Supports multiple nametags per address (e.g., from Nostr recovery) */
5461
5948
  _addressNametags = /* @__PURE__ */ new Map();
5462
5949
  // Providers
5463
5950
  _storage;
@@ -5493,9 +5980,9 @@ var Sphere = class _Sphere {
5493
5980
  if (!storage.isConnected()) {
5494
5981
  await storage.connect();
5495
5982
  }
5496
- const mnemonic = await storage.get(STORAGE_KEYS.MNEMONIC);
5983
+ const mnemonic = await storage.get(STORAGE_KEYS_GLOBAL.MNEMONIC);
5497
5984
  if (mnemonic) return true;
5498
- const masterKey = await storage.get(STORAGE_KEYS.MASTER_KEY);
5985
+ const masterKey = await storage.get(STORAGE_KEYS_GLOBAL.MASTER_KEY);
5499
5986
  if (masterKey) return true;
5500
5987
  return false;
5501
5988
  } catch {
@@ -5589,6 +6076,8 @@ var Sphere = class _Sphere {
5589
6076
  _Sphere.instance = sphere;
5590
6077
  if (options.nametag) {
5591
6078
  await sphere.registerNametag(options.nametag);
6079
+ } else {
6080
+ await sphere.recoverNametagFromNostr();
5592
6081
  }
5593
6082
  return sphere;
5594
6083
  }
@@ -5651,6 +6140,9 @@ var Sphere = class _Sphere {
5651
6140
  }
5652
6141
  await sphere.initializeProviders();
5653
6142
  await sphere.initializeModules();
6143
+ if (!options.nametag) {
6144
+ await sphere.recoverNametagFromNostr();
6145
+ }
5654
6146
  await sphere.finalizeWalletCreation();
5655
6147
  sphere._initialized = true;
5656
6148
  _Sphere.instance = sphere;
@@ -5661,20 +6153,20 @@ var Sphere = class _Sphere {
5661
6153
  }
5662
6154
  /**
5663
6155
  * Clear wallet data from storage
6156
+ * Note: Token data is cleared via TokenStorageProvider, not here
5664
6157
  */
5665
6158
  static async clear(storage) {
5666
- await storage.remove(STORAGE_KEYS.MNEMONIC);
5667
- await storage.remove(STORAGE_KEYS.MASTER_KEY);
5668
- await storage.remove(STORAGE_KEYS.CHAIN_CODE);
5669
- await storage.remove(STORAGE_KEYS.DERIVATION_PATH);
5670
- await storage.remove(STORAGE_KEYS.BASE_PATH);
5671
- await storage.remove(STORAGE_KEYS.DERIVATION_MODE);
5672
- await storage.remove(STORAGE_KEYS.WALLET_SOURCE);
5673
- await storage.remove(STORAGE_KEYS.WALLET_EXISTS);
5674
- await storage.remove(STORAGE_KEYS.NAMETAG);
5675
- await storage.remove(STORAGE_KEYS.TOKENS);
5676
- await storage.remove(STORAGE_KEYS.PENDING_TRANSFERS);
5677
- await storage.remove(STORAGE_KEYS.OUTBOX);
6159
+ await storage.remove(STORAGE_KEYS_GLOBAL.MNEMONIC);
6160
+ await storage.remove(STORAGE_KEYS_GLOBAL.MASTER_KEY);
6161
+ await storage.remove(STORAGE_KEYS_GLOBAL.CHAIN_CODE);
6162
+ await storage.remove(STORAGE_KEYS_GLOBAL.DERIVATION_PATH);
6163
+ await storage.remove(STORAGE_KEYS_GLOBAL.BASE_PATH);
6164
+ await storage.remove(STORAGE_KEYS_GLOBAL.DERIVATION_MODE);
6165
+ await storage.remove(STORAGE_KEYS_GLOBAL.WALLET_SOURCE);
6166
+ await storage.remove(STORAGE_KEYS_GLOBAL.WALLET_EXISTS);
6167
+ await storage.remove(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS);
6168
+ await storage.remove(STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS);
6169
+ await storage.remove(STORAGE_KEYS_ADDRESS.OUTBOX);
5678
6170
  if (_Sphere.instance) {
5679
6171
  await _Sphere.instance.destroy();
5680
6172
  }
@@ -5724,9 +6216,9 @@ var Sphere = class _Sphere {
5724
6216
  get identity() {
5725
6217
  if (!this._identity) return null;
5726
6218
  return {
5727
- publicKey: this._identity.publicKey,
5728
- address: this._identity.address,
5729
- predicateAddress: this._identity.predicateAddress,
6219
+ chainPubkey: this._identity.chainPubkey,
6220
+ l1Address: this._identity.l1Address,
6221
+ directAddress: this._identity.directAddress,
5730
6222
  ipnsName: this._identity.ipnsName,
5731
6223
  nametag: this._identity.nametag
5732
6224
  };
@@ -5843,7 +6335,7 @@ var Sphere = class _Sphere {
5843
6335
  if (this._masterKey) {
5844
6336
  address0 = this.deriveAddress(0).address;
5845
6337
  } else if (this._identity) {
5846
- address0 = this._identity.address;
6338
+ address0 = this._identity.l1Address;
5847
6339
  }
5848
6340
  } catch {
5849
6341
  }
@@ -5890,8 +6382,8 @@ var Sphere = class _Sphere {
5890
6382
  } catch {
5891
6383
  if (i === 0 && this._identity) {
5892
6384
  addresses.push({
5893
- address: this._identity.address,
5894
- publicKey: this._identity.publicKey,
6385
+ address: this._identity.l1Address,
6386
+ publicKey: this._identity.chainPubkey,
5895
6387
  path: this.getDefaultAddressPath(),
5896
6388
  index: 0
5897
6389
  });
@@ -5970,7 +6462,7 @@ var Sphere = class _Sphere {
5970
6462
  } catch {
5971
6463
  if (i === 0 && this._identity) {
5972
6464
  addresses.push({
5973
- address: this._identity.address,
6465
+ address: this._identity.l1Address,
5974
6466
  path: this.getDefaultAddressPath(),
5975
6467
  index: 0,
5976
6468
  isChange: false
@@ -6274,22 +6766,47 @@ var Sphere = class _Sphere {
6274
6766
  return this._currentAddressIndex;
6275
6767
  }
6276
6768
  /**
6277
- * Get nametag for a specific address index
6769
+ * Get primary nametag for a specific address
6278
6770
  *
6279
- * @param index - Address index (default: current address)
6280
- * @returns Nametag or undefined if not registered
6771
+ * @param addressId - Address identifier (DIRECT://xxx), defaults to current address
6772
+ * @returns Primary nametag (index 0) or undefined if not registered
6281
6773
  */
6282
- getNametagForAddress(index) {
6283
- const addressIndex = index ?? this._currentAddressIndex;
6284
- return this._addressNametags.get(addressIndex);
6774
+ getNametagForAddress(addressId) {
6775
+ const id = addressId ?? this.getCurrentAddressId();
6776
+ if (!id) return void 0;
6777
+ const nametagsMap = this._addressNametags.get(id);
6778
+ return nametagsMap?.get(0);
6779
+ }
6780
+ /**
6781
+ * Get all nametags for a specific address
6782
+ *
6783
+ * @param addressId - Address identifier (DIRECT://xxx), defaults to current address
6784
+ * @returns Map of nametagIndex to nametag, or undefined if no nametags
6785
+ */
6786
+ getNametagsForAddress(addressId) {
6787
+ const id = addressId ?? this.getCurrentAddressId();
6788
+ if (!id) return void 0;
6789
+ const nametagsMap = this._addressNametags.get(id);
6790
+ return nametagsMap ? new Map(nametagsMap) : void 0;
6285
6791
  }
6286
6792
  /**
6287
6793
  * Get all registered address nametags
6288
6794
  *
6289
- * @returns Map of address index to nametag
6795
+ * @returns Map of addressId to (nametagIndex -> nametag)
6290
6796
  */
6291
6797
  getAllAddressNametags() {
6292
- return new Map(this._addressNametags);
6798
+ const result = /* @__PURE__ */ new Map();
6799
+ this._addressNametags.forEach((nametagsMap, addressId) => {
6800
+ result.set(addressId, new Map(nametagsMap));
6801
+ });
6802
+ return result;
6803
+ }
6804
+ /**
6805
+ * Get current address identifier (DIRECT://xxx format)
6806
+ */
6807
+ getCurrentAddressId() {
6808
+ if (!this._identity?.directAddress) return void 0;
6809
+ return getAddressId(this._identity.directAddress);
6293
6810
  }
6294
6811
  /**
6295
6812
  * Switch to a different address by index
@@ -6321,17 +6838,19 @@ var Sphere = class _Sphere {
6321
6838
  const addressInfo = this.deriveAddress(index, false);
6322
6839
  const ipnsHash = sha256(addressInfo.publicKey, "hex").slice(0, 40);
6323
6840
  const predicateAddress = await deriveL3PredicateAddress(addressInfo.privateKey);
6324
- const nametag = this._addressNametags.get(index);
6841
+ const addressId = getAddressId(predicateAddress);
6842
+ const nametagsMap = this._addressNametags.get(addressId);
6843
+ const nametag = nametagsMap?.get(0);
6325
6844
  this._identity = {
6326
6845
  privateKey: addressInfo.privateKey,
6327
- publicKey: addressInfo.publicKey,
6328
- address: addressInfo.address,
6329
- predicateAddress,
6846
+ chainPubkey: addressInfo.publicKey,
6847
+ l1Address: addressInfo.address,
6848
+ directAddress: predicateAddress,
6330
6849
  ipnsName: "12D3KooW" + ipnsHash,
6331
6850
  nametag
6332
6851
  };
6333
6852
  this._currentAddressIndex = index;
6334
- await this._storage.set(STORAGE_KEYS.CURRENT_ADDRESS_INDEX, index.toString());
6853
+ await this._storage.set(STORAGE_KEYS_GLOBAL.CURRENT_ADDRESS_INDEX, index.toString());
6335
6854
  this._storage.setIdentity(this._identity);
6336
6855
  this._transport.setIdentity(this._identity);
6337
6856
  for (const provider of this._tokenStorageProviders.values()) {
@@ -6339,13 +6858,13 @@ var Sphere = class _Sphere {
6339
6858
  }
6340
6859
  await this.reinitializeModulesForNewAddress();
6341
6860
  this.emitEvent("identity:changed", {
6342
- address: this._identity.address,
6343
- predicateAddress: this._identity.predicateAddress,
6344
- publicKey: this._identity.publicKey,
6861
+ l1Address: this._identity.l1Address,
6862
+ directAddress: this._identity.directAddress,
6863
+ chainPubkey: this._identity.chainPubkey,
6345
6864
  nametag: this._identity.nametag,
6346
6865
  addressIndex: index
6347
6866
  });
6348
- console.log(`[Sphere] Switched to address ${index}:`, this._identity.address);
6867
+ console.log(`[Sphere] Switched to address ${index}:`, this._identity.l1Address);
6349
6868
  }
6350
6869
  /**
6351
6870
  * Re-initialize modules after address switch
@@ -6403,7 +6922,7 @@ var Sphere = class _Sphere {
6403
6922
  );
6404
6923
  return {
6405
6924
  ...info,
6406
- address: "alpha1" + info.address.slice(0, 38)
6925
+ address: publicKeyToAddress(info.publicKey, "alpha")
6407
6926
  };
6408
6927
  }
6409
6928
  /**
@@ -6430,11 +6949,10 @@ var Sphere = class _Sphere {
6430
6949
  path
6431
6950
  );
6432
6951
  const publicKey = getPublicKey(derived.privateKey);
6433
- const addressHash = hash160(publicKey);
6434
6952
  return {
6435
6953
  privateKey: derived.privateKey,
6436
6954
  publicKey,
6437
- address: "alpha1" + addressHash.slice(0, 38),
6955
+ address: publicKeyToAddress(publicKey, "alpha"),
6438
6956
  path,
6439
6957
  index
6440
6958
  };
@@ -6522,6 +7040,17 @@ var Sphere = class _Sphere {
6522
7040
  hasNametag() {
6523
7041
  return !!this._identity?.nametag;
6524
7042
  }
7043
+ /**
7044
+ * Get the PROXY address for the current nametag
7045
+ * PROXY addresses are derived from the nametag hash and require
7046
+ * the nametag token to claim funds sent to them
7047
+ * @returns PROXY address string or undefined if no nametag
7048
+ */
7049
+ getProxyAddress() {
7050
+ const nametag = this._identity?.nametag;
7051
+ if (!nametag) return void 0;
7052
+ return `PROXY:${(0, import_nostr_js_sdk.hashNametag)(nametag)}`;
7053
+ }
6525
7054
  /**
6526
7055
  * Register a nametag for the current active address
6527
7056
  * Each address can have its own independent nametag
@@ -6550,14 +7079,25 @@ var Sphere = class _Sphere {
6550
7079
  throw new Error(`Nametag already registered for address ${this._currentAddressIndex}: @${this._identity.nametag}`);
6551
7080
  }
6552
7081
  if (this._transport.registerNametag) {
6553
- const success = await this._transport.registerNametag(cleanNametag, this._identity.publicKey);
7082
+ const success = await this._transport.registerNametag(
7083
+ cleanNametag,
7084
+ this._identity.chainPubkey,
7085
+ this._identity.directAddress || ""
7086
+ );
6554
7087
  if (!success) {
6555
7088
  throw new Error("Failed to register nametag. It may already be taken.");
6556
7089
  }
6557
7090
  }
6558
7091
  this._identity.nametag = cleanNametag;
6559
- this._addressNametags.set(this._currentAddressIndex, cleanNametag);
6560
- await this._storage.set(STORAGE_KEYS.NAMETAG, cleanNametag);
7092
+ const addressId = this.getCurrentAddressId();
7093
+ if (addressId) {
7094
+ let nametagsMap = this._addressNametags.get(addressId);
7095
+ if (!nametagsMap) {
7096
+ nametagsMap = /* @__PURE__ */ new Map();
7097
+ this._addressNametags.set(addressId, nametagsMap);
7098
+ }
7099
+ nametagsMap.set(0, cleanNametag);
7100
+ }
6561
7101
  await this.persistAddressNametags();
6562
7102
  if (!this._payments.hasNametag()) {
6563
7103
  console.log(`[Sphere] Minting nametag token for @${cleanNametag}...`);
@@ -6576,13 +7116,18 @@ var Sphere = class _Sphere {
6576
7116
  }
6577
7117
  /**
6578
7118
  * Persist address nametags to storage
7119
+ * Format: { "DIRECT://abc...xyz": { "0": "alice", "1": "alice2" }, ... }
6579
7120
  */
6580
7121
  async persistAddressNametags() {
6581
- const nametagsObj = {};
6582
- this._addressNametags.forEach((nametag, index) => {
6583
- nametagsObj[index.toString()] = nametag;
7122
+ const result = {};
7123
+ this._addressNametags.forEach((nametagsMap, addressId) => {
7124
+ const innerObj = {};
7125
+ nametagsMap.forEach((nametag, index) => {
7126
+ innerObj[index.toString()] = nametag;
7127
+ });
7128
+ result[addressId] = innerObj;
6584
7129
  });
6585
- await this._storage.set(STORAGE_KEYS.ADDRESS_NAMETAGS, JSON.stringify(nametagsObj));
7130
+ await this._storage.set(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS, JSON.stringify(result));
6586
7131
  }
6587
7132
  /**
6588
7133
  * Mint a nametag token on-chain (like Sphere wallet and lottery)
@@ -6617,15 +7162,24 @@ var Sphere = class _Sphere {
6617
7162
  }
6618
7163
  /**
6619
7164
  * Load address nametags from storage
7165
+ * Supports new format: { "DIRECT://abc...xyz": { "0": "alice" } }
7166
+ * And legacy format: { "0": "alice" } (migrates to new format on save)
6620
7167
  */
6621
7168
  async loadAddressNametags() {
6622
7169
  try {
6623
- const saved = await this._storage.get(STORAGE_KEYS.ADDRESS_NAMETAGS);
7170
+ const saved = await this._storage.get(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS);
6624
7171
  if (saved) {
6625
- const nametagsObj = JSON.parse(saved);
7172
+ const parsed = JSON.parse(saved);
6626
7173
  this._addressNametags.clear();
6627
- for (const [indexStr, nametag] of Object.entries(nametagsObj)) {
6628
- this._addressNametags.set(parseInt(indexStr, 10), nametag);
7174
+ for (const [key, value] of Object.entries(parsed)) {
7175
+ if (typeof value === "object" && value !== null) {
7176
+ const nametagsMap = /* @__PURE__ */ new Map();
7177
+ for (const [indexStr, nametag] of Object.entries(value)) {
7178
+ nametagsMap.set(parseInt(indexStr, 10), nametag);
7179
+ }
7180
+ this._addressNametags.set(key, nametagsMap);
7181
+ } else if (typeof value === "string") {
7182
+ }
6629
7183
  }
6630
7184
  }
6631
7185
  } catch {
@@ -6644,7 +7198,11 @@ var Sphere = class _Sphere {
6644
7198
  return;
6645
7199
  }
6646
7200
  try {
6647
- const success = await this._transport.registerNametag(nametag, this._identity.publicKey);
7201
+ const success = await this._transport.registerNametag(
7202
+ nametag,
7203
+ this._identity.chainPubkey,
7204
+ this._identity.directAddress || ""
7205
+ );
6648
7206
  if (success) {
6649
7207
  console.log(`[Sphere] Nametag @${nametag} synced with Nostr`);
6650
7208
  } else {
@@ -6654,6 +7212,47 @@ var Sphere = class _Sphere {
6654
7212
  console.warn(`[Sphere] Nametag sync failed:`, error);
6655
7213
  }
6656
7214
  }
7215
+ /**
7216
+ * Recover nametag from Nostr after wallet import
7217
+ * Searches for encrypted nametag events authored by this wallet's pubkey
7218
+ * and decrypts them to restore the nametag association
7219
+ */
7220
+ async recoverNametagFromNostr() {
7221
+ if (this._identity?.nametag) {
7222
+ return;
7223
+ }
7224
+ if (!this._transport.recoverNametag) {
7225
+ return;
7226
+ }
7227
+ try {
7228
+ const recoveredNametag = await this._transport.recoverNametag();
7229
+ if (recoveredNametag) {
7230
+ if (this._identity) {
7231
+ this._identity.nametag = recoveredNametag;
7232
+ }
7233
+ const addressId = this.getCurrentAddressId();
7234
+ if (addressId) {
7235
+ let nametagsMap = this._addressNametags.get(addressId);
7236
+ if (!nametagsMap) {
7237
+ nametagsMap = /* @__PURE__ */ new Map();
7238
+ this._addressNametags.set(addressId, nametagsMap);
7239
+ }
7240
+ const nextIndex = nametagsMap.size;
7241
+ nametagsMap.set(nextIndex, recoveredNametag);
7242
+ }
7243
+ await this.persistAddressNametags();
7244
+ if (this._transport.registerNametag) {
7245
+ await this._transport.registerNametag(
7246
+ recoveredNametag,
7247
+ this._identity.chainPubkey,
7248
+ this._identity.directAddress || ""
7249
+ );
7250
+ }
7251
+ this.emitEvent("nametag:recovered", { nametag: recoveredNametag });
7252
+ }
7253
+ } catch {
7254
+ }
7255
+ }
6657
7256
  /**
6658
7257
  * Validate nametag format
6659
7258
  */
@@ -6684,22 +7283,22 @@ var Sphere = class _Sphere {
6684
7283
  // ===========================================================================
6685
7284
  async storeMnemonic(mnemonic, derivationPath, basePath) {
6686
7285
  const encrypted = this.encrypt(mnemonic);
6687
- await this._storage.set(STORAGE_KEYS.MNEMONIC, encrypted);
7286
+ await this._storage.set(STORAGE_KEYS_GLOBAL.MNEMONIC, encrypted);
6688
7287
  this._mnemonic = mnemonic;
6689
7288
  this._source = "mnemonic";
6690
7289
  this._derivationMode = "bip32";
6691
7290
  if (derivationPath) {
6692
- await this._storage.set(STORAGE_KEYS.DERIVATION_PATH, derivationPath);
7291
+ await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_PATH, derivationPath);
6693
7292
  }
6694
7293
  const effectiveBasePath = basePath ?? DEFAULT_BASE_PATH;
6695
7294
  this._basePath = effectiveBasePath;
6696
- await this._storage.set(STORAGE_KEYS.BASE_PATH, effectiveBasePath);
6697
- await this._storage.set(STORAGE_KEYS.DERIVATION_MODE, this._derivationMode);
6698
- await this._storage.set(STORAGE_KEYS.WALLET_SOURCE, this._source);
7295
+ await this._storage.set(STORAGE_KEYS_GLOBAL.BASE_PATH, effectiveBasePath);
7296
+ await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_MODE, this._derivationMode);
7297
+ await this._storage.set(STORAGE_KEYS_GLOBAL.WALLET_SOURCE, this._source);
6699
7298
  }
6700
7299
  async storeMasterKey(masterKey, chainCode, derivationPath, basePath, derivationMode) {
6701
7300
  const encrypted = this.encrypt(masterKey);
6702
- await this._storage.set(STORAGE_KEYS.MASTER_KEY, encrypted);
7301
+ await this._storage.set(STORAGE_KEYS_GLOBAL.MASTER_KEY, encrypted);
6703
7302
  this._source = "file";
6704
7303
  this._mnemonic = null;
6705
7304
  if (derivationMode) {
@@ -6708,16 +7307,16 @@ var Sphere = class _Sphere {
6708
7307
  this._derivationMode = chainCode ? "bip32" : "wif_hmac";
6709
7308
  }
6710
7309
  if (chainCode) {
6711
- await this._storage.set(STORAGE_KEYS.CHAIN_CODE, chainCode);
7310
+ await this._storage.set(STORAGE_KEYS_GLOBAL.CHAIN_CODE, chainCode);
6712
7311
  }
6713
7312
  if (derivationPath) {
6714
- await this._storage.set(STORAGE_KEYS.DERIVATION_PATH, derivationPath);
7313
+ await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_PATH, derivationPath);
6715
7314
  }
6716
7315
  const effectiveBasePath = basePath ?? DEFAULT_BASE_PATH;
6717
7316
  this._basePath = effectiveBasePath;
6718
- await this._storage.set(STORAGE_KEYS.BASE_PATH, effectiveBasePath);
6719
- await this._storage.set(STORAGE_KEYS.DERIVATION_MODE, this._derivationMode);
6720
- await this._storage.set(STORAGE_KEYS.WALLET_SOURCE, this._source);
7317
+ await this._storage.set(STORAGE_KEYS_GLOBAL.BASE_PATH, effectiveBasePath);
7318
+ await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_MODE, this._derivationMode);
7319
+ await this._storage.set(STORAGE_KEYS_GLOBAL.WALLET_SOURCE, this._source);
6721
7320
  }
6722
7321
  /**
6723
7322
  * Mark wallet as fully created (after successful initialization)
@@ -6725,20 +7324,20 @@ var Sphere = class _Sphere {
6725
7324
  * marked as existing after all initialization steps succeed.
6726
7325
  */
6727
7326
  async finalizeWalletCreation() {
6728
- await this._storage.set(STORAGE_KEYS.WALLET_EXISTS, "true");
7327
+ await this._storage.set(STORAGE_KEYS_GLOBAL.WALLET_EXISTS, "true");
6729
7328
  }
6730
7329
  // ===========================================================================
6731
7330
  // Private: Identity Initialization
6732
7331
  // ===========================================================================
6733
7332
  async loadIdentityFromStorage() {
6734
- const encryptedMnemonic = await this._storage.get(STORAGE_KEYS.MNEMONIC);
6735
- const encryptedMasterKey = await this._storage.get(STORAGE_KEYS.MASTER_KEY);
6736
- const chainCode = await this._storage.get(STORAGE_KEYS.CHAIN_CODE);
6737
- const derivationPath = await this._storage.get(STORAGE_KEYS.DERIVATION_PATH);
6738
- const savedBasePath = await this._storage.get(STORAGE_KEYS.BASE_PATH);
6739
- const savedDerivationMode = await this._storage.get(STORAGE_KEYS.DERIVATION_MODE);
6740
- const savedSource = await this._storage.get(STORAGE_KEYS.WALLET_SOURCE);
6741
- const savedAddressIndex = await this._storage.get(STORAGE_KEYS.CURRENT_ADDRESS_INDEX);
7333
+ const encryptedMnemonic = await this._storage.get(STORAGE_KEYS_GLOBAL.MNEMONIC);
7334
+ const encryptedMasterKey = await this._storage.get(STORAGE_KEYS_GLOBAL.MASTER_KEY);
7335
+ const chainCode = await this._storage.get(STORAGE_KEYS_GLOBAL.CHAIN_CODE);
7336
+ const derivationPath = await this._storage.get(STORAGE_KEYS_GLOBAL.DERIVATION_PATH);
7337
+ const savedBasePath = await this._storage.get(STORAGE_KEYS_GLOBAL.BASE_PATH);
7338
+ const savedDerivationMode = await this._storage.get(STORAGE_KEYS_GLOBAL.DERIVATION_MODE);
7339
+ const savedSource = await this._storage.get(STORAGE_KEYS_GLOBAL.WALLET_SOURCE);
7340
+ const savedAddressIndex = await this._storage.get(STORAGE_KEYS_GLOBAL.CURRENT_ADDRESS_INDEX);
6742
7341
  this._basePath = savedBasePath ?? DEFAULT_BASE_PATH;
6743
7342
  this._derivationMode = savedDerivationMode ?? "bip32";
6744
7343
  this._source = savedSource ?? "unknown";
@@ -6772,65 +7371,60 @@ var Sphere = class _Sphere {
6772
7371
  this._storage.setIdentity(this._identity);
6773
7372
  }
6774
7373
  await this.loadAddressNametags();
6775
- const savedNametag = await this._storage.get(STORAGE_KEYS.NAMETAG);
6776
7374
  if (this._currentAddressIndex > 0 && this._masterKey) {
6777
7375
  const addressInfo = this.deriveAddress(this._currentAddressIndex, false);
6778
7376
  const ipnsHash = sha256(addressInfo.publicKey, "hex").slice(0, 40);
6779
7377
  const predicateAddress = await deriveL3PredicateAddress(addressInfo.privateKey);
6780
- const nametag = this._addressNametags.get(this._currentAddressIndex);
7378
+ const addressId = getAddressId(predicateAddress);
7379
+ const nametagsMap = this._addressNametags.get(addressId);
7380
+ const nametag = nametagsMap?.get(0);
6781
7381
  this._identity = {
6782
7382
  privateKey: addressInfo.privateKey,
6783
- publicKey: addressInfo.publicKey,
6784
- address: addressInfo.address,
6785
- predicateAddress,
7383
+ chainPubkey: addressInfo.publicKey,
7384
+ l1Address: addressInfo.address,
7385
+ directAddress: predicateAddress,
6786
7386
  ipnsName: "12D3KooW" + ipnsHash,
6787
7387
  nametag
6788
7388
  };
6789
7389
  this._storage.setIdentity(this._identity);
6790
- console.log(`[Sphere] Restored to address ${this._currentAddressIndex}:`, this._identity.address);
6791
- } else {
6792
- if (savedNametag && this._identity) {
6793
- this._identity.nametag = savedNametag;
6794
- if (!this._addressNametags.has(0)) {
6795
- this._addressNametags.set(0, savedNametag);
6796
- }
6797
- console.log("[Sphere] Restored nametag:", savedNametag);
6798
- } else if (this._identity) {
6799
- const nametag = this._addressNametags.get(0);
6800
- if (nametag) {
6801
- this._identity.nametag = nametag;
6802
- console.log("[Sphere] Restored nametag from map:", nametag);
6803
- }
7390
+ console.log(`[Sphere] Restored to address ${this._currentAddressIndex}:`, this._identity.l1Address);
7391
+ } else if (this._identity) {
7392
+ const addressId = this.getCurrentAddressId();
7393
+ const nametagsMap = addressId ? this._addressNametags.get(addressId) : void 0;
7394
+ const nametag = nametagsMap?.get(0);
7395
+ if (nametag) {
7396
+ this._identity.nametag = nametag;
6804
7397
  }
6805
7398
  }
6806
7399
  }
6807
7400
  async initializeIdentityFromMnemonic(mnemonic, derivationPath) {
6808
- const path = derivationPath ?? DEFAULT_DERIVATION_PATH2;
7401
+ const basePath = derivationPath ?? DEFAULT_BASE_PATH;
7402
+ const fullPath = `${basePath}/0/0`;
6809
7403
  const masterKey = identityFromMnemonicSync(mnemonic);
6810
7404
  const derivedKey = deriveKeyAtPath(
6811
7405
  masterKey.privateKey,
6812
7406
  masterKey.chainCode,
6813
- `${path}/0/0`
7407
+ fullPath
6814
7408
  );
6815
7409
  const publicKey = getPublicKey(derivedKey.privateKey);
6816
- const addressHash = hash160(publicKey);
7410
+ const address = publicKeyToAddress(publicKey, "alpha");
6817
7411
  const ipnsHash = sha256(publicKey, "hex").slice(0, 40);
6818
7412
  const predicateAddress = await deriveL3PredicateAddress(derivedKey.privateKey);
6819
7413
  this._identity = {
6820
7414
  privateKey: derivedKey.privateKey,
6821
- publicKey,
6822
- address: "alpha1" + addressHash.slice(0, 38),
6823
- predicateAddress,
7415
+ chainPubkey: publicKey,
7416
+ l1Address: address,
7417
+ directAddress: predicateAddress,
6824
7418
  ipnsName: "12D3KooW" + ipnsHash
6825
7419
  };
6826
7420
  this._masterKey = masterKey;
6827
- console.log("[Sphere] Identity initialized from mnemonic, path:", path);
6828
7421
  }
6829
7422
  async initializeIdentityFromMasterKey(masterKey, chainCode, derivationPath) {
6830
- const path = derivationPath ?? DEFAULT_DERIVATION_PATH2;
7423
+ const basePath = derivationPath ?? DEFAULT_BASE_PATH;
7424
+ const fullPath = `${basePath}/0/0`;
6831
7425
  let privateKey;
6832
7426
  if (chainCode) {
6833
- const derivedKey = deriveKeyAtPath(masterKey, chainCode, `${path}/0/0`);
7427
+ const derivedKey = deriveKeyAtPath(masterKey, chainCode, fullPath);
6834
7428
  privateKey = derivedKey.privateKey;
6835
7429
  this._masterKey = {
6836
7430
  privateKey: masterKey,
@@ -6841,17 +7435,16 @@ var Sphere = class _Sphere {
6841
7435
  this._masterKey = null;
6842
7436
  }
6843
7437
  const publicKey = getPublicKey(privateKey);
6844
- const addressHash = hash160(publicKey);
7438
+ const address = publicKeyToAddress(publicKey, "alpha");
6845
7439
  const ipnsHash = sha256(publicKey, "hex").slice(0, 40);
6846
7440
  const predicateAddress = await deriveL3PredicateAddress(privateKey);
6847
7441
  this._identity = {
6848
7442
  privateKey,
6849
- publicKey,
6850
- address: "alpha1" + addressHash.slice(0, 38),
6851
- predicateAddress,
7443
+ chainPubkey: publicKey,
7444
+ l1Address: address,
7445
+ directAddress: predicateAddress,
6852
7446
  ipnsName: "12D3KooW" + ipnsHash
6853
7447
  };
6854
- console.log("[Sphere] Identity initialized from master key, path:", path, "chainCode:", !!chainCode);
6855
7448
  }
6856
7449
  // ===========================================================================
6857
7450
  // Private: Provider & Module Initialization