@unicitylabs/sphere-sdk 0.3.4 → 0.3.6

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.
@@ -1112,6 +1112,7 @@ declare const NETWORKS: {
1112
1112
  readonly ipfsGateways: readonly ["https://unicity-ipfs1.dyndns.org"];
1113
1113
  readonly electrumUrl: "wss://fulcrum.alpha.unicity.network:50004";
1114
1114
  readonly groupRelays: readonly ["wss://sphere-relay.unicity.network"];
1115
+ readonly tokenRegistryUrl: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json";
1115
1116
  };
1116
1117
  readonly testnet: {
1117
1118
  readonly name: "Testnet";
@@ -1120,6 +1121,7 @@ declare const NETWORKS: {
1120
1121
  readonly ipfsGateways: readonly ["https://unicity-ipfs1.dyndns.org"];
1121
1122
  readonly electrumUrl: "wss://fulcrum.alpha.testnet.unicity.network:50004";
1122
1123
  readonly groupRelays: readonly ["wss://sphere-relay.unicity.network"];
1124
+ readonly tokenRegistryUrl: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json";
1123
1125
  };
1124
1126
  readonly dev: {
1125
1127
  readonly name: "Development";
@@ -1128,6 +1130,7 @@ declare const NETWORKS: {
1128
1130
  readonly ipfsGateways: readonly ["https://unicity-ipfs1.dyndns.org"];
1129
1131
  readonly electrumUrl: "wss://fulcrum.alpha.testnet.unicity.network:50004";
1130
1132
  readonly groupRelays: readonly ["wss://sphere-relay.unicity.network"];
1133
+ readonly tokenRegistryUrl: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json";
1131
1134
  };
1132
1135
  };
1133
1136
  type NetworkType = keyof typeof NETWORKS;
@@ -1312,6 +1315,15 @@ interface BasePriceConfig {
1312
1315
  /** Enable debug logging */
1313
1316
  debug?: boolean;
1314
1317
  }
1318
+ /**
1319
+ * Base market module configuration
1320
+ */
1321
+ interface BaseMarketConfig {
1322
+ /** Market API base URL (default: https://market-api.unicity.network) */
1323
+ apiUrl?: string;
1324
+ /** Request timeout in ms (default: 30000) */
1325
+ timeout?: number;
1326
+ }
1315
1327
  /**
1316
1328
  * Base providers result
1317
1329
  * Common structure for all platforms
@@ -1327,6 +1339,13 @@ interface BaseProviders {
1327
1339
  price?: PriceProvider;
1328
1340
  }
1329
1341
 
1342
+ interface MarketModuleConfig {
1343
+ /** Market API base URL (default: https://market-api.unicity.network) */
1344
+ apiUrl?: string;
1345
+ /** Request timeout in ms (default: 30000) */
1346
+ timeout?: number;
1347
+ }
1348
+
1330
1349
  /** IPFS storage provider configuration */
1331
1350
  interface IpfsStorageConfig {
1332
1351
  /** Gateway URLs for HTTP API (defaults to Unicity dedicated nodes) */
@@ -1419,6 +1438,8 @@ interface NodeProvidersConfig {
1419
1438
  enabled?: boolean;
1420
1439
  relays?: string[];
1421
1440
  } | boolean;
1441
+ /** Market module configuration. true = enable with defaults, object = custom config */
1442
+ market?: BaseMarketConfig | boolean;
1422
1443
  }
