@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.
package/dist/index.js CHANGED
@@ -1542,6 +1542,7 @@ var L1PaymentsModule = class {
1542
1542
  _chainCode;
1543
1543
  _addresses = [];
1544
1544
  _wallet;
1545
+ _transport;
1545
1546
  constructor(config) {
1546
1547
  this._config = {
1547
1548
  electrumUrl: config?.electrumUrl ?? "wss://fulcrum.alpha.unicity.network:50004",
@@ -1554,13 +1555,14 @@ var L1PaymentsModule = class {
1554
1555
  this._identity = deps.identity;
1555
1556
  this._chainCode = deps.chainCode;
1556
1557
  this._addresses = deps.addresses ?? [];
1558
+ this._transport = deps.transport;
1557
1559
  this._wallet = {
1558
1560
  masterPrivateKey: deps.identity.privateKey,
1559
1561
  chainCode: deps.chainCode,
1560
1562
  addresses: [
1561
1563
  {
1562
- address: deps.identity.address,
1563
- publicKey: deps.identity.publicKey,
1564
+ address: deps.identity.l1Address,
1565
+ publicKey: deps.identity.chainPubkey,
1564
1566
  privateKey: deps.identity.privateKey,
1565
1567
  path: "m/0",
1566
1568
  index: 0
@@ -1568,7 +1570,7 @@ var L1PaymentsModule = class {
1568
1570
  ]
1569
1571
  };
1570
1572
  for (const addr of this._addresses) {
1571
- if (addr !== deps.identity.address) {
1573
+ if (addr !== deps.identity.l1Address) {
1572
1574
  this._wallet.addresses.push({
1573
1575
  address: addr,
1574
1576
  path: null,
@@ -1591,18 +1593,64 @@ var L1PaymentsModule = class {
1591
1593
  this._addresses = [];
1592
1594
  this._wallet = void 0;
1593
1595
  }
1596
+ /**
1597
+ * Check if a string looks like an L1 address (alpha1... or alphat1...)
1598
+ */
1599
+ isL1Address(value) {
1600
+ return value.startsWith("alpha1") || value.startsWith("alphat1");
1601
+ }
1602
+ /**
1603
+ * Resolve recipient to L1 address
1604
+ * Supports: L1 address (alpha1...), nametag (with or without @)
1605
+ */
1606
+ async resolveL1Address(recipient) {
1607
+ if (recipient.startsWith("@")) {
1608
+ const nametag = recipient.slice(1);
1609
+ return this.resolveNametagToL1Address(nametag);
1610
+ }
1611
+ if (this.isL1Address(recipient)) {
1612
+ return recipient;
1613
+ }
1614
+ try {
1615
+ const l1Address = await this.resolveNametagToL1Address(recipient);
1616
+ return l1Address;
1617
+ } catch {
1618
+ throw new Error(
1619
+ `Recipient "${recipient}" is not a valid nametag or L1 address. Use @nametag for explicit nametag or a valid alpha1... address.`
1620
+ );
1621
+ }
1622
+ }
1623
+ /**
1624
+ * Resolve nametag to L1 address using transport provider
1625
+ */
1626
+ async resolveNametagToL1Address(nametag) {
1627
+ if (!this._transport?.resolveNametagInfo) {
1628
+ throw new Error("Transport provider does not support nametag resolution");
1629
+ }
1630
+ const info = await this._transport.resolveNametagInfo(nametag);
1631
+ if (!info) {
1632
+ throw new Error(`Nametag not found: ${nametag}`);
1633
+ }
1634
+ if (!info.l1Address) {
1635
+ throw new Error(
1636
+ `Nametag @${nametag} does not have L1 address information. The owner needs to update their nametag registration.`
1637
+ );
1638
+ }
1639
+ return info.l1Address;
1640
+ }
1594
1641
  async send(request) {
1595
1642
  this.ensureInitialized();
1596
1643
  if (!this._wallet || !this._identity) {
1597
1644
  return { success: false, error: "No wallet available" };
1598
1645
  }
1599
1646
  try {
1647
+ const recipientAddress = await this.resolveL1Address(request.to);
1600
1648
  const amountAlpha = parseInt(request.amount, 10) / 1e8;
1601
1649
  const results = await sendAlpha(
1602
1650
  this._wallet,
1603
- request.to,
1651
+ recipientAddress,
1604
1652
  amountAlpha,
1605
- this._identity.address
1653
+ this._identity.l1Address
1606
1654
  );
1607
1655
  if (results && results.length > 0) {
1608
1656
  const txids = results.map((r) => r.txid);
@@ -1824,8 +1872,8 @@ var L1PaymentsModule = class {
1824
1872
  }
1825
1873
  _getWatchedAddresses() {
1826
1874
  const addresses = [...this._addresses];
1827
- if (this._identity?.address && !addresses.includes(this._identity.address)) {
1828
- addresses.unshift(this._identity.address);
1875
+ if (this._identity?.l1Address && !addresses.includes(this._identity.l1Address)) {
1876
+ addresses.unshift(this._identity.l1Address);
1829
1877
  }
1830
1878
  return addresses;
1831
1879
  }
@@ -2302,48 +2350,55 @@ var NametagMinter = class {
2302
2350
  // constants.ts
2303
2351
  var STORAGE_PREFIX = "sphere_";
2304
2352
  var DEFAULT_ENCRYPTION_KEY = "sphere-default-key";
2305
- var STORAGE_KEYS = {
2353
+ var STORAGE_KEYS_GLOBAL = {
2306
2354
  /** Encrypted BIP39 mnemonic */
2307
- MNEMONIC: `${STORAGE_PREFIX}mnemonic`,
2355
+ MNEMONIC: "mnemonic",
2308
2356
  /** Encrypted master private key */
2309
- MASTER_KEY: `${STORAGE_PREFIX}master_key`,
2357
+ MASTER_KEY: "master_key",
2310
2358
  /** BIP32 chain code */
2311
- CHAIN_CODE: `${STORAGE_PREFIX}chain_code`,
2359
+ CHAIN_CODE: "chain_code",
2312
2360
  /** HD derivation path (full path like m/44'/0'/0'/0/0) */
2313
- DERIVATION_PATH: `${STORAGE_PREFIX}derivation_path`,
2361
+ DERIVATION_PATH: "derivation_path",
2314
2362
  /** Base derivation path (like m/44'/0'/0' without chain/index) */
2315
- BASE_PATH: `${STORAGE_PREFIX}base_path`,
2363
+ BASE_PATH: "base_path",
2316
2364
  /** Derivation mode: bip32, wif_hmac, legacy_hmac */
2317
- DERIVATION_MODE: `${STORAGE_PREFIX}derivation_mode`,
2365
+ DERIVATION_MODE: "derivation_mode",
2318
2366
  /** Wallet source: mnemonic, file, unknown */
2319
- WALLET_SOURCE: `${STORAGE_PREFIX}wallet_source`,
2367
+ WALLET_SOURCE: "wallet_source",
2320
2368
  /** Wallet existence flag */
2321
- WALLET_EXISTS: `${STORAGE_PREFIX}wallet_exists`,
2322
- /** Registered nametag (legacy - single address) */
2323
- NAMETAG: `${STORAGE_PREFIX}nametag`,
2369
+ WALLET_EXISTS: "wallet_exists",
2324
2370
  /** Current active address index */
2325
- CURRENT_ADDRESS_INDEX: `${STORAGE_PREFIX}current_address_index`,
2326
- /** Address nametags map (JSON: { "0": "alice", "1": "bob" }) */
2327
- ADDRESS_NAMETAGS: `${STORAGE_PREFIX}address_nametags`,
2328
- /** Token data */
2329
- TOKENS: `${STORAGE_PREFIX}tokens`,
2330
- /** Pending transfers */
2331
- PENDING_TRANSFERS: `${STORAGE_PREFIX}pending_transfers`,
2332
- /** Transfer outbox */
2333
- OUTBOX: `${STORAGE_PREFIX}outbox`,
2334
- /** Conversations */
2335
- CONVERSATIONS: `${STORAGE_PREFIX}conversations`,
2336
- /** Messages */
2337
- MESSAGES: `${STORAGE_PREFIX}messages`,
2338
- /** Transaction history */
2339
- TRANSACTION_HISTORY: `${STORAGE_PREFIX}transaction_history`,
2340
- /** Archived tokens (spent token history) */
2341
- ARCHIVED_TOKENS: `${STORAGE_PREFIX}archived_tokens`,
2342
- /** Tombstones (records of deleted/spent tokens) */
2343
- TOMBSTONES: `${STORAGE_PREFIX}tombstones`,
2344
- /** Forked tokens (alternative histories) */
2345
- FORKED_TOKENS: `${STORAGE_PREFIX}forked_tokens`
2371
+ CURRENT_ADDRESS_INDEX: "current_address_index",
2372
+ /** Index of address nametags (JSON: { "0": "alice", "1": "bob" }) - for discovery */
2373
+ ADDRESS_NAMETAGS: "address_nametags"
2374
+ };
2375
+ var STORAGE_KEYS_ADDRESS = {
2376
+ /** Pending transfers for this address */
2377
+ PENDING_TRANSFERS: "pending_transfers",
2378
+ /** Transfer outbox for this address */
2379
+ OUTBOX: "outbox",
2380
+ /** Conversations for this address */
2381
+ CONVERSATIONS: "conversations",
2382
+ /** Messages for this address */
2383
+ MESSAGES: "messages",
2384
+ /** Transaction history for this address */
2385
+ TRANSACTION_HISTORY: "transaction_history"
2386
+ };
2387
+ var STORAGE_KEYS = {
2388
+ ...STORAGE_KEYS_GLOBAL,
2389
+ ...STORAGE_KEYS_ADDRESS
2346
2390
  };
2391
+ function getAddressId(directAddress) {
2392
+ let hash = directAddress;
2393
+ if (hash.startsWith("DIRECT://")) {
2394
+ hash = hash.slice(9);
2395
+ } else if (hash.startsWith("DIRECT:")) {
2396
+ hash = hash.slice(7);
2397
+ }
2398
+ const first = hash.slice(0, 6).toLowerCase();
2399
+ const last = hash.slice(-6).toLowerCase();
2400
+ return `DIRECT_${first}_${last}`;
2401
+ }
2347
2402
  var DEFAULT_NOSTR_RELAYS = [
2348
2403
  "wss://relay.unicity.network",
2349
2404
  "wss://relay.damus.io",
@@ -2606,6 +2661,7 @@ function txfToToken(tokenId, txf) {
2606
2661
  coinId,
2607
2662
  symbol: isNft ? "NFT" : "UCT",
2608
2663
  name: isNft ? "NFT" : "Token",
2664
+ decimals: isNft ? 0 : 8,
2609
2665
  amount: totalAmount.toString(),
2610
2666
  status: determineTokenStatus(txf),
2611
2667
  createdAt: now,
@@ -2620,9 +2676,6 @@ async function buildTxfStorageData(tokens, meta, options) {
2620
2676
  formatVersion: "2.0"
2621
2677
  }
2622
2678
  };
2623
- if (options?.nametag) {
2624
- storageData._nametag = options.nametag;
2625
- }
2626
2679
  if (options?.tombstones && options.tombstones.length > 0) {
2627
2680
  storageData._tombstones = options.tombstones;
2628
2681
  }
@@ -2813,23 +2866,354 @@ function countCommittedTransactions(token) {
2813
2866
  }
2814
2867
  }
2815
2868
 
2869
+ // registry/token-registry.testnet.json
2870
+ var token_registry_testnet_default = [
2871
+ {
2872
+ network: "unicity:testnet",
2873
+ assetKind: "non-fungible",
2874
+ name: "unicity",
2875
+ description: "Unicity testnet token type",
2876
+ id: "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509"
2877
+ },
2878
+ {
2879
+ network: "unicity:testnet",
2880
+ assetKind: "fungible",
2881
+ name: "unicity",
2882
+ symbol: "UCT",
2883
+ decimals: 18,
2884
+ description: "Unicity testnet native coin",
2885
+ icons: [
2886
+ { url: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity_logo_32.png" }
2887
+ ],
2888
+ id: "455ad8720656b08e8dbd5bac1f3c73eeea5431565f6c1c3af742b1aa12d41d89"
2889
+ },
2890
+ {
2891
+ network: "unicity:testnet",
2892
+ assetKind: "fungible",
2893
+ name: "unicity-usd",
2894
+ symbol: "USDU",
2895
+ decimals: 6,
2896
+ description: "Unicity testnet USD stablecoin",
2897
+ icons: [
2898
+ { url: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/usdu_logo_32.png" }
2899
+ ],
2900
+ id: "8f0f3d7a5e7297be0ee98c63b81bcebb2740f43f616566fc290f9823a54f52d7"
2901
+ },
2902
+ {
2903
+ network: "unicity:testnet",
2904
+ assetKind: "fungible",
2905
+ name: "unicity-eur",
2906
+ symbol: "EURU",
2907
+ decimals: 6,
2908
+ description: "Unicity testnet EUR stablecoin",
2909
+ icons: [
2910
+ { url: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/euru_logo_32.png" }
2911
+ ],
2912
+ id: "5e160d5e9fdbb03b553fb9c3f6e6c30efa41fa807be39fb4f18e43776e492925"
2913
+ },
2914
+ {
2915
+ network: "unicity:testnet",
2916
+ assetKind: "fungible",
2917
+ name: "solana",
2918
+ symbol: "SOL",
2919
+ decimals: 9,
2920
+ description: "Solana testnet coin on Unicity",
2921
+ icons: [
2922
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/sol.svg" },
2923
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/sol.png" }
2924
+ ],
2925
+ id: "dee5f8ce778562eec90e9c38a91296a023210ccc76ff4c29d527ac3eb64ade93"
2926
+ },
2927
+ {
2928
+ network: "unicity:testnet",
2929
+ assetKind: "fungible",
2930
+ name: "bitcoin",
2931
+ symbol: "BTC",
2932
+ decimals: 8,
2933
+ description: "Bitcoin testnet coin on Unicity",
2934
+ icons: [
2935
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/btc.svg" },
2936
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/btc.png" }
2937
+ ],
2938
+ id: "86bc190fcf7b2d07c6078de93db803578760148b16d4431aa2f42a3241ff0daa"
2939
+ },
2940
+ {
2941
+ network: "unicity:testnet",
2942
+ assetKind: "fungible",
2943
+ name: "ethereum",
2944
+ symbol: "ETH",
2945
+ decimals: 18,
2946
+ description: "Ethereum testnet coin on Unicity",
2947
+ icons: [
2948
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/eth.svg" },
2949
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/eth.png" }
2950
+ ],
2951
+ id: "3c2450f2fd867e7bb60c6a69d7ad0e53ce967078c201a3ecaa6074ed4c0deafb"
2952
+ },
2953
+ {
2954
+ network: "unicity:testnet",
2955
+ assetKind: "fungible",
2956
+ name: "alpha_test",
2957
+ symbol: "ALPHT",
2958
+ decimals: 8,
2959
+ description: "ALPHA testnet coin on Unicity",
2960
+ icons: [
2961
+ { url: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/alpha_coin.png" }
2962
+ ],
2963
+ id: "cde78ded16ef65818a51f43138031c4284e519300ab0cb60c30a8f9078080e5f"
2964
+ },
2965
+ {
2966
+ network: "unicity:testnet",
2967
+ assetKind: "fungible",
2968
+ name: "tether",
2969
+ symbol: "USDT",
2970
+ decimals: 6,
2971
+ description: "Tether (Ethereum) testnet coin on Unicity",
2972
+ icons: [
2973
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/usdt.svg" },
2974
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/usdt.png" }
2975
+ ],
2976
+ id: "40d25444648418fe7efd433e147187a3a6adf049ac62bc46038bda5b960bf690"
2977
+ },
2978
+ {
2979
+ network: "unicity:testnet",
2980
+ assetKind: "fungible",
2981
+ name: "usd-coin",
2982
+ symbol: "USDC",
2983
+ decimals: 6,
2984
+ description: "USDC (Ethereum) testnet coin on Unicity",
2985
+ icons: [
2986
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/svg/icon/usdc.svg" },
2987
+ { url: "https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/32/icon/usdc.png" }
2988
+ ],
2989
+ id: "2265121770fa6f41131dd9a6cc571e28679263d09a53eb2642e145b5b9a5b0a2"
2990
+ }
2991
+ ];
2992
+
2993
+ // registry/TokenRegistry.ts
2994
+ var TokenRegistry = class _TokenRegistry {
2995
+ static instance = null;
2996
+ definitionsById;
2997
+ definitionsBySymbol;
2998
+ definitionsByName;
2999
+ constructor() {
3000
+ this.definitionsById = /* @__PURE__ */ new Map();
3001
+ this.definitionsBySymbol = /* @__PURE__ */ new Map();
3002
+ this.definitionsByName = /* @__PURE__ */ new Map();
3003
+ this.loadRegistry();
3004
+ }
3005
+ /**
3006
+ * Get singleton instance of TokenRegistry
3007
+ */
3008
+ static getInstance() {
3009
+ if (!_TokenRegistry.instance) {
3010
+ _TokenRegistry.instance = new _TokenRegistry();
3011
+ }
3012
+ return _TokenRegistry.instance;
3013
+ }
3014
+ /**
3015
+ * Reset the singleton instance (useful for testing)
3016
+ */
3017
+ static resetInstance() {
3018
+ _TokenRegistry.instance = null;
3019
+ }
3020
+ /**
3021
+ * Load registry data from bundled JSON
3022
+ */
3023
+ loadRegistry() {
3024
+ const definitions = token_registry_testnet_default;
3025
+ for (const def of definitions) {
3026
+ const idLower = def.id.toLowerCase();
3027
+ this.definitionsById.set(idLower, def);
3028
+ if (def.symbol) {
3029
+ this.definitionsBySymbol.set(def.symbol.toUpperCase(), def);
3030
+ }
3031
+ this.definitionsByName.set(def.name.toLowerCase(), def);
3032
+ }
3033
+ }
3034
+ // ===========================================================================
3035
+ // Lookup Methods
3036
+ // ===========================================================================
3037
+ /**
3038
+ * Get token definition by hex coin ID
3039
+ * @param coinId - 64-character hex string
3040
+ * @returns Token definition or undefined if not found
3041
+ */
3042
+ getDefinition(coinId) {
3043
+ if (!coinId) return void 0;
3044
+ return this.definitionsById.get(coinId.toLowerCase());
3045
+ }
3046
+ /**
3047
+ * Get token definition by symbol (e.g., "UCT", "BTC")
3048
+ * @param symbol - Token symbol (case-insensitive)
3049
+ * @returns Token definition or undefined if not found
3050
+ */
3051
+ getDefinitionBySymbol(symbol) {
3052
+ if (!symbol) return void 0;
3053
+ return this.definitionsBySymbol.get(symbol.toUpperCase());
3054
+ }
3055
+ /**
3056
+ * Get token definition by name (e.g., "bitcoin", "ethereum")
3057
+ * @param name - Token name (case-insensitive)
3058
+ * @returns Token definition or undefined if not found
3059
+ */
3060
+ getDefinitionByName(name) {
3061
+ if (!name) return void 0;
3062
+ return this.definitionsByName.get(name.toLowerCase());
3063
+ }
3064
+ /**
3065
+ * Get token symbol for a coin ID
3066
+ * @param coinId - 64-character hex string
3067
+ * @returns Symbol (e.g., "UCT") or truncated ID if not found
3068
+ */
3069
+ getSymbol(coinId) {
3070
+ const def = this.getDefinition(coinId);
3071
+ if (def?.symbol) {
3072
+ return def.symbol;
3073
+ }
3074
+ return coinId.slice(0, 6).toUpperCase();
3075
+ }
3076
+ /**
3077
+ * Get token name for a coin ID
3078
+ * @param coinId - 64-character hex string
3079
+ * @returns Name (e.g., "Bitcoin") or coin ID if not found
3080
+ */
3081
+ getName(coinId) {
3082
+ const def = this.getDefinition(coinId);
3083
+ if (def?.name) {
3084
+ return def.name.charAt(0).toUpperCase() + def.name.slice(1);
3085
+ }
3086
+ return coinId;
3087
+ }
3088
+ /**
3089
+ * Get decimal places for a coin ID
3090
+ * @param coinId - 64-character hex string
3091
+ * @returns Decimals or 0 if not found
3092
+ */
3093
+ getDecimals(coinId) {
3094
+ const def = this.getDefinition(coinId);
3095
+ return def?.decimals ?? 0;
3096
+ }
3097
+ /**
3098
+ * Get icon URL for a coin ID
3099
+ * @param coinId - 64-character hex string
3100
+ * @param preferPng - Prefer PNG format over SVG
3101
+ * @returns Icon URL or null if not found
3102
+ */
3103
+ getIconUrl(coinId, preferPng = true) {
3104
+ const def = this.getDefinition(coinId);
3105
+ if (!def?.icons || def.icons.length === 0) {
3106
+ return null;
3107
+ }
3108
+ if (preferPng) {
3109
+ const pngIcon = def.icons.find((i) => i.url.toLowerCase().includes(".png"));
3110
+ if (pngIcon) return pngIcon.url;
3111
+ }
3112
+ return def.icons[0].url;
3113
+ }
3114
+ /**
3115
+ * Check if a coin ID is known in the registry
3116
+ * @param coinId - 64-character hex string
3117
+ * @returns true if the coin is in the registry
3118
+ */
3119
+ isKnown(coinId) {
3120
+ return this.definitionsById.has(coinId.toLowerCase());
3121
+ }
3122
+ /**
3123
+ * Get all token definitions
3124
+ * @returns Array of all token definitions
3125
+ */
3126
+ getAllDefinitions() {
3127
+ return Array.from(this.definitionsById.values());
3128
+ }
3129
+ /**
3130
+ * Get all fungible token definitions
3131
+ * @returns Array of fungible token definitions
3132
+ */
3133
+ getFungibleTokens() {
3134
+ return this.getAllDefinitions().filter((def) => def.assetKind === "fungible");
3135
+ }
3136
+ /**
3137
+ * Get all non-fungible token definitions
3138
+ * @returns Array of non-fungible token definitions
3139
+ */
3140
+ getNonFungibleTokens() {
3141
+ return this.getAllDefinitions().filter((def) => def.assetKind === "non-fungible");
3142
+ }
3143
+ /**
3144
+ * Get coin ID by symbol
3145
+ * @param symbol - Token symbol (e.g., "UCT")
3146
+ * @returns Coin ID hex string or undefined if not found
3147
+ */
3148
+ getCoinIdBySymbol(symbol) {
3149
+ const def = this.getDefinitionBySymbol(symbol);
3150
+ return def?.id;
3151
+ }
3152
+ /**
3153
+ * Get coin ID by name
3154
+ * @param name - Token name (e.g., "bitcoin")
3155
+ * @returns Coin ID hex string or undefined if not found
3156
+ */
3157
+ getCoinIdByName(name) {
3158
+ const def = this.getDefinitionByName(name);
3159
+ return def?.id;
3160
+ }
3161
+ };
3162
+ function getTokenDefinition(coinId) {
3163
+ return TokenRegistry.getInstance().getDefinition(coinId);
3164
+ }
3165
+ function getTokenSymbol(coinId) {
3166
+ return TokenRegistry.getInstance().getSymbol(coinId);
3167
+ }
3168
+ function getTokenName(coinId) {
3169
+ return TokenRegistry.getInstance().getName(coinId);
3170
+ }
3171
+ function getTokenDecimals(coinId) {
3172
+ return TokenRegistry.getInstance().getDecimals(coinId);
3173
+ }
3174
+ function getTokenIconUrl(coinId, preferPng = true) {
3175
+ return TokenRegistry.getInstance().getIconUrl(coinId, preferPng);
3176
+ }
3177
+ function isKnownToken(coinId) {
3178
+ return TokenRegistry.getInstance().isKnown(coinId);
3179
+ }
3180
+ function getCoinIdBySymbol(symbol) {
3181
+ return TokenRegistry.getInstance().getCoinIdBySymbol(symbol);
3182
+ }
3183
+ function getCoinIdByName(name) {
3184
+ return TokenRegistry.getInstance().getCoinIdByName(name);
3185
+ }
3186
+
2816
3187
  // modules/payments/PaymentsModule.ts
2817
3188
  import { Token as SdkToken2 } from "@unicitylabs/state-transition-sdk/lib/token/Token";
2818
- import { TokenId as TokenId3 } from "@unicitylabs/state-transition-sdk/lib/token/TokenId";
2819
3189
  import { CoinId as CoinId3 } from "@unicitylabs/state-transition-sdk/lib/token/fungible/CoinId";
2820
3190
  import { TransferCommitment as TransferCommitment2 } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment";
2821
3191
  import { TransferTransaction } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction";
2822
3192
  import { SigningService } from "@unicitylabs/state-transition-sdk/lib/sign/SigningService";
2823
- import { ProxyAddress } from "@unicitylabs/state-transition-sdk/lib/address/ProxyAddress";
2824
3193
  import { AddressScheme } from "@unicitylabs/state-transition-sdk/lib/address/AddressScheme";
2825
3194
  import { UnmaskedPredicate as UnmaskedPredicate3 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicate";
2826
3195
  import { TokenState as TokenState3 } from "@unicitylabs/state-transition-sdk/lib/token/TokenState";
2827
3196
  import { HashAlgorithm as HashAlgorithm3 } from "@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm";
3197
+ function enrichWithRegistry(info) {
3198
+ const registry = TokenRegistry.getInstance();
3199
+ const def = registry.getDefinition(info.coinId);
3200
+ if (def) {
3201
+ return {
3202
+ ...info,
3203
+ symbol: def.symbol || info.symbol,
3204
+ name: def.name.charAt(0).toUpperCase() + def.name.slice(1),
3205
+ decimals: def.decimals ?? 0,
3206
+ iconUrl: registry.getIconUrl(info.coinId) ?? void 0
3207
+ };
3208
+ }
3209
+ return info;
3210
+ }
2828
3211
  async function parseTokenInfo(tokenData) {
2829
3212
  const defaultInfo = {
2830
3213
  coinId: "ALPHA",
2831
3214
  symbol: "ALPHA",
2832
3215
  name: "Alpha Token",
3216
+ decimals: 0,
2833
3217
  amount: "0"
2834
3218
  };
2835
3219
  try {
@@ -2850,23 +3234,25 @@ async function parseTokenInfo(tokenData) {
2850
3234
  }
2851
3235
  if (coinIdObj instanceof CoinId3) {
2852
3236
  const coinIdHex = coinIdObj.toJSON();
2853
- return {
3237
+ return enrichWithRegistry({
2854
3238
  coinId: coinIdHex,
2855
3239
  symbol: coinIdHex.slice(0, 8),
2856
3240
  name: `Token ${coinIdHex.slice(0, 8)}`,
3241
+ decimals: 0,
2857
3242
  amount: String(amount ?? "0"),
2858
3243
  tokenId: defaultInfo.tokenId
2859
- };
3244
+ });
2860
3245
  } else if (coinIdObj && typeof coinIdObj === "object" && "bytes" in coinIdObj) {
2861
3246
  const bytes = coinIdObj.bytes;
2862
3247
  const coinIdHex = Buffer.isBuffer(bytes) ? bytes.toString("hex") : Array.isArray(bytes) ? Buffer.from(bytes).toString("hex") : String(bytes);
2863
- return {
3248
+ return enrichWithRegistry({
2864
3249
  coinId: coinIdHex,
2865
3250
  symbol: coinIdHex.slice(0, 8),
2866
3251
  name: `Token ${coinIdHex.slice(0, 8)}`,
3252
+ decimals: 0,
2867
3253
  amount: String(amount ?? "0"),
2868
3254
  tokenId: defaultInfo.tokenId
2869
- };
3255
+ });
2870
3256
  }
2871
3257
  }
2872
3258
  }
@@ -2881,13 +3267,14 @@ async function parseTokenInfo(tokenData) {
2881
3267
  if (Array.isArray(firstEntry) && firstEntry.length === 2) {
2882
3268
  const [coinIdHex, amount] = firstEntry;
2883
3269
  const coinIdStr = typeof coinIdHex === "string" ? coinIdHex : String(coinIdHex);
2884
- return {
3270
+ return enrichWithRegistry({
2885
3271
  coinId: coinIdStr,
2886
3272
  symbol: coinIdStr.slice(0, 8),
2887
3273
  name: `Token ${coinIdStr.slice(0, 8)}`,
3274
+ decimals: 0,
2888
3275
  amount: String(amount),
2889
3276
  tokenId: defaultInfo.tokenId
2890
- };
3277
+ });
2891
3278
  }
2892
3279
  }
2893
3280
  }
@@ -2902,25 +3289,27 @@ async function parseTokenInfo(tokenData) {
2902
3289
  const firstEntry = coinData[0];
2903
3290
  if (Array.isArray(firstEntry) && firstEntry.length === 2) {
2904
3291
  const [coinIdHex, amount] = firstEntry;
2905
- return {
3292
+ return enrichWithRegistry({
2906
3293
  coinId: String(coinIdHex),
2907
3294
  symbol: String(coinIdHex).slice(0, 8),
2908
3295
  name: `Token ${String(coinIdHex).slice(0, 8)}`,
3296
+ decimals: 0,
2909
3297
  amount: String(amount),
2910
3298
  tokenId: genesis.tokenId
2911
- };
3299
+ });
2912
3300
  }
2913
3301
  } else if (typeof coinData === "object") {
2914
3302
  const coinEntries = Object.entries(coinData);
2915
3303
  if (coinEntries.length > 0) {
2916
3304
  const [coinId, amount] = coinEntries[0];
2917
- return {
3305
+ return enrichWithRegistry({
2918
3306
  coinId,
2919
3307
  symbol: coinId.slice(0, 8),
2920
3308
  name: `Token ${coinId.slice(0, 8)}`,
3309
+ decimals: 0,
2921
3310
  amount: String(amount),
2922
3311
  tokenId: genesis.tokenId
2923
- };
3312
+ });
2924
3313
  }
2925
3314
  }
2926
3315
  }
@@ -2934,25 +3323,27 @@ async function parseTokenInfo(tokenData) {
2934
3323
  const firstEntry = coinData[0];
2935
3324
  if (Array.isArray(firstEntry) && firstEntry.length === 2) {
2936
3325
  const [coinIdHex, amount] = firstEntry;
2937
- return {
3326
+ return enrichWithRegistry({
2938
3327
  coinId: String(coinIdHex),
2939
3328
  symbol: String(coinIdHex).slice(0, 8),
2940
3329
  name: `Token ${String(coinIdHex).slice(0, 8)}`,
3330
+ decimals: 0,
2941
3331
  amount: String(amount),
2942
3332
  tokenId: defaultInfo.tokenId
2943
- };
3333
+ });
2944
3334
  }
2945
3335
  } else if (typeof coinData === "object") {
2946
3336
  const coinEntries = Object.entries(coinData);
2947
3337
  if (coinEntries.length > 0) {
2948
3338
  const [coinId, amount] = coinEntries[0];
2949
- return {
3339
+ return enrichWithRegistry({
2950
3340
  coinId,
2951
3341
  symbol: coinId.slice(0, 8),
2952
3342
  name: `Token ${coinId.slice(0, 8)}`,
3343
+ decimals: 0,
2953
3344
  amount: String(amount),
2954
3345
  tokenId: defaultInfo.tokenId
2955
- };
3346
+ });
2956
3347
  }
2957
3348
  }
2958
3349
  }
@@ -3111,7 +3502,8 @@ var PaymentsModule = class {
3111
3502
  this.l1.initialize({
3112
3503
  identity: deps.identity,
3113
3504
  chainCode: deps.chainCode,
3114
- addresses: deps.l1Addresses
3505
+ addresses: deps.l1Addresses,
3506
+ transport: deps.transport
3115
3507
  });
3116
3508
  }
3117
3509
  this.unsubscribeTransfers = deps.transport.onTokenTransfer((transfer) => {
@@ -3133,37 +3525,24 @@ var PaymentsModule = class {
3133
3525
  */
3134
3526
  async load() {
3135
3527
  this.ensureInitialized();
3136
- const data = await this.deps.storage.get(STORAGE_KEYS.TOKENS);
3137
- if (data) {
3528
+ const providers = this.getTokenStorageProviders();
3529
+ for (const [id, provider] of providers) {
3138
3530
  try {
3139
- const parsed = JSON.parse(data);
3140
- const tokens = parsed.tokens || [];
3141
- this.tokens.clear();
3142
- for (const token of tokens) {
3143
- this.tokens.set(token.id, token);
3144
- }
3145
- if (Array.isArray(parsed.tombstones)) {
3146
- this.tombstones = parsed.tombstones.filter(
3147
- (t) => typeof t === "object" && t !== null && typeof t.tokenId === "string" && typeof t.stateHash === "string"
3148
- );
3149
- }
3150
- if (parsed.archivedTokens && typeof parsed.archivedTokens === "object") {
3151
- this.archivedTokens = new Map(Object.entries(parsed.archivedTokens));
3152
- }
3153
- if (parsed.forkedTokens && typeof parsed.forkedTokens === "object") {
3154
- this.forkedTokens = new Map(Object.entries(parsed.forkedTokens));
3155
- }
3156
- if (parsed.nametag) {
3157
- this.nametag = parsed.nametag;
3531
+ const result = await provider.load();
3532
+ if (result.success && result.data) {
3533
+ this.loadFromStorageData(result.data);
3534
+ this.log(`Loaded from provider ${id}: ${this.tokens.size} tokens`);
3535
+ break;
3158
3536
  }
3159
- this.log(`Loaded ${this.tokens.size} tokens, ${this.tombstones.length} tombstones, ${this.archivedTokens.size} archived`);
3160
3537
  } catch (err) {
3161
- console.error("[Payments] Failed to parse stored data:", err);
3538
+ console.error(`[Payments] Failed to load from provider ${id}:`, err);
3162
3539
  }
3163
3540
  }
3164
- await this.loadTokensFromFileStorage();
3541
+ if (this.tokens.size === 0) {
3542
+ await this.loadTokensFromFileStorage();
3543
+ }
3165
3544
  await this.loadNametagFromFileStorage();
3166
- const historyData = await this.deps.storage.get(STORAGE_KEYS.TRANSACTION_HISTORY);
3545
+ const historyData = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY);
3167
3546
  if (historyData) {
3168
3547
  try {
3169
3548
  this.transactionHistory = JSON.parse(historyData);
@@ -3171,7 +3550,7 @@ var PaymentsModule = class {
3171
3550
  this.transactionHistory = [];
3172
3551
  }
3173
3552
  }
3174
- const pending2 = await this.deps.storage.get(STORAGE_KEYS.PENDING_TRANSFERS);
3553
+ const pending2 = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS);
3175
3554
  if (pending2) {
3176
3555
  const transfers = JSON.parse(pending2);
3177
3556
  for (const transfer of transfers) {
@@ -3269,6 +3648,8 @@ var PaymentsModule = class {
3269
3648
  coinId: request.coinId,
3270
3649
  symbol: this.getCoinSymbol(request.coinId),
3271
3650
  name: this.getCoinName(request.coinId),
3651
+ decimals: this.getCoinDecimals(request.coinId),
3652
+ iconUrl: this.getCoinIconUrl(request.coinId),
3272
3653
  amount: splitPlan.remainderAmount.toString(),
3273
3654
  status: "confirmed",
3274
3655
  createdAt: Date.now(),
@@ -3337,20 +3718,25 @@ var PaymentsModule = class {
3337
3718
  * Get coin symbol from coinId
3338
3719
  */
3339
3720
  getCoinSymbol(coinId) {
3340
- const symbols = {
3341
- "UCT": "UCT"
3342
- // Add more as needed
3343
- };
3344
- return symbols[coinId] || coinId.slice(0, 6).toUpperCase();
3721
+ return TokenRegistry.getInstance().getSymbol(coinId);
3345
3722
  }
3346
3723
  /**
3347
3724
  * Get coin name from coinId
3348
3725
  */
3349
3726
  getCoinName(coinId) {
3350
- const names = {
3351
- "UCT": "Unicity Token"
3352
- };
3353
- return names[coinId] || coinId;
3727
+ return TokenRegistry.getInstance().getName(coinId);
3728
+ }
3729
+ /**
3730
+ * Get coin decimals from coinId
3731
+ */
3732
+ getCoinDecimals(coinId) {
3733
+ return TokenRegistry.getInstance().getDecimals(coinId);
3734
+ }
3735
+ /**
3736
+ * Get coin icon URL from coinId
3737
+ */
3738
+ getCoinIconUrl(coinId) {
3739
+ return TokenRegistry.getInstance().getIconUrl(coinId) ?? void 0;
3354
3740
  }
3355
3741
  // ===========================================================================
3356
3742
  // Public API - Payment Requests
@@ -3508,7 +3894,7 @@ var PaymentsModule = class {
3508
3894
  }
3509
3895
  const request = {
3510
3896
  id: transportRequest.id,
3511
- senderPubkey: transportRequest.senderPubkey,
3897
+ senderPubkey: transportRequest.senderTransportPubkey,
3512
3898
  amount: transportRequest.request.amount,
3513
3899
  coinId: transportRequest.request.coinId,
3514
3900
  symbol: transportRequest.request.coinId,
@@ -3620,7 +4006,7 @@ var PaymentsModule = class {
3620
4006
  }
3621
4007
  const response = {
3622
4008
  id: transportResponse.id,
3623
- responderPubkey: transportResponse.responderPubkey,
4009
+ responderPubkey: transportResponse.responderTransportPubkey,
3624
4010
  requestId: transportResponse.response.requestId,
3625
4011
  responseType: transportResponse.response.responseType,
3626
4012
  message: transportResponse.response.message,
@@ -3825,6 +4211,8 @@ var PaymentsModule = class {
3825
4211
  coinId: tokenInfo.coinId,
3826
4212
  symbol: tokenInfo.symbol,
3827
4213
  name: tokenInfo.name,
4214
+ decimals: tokenInfo.decimals,
4215
+ iconUrl: tokenInfo.iconUrl,
3828
4216
  amount: tokenInfo.amount,
3829
4217
  status: "confirmed",
3830
4218
  createdAt: data.receivedAt || Date.now(),
@@ -4080,7 +4468,7 @@ var PaymentsModule = class {
4080
4468
  };
4081
4469
  this.transactionHistory.push(historyEntry);
4082
4470
  await this.deps.storage.set(
4083
- STORAGE_KEYS.TRANSACTION_HISTORY,
4471
+ STORAGE_KEYS_ADDRESS.TRANSACTION_HISTORY,
4084
4472
  JSON.stringify(this.transactionHistory)
4085
4473
  );
4086
4474
  }
@@ -4374,6 +4762,23 @@ var PaymentsModule = class {
4374
4762
  // ===========================================================================
4375
4763
  // Private: Transfer Operations
4376
4764
  // ===========================================================================
4765
+ /**
4766
+ * Detect if a string is an L3 address (not a nametag)
4767
+ * Returns true for: hex pubkeys (64+ chars), PROXY:, DIRECT: prefixed addresses
4768
+ */
4769
+ isL3Address(value) {
4770
+ if (value.startsWith("PROXY:") || value.startsWith("DIRECT:")) {
4771
+ return true;
4772
+ }
4773
+ if (value.length >= 64 && /^[0-9a-fA-F]+$/.test(value)) {
4774
+ return true;
4775
+ }
4776
+ return false;
4777
+ }
4778
+ /**
4779
+ * Resolve recipient to Nostr pubkey for messaging
4780
+ * Supports: nametag (with or without @), hex pubkey
4781
+ */
4377
4782
  async resolveRecipient(recipient) {
4378
4783
  if (recipient.startsWith("@")) {
4379
4784
  const nametag = recipient.slice(1);
@@ -4383,7 +4788,19 @@ var PaymentsModule = class {
4383
4788
  }
4384
4789
  return pubkey;
4385
4790
  }
4386
- return recipient;
4791
+ if (this.isL3Address(recipient)) {
4792
+ return recipient;
4793
+ }
4794
+ if (this.deps?.transport.resolveNametag) {
4795
+ const pubkey = await this.deps.transport.resolveNametag(recipient);
4796
+ if (pubkey) {
4797
+ this.log(`Resolved "${recipient}" as nametag to pubkey`);
4798
+ return pubkey;
4799
+ }
4800
+ }
4801
+ throw new Error(
4802
+ `Recipient "${recipient}" is not a valid nametag or address. Use @nametag for explicit nametag or a valid hex pubkey/PROXY:/DIRECT: address.`
4803
+ );
4387
4804
  }
4388
4805
  /**
4389
4806
  * Create SDK TransferCommitment for a token transfer
@@ -4415,19 +4832,74 @@ var PaymentsModule = class {
4415
4832
  return SigningService.createFromSecret(privateKeyBytes);
4416
4833
  }
4417
4834
  /**
4418
- * Resolve recipient to IAddress
4835
+ * Create DirectAddress from a public key using UnmaskedPredicateReference
4836
+ */
4837
+ async createDirectAddressFromPubkey(pubkeyHex) {
4838
+ const { UnmaskedPredicateReference: UnmaskedPredicateReference3 } = await import("@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference");
4839
+ const { TokenType: TokenType3 } = await import("@unicitylabs/state-transition-sdk/lib/token/TokenType");
4840
+ const UNICITY_TOKEN_TYPE_HEX3 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
4841
+ const tokenType = new TokenType3(Buffer.from(UNICITY_TOKEN_TYPE_HEX3, "hex"));
4842
+ const pubkeyBytes = new Uint8Array(
4843
+ pubkeyHex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
4844
+ );
4845
+ const addressRef = await UnmaskedPredicateReference3.create(
4846
+ tokenType,
4847
+ "secp256k1",
4848
+ pubkeyBytes,
4849
+ HashAlgorithm3.SHA256
4850
+ );
4851
+ return addressRef.toAddress();
4852
+ }
4853
+ /**
4854
+ * Resolve nametag to 33-byte compressed public key using resolveNametagInfo
4855
+ * Returns null if nametag not found or publicKey not available
4856
+ */
4857
+ async resolveNametagToPublicKey(nametag) {
4858
+ if (!this.deps?.transport.resolveNametagInfo) {
4859
+ this.log("resolveNametagInfo not available on transport");
4860
+ return null;
4861
+ }
4862
+ const info = await this.deps.transport.resolveNametagInfo(nametag);
4863
+ if (!info) {
4864
+ this.log(`Nametag "${nametag}" not found`);
4865
+ return null;
4866
+ }
4867
+ if (!info.chainPubkey) {
4868
+ this.log(`Nametag "${nametag}" has no 33-byte chainPubkey (legacy event)`);
4869
+ return null;
4870
+ }
4871
+ return info.chainPubkey;
4872
+ }
4873
+ /**
4874
+ * Resolve recipient to IAddress for L3 transfers
4875
+ * Supports: nametag (with or without @), PROXY:, DIRECT:, hex pubkey
4419
4876
  */
4420
4877
  async resolveRecipientAddress(recipient) {
4878
+ const { AddressFactory } = await import("@unicitylabs/state-transition-sdk/lib/address/AddressFactory");
4421
4879
  if (recipient.startsWith("@")) {
4422
4880
  const nametag = recipient.slice(1);
4423
- const tokenId2 = await TokenId3.fromNameTag(nametag);
4424
- return ProxyAddress.fromTokenId(tokenId2);
4881
+ const publicKey2 = await this.resolveNametagToPublicKey(nametag);
4882
+ if (publicKey2) {
4883
+ this.log(`Resolved @${nametag} to 33-byte publicKey for DirectAddress`);
4884
+ return this.createDirectAddressFromPubkey(publicKey2);
4885
+ }
4886
+ throw new Error(`Nametag "${nametag}" not found or missing publicKey`);
4425
4887
  }
4426
- const pubkeyBytes = new Uint8Array(
4427
- recipient.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
4888
+ if (recipient.startsWith("PROXY:") || recipient.startsWith("DIRECT:")) {
4889
+ return AddressFactory.createAddress(recipient);
4890
+ }
4891
+ if (recipient.length === 66 && /^[0-9a-fA-F]+$/.test(recipient)) {
4892
+ this.log(`Creating DirectAddress from 33-byte compressed pubkey`);
4893
+ return this.createDirectAddressFromPubkey(recipient);
4894
+ }
4895
+ const publicKey = await this.resolveNametagToPublicKey(recipient);
4896
+ if (publicKey) {
4897
+ this.log(`Resolved "${recipient}" as nametag to 33-byte publicKey for DirectAddress`);
4898
+ return this.createDirectAddressFromPubkey(publicKey);
4899
+ }
4900
+ throw new Error(
4901
+ `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.`
4428
4902
  );
4429
- const tokenId = new TokenId3(pubkeyBytes.slice(0, 32));
4430
- return ProxyAddress.fromTokenId(tokenId);
4431
4903
  }
4432
4904
  async handleIncomingTransfer(transfer) {
4433
4905
  try {
@@ -4485,7 +4957,39 @@ var PaymentsModule = class {
4485
4957
  }
4486
4958
  }
4487
4959
  } else {
4488
- tokenData = sourceTokenInput;
4960
+ this.log("Finalizing DIRECT address transfer for state tracking...");
4961
+ try {
4962
+ const signingService = await this.createSigningService();
4963
+ const transferSalt = transferTx.data.salt;
4964
+ const recipientPredicate = await UnmaskedPredicate3.create(
4965
+ sourceToken.id,
4966
+ sourceToken.type,
4967
+ signingService,
4968
+ HashAlgorithm3.SHA256,
4969
+ transferSalt
4970
+ );
4971
+ const recipientState = new TokenState3(recipientPredicate, null);
4972
+ const stClient = this.deps.oracle.getStateTransitionClient?.();
4973
+ const trustBase = this.deps.oracle.getTrustBase?.();
4974
+ if (!stClient || !trustBase) {
4975
+ this.log("Cannot finalize DIRECT transfer - missing client, using source token");
4976
+ tokenData = sourceTokenInput;
4977
+ } else {
4978
+ finalizedSdkToken = await stClient.finalizeTransaction(
4979
+ trustBase,
4980
+ sourceToken,
4981
+ recipientState,
4982
+ transferTx,
4983
+ []
4984
+ // No nametag tokens needed for DIRECT
4985
+ );
4986
+ tokenData = finalizedSdkToken.toJSON();
4987
+ this.log("DIRECT transfer finalized successfully");
4988
+ }
4989
+ } catch (finalizeError) {
4990
+ this.log("DIRECT finalization failed, using source token:", finalizeError);
4991
+ tokenData = sourceTokenInput;
4992
+ }
4489
4993
  }
4490
4994
  } else if (payload.token) {
4491
4995
  tokenData = payload.token;
@@ -4504,6 +5008,8 @@ var PaymentsModule = class {
4504
5008
  coinId: tokenInfo.coinId,
4505
5009
  symbol: tokenInfo.symbol,
4506
5010
  name: tokenInfo.name,
5011
+ decimals: tokenInfo.decimals,
5012
+ iconUrl: tokenInfo.iconUrl,
4507
5013
  amount: tokenInfo.amount,
4508
5014
  status: "confirmed",
4509
5015
  createdAt: Date.now(),
@@ -4519,7 +5025,7 @@ var PaymentsModule = class {
4519
5025
  await this.addToken(token);
4520
5026
  const incomingTransfer = {
4521
5027
  id: transfer.id,
4522
- senderPubkey: transfer.senderPubkey,
5028
+ senderPubkey: transfer.senderTransportPubkey,
4523
5029
  tokens: [token],
4524
5030
  memo: payload.memo,
4525
5031
  receivedAt: transfer.timestamp
@@ -4557,28 +5063,32 @@ var PaymentsModule = class {
4557
5063
  // Private: Storage
4558
5064
  // ===========================================================================
4559
5065
  async save() {
4560
- const tokens = Array.from(this.tokens.values());
4561
- const data = {
4562
- tokens,
4563
- tombstones: this.tombstones.length > 0 ? this.tombstones : void 0,
4564
- archivedTokens: this.archivedTokens.size > 0 ? Object.fromEntries(this.archivedTokens) : void 0,
4565
- forkedTokens: this.forkedTokens.size > 0 ? Object.fromEntries(this.forkedTokens) : void 0,
4566
- nametag: this.nametag || void 0
4567
- };
4568
- await this.deps.storage.set(STORAGE_KEYS.TOKENS, JSON.stringify(data));
5066
+ const providers = this.getTokenStorageProviders();
5067
+ if (providers.size === 0) {
5068
+ this.log("No token storage providers - tokens not persisted");
5069
+ return;
5070
+ }
5071
+ const data = await this.createStorageData();
5072
+ for (const [id, provider] of providers) {
5073
+ try {
5074
+ await provider.save(data);
5075
+ } catch (err) {
5076
+ console.error(`[Payments] Failed to save to provider ${id}:`, err);
5077
+ }
5078
+ }
4569
5079
  }
4570
5080
  async saveToOutbox(transfer, recipient) {
4571
5081
  const outbox = await this.loadOutbox();
4572
5082
  outbox.push({ transfer, recipient, createdAt: Date.now() });
4573
- await this.deps.storage.set(STORAGE_KEYS.OUTBOX, JSON.stringify(outbox));
5083
+ await this.deps.storage.set(STORAGE_KEYS_ADDRESS.OUTBOX, JSON.stringify(outbox));
4574
5084
  }
4575
5085
  async removeFromOutbox(transferId) {
4576
5086
  const outbox = await this.loadOutbox();
4577
5087
  const filtered = outbox.filter((e) => e.transfer.id !== transferId);
4578
- await this.deps.storage.set(STORAGE_KEYS.OUTBOX, JSON.stringify(filtered));
5088
+ await this.deps.storage.set(STORAGE_KEYS_ADDRESS.OUTBOX, JSON.stringify(filtered));
4579
5089
  }
4580
5090
  async loadOutbox() {
4581
- const data = await this.deps.storage.get(STORAGE_KEYS.OUTBOX);
5091
+ const data = await this.deps.storage.get(STORAGE_KEYS_ADDRESS.OUTBOX);
4582
5092
  return data ? JSON.parse(data) : [];
4583
5093
  }
4584
5094
  async createStorageData() {
@@ -4587,11 +5097,10 @@ var PaymentsModule = class {
4587
5097
  tokens,
4588
5098
  {
4589
5099
  version: 1,
4590
- address: this.deps.identity.address,
5100
+ address: this.deps.identity.l1Address,
4591
5101
  ipnsName: this.deps.identity.ipnsName ?? ""
4592
5102
  },
4593
5103
  {
4594
- nametag: this.nametag || void 0,
4595
5104
  tombstones: this.tombstones,
4596
5105
  archivedTokens: this.archivedTokens,
4597
5106
  forkedTokens: this.forkedTokens
@@ -4691,7 +5200,7 @@ var CommunicationsModule = class {
4691
5200
  const eventId = await this.deps.transport.sendMessage(recipientPubkey, content);
4692
5201
  const message = {
4693
5202
  id: eventId,
4694
- senderPubkey: this.deps.identity.publicKey,
5203
+ senderPubkey: this.deps.identity.chainPubkey,
4695
5204
  senderNametag: this.deps.identity.nametag,
4696
5205
  recipientPubkey,
4697
5206
  content,
@@ -4718,7 +5227,7 @@ var CommunicationsModule = class {
4718
5227
  getConversations() {
4719
5228
  const conversations = /* @__PURE__ */ new Map();
4720
5229
  for (const message of this.messages.values()) {
4721
- const peer = message.senderPubkey === this.deps?.identity.publicKey ? message.recipientPubkey : message.senderPubkey;
5230
+ const peer = message.senderPubkey === this.deps?.identity.chainPubkey ? message.recipientPubkey : message.senderPubkey;
4722
5231
  if (!conversations.has(peer)) {
4723
5232
  conversations.set(peer, []);
4724
5233
  }
@@ -4748,7 +5257,7 @@ var CommunicationsModule = class {
4748
5257
  */
4749
5258
  getUnreadCount(peerPubkey) {
4750
5259
  let messages = Array.from(this.messages.values()).filter(
4751
- (m) => !m.isRead && m.senderPubkey !== this.deps?.identity.publicKey
5260
+ (m) => !m.isRead && m.senderPubkey !== this.deps?.identity.chainPubkey
4752
5261
  );
4753
5262
  if (peerPubkey) {
4754
5263
  messages = messages.filter((m) => m.senderPubkey === peerPubkey);
@@ -4773,7 +5282,7 @@ var CommunicationsModule = class {
4773
5282
  const eventId = await this.deps.transport.publishBroadcast?.(content, tags);
4774
5283
  const message = {
4775
5284
  id: eventId ?? crypto.randomUUID(),
4776
- authorPubkey: this.deps.identity.publicKey,
5285
+ authorPubkey: this.deps.identity.chainPubkey,
4777
5286
  authorNametag: this.deps.identity.nametag,
4778
5287
  content,
4779
5288
  timestamp: Date.now(),
@@ -4824,11 +5333,12 @@ var CommunicationsModule = class {
4824
5333
  // Private: Message Handling
4825
5334
  // ===========================================================================
4826
5335
  handleIncomingMessage(msg) {
4827
- if (msg.senderPubkey === this.deps?.identity.publicKey) return;
5336
+ if (msg.senderTransportPubkey === this.deps?.identity.chainPubkey) return;
4828
5337
  const message = {
4829
5338
  id: msg.id,
4830
- senderPubkey: msg.senderPubkey,
4831
- recipientPubkey: this.deps.identity.publicKey,
5339
+ senderPubkey: msg.senderTransportPubkey,
5340
+ senderNametag: msg.senderNametag,
5341
+ recipientPubkey: this.deps.identity.chainPubkey,
4832
5342
  content: msg.content,
4833
5343
  timestamp: msg.timestamp,
4834
5344
  isRead: false
@@ -4850,7 +5360,7 @@ var CommunicationsModule = class {
4850
5360
  handleIncomingBroadcast(incoming) {
4851
5361
  const message = {
4852
5362
  id: incoming.id,
4853
- authorPubkey: incoming.authorPubkey,
5363
+ authorPubkey: incoming.authorTransportPubkey,
4854
5364
  content: incoming.content,
4855
5365
  timestamp: incoming.timestamp,
4856
5366
  tags: incoming.tags
@@ -5592,6 +6102,7 @@ import { SigningService as SigningService2 } from "@unicitylabs/state-transition
5592
6102
  import { TokenType as TokenType2 } from "@unicitylabs/state-transition-sdk/lib/token/TokenType";
5593
6103
  import { HashAlgorithm as HashAlgorithm4 } from "@unicitylabs/state-transition-sdk/lib/hash/HashAlgorithm";
5594
6104
  import { UnmaskedPredicateReference as UnmaskedPredicateReference2 } from "@unicitylabs/state-transition-sdk/lib/predicate/embedded/UnmaskedPredicateReference";
6105
+ import { hashNametag } from "@unicitylabs/nostr-js-sdk";
5595
6106
  var UNICITY_TOKEN_TYPE_HEX2 = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
5596
6107
  async function deriveL3PredicateAddress(privateKey) {
5597
6108
  const secret = Buffer.from(privateKey, "hex");
@@ -5618,6 +6129,7 @@ var Sphere = class _Sphere {
5618
6129
  _derivationMode = "bip32";
5619
6130
  _basePath = DEFAULT_BASE_PATH;
5620
6131
  _currentAddressIndex = 0;
6132
+ /** Map of addressId -> (nametagIndex -> nametag). Supports multiple nametags per address (e.g., from Nostr recovery) */
5621
6133
  _addressNametags = /* @__PURE__ */ new Map();
5622
6134
  // Providers
5623
6135
  _storage;
@@ -5653,9 +6165,9 @@ var Sphere = class _Sphere {
5653
6165
  if (!storage.isConnected()) {
5654
6166
  await storage.connect();
5655
6167
  }
5656
- const mnemonic = await storage.get(STORAGE_KEYS.MNEMONIC);
6168
+ const mnemonic = await storage.get(STORAGE_KEYS_GLOBAL.MNEMONIC);
5657
6169
  if (mnemonic) return true;
5658
- const masterKey = await storage.get(STORAGE_KEYS.MASTER_KEY);
6170
+ const masterKey = await storage.get(STORAGE_KEYS_GLOBAL.MASTER_KEY);
5659
6171
  if (masterKey) return true;
5660
6172
  return false;
5661
6173
  } catch {
@@ -5749,6 +6261,8 @@ var Sphere = class _Sphere {
5749
6261
  _Sphere.instance = sphere;
5750
6262
  if (options.nametag) {
5751
6263
  await sphere.registerNametag(options.nametag);
6264
+ } else {
6265
+ await sphere.recoverNametagFromNostr();
5752
6266
  }
5753
6267
  return sphere;
5754
6268
  }
@@ -5811,6 +6325,9 @@ var Sphere = class _Sphere {
5811
6325
  }
5812
6326
  await sphere.initializeProviders();
5813
6327
  await sphere.initializeModules();
6328
+ if (!options.nametag) {
6329
+ await sphere.recoverNametagFromNostr();
6330
+ }
5814
6331
  await sphere.finalizeWalletCreation();
5815
6332
  sphere._initialized = true;
5816
6333
  _Sphere.instance = sphere;
@@ -5821,20 +6338,20 @@ var Sphere = class _Sphere {
5821
6338
  }
5822
6339
  /**
5823
6340
  * Clear wallet data from storage
6341
+ * Note: Token data is cleared via TokenStorageProvider, not here
5824
6342
  */
5825
6343
  static async clear(storage) {
5826
- await storage.remove(STORAGE_KEYS.MNEMONIC);
5827
- await storage.remove(STORAGE_KEYS.MASTER_KEY);
5828
- await storage.remove(STORAGE_KEYS.CHAIN_CODE);
5829
- await storage.remove(STORAGE_KEYS.DERIVATION_PATH);
5830
- await storage.remove(STORAGE_KEYS.BASE_PATH);
5831
- await storage.remove(STORAGE_KEYS.DERIVATION_MODE);
5832
- await storage.remove(STORAGE_KEYS.WALLET_SOURCE);
5833
- await storage.remove(STORAGE_KEYS.WALLET_EXISTS);
5834
- await storage.remove(STORAGE_KEYS.NAMETAG);
5835
- await storage.remove(STORAGE_KEYS.TOKENS);
5836
- await storage.remove(STORAGE_KEYS.PENDING_TRANSFERS);
5837
- await storage.remove(STORAGE_KEYS.OUTBOX);
6344
+ await storage.remove(STORAGE_KEYS_GLOBAL.MNEMONIC);
6345
+ await storage.remove(STORAGE_KEYS_GLOBAL.MASTER_KEY);
6346
+ await storage.remove(STORAGE_KEYS_GLOBAL.CHAIN_CODE);
6347
+ await storage.remove(STORAGE_KEYS_GLOBAL.DERIVATION_PATH);
6348
+ await storage.remove(STORAGE_KEYS_GLOBAL.BASE_PATH);
6349
+ await storage.remove(STORAGE_KEYS_GLOBAL.DERIVATION_MODE);
6350
+ await storage.remove(STORAGE_KEYS_GLOBAL.WALLET_SOURCE);
6351
+ await storage.remove(STORAGE_KEYS_GLOBAL.WALLET_EXISTS);
6352
+ await storage.remove(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS);
6353
+ await storage.remove(STORAGE_KEYS_ADDRESS.PENDING_TRANSFERS);
6354
+ await storage.remove(STORAGE_KEYS_ADDRESS.OUTBOX);
5838
6355
  if (_Sphere.instance) {
5839
6356
  await _Sphere.instance.destroy();
5840
6357
  }
@@ -5884,9 +6401,9 @@ var Sphere = class _Sphere {
5884
6401
  get identity() {
5885
6402
  if (!this._identity) return null;
5886
6403
  return {
5887
- publicKey: this._identity.publicKey,
5888
- address: this._identity.address,
5889
- predicateAddress: this._identity.predicateAddress,
6404
+ chainPubkey: this._identity.chainPubkey,
6405
+ l1Address: this._identity.l1Address,
6406
+ directAddress: this._identity.directAddress,
5890
6407
  ipnsName: this._identity.ipnsName,
5891
6408
  nametag: this._identity.nametag
5892
6409
  };
@@ -6003,7 +6520,7 @@ var Sphere = class _Sphere {
6003
6520
  if (this._masterKey) {
6004
6521
  address0 = this.deriveAddress(0).address;
6005
6522
  } else if (this._identity) {
6006
- address0 = this._identity.address;
6523
+ address0 = this._identity.l1Address;
6007
6524
  }
6008
6525
  } catch {
6009
6526
  }
@@ -6050,8 +6567,8 @@ var Sphere = class _Sphere {
6050
6567
  } catch {
6051
6568
  if (i === 0 && this._identity) {
6052
6569
  addresses.push({
6053
- address: this._identity.address,
6054
- publicKey: this._identity.publicKey,
6570
+ address: this._identity.l1Address,
6571
+ publicKey: this._identity.chainPubkey,
6055
6572
  path: this.getDefaultAddressPath(),
6056
6573
  index: 0
6057
6574
  });
@@ -6130,7 +6647,7 @@ var Sphere = class _Sphere {
6130
6647
  } catch {
6131
6648
  if (i === 0 && this._identity) {
6132
6649
  addresses.push({
6133
- address: this._identity.address,
6650
+ address: this._identity.l1Address,
6134
6651
  path: this.getDefaultAddressPath(),
6135
6652
  index: 0,
6136
6653
  isChange: false
@@ -6434,22 +6951,47 @@ var Sphere = class _Sphere {
6434
6951
  return this._currentAddressIndex;
6435
6952
  }
6436
6953
  /**
6437
- * Get nametag for a specific address index
6954
+ * Get primary nametag for a specific address
6955
+ *
6956
+ * @param addressId - Address identifier (DIRECT://xxx), defaults to current address
6957
+ * @returns Primary nametag (index 0) or undefined if not registered
6958
+ */
6959
+ getNametagForAddress(addressId) {
6960
+ const id = addressId ?? this.getCurrentAddressId();
6961
+ if (!id) return void 0;
6962
+ const nametagsMap = this._addressNametags.get(id);
6963
+ return nametagsMap?.get(0);
6964
+ }
6965
+ /**
6966
+ * Get all nametags for a specific address
6438
6967
  *
6439
- * @param index - Address index (default: current address)
6440
- * @returns Nametag or undefined if not registered
6968
+ * @param addressId - Address identifier (DIRECT://xxx), defaults to current address
6969
+ * @returns Map of nametagIndex to nametag, or undefined if no nametags
6441
6970
  */
6442
- getNametagForAddress(index) {
6443
- const addressIndex = index ?? this._currentAddressIndex;
6444
- return this._addressNametags.get(addressIndex);
6971
+ getNametagsForAddress(addressId) {
6972
+ const id = addressId ?? this.getCurrentAddressId();
6973
+ if (!id) return void 0;
6974
+ const nametagsMap = this._addressNametags.get(id);
6975
+ return nametagsMap ? new Map(nametagsMap) : void 0;
6445
6976
  }
6446
6977
  /**
6447
6978
  * Get all registered address nametags
6448
6979
  *
6449
- * @returns Map of address index to nametag
6980
+ * @returns Map of addressId to (nametagIndex -> nametag)
6450
6981
  */
6451
6982
  getAllAddressNametags() {
6452
- return new Map(this._addressNametags);
6983
+ const result = /* @__PURE__ */ new Map();
6984
+ this._addressNametags.forEach((nametagsMap, addressId) => {
6985
+ result.set(addressId, new Map(nametagsMap));
6986
+ });
6987
+ return result;
6988
+ }
6989
+ /**
6990
+ * Get current address identifier (DIRECT://xxx format)
6991
+ */
6992
+ getCurrentAddressId() {
6993
+ if (!this._identity?.directAddress) return void 0;
6994
+ return getAddressId(this._identity.directAddress);
6453
6995
  }
6454
6996
  /**
6455
6997
  * Switch to a different address by index
@@ -6481,17 +7023,19 @@ var Sphere = class _Sphere {
6481
7023
  const addressInfo = this.deriveAddress(index, false);
6482
7024
  const ipnsHash = sha256(addressInfo.publicKey, "hex").slice(0, 40);
6483
7025
  const predicateAddress = await deriveL3PredicateAddress(addressInfo.privateKey);
6484
- const nametag = this._addressNametags.get(index);
7026
+ const addressId = getAddressId(predicateAddress);
7027
+ const nametagsMap = this._addressNametags.get(addressId);
7028
+ const nametag = nametagsMap?.get(0);
6485
7029
  this._identity = {
6486
7030
  privateKey: addressInfo.privateKey,
6487
- publicKey: addressInfo.publicKey,
6488
- address: addressInfo.address,
6489
- predicateAddress,
7031
+ chainPubkey: addressInfo.publicKey,
7032
+ l1Address: addressInfo.address,
7033
+ directAddress: predicateAddress,
6490
7034
  ipnsName: "12D3KooW" + ipnsHash,
6491
7035
  nametag
6492
7036
  };
6493
7037
  this._currentAddressIndex = index;
6494
- await this._storage.set(STORAGE_KEYS.CURRENT_ADDRESS_INDEX, index.toString());
7038
+ await this._storage.set(STORAGE_KEYS_GLOBAL.CURRENT_ADDRESS_INDEX, index.toString());
6495
7039
  this._storage.setIdentity(this._identity);
6496
7040
  this._transport.setIdentity(this._identity);
6497
7041
  for (const provider of this._tokenStorageProviders.values()) {
@@ -6499,13 +7043,13 @@ var Sphere = class _Sphere {
6499
7043
  }
6500
7044
  await this.reinitializeModulesForNewAddress();
6501
7045
  this.emitEvent("identity:changed", {
6502
- address: this._identity.address,
6503
- predicateAddress: this._identity.predicateAddress,
6504
- publicKey: this._identity.publicKey,
7046
+ l1Address: this._identity.l1Address,
7047
+ directAddress: this._identity.directAddress,
7048
+ chainPubkey: this._identity.chainPubkey,
6505
7049
  nametag: this._identity.nametag,
6506
7050
  addressIndex: index
6507
7051
  });
6508
- console.log(`[Sphere] Switched to address ${index}:`, this._identity.address);
7052
+ console.log(`[Sphere] Switched to address ${index}:`, this._identity.l1Address);
6509
7053
  }
6510
7054
  /**
6511
7055
  * Re-initialize modules after address switch
@@ -6563,7 +7107,7 @@ var Sphere = class _Sphere {
6563
7107
  );
6564
7108
  return {
6565
7109
  ...info,
6566
- address: "alpha1" + info.address.slice(0, 38)
7110
+ address: publicKeyToAddress(info.publicKey, "alpha")
6567
7111
  };
6568
7112
  }
6569
7113
  /**
@@ -6590,11 +7134,10 @@ var Sphere = class _Sphere {
6590
7134
  path
6591
7135
  );
6592
7136
  const publicKey = getPublicKey(derived.privateKey);
6593
- const addressHash = hash160(publicKey);
6594
7137
  return {
6595
7138
  privateKey: derived.privateKey,
6596
7139
  publicKey,
6597
- address: "alpha1" + addressHash.slice(0, 38),
7140
+ address: publicKeyToAddress(publicKey, "alpha"),
6598
7141
  path,
6599
7142
  index
6600
7143
  };
@@ -6682,6 +7225,17 @@ var Sphere = class _Sphere {
6682
7225
  hasNametag() {
6683
7226
  return !!this._identity?.nametag;
6684
7227
  }
7228
+ /**
7229
+ * Get the PROXY address for the current nametag
7230
+ * PROXY addresses are derived from the nametag hash and require
7231
+ * the nametag token to claim funds sent to them
7232
+ * @returns PROXY address string or undefined if no nametag
7233
+ */
7234
+ getProxyAddress() {
7235
+ const nametag = this._identity?.nametag;
7236
+ if (!nametag) return void 0;
7237
+ return `PROXY:${hashNametag(nametag)}`;
7238
+ }
6685
7239
  /**
6686
7240
  * Register a nametag for the current active address
6687
7241
  * Each address can have its own independent nametag
@@ -6710,14 +7264,25 @@ var Sphere = class _Sphere {
6710
7264
  throw new Error(`Nametag already registered for address ${this._currentAddressIndex}: @${this._identity.nametag}`);
6711
7265
  }
6712
7266
  if (this._transport.registerNametag) {
6713
- const success = await this._transport.registerNametag(cleanNametag, this._identity.publicKey);
7267
+ const success = await this._transport.registerNametag(
7268
+ cleanNametag,
7269
+ this._identity.chainPubkey,
7270
+ this._identity.directAddress || ""
7271
+ );
6714
7272
  if (!success) {
6715
7273
  throw new Error("Failed to register nametag. It may already be taken.");
6716
7274
  }
6717
7275
  }
6718
7276
  this._identity.nametag = cleanNametag;
6719
- this._addressNametags.set(this._currentAddressIndex, cleanNametag);
6720
- await this._storage.set(STORAGE_KEYS.NAMETAG, cleanNametag);
7277
+ const addressId = this.getCurrentAddressId();
7278
+ if (addressId) {
7279
+ let nametagsMap = this._addressNametags.get(addressId);
7280
+ if (!nametagsMap) {
7281
+ nametagsMap = /* @__PURE__ */ new Map();
7282
+ this._addressNametags.set(addressId, nametagsMap);
7283
+ }
7284
+ nametagsMap.set(0, cleanNametag);
7285
+ }
6721
7286
  await this.persistAddressNametags();
6722
7287
  if (!this._payments.hasNametag()) {
6723
7288
  console.log(`[Sphere] Minting nametag token for @${cleanNametag}...`);
@@ -6736,13 +7301,18 @@ var Sphere = class _Sphere {
6736
7301
  }
6737
7302
  /**
6738
7303
  * Persist address nametags to storage
7304
+ * Format: { "DIRECT://abc...xyz": { "0": "alice", "1": "alice2" }, ... }
6739
7305
  */
6740
7306
  async persistAddressNametags() {
6741
- const nametagsObj = {};
6742
- this._addressNametags.forEach((nametag, index) => {
6743
- nametagsObj[index.toString()] = nametag;
7307
+ const result = {};
7308
+ this._addressNametags.forEach((nametagsMap, addressId) => {
7309
+ const innerObj = {};
7310
+ nametagsMap.forEach((nametag, index) => {
7311
+ innerObj[index.toString()] = nametag;
7312
+ });
7313
+ result[addressId] = innerObj;
6744
7314
  });
6745
- await this._storage.set(STORAGE_KEYS.ADDRESS_NAMETAGS, JSON.stringify(nametagsObj));
7315
+ await this._storage.set(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS, JSON.stringify(result));
6746
7316
  }
6747
7317
  /**
6748
7318
  * Mint a nametag token on-chain (like Sphere wallet and lottery)
@@ -6777,15 +7347,24 @@ var Sphere = class _Sphere {
6777
7347
  }
6778
7348
  /**
6779
7349
  * Load address nametags from storage
7350
+ * Supports new format: { "DIRECT://abc...xyz": { "0": "alice" } }
7351
+ * And legacy format: { "0": "alice" } (migrates to new format on save)
6780
7352
  */
6781
7353
  async loadAddressNametags() {
6782
7354
  try {
6783
- const saved = await this._storage.get(STORAGE_KEYS.ADDRESS_NAMETAGS);
7355
+ const saved = await this._storage.get(STORAGE_KEYS_GLOBAL.ADDRESS_NAMETAGS);
6784
7356
  if (saved) {
6785
- const nametagsObj = JSON.parse(saved);
7357
+ const parsed = JSON.parse(saved);
6786
7358
  this._addressNametags.clear();
6787
- for (const [indexStr, nametag] of Object.entries(nametagsObj)) {
6788
- this._addressNametags.set(parseInt(indexStr, 10), nametag);
7359
+ for (const [key, value] of Object.entries(parsed)) {
7360
+ if (typeof value === "object" && value !== null) {
7361
+ const nametagsMap = /* @__PURE__ */ new Map();
7362
+ for (const [indexStr, nametag] of Object.entries(value)) {
7363
+ nametagsMap.set(parseInt(indexStr, 10), nametag);
7364
+ }
7365
+ this._addressNametags.set(key, nametagsMap);
7366
+ } else if (typeof value === "string") {
7367
+ }
6789
7368
  }
6790
7369
  }
6791
7370
  } catch {
@@ -6804,7 +7383,11 @@ var Sphere = class _Sphere {
6804
7383
  return;
6805
7384
  }
6806
7385
  try {
6807
- const success = await this._transport.registerNametag(nametag, this._identity.publicKey);
7386
+ const success = await this._transport.registerNametag(
7387
+ nametag,
7388
+ this._identity.chainPubkey,
7389
+ this._identity.directAddress || ""
7390
+ );
6808
7391
  if (success) {
6809
7392
  console.log(`[Sphere] Nametag @${nametag} synced with Nostr`);
6810
7393
  } else {
@@ -6814,6 +7397,47 @@ var Sphere = class _Sphere {
6814
7397
  console.warn(`[Sphere] Nametag sync failed:`, error);
6815
7398
  }
6816
7399
  }
7400
+ /**
7401
+ * Recover nametag from Nostr after wallet import
7402
+ * Searches for encrypted nametag events authored by this wallet's pubkey
7403
+ * and decrypts them to restore the nametag association
7404
+ */
7405
+ async recoverNametagFromNostr() {
7406
+ if (this._identity?.nametag) {
7407
+ return;
7408
+ }
7409
+ if (!this._transport.recoverNametag) {
7410
+ return;
7411
+ }
7412
+ try {
7413
+ const recoveredNametag = await this._transport.recoverNametag();
7414
+ if (recoveredNametag) {
7415
+ if (this._identity) {
7416
+ this._identity.nametag = recoveredNametag;
7417
+ }
7418
+ const addressId = this.getCurrentAddressId();
7419
+ if (addressId) {
7420
+ let nametagsMap = this._addressNametags.get(addressId);
7421
+ if (!nametagsMap) {
7422
+ nametagsMap = /* @__PURE__ */ new Map();
7423
+ this._addressNametags.set(addressId, nametagsMap);
7424
+ }
7425
+ const nextIndex = nametagsMap.size;
7426
+ nametagsMap.set(nextIndex, recoveredNametag);
7427
+ }
7428
+ await this.persistAddressNametags();
7429
+ if (this._transport.registerNametag) {
7430
+ await this._transport.registerNametag(
7431
+ recoveredNametag,
7432
+ this._identity.chainPubkey,
7433
+ this._identity.directAddress || ""
7434
+ );
7435
+ }
7436
+ this.emitEvent("nametag:recovered", { nametag: recoveredNametag });
7437
+ }
7438
+ } catch {
7439
+ }
7440
+ }
6817
7441
  /**
6818
7442
  * Validate nametag format
6819
7443
  */
@@ -6844,22 +7468,22 @@ var Sphere = class _Sphere {
6844
7468
  // ===========================================================================
6845
7469
  async storeMnemonic(mnemonic, derivationPath, basePath) {
6846
7470
  const encrypted = this.encrypt(mnemonic);
6847
- await this._storage.set(STORAGE_KEYS.MNEMONIC, encrypted);
7471
+ await this._storage.set(STORAGE_KEYS_GLOBAL.MNEMONIC, encrypted);
6848
7472
  this._mnemonic = mnemonic;
6849
7473
  this._source = "mnemonic";
6850
7474
  this._derivationMode = "bip32";
6851
7475
  if (derivationPath) {
6852
- await this._storage.set(STORAGE_KEYS.DERIVATION_PATH, derivationPath);
7476
+ await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_PATH, derivationPath);
6853
7477
  }
6854
7478
  const effectiveBasePath = basePath ?? DEFAULT_BASE_PATH;
6855
7479
  this._basePath = effectiveBasePath;
6856
- await this._storage.set(STORAGE_KEYS.BASE_PATH, effectiveBasePath);
6857
- await this._storage.set(STORAGE_KEYS.DERIVATION_MODE, this._derivationMode);
6858
- await this._storage.set(STORAGE_KEYS.WALLET_SOURCE, this._source);
7480
+ await this._storage.set(STORAGE_KEYS_GLOBAL.BASE_PATH, effectiveBasePath);
7481
+ await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_MODE, this._derivationMode);
7482
+ await this._storage.set(STORAGE_KEYS_GLOBAL.WALLET_SOURCE, this._source);
6859
7483
  }
6860
7484
  async storeMasterKey(masterKey, chainCode, derivationPath, basePath, derivationMode) {
6861
7485
  const encrypted = this.encrypt(masterKey);
6862
- await this._storage.set(STORAGE_KEYS.MASTER_KEY, encrypted);
7486
+ await this._storage.set(STORAGE_KEYS_GLOBAL.MASTER_KEY, encrypted);
6863
7487
  this._source = "file";
6864
7488
  this._mnemonic = null;
6865
7489
  if (derivationMode) {
@@ -6868,16 +7492,16 @@ var Sphere = class _Sphere {
6868
7492
  this._derivationMode = chainCode ? "bip32" : "wif_hmac";
6869
7493
  }
6870
7494
  if (chainCode) {
6871
- await this._storage.set(STORAGE_KEYS.CHAIN_CODE, chainCode);
7495
+ await this._storage.set(STORAGE_KEYS_GLOBAL.CHAIN_CODE, chainCode);
6872
7496
  }
6873
7497
  if (derivationPath) {
6874
- await this._storage.set(STORAGE_KEYS.DERIVATION_PATH, derivationPath);
7498
+ await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_PATH, derivationPath);
6875
7499
  }
6876
7500
  const effectiveBasePath = basePath ?? DEFAULT_BASE_PATH;
6877
7501
  this._basePath = effectiveBasePath;
6878
- await this._storage.set(STORAGE_KEYS.BASE_PATH, effectiveBasePath);
6879
- await this._storage.set(STORAGE_KEYS.DERIVATION_MODE, this._derivationMode);
6880
- await this._storage.set(STORAGE_KEYS.WALLET_SOURCE, this._source);
7502
+ await this._storage.set(STORAGE_KEYS_GLOBAL.BASE_PATH, effectiveBasePath);
7503
+ await this._storage.set(STORAGE_KEYS_GLOBAL.DERIVATION_MODE, this._derivationMode);
7504
+ await this._storage.set(STORAGE_KEYS_GLOBAL.WALLET_SOURCE, this._source);
6881
7505
  }
6882
7506
  /**
6883
7507
  * Mark wallet as fully created (after successful initialization)
@@ -6885,20 +7509,20 @@ var Sphere = class _Sphere {
6885
7509
  * marked as existing after all initialization steps succeed.
6886
7510
  */
6887
7511
  async finalizeWalletCreation() {
6888
- await this._storage.set(STORAGE_KEYS.WALLET_EXISTS, "true");
7512
+ await this._storage.set(STORAGE_KEYS_GLOBAL.WALLET_EXISTS, "true");
6889
7513
  }
6890
7514
  // ===========================================================================
6891
7515
  // Private: Identity Initialization
6892
7516
  // ===========================================================================
6893
7517
  async loadIdentityFromStorage() {
6894
- const encryptedMnemonic = await this._storage.get(STORAGE_KEYS.MNEMONIC);
6895
- const encryptedMasterKey = await this._storage.get(STORAGE_KEYS.MASTER_KEY);
6896
- const chainCode = await this._storage.get(STORAGE_KEYS.CHAIN_CODE);
6897
- const derivationPath = await this._storage.get(STORAGE_KEYS.DERIVATION_PATH);
6898
- const savedBasePath = await this._storage.get(STORAGE_KEYS.BASE_PATH);
6899
- const savedDerivationMode = await this._storage.get(STORAGE_KEYS.DERIVATION_MODE);
6900
- const savedSource = await this._storage.get(STORAGE_KEYS.WALLET_SOURCE);
6901
- const savedAddressIndex = await this._storage.get(STORAGE_KEYS.CURRENT_ADDRESS_INDEX);
7518
+ const encryptedMnemonic = await this._storage.get(STORAGE_KEYS_GLOBAL.MNEMONIC);
7519
+ const encryptedMasterKey = await this._storage.get(STORAGE_KEYS_GLOBAL.MASTER_KEY);
7520
+ const chainCode = await this._storage.get(STORAGE_KEYS_GLOBAL.CHAIN_CODE);
7521
+ const derivationPath = await this._storage.get(STORAGE_KEYS_GLOBAL.DERIVATION_PATH);
7522
+ const savedBasePath = await this._storage.get(STORAGE_KEYS_GLOBAL.BASE_PATH);
7523
+ const savedDerivationMode = await this._storage.get(STORAGE_KEYS_GLOBAL.DERIVATION_MODE);
7524
+ const savedSource = await this._storage.get(STORAGE_KEYS_GLOBAL.WALLET_SOURCE);
7525
+ const savedAddressIndex = await this._storage.get(STORAGE_KEYS_GLOBAL.CURRENT_ADDRESS_INDEX);
6902
7526
  this._basePath = savedBasePath ?? DEFAULT_BASE_PATH;
6903
7527
  this._derivationMode = savedDerivationMode ?? "bip32";
6904
7528
  this._source = savedSource ?? "unknown";
@@ -6932,65 +7556,60 @@ var Sphere = class _Sphere {
6932
7556
  this._storage.setIdentity(this._identity);
6933
7557
  }
6934
7558
  await this.loadAddressNametags();
6935
- const savedNametag = await this._storage.get(STORAGE_KEYS.NAMETAG);
6936
7559
  if (this._currentAddressIndex > 0 && this._masterKey) {
6937
7560
  const addressInfo = this.deriveAddress(this._currentAddressIndex, false);
6938
7561
  const ipnsHash = sha256(addressInfo.publicKey, "hex").slice(0, 40);
6939
7562
  const predicateAddress = await deriveL3PredicateAddress(addressInfo.privateKey);
6940
- const nametag = this._addressNametags.get(this._currentAddressIndex);
7563
+ const addressId = getAddressId(predicateAddress);
7564
+ const nametagsMap = this._addressNametags.get(addressId);
7565
+ const nametag = nametagsMap?.get(0);
6941
7566
  this._identity = {
6942
7567
  privateKey: addressInfo.privateKey,
6943
- publicKey: addressInfo.publicKey,
6944
- address: addressInfo.address,
6945
- predicateAddress,
7568
+ chainPubkey: addressInfo.publicKey,
7569
+ l1Address: addressInfo.address,
7570
+ directAddress: predicateAddress,
6946
7571
  ipnsName: "12D3KooW" + ipnsHash,
6947
7572
  nametag
6948
7573
  };
6949
7574
  this._storage.setIdentity(this._identity);
6950
- console.log(`[Sphere] Restored to address ${this._currentAddressIndex}:`, this._identity.address);
6951
- } else {
6952
- if (savedNametag && this._identity) {
6953
- this._identity.nametag = savedNametag;
6954
- if (!this._addressNametags.has(0)) {
6955
- this._addressNametags.set(0, savedNametag);
6956
- }
6957
- console.log("[Sphere] Restored nametag:", savedNametag);
6958
- } else if (this._identity) {
6959
- const nametag = this._addressNametags.get(0);
6960
- if (nametag) {
6961
- this._identity.nametag = nametag;
6962
- console.log("[Sphere] Restored nametag from map:", nametag);
6963
- }
7575
+ console.log(`[Sphere] Restored to address ${this._currentAddressIndex}:`, this._identity.l1Address);
7576
+ } else if (this._identity) {
7577
+ const addressId = this.getCurrentAddressId();
7578
+ const nametagsMap = addressId ? this._addressNametags.get(addressId) : void 0;
7579
+ const nametag = nametagsMap?.get(0);
7580
+ if (nametag) {
7581
+ this._identity.nametag = nametag;
6964
7582
  }
6965
7583
  }
6966
7584
  }
6967
7585
  async initializeIdentityFromMnemonic(mnemonic, derivationPath) {
6968
- const path = derivationPath ?? DEFAULT_DERIVATION_PATH;
7586
+ const basePath = derivationPath ?? DEFAULT_BASE_PATH;
7587
+ const fullPath = `${basePath}/0/0`;
6969
7588
  const masterKey = identityFromMnemonicSync(mnemonic);
6970
7589
  const derivedKey = deriveKeyAtPath(
6971
7590
  masterKey.privateKey,
6972
7591
  masterKey.chainCode,
6973
- `${path}/0/0`
7592
+ fullPath
6974
7593
  );
6975
7594
  const publicKey = getPublicKey(derivedKey.privateKey);
6976
- const addressHash = hash160(publicKey);
7595
+ const address = publicKeyToAddress(publicKey, "alpha");
6977
7596
  const ipnsHash = sha256(publicKey, "hex").slice(0, 40);
6978
7597
  const predicateAddress = await deriveL3PredicateAddress(derivedKey.privateKey);
6979
7598
  this._identity = {
6980
7599
  privateKey: derivedKey.privateKey,
6981
- publicKey,
6982
- address: "alpha1" + addressHash.slice(0, 38),
6983
- predicateAddress,
7600
+ chainPubkey: publicKey,
7601
+ l1Address: address,
7602
+ directAddress: predicateAddress,
6984
7603
  ipnsName: "12D3KooW" + ipnsHash
6985
7604
  };
6986
7605
  this._masterKey = masterKey;
6987
- console.log("[Sphere] Identity initialized from mnemonic, path:", path);
6988
7606
  }
6989
7607
  async initializeIdentityFromMasterKey(masterKey, chainCode, derivationPath) {
6990
- const path = derivationPath ?? DEFAULT_DERIVATION_PATH;
7608
+ const basePath = derivationPath ?? DEFAULT_BASE_PATH;
7609
+ const fullPath = `${basePath}/0/0`;
6991
7610
  let privateKey;
6992
7611
  if (chainCode) {
6993
- const derivedKey = deriveKeyAtPath(masterKey, chainCode, `${path}/0/0`);
7612
+ const derivedKey = deriveKeyAtPath(masterKey, chainCode, fullPath);
6994
7613
  privateKey = derivedKey.privateKey;
6995
7614
  this._masterKey = {
6996
7615
  privateKey: masterKey,
@@ -7001,17 +7620,16 @@ var Sphere = class _Sphere {
7001
7620
  this._masterKey = null;
7002
7621
  }
7003
7622
  const publicKey = getPublicKey(privateKey);
7004
- const addressHash = hash160(publicKey);
7623
+ const address = publicKeyToAddress(publicKey, "alpha");
7005
7624
  const ipnsHash = sha256(publicKey, "hex").slice(0, 40);
7006
7625
  const predicateAddress = await deriveL3PredicateAddress(privateKey);
7007
7626
  this._identity = {
7008
7627
  privateKey,
7009
- publicKey,
7010
- address: "alpha1" + addressHash.slice(0, 38),
7011
- predicateAddress,
7628
+ chainPubkey: publicKey,
7629
+ l1Address: address,
7630
+ directAddress: predicateAddress,
7012
7631
  ipnsName: "12D3KooW" + ipnsHash
7013
7632
  };
7014
- console.log("[Sphere] Identity initialized from master key, path:", path, "chainCode:", !!chainCode);
7015
7633
  }
7016
7634
  // ===========================================================================
7017
7635
  // Private: Provider & Module Initialization
@@ -7439,6 +8057,7 @@ export {
7439
8057
  TEST_ELECTRUM_URL,
7440
8058
  TEST_NOSTR_RELAYS,
7441
8059
  TIMEOUTS,
8060
+ TokenRegistry,
7442
8061
  TokenValidator,
7443
8062
  archivedKeyFromTokenId,
7444
8063
  base58Decode,
@@ -7469,10 +8088,17 @@ export {
7469
8088
  generateMasterKey,
7470
8089
  generateMnemonic2 as generateMnemonic,
7471
8090
  getAddressHrp,
8091
+ getCoinIdByName,
8092
+ getCoinIdBySymbol,
7472
8093
  getCurrentStateHash,
7473
8094
  getPublicKey,
7474
8095
  getSphere,
8096
+ getTokenDecimals,
8097
+ getTokenDefinition,
8098
+ getTokenIconUrl,
7475
8099
  getTokenId,
8100
+ getTokenName,
8101
+ getTokenSymbol,
7476
8102
  hasMissingNewStateHash,
7477
8103
  hasUncommittedTransactions,
7478
8104
  hasValidTxfData,
@@ -7482,6 +8108,7 @@ export {
7482
8108
  initSphere,
7483
8109
  isArchivedKey,
7484
8110
  isForkedKey,
8111
+ isKnownToken,
7485
8112
  isSQLiteDatabase,
7486
8113
  isTextWalletEncrypted,
7487
8114
  isTokenKey,