1423
1444
  interface NodeProviders {
1424
1445
  storage: StorageProvider;
@@ -1433,6 +1454,8 @@ interface NodeProviders {
1433
1454
  ipfsTokenStorage?: TokenStorageProvider<TxfStorageDataBase>;
1434
1455
  /** Group chat config (resolved, for passing to Sphere.init) */
1435
1456
  groupChat?: GroupChatModuleConfig | boolean;
1457
+ /** Market module config (resolved, for passing to Sphere.init) */
1458
+ market?: MarketModuleConfig | boolean;
1436
1459
  }
1437
1460
  /**
1438
1461
  * Create all Node.js providers with default configuration
@@ -1112,6 +1112,7 @@ declare const NETWORKS: {
1112
1112
  readonly ipfsGateways: readonly ["https://unicity-ipfs1.dyndns.org"];
1113
1113
  readonly electrumUrl: "wss://fulcrum.alpha.unicity.network:50004";
1114
1114
  readonly groupRelays: readonly ["wss://sphere-relay.unicity.network"];
1115
+ readonly tokenRegistryUrl: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json";
1115
1116
  };
1116
1117
  readonly testnet: {
1117
1118
  readonly name: "Testnet";
@@ -1120,6 +1121,7 @@ declare const NETWORKS: {
1120
1121
  readonly ipfsGateways: readonly ["https://unicity-ipfs1.dyndns.org"];
1121
1122
  readonly electrumUrl: "wss://fulcrum.alpha.testnet.unicity.network:50004";
1122
1123
  readonly groupRelays: readonly ["wss://sphere-relay.unicity.network"];
1124
+ readonly tokenRegistryUrl: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json";
1123
1125
  };
1124
1126
  readonly dev: {
1125
1127
  readonly name: "Development";
@@ -1128,6 +1130,7 @@ declare const NETWORKS: {
1128
1130
  readonly ipfsGateways: readonly ["https://unicity-ipfs1.dyndns.org"];
1129
1131
  readonly electrumUrl: "wss://fulcrum.alpha.testnet.unicity.network:50004";
1130
1132
  readonly groupRelays: readonly ["wss://sphere-relay.unicity.network"];
1133
+ readonly tokenRegistryUrl: "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json";
1131
1134
  };
1132
1135
  };
1133
1136
  type NetworkType = keyof typeof NETWORKS;
@@ -1312,6 +1315,15 @@ interface BasePriceConfig {
1312
1315
  /** Enable debug logging */
1313
1316
  debug?: boolean;
1314
1317
  }
1318
+ /**
1319
+ * Base market module configuration
1320
+ */
1321
+ interface BaseMarketConfig {
1322
+ /** Market API base URL (default: https://market-api.unicity.network) */
1323
+ apiUrl?: string;
1324
+ /** Request timeout in ms (default: 30000) */
1325
+ timeout?: number;
1326
+ }
1315
1327
  /**
1316
1328
  * Base providers result
1317
1329
  * Common structure for all platforms
@@ -1327,6 +1339,13 @@ interface BaseProviders {
1327
1339
  price?: PriceProvider;
1328
1340
  }
1329
1341
 
1342
+ interface MarketModuleConfig {
1343
+ /** Market API base URL (default: https://market-api.unicity.network) */
1344
+ apiUrl?: string;
1345
+ /** Request timeout in ms (default: 30000) */
1346
+ timeout?: number;
1347
+ }
1348
+
1330
1349
  /** IPFS storage provider configuration */
1331
1350
  interface IpfsStorageConfig {
1332
1351
  /** Gateway URLs for HTTP API (defaults to Unicity dedicated nodes) */
@@ -1419,6 +1438,8 @@ interface NodeProvidersConfig {
1419
1438
  enabled?: boolean;
1420
1439
  relays?: string[];
1421
1440
  } | boolean;
1441
+ /** Market module configuration. true = enable with defaults, object = custom config */
1442
+ market?: BaseMarketConfig | boolean;
1422
1443
  }
1423
1444
  interface NodeProviders {
1424
1445
  storage: StorageProvider;
@@ -1433,6 +1454,8 @@ interface NodeProviders {
1433
1454
  ipfsTokenStorage?: TokenStorageProvider<TxfStorageDataBase>;
1434
1455
  /** Group chat config (resolved, for passing to Sphere.init) */
1435
1456
  groupChat?: GroupChatModuleConfig | boolean;
1457
+ /** Market module config (resolved, for passing to Sphere.init) */
1458
+ market?: MarketModuleConfig | boolean;
1436
1459
  }
1437
1460
  /**
1438
1461
  * Create all Node.js providers with default configuration
@@ -37,7 +37,11 @@ var STORAGE_KEYS_GLOBAL = {
37
37
  /** Group chat: processed event IDs for deduplication */
38
38
  GROUP_CHAT_PROCESSED_EVENTS: "group_chat_processed_events",
39
39
  /** Group chat: last used relay URL (stale data detection) */
40
- GROUP_CHAT_RELAY_URL: "group_chat_relay_url"
40
+ GROUP_CHAT_RELAY_URL: "group_chat_relay_url",
41
+ /** Cached token registry JSON (fetched from remote) */
42
+ TOKEN_REGISTRY_CACHE: "token_registry_cache",
43
+ /** Timestamp of last token registry cache update (ms since epoch) */
44
+ TOKEN_REGISTRY_CACHE_TS: "token_registry_cache_ts"
41
45
  };
42
46
  var STORAGE_KEYS_ADDRESS = {
43
47
  /** Pending transfers for this address */
@@ -113,6 +117,8 @@ var DEFAULT_BASE_PATH = "m/44'/0'/0'";
113
117
  var DEFAULT_DERIVATION_PATH = `${DEFAULT_BASE_PATH}/0/0`;
114
118
  var DEFAULT_ELECTRUM_URL = "wss://fulcrum.alpha.unicity.network:50004";
115
119
  var TEST_ELECTRUM_URL = "wss://fulcrum.alpha.testnet.unicity.network:50004";
120
+ var TOKEN_REGISTRY_URL = "https://raw.githubusercontent.com/unicitynetwork/unicity-ids/refs/heads/main/unicity-ids.testnet.json";
121
+ var TOKEN_REGISTRY_REFRESH_INTERVAL = 36e5;
116
122
  var TEST_NOSTR_RELAYS = [
117
123
  "wss://nostr-relay.testnet.unicity.network"
118
124
  ];
@@ -126,7 +132,8 @@ var NETWORKS = {
126
132
  nostrRelays: DEFAULT_NOSTR_RELAYS,
127
133
  ipfsGateways: DEFAULT_IPFS_GATEWAYS,
128
134
  electrumUrl: DEFAULT_ELECTRUM_URL,
129
- groupRelays: DEFAULT_GROUP_RELAYS
135
+ groupRelays: DEFAULT_GROUP_RELAYS,
136
+ tokenRegistryUrl: TOKEN_REGISTRY_URL
130
137
  },
131
138
  testnet: {
132
139
  name: "Testnet",
@@ -134,7 +141,8 @@ var NETWORKS = {
134
141
  nostrRelays: TEST_NOSTR_RELAYS,
135
142
  ipfsGateways: DEFAULT_IPFS_GATEWAYS,
136
143
  electrumUrl: TEST_ELECTRUM_URL,
137
- groupRelays: DEFAULT_GROUP_RELAYS
144
+ groupRelays: DEFAULT_GROUP_RELAYS,
145
+ tokenRegistryUrl: TOKEN_REGISTRY_URL
138
146
  },
139
147
  dev: {
140
148
  name: "Development",
@@ -142,7 +150,8 @@ var NETWORKS = {
142
150
  nostrRelays: TEST_NOSTR_RELAYS,
143
151
  ipfsGateways: DEFAULT_IPFS_GATEWAYS,
144
152
  electrumUrl: TEST_ELECTRUM_URL,
145
- groupRelays: DEFAULT_GROUP_RELAYS
153
+ groupRelays: DEFAULT_GROUP_RELAYS,
154
+ tokenRegistryUrl: TOKEN_REGISTRY_URL
146
155
  }
147
156
  };
148
157
  var TIMEOUTS = {
@@ -157,6 +166,7 @@ var TIMEOUTS = {
157
166
  /** Sync interval */
158
167
  SYNC_INTERVAL: 6e4
159
168
  };
169
+ var DEFAULT_MARKET_API_URL = "https://market-api.unicity.network";
160
170
 
161
171
  // impl/nodejs/storage/FileStorageProvider.ts
162
172
  var FileStorageProvider = class {
@@ -4831,6 +4841,363 @@ function createPriceProvider(config) {
4831
4841
  }
4832
4842
  }
4833
4843
 
4844
+ // registry/TokenRegistry.ts
4845
+ var FETCH_TIMEOUT_MS = 1e4;
4846
+ var TokenRegistry = class _TokenRegistry {
4847
+ static instance = null;
4848
+ definitionsById;
4849
+ definitionsBySymbol;
4850
+ definitionsByName;
4851
+ // Remote refresh state
4852
+ remoteUrl = null;
4853
+ storage = null;
4854
+ refreshIntervalMs = TOKEN_REGISTRY_REFRESH_INTERVAL;
4855
+ refreshTimer = null;
4856
+ lastRefreshAt = 0;
4857
+ refreshPromise = null;
4858
+ constructor() {
4859
+ this.definitionsById = /* @__PURE__ */ new Map();
4860
+ this.definitionsBySymbol = /* @__PURE__ */ new Map();
4861
+ this.definitionsByName = /* @__PURE__ */ new Map();
4862
+ }
4863
+ /**
4864
+ * Get singleton instance of TokenRegistry
4865
+ */
4866
+ static getInstance() {
4867
+ if (!_TokenRegistry.instance) {
4868
+ _TokenRegistry.instance = new _TokenRegistry();
4869
+ }
4870
+ return _TokenRegistry.instance;
4871
+ }
4872
+ /**
4873
+ * Configure remote registry refresh with persistent caching.
4874
+ *
4875
+ * On first call:
4876
+ * 1. Loads cached data from StorageProvider (if available and fresh)
4877
+ * 2. Starts periodic remote fetch (if autoRefresh is true, which is default)
4878
+ *
4879
+ * @param options - Configuration options
4880
+ * @param options.remoteUrl - Remote URL to fetch definitions from
4881
+ * @param options.storage - StorageProvider for persistent caching
4882
+ * @param options.refreshIntervalMs - Refresh interval in ms (default: 1 hour)
4883
+ * @param options.autoRefresh - Start auto-refresh immediately (default: true)
4884
+ */
4885
+ static configure(options) {
4886
+ const instance = _TokenRegistry.getInstance();
4887
+ if (options.remoteUrl !== void 0) {
4888
+ instance.remoteUrl = options.remoteUrl;
4889
+ }
4890
+ if (options.storage !== void 0) {
4891
+ instance.storage = options.storage;
4892
+ }
4893
+ if (options.refreshIntervalMs !== void 0) {
4894
+ instance.refreshIntervalMs = options.refreshIntervalMs;
4895
+ }
4896
+ if (instance.storage) {
4897
+ instance.loadFromCache();
4898
+ }
4899
+ const autoRefresh = options.autoRefresh ?? true;
4900
+ if (autoRefresh && instance.remoteUrl) {
4901
+ instance.startAutoRefresh();
4902
+ }
4903
+ }
4904
+ /**
4905
+ * Reset the singleton instance (useful for testing).
4906
+ * Stops auto-refresh if running.
4907
+ */
4908
+ static resetInstance() {
4909
+ if (_TokenRegistry.instance) {
4910
+ _TokenRegistry.instance.stopAutoRefresh();
4911
+ }
4912
+ _TokenRegistry.instance = null;
4913
+ }
4914
+ /**
4915
+ * Destroy the singleton: stop auto-refresh and reset.
4916
+ */
4917
+ static destroy() {
4918
+ _TokenRegistry.resetInstance();
4919
+ }
4920
+ // ===========================================================================
4921
+ // Cache (StorageProvider)
4922
+ // ===========================================================================
4923
+ /**
4924
+ * Load definitions from StorageProvider cache.
4925
+ * Only applies if cache exists and is fresh (within refreshIntervalMs).
4926
+ */
4927
+ async loadFromCache() {
4928
+ if (!this.storage) return false;
4929
+ try {
4930
+ const [cached, cachedTs] = await Promise.all([
4931
+ this.storage.get(STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE),
4932
+ this.storage.get(STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE_TS)
4933
+ ]);
4934
+ if (!cached || !cachedTs) return false;
4935
+ const ts = parseInt(cachedTs, 10);
4936
+ if (isNaN(ts)) return false;
4937
+ const age = Date.now() - ts;
4938
+ if (age > this.refreshIntervalMs) return false;
4939
+ if (this.lastRefreshAt > ts) return false;
4940
+ const data = JSON.parse(cached);
4941
+ if (!this.isValidDefinitionsArray(data)) return false;
4942
+ this.applyDefinitions(data);
4943
+ this.lastRefreshAt = ts;
4944
+ return true;
4945
+ } catch {
4946
+ return false;
4947
+ }
4948
+ }
4949
+ /**
4950
+ * Save definitions to StorageProvider cache.
4951
+ */
4952
+ async saveToCache(definitions) {
4953
+ if (!this.storage) return;
4954
+ try {
4955
+ await Promise.all([
4956
+ this.storage.set(STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE, JSON.stringify(definitions)),
4957
+ this.storage.set(STORAGE_KEYS_GLOBAL.TOKEN_REGISTRY_CACHE_TS, String(Date.now()))
4958
+ ]);
4959
+ } catch {
4960
+ }
4961
+ }
4962
+ // ===========================================================================
4963
+ // Remote Refresh
4964
+ // ===========================================================================
4965
+ /**
4966
+ * Apply an array of token definitions to the internal maps.
4967
+ * Clears existing data before applying.
4968
+ */
4969
+ applyDefinitions(definitions) {
4970
+ this.definitionsById.clear();
4971
+ this.definitionsBySymbol.clear();
4972
+ this.definitionsByName.clear();
4973
+ for (const def of definitions) {
4974
+ const idLower = def.id.toLowerCase();
4975
+ this.definitionsById.set(idLower, def);
4976
+ if (def.symbol) {
4977
+ this.definitionsBySymbol.set(def.symbol.toUpperCase(), def);
4978
+ }
4979
+ this.definitionsByName.set(def.name.toLowerCase(), def);
4980
+ }
4981
+ }
4982
+ /**
4983
+ * Validate that data is an array of objects with 'id' field
4984
+ */
4985
+ isValidDefinitionsArray(data) {
4986
+ return Array.isArray(data) && data.every((item) => item && typeof item === "object" && "id" in item);
4987
+ }
4988
+ /**
4989
+ * Fetch token definitions from the remote URL and update the registry.
4990
+ * On success, also persists to StorageProvider cache.
4991
+ * Returns true on success, false on failure. On failure, existing data is preserved.
4992
+ * Concurrent calls are deduplicated — only one fetch runs at a time.
4993
+ */
4994
+ async refreshFromRemote() {
4995
+ if (!this.remoteUrl) {
4996
+ return false;
4997
+ }
4998
+ if (this.refreshPromise) {
4999
+ return this.refreshPromise;
5000
+ }
5001
+ this.refreshPromise = this.doRefresh();
5002
+ try {
5003
+ return await this.refreshPromise;
5004
+ } finally {
5005
+ this.refreshPromise = null;
5006
+ }
5007
+ }
5008
+ async doRefresh() {
5009
+ try {
5010
+ const controller = new AbortController();
5011
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
5012
+ let response;
5013
+ try {
5014
+ response = await fetch(this.remoteUrl, {
5015
+ headers: { Accept: "application/json" },
5016
+ signal: controller.signal
5017
+ });
5018
+ } finally {
5019
+ clearTimeout(timer);
5020
+ }
5021
+ if (!response.ok) {
5022
+ console.warn(
5023
+ `[TokenRegistry] Remote fetch failed: HTTP ${response.status} ${response.statusText}`
5024
+ );
5025
+ return false;
5026
+ }
5027
+ const data = await response.json();
5028
+ if (!this.isValidDefinitionsArray(data)) {
5029
+ console.warn("[TokenRegistry] Remote data is not a valid token definitions array");
5030
+ return false;
5031
+ }
5032
+ const definitions = data;
5033
+ this.applyDefinitions(definitions);
5034
+ this.lastRefreshAt = Date.now();
5035
+ this.saveToCache(definitions);
5036
+ return true;
5037
+ } catch (error) {
5038
+ const message = error instanceof Error ? error.message : String(error);
5039
+ console.warn(`[TokenRegistry] Remote refresh failed: ${message}`);
5040
+ return false;
5041
+ }
5042
+ }
5043
+ /**
5044
+ * Start periodic auto-refresh from the remote URL.
5045
+ * Does an immediate fetch, then repeats at the configured interval.
5046
+ */
5047
+ startAutoRefresh(intervalMs) {
5048
+ this.stopAutoRefresh();
5049
+ if (intervalMs !== void 0) {
5050
+ this.refreshIntervalMs = intervalMs;
5051
+ }
5052
+ this.refreshFromRemote();
5053
+ this.refreshTimer = setInterval(() => {
5054
+ this.refreshFromRemote();
5055
+ }, this.refreshIntervalMs);
5056
+ }
5057
+ /**
5058
+ * Stop periodic auto-refresh
5059
+ */
5060
+ stopAutoRefresh() {
5061
+ if (this.refreshTimer !== null) {
5062
+ clearInterval(this.refreshTimer);
5063
+ this.refreshTimer = null;
5064
+ }
5065
+ }
5066
+ /**
5067
+ * Timestamp of the last successful remote refresh (0 if never refreshed)
5068
+ */
5069
+ getLastRefreshAt() {
5070
+ return this.lastRefreshAt;
5071
+ }
5072
+ // ===========================================================================
5073
+ // Lookup Methods
5074
+ // ===========================================================================
5075
+ /**
5076
+ * Get token definition by hex coin ID
5077
+ * @param coinId - 64-character hex string
5078
+ * @returns Token definition or undefined if not found
5079
+ */
5080
+ getDefinition(coinId) {
5081
+ if (!coinId) return void 0;
5082
+ return this.definitionsById.get(coinId.toLowerCase());
5083
+ }
5084
+ /**
5085
+ * Get token definition by symbol (e.g., "UCT", "BTC")
5086
+ * @param symbol - Token symbol (case-insensitive)
5087
+ * @returns Token definition or undefined if not found
5088
+ */
5089
+ getDefinitionBySymbol(symbol) {
5090
+ if (!symbol) return void 0;
5091
+ return this.definitionsBySymbol.get(symbol.toUpperCase());
5092
+ }
5093
+ /**
5094
+ * Get token definition by name (e.g., "bitcoin", "ethereum")
5095
+ * @param name - Token name (case-insensitive)
5096
+ * @returns Token definition or undefined if not found
5097
+ */
5098
+ getDefinitionByName(name) {
5099
+ if (!name) return void 0;
5100
+ return this.definitionsByName.get(name.toLowerCase());
5101
+ }
5102
+ /**
5103
+ * Get token symbol for a coin ID
5104
+ * @param coinId - 64-character hex string
5105
+ * @returns Symbol (e.g., "UCT") or truncated ID if not found
5106
+ */
5107
+ getSymbol(coinId) {
5108
+ const def = this.getDefinition(coinId);
5109
+ if (def?.symbol) {
5110
+ return def.symbol;
5111
+ }
5112
+ return coinId.slice(0, 6).toUpperCase();
5113
+ }
5114
+ /**
5115
+ * Get token name for a coin ID
5116
+ * @param coinId - 64-character hex string
5117
+ * @returns Name (e.g., "Bitcoin") or coin ID if not found
5118
+ */
5119
+ getName(coinId) {
5120
+ const def = this.getDefinition(coinId);
5121
+ if (def?.name) {
5122
+ return def.name.charAt(0).toUpperCase() + def.name.slice(1);
5123
+ }
5124
+ return coinId;
5125
+ }
5126
+ /**
5127
+ * Get decimal places for a coin ID
5128
+ * @param coinId - 64-character hex string
5129
+ * @returns Decimals or 0 if not found
5130
+ */
5131
+ getDecimals(coinId) {
5132
+ const def = this.getDefinition(coinId);
5133
+ return def?.decimals ?? 0;
5134
+ }
5135
+ /**
5136
+ * Get icon URL for a coin ID
5137
+ * @param coinId - 64-character hex string
5138
+ * @param preferPng - Prefer PNG format over SVG
5139
+ * @returns Icon URL or null if not found
5140
+ */
5141
+ getIconUrl(coinId, preferPng = true) {
5142
+ const def = this.getDefinition(coinId);
5143
+ if (!def?.icons || def.icons.length === 0) {
5144
+ return null;
5145
+ }
5146
+ if (preferPng) {
5147
+ const pngIcon = def.icons.find((i) => i.url.toLowerCase().includes(".png"));
5148
+ if (pngIcon) return pngIcon.url;
5149
+ }
5150
+ return def.icons[0].url;
5151
+ }
5152
+ /**
5153
+ * Check if a coin ID is known in the registry
5154
+ * @param coinId - 64-character hex string
5155
+ * @returns true if the coin is in the registry
5156
+ */
5157
+ isKnown(coinId) {
5158
+ return this.definitionsById.has(coinId.toLowerCase());
5159
+ }
5160
+ /**
5161
+ * Get all token definitions
5162
+ * @returns Array of all token definitions
5163
+ */
5164
+ getAllDefinitions() {
5165
+ return Array.from(this.definitionsById.values());
5166
+ }
5167
+ /**
5168
+ * Get all fungible token definitions
5169
+ * @returns Array of fungible token definitions
5170
+ */
5171
+ getFungibleTokens() {
5172
+ return this.getAllDefinitions().filter((def) => def.assetKind === "fungible");
5173
+ }
5174
+ /**
5175
+ * Get all non-fungible token definitions
5176
+ * @returns Array of non-fungible token definitions
5177
+ */
5178
+ getNonFungibleTokens() {
5179
+ return this.getAllDefinitions().filter((def) => def.assetKind === "non-fungible");
5180
+ }
5181
+ /**
5182
+ * Get coin ID by symbol
5183
+ * @param symbol - Token symbol (e.g., "UCT")
5184
+ * @returns Coin ID hex string or undefined if not found
5185
+ */
5186
+ getCoinIdBySymbol(symbol) {
5187
+ const def = this.getDefinitionBySymbol(symbol);
5188
+ return def?.id;
5189
+ }
5190
+ /**
5191
+ * Get coin ID by name
5192
+ * @param name - Token name (e.g., "bitcoin")
5193
+ * @returns Coin ID hex string or undefined if not found
5194
+ */
5195
+ getCoinIdByName(name) {
5196
+ const def = this.getDefinitionByName(name);
5197
+ return def?.id;
5198
+ }
5199
+ };
5200
+
4834
5201
  // impl/shared/resolvers.ts
4835
5202
  function getNetworkConfig(network = "mainnet") {
4836
5203
  return NETWORKS[network];
@@ -4906,6 +5273,16 @@ function resolveGroupChatConfig(network, config) {
4906
5273
  relays: config.relays ?? [...netConfig.groupRelays]
4907
5274
  };
4908
5275
  }
5276
+ function resolveMarketConfig(config) {
5277
+ if (!config) return void 0;
5278
+ if (config === true) {
5279
+ return { apiUrl: DEFAULT_MARKET_API_URL };
5280
+ }
5281
+ return {
5282
+ apiUrl: config.apiUrl ?? DEFAULT_MARKET_API_URL,
5283
+ timeout: config.timeout
5284
+ };
5285
+ }
4909
5286
 
4910
5287
  // impl/nodejs/index.ts
4911
5288
  function createNodeProviders(config) {
@@ -4921,9 +5298,13 @@ function createNodeProviders(config) {
4921
5298
  const ipfsSync = config?.tokenSync?.ipfs;
4922
5299
  const ipfsTokenStorage = ipfsSync?.enabled ? createNodeIpfsStorageProvider(ipfsSync.config, storage) : void 0;
4923
5300
  const groupChat = resolveGroupChatConfig(network, config?.groupChat);
5301
+ const networkConfig = getNetworkConfig(network);
5302
+ TokenRegistry.configure({ remoteUrl: networkConfig.tokenRegistryUrl, storage });
5303
+ const market = resolveMarketConfig(config?.market);
4924
5304
  return {
4925
5305
  storage,
4926
5306
  groupChat,
5307
+ market,
4927
5308
  tokenStorage: createFileTokenStorageProvider({
4928
5309
  tokensDir: config?.tokensDir ?? "./sphere-tokens"
4929
5310
  }),