@unicitylabs/sphere-sdk 0.3.6 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.cjs +96 -2528
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +10 -165
- package/dist/core/index.d.ts +10 -165
- package/dist/core/index.js +92 -2524
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +201 -28
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +201 -28
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/browser/ipfs.cjs +6 -1
- package/dist/impl/browser/ipfs.cjs.map +1 -1
- package/dist/impl/browser/ipfs.js +6 -1
- package/dist/impl/browser/ipfs.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +201 -28
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +2 -21
- package/dist/impl/nodejs/index.d.ts +2 -21
- package/dist/impl/nodejs/index.js +201 -28
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +238 -2539
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -169
- package/dist/index.d.ts +59 -169
- package/dist/index.js +234 -2532
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1190,6 +1190,7 @@ declare const createUnicityOracleProvider: typeof createUnicityAggregatorProvide
|
|
|
1190
1190
|
* Platform-independent abstraction for fetching token market prices.
|
|
1191
1191
|
* Does not extend BaseProvider — stateless HTTP client with internal caching.
|
|
1192
1192
|
*/
|
|
1193
|
+
|
|
1193
1194
|
/**
|
|
1194
1195
|
* Supported price provider platforms
|
|
1195
1196
|
*/
|
|
@@ -1234,7 +1235,7 @@ interface PriceProvider {
|
|
|
1234
1235
|
/**
|
|
1235
1236
|
* Get price for a single token
|
|
1236
1237
|
* @param tokenName - Token name (e.g., 'bitcoin')
|
|
1237
|
-
* @returns Token price or null
|
|
1238
|
+
* @returns Token price (zero-price entry for tokens not listed on the platform), or null on network error with no cache
|
|
1238
1239
|
*/
|
|
1239
1240
|
getPrice(tokenName: string): Promise<TokenPrice | null>;
|
|
1240
1241
|
/**
|
|
@@ -1315,15 +1316,6 @@ interface BasePriceConfig {
|
|
|
1315
1316
|
/** Enable debug logging */
|
|
1316
1317
|
debug?: boolean;
|
|
1317
1318
|
}
|
|
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
|
-
}
|
|
1327
1319
|
/**
|
|
1328
1320
|
* Base providers result
|
|
1329
1321
|
* Common structure for all platforms
|
|
@@ -1339,13 +1331,6 @@ interface BaseProviders {
|
|
|
1339
1331
|
price?: PriceProvider;
|
|
1340
1332
|
}
|
|
1341
1333
|
|
|
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
|
-
|
|
1349
1334
|
/** IPFS storage provider configuration */
|
|
1350
1335
|
interface IpfsStorageConfig {
|
|
1351
1336
|
/** Gateway URLs for HTTP API (defaults to Unicity dedicated nodes) */
|
|
@@ -1438,8 +1423,6 @@ interface NodeProvidersConfig {
|
|
|
1438
1423
|
enabled?: boolean;
|
|
1439
1424
|
relays?: string[];
|
|
1440
1425
|
} | boolean;
|
|
1441
|
-
/** Market module configuration. true = enable with defaults, object = custom config */
|
|
1442
|
-
market?: BaseMarketConfig | boolean;
|
|
1443
1426
|
}
|
|
1444
1427
|
interface NodeProviders {
|
|
1445
1428
|
storage: StorageProvider;
|
|
@@ -1454,8 +1437,6 @@ interface NodeProviders {
|
|
|
1454
1437
|
ipfsTokenStorage?: TokenStorageProvider<TxfStorageDataBase>;
|
|
1455
1438
|
/** Group chat config (resolved, for passing to Sphere.init) */
|
|
1456
1439
|
groupChat?: GroupChatModuleConfig | boolean;
|
|
1457
|
-
/** Market module config (resolved, for passing to Sphere.init) */
|
|
1458
|
-
market?: MarketModuleConfig | boolean;
|
|
1459
1440
|
}
|
|
1460
1441
|
/**
|
|
1461
1442
|
* Create all Node.js providers with default configuration
|
|
@@ -1190,6 +1190,7 @@ declare const createUnicityOracleProvider: typeof createUnicityAggregatorProvide
|
|
|
1190
1190
|
* Platform-independent abstraction for fetching token market prices.
|
|
1191
1191
|
* Does not extend BaseProvider — stateless HTTP client with internal caching.
|
|
1192
1192
|
*/
|
|
1193
|
+
|
|
1193
1194
|
/**
|
|
1194
1195
|
* Supported price provider platforms
|
|
1195
1196
|
*/
|
|
@@ -1234,7 +1235,7 @@ interface PriceProvider {
|
|
|
1234
1235
|
/**
|
|
1235
1236
|
* Get price for a single token
|
|
1236
1237
|
* @param tokenName - Token name (e.g., 'bitcoin')
|
|
1237
|
-
* @returns Token price or null
|
|
1238
|
+
* @returns Token price (zero-price entry for tokens not listed on the platform), or null on network error with no cache
|
|
1238
1239
|
*/
|
|
1239
1240
|
getPrice(tokenName: string): Promise<TokenPrice | null>;
|
|
1240
1241
|
/**
|
|
@@ -1315,15 +1316,6 @@ interface BasePriceConfig {
|
|
|
1315
1316
|
/** Enable debug logging */
|
|
1316
1317
|
debug?: boolean;
|
|
1317
1318
|
}
|
|
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
|
-
}
|
|
1327
1319
|
/**
|
|
1328
1320
|
* Base providers result
|
|
1329
1321
|
* Common structure for all platforms
|
|
@@ -1339,13 +1331,6 @@ interface BaseProviders {
|
|
|
1339
1331
|
price?: PriceProvider;
|
|
1340
1332
|
}
|
|
1341
1333
|
|
|
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
|
-
|
|
1349
1334
|
/** IPFS storage provider configuration */
|
|
1350
1335
|
interface IpfsStorageConfig {
|
|
1351
1336
|
/** Gateway URLs for HTTP API (defaults to Unicity dedicated nodes) */
|
|
@@ -1438,8 +1423,6 @@ interface NodeProvidersConfig {
|
|
|
1438
1423
|
enabled?: boolean;
|
|
1439
1424
|
relays?: string[];
|
|
1440
1425
|
} | boolean;
|
|
1441
|
-
/** Market module configuration. true = enable with defaults, object = custom config */
|
|
1442
|
-
market?: BaseMarketConfig | boolean;
|
|
1443
1426
|
}
|
|
1444
1427
|
interface NodeProviders {
|
|
1445
1428
|
storage: StorageProvider;
|
|
@@ -1454,8 +1437,6 @@ interface NodeProviders {
|
|
|
1454
1437
|
ipfsTokenStorage?: TokenStorageProvider<TxfStorageDataBase>;
|
|
1455
1438
|
/** Group chat config (resolved, for passing to Sphere.init) */
|
|
1456
1439
|
groupChat?: GroupChatModuleConfig | boolean;
|
|
1457
|
-
/** Market module config (resolved, for passing to Sphere.init) */
|
|
1458
|
-
market?: MarketModuleConfig | boolean;
|
|
1459
1440
|
}
|
|
1460
1441
|
/**
|
|
1461
1442
|
* Create all Node.js providers with default configuration
|
|
@@ -41,7 +41,11 @@ var STORAGE_KEYS_GLOBAL = {
|
|
|
41
41
|
/** Cached token registry JSON (fetched from remote) */
|
|
42
42
|
TOKEN_REGISTRY_CACHE: "token_registry_cache",
|
|
43
43
|
/** Timestamp of last token registry cache update (ms since epoch) */
|
|
44
|
-
TOKEN_REGISTRY_CACHE_TS: "token_registry_cache_ts"
|
|
44
|
+
TOKEN_REGISTRY_CACHE_TS: "token_registry_cache_ts",
|
|
45
|
+
/** Cached price data JSON (from CoinGecko or other provider) */
|
|
46
|
+
PRICE_CACHE: "price_cache",
|
|
47
|
+
/** Timestamp of last price cache update (ms since epoch) */
|
|
48
|
+
PRICE_CACHE_TS: "price_cache_ts"
|
|
45
49
|
};
|
|
46
50
|
var STORAGE_KEYS_ADDRESS = {
|
|
47
51
|
/** Pending transfers for this address */
|
|
@@ -166,7 +170,6 @@ var TIMEOUTS = {
|
|
|
166
170
|
/** Sync interval */
|
|
167
171
|
SYNC_INTERVAL: 6e4
|
|
168
172
|
};
|
|
169
|
-
var DEFAULT_MARKET_API_URL = "https://market-api.unicity.network";
|
|
170
173
|
|
|
171
174
|
// impl/nodejs/storage/FileStorageProvider.ts
|
|
172
175
|
var FileStorageProvider = class {
|
|
@@ -3002,6 +3005,7 @@ async function loadIpnsModule() {
|
|
|
3002
3005
|
async function createSignedRecord(keyPair, cid, sequenceNumber, lifetimeMs = DEFAULT_LIFETIME_MS) {
|
|
3003
3006
|
const { createIPNSRecord, marshalIPNSRecord } = await loadIpnsModule();
|
|
3004
3007
|
const record = await createIPNSRecord(
|
|
3008
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3005
3009
|
keyPair,
|
|
3006
3010
|
`/ipfs/${cid}`,
|
|
3007
3011
|
sequenceNumber,
|
|
@@ -4743,26 +4747,37 @@ var CoinGeckoPriceProvider = class {
|
|
|
4743
4747
|
timeout;
|
|
4744
4748
|
debug;
|
|
4745
4749
|
baseUrl;
|
|
4750
|
+
storage;
|
|
4751
|
+
/** In-flight fetch promise for deduplication of concurrent getPrices() calls */
|
|
4752
|
+
fetchPromise = null;
|
|
4753
|
+
/** Token names being fetched in the current in-flight request */
|
|
4754
|
+
fetchNames = null;
|
|
4755
|
+
/** Whether persistent cache has been loaded into memory */
|
|
4756
|
+
persistentCacheLoaded = false;
|
|
4757
|
+
/** Promise for loading persistent cache (deduplication) */
|
|
4758
|
+
loadCachePromise = null;
|
|
4746
4759
|
constructor(config) {
|
|
4747
4760
|
this.apiKey = config?.apiKey;
|
|
4748
4761
|
this.cacheTtlMs = config?.cacheTtlMs ?? 6e4;
|
|
4749
4762
|
this.timeout = config?.timeout ?? 1e4;
|
|
4750
4763
|
this.debug = config?.debug ?? false;
|
|
4764
|
+
this.storage = config?.storage ?? null;
|
|
4751
4765
|
this.baseUrl = config?.baseUrl ?? (this.apiKey ? "https://pro-api.coingecko.com/api/v3" : "https://api.coingecko.com/api/v3");
|
|
4752
4766
|
}
|
|
4753
4767
|
async getPrices(tokenNames) {
|
|
4754
4768
|
if (tokenNames.length === 0) {
|
|
4755
4769
|
return /* @__PURE__ */ new Map();
|
|
4756
4770
|
}
|
|
4771
|
+
if (!this.persistentCacheLoaded && this.storage) {
|
|
4772
|
+
await this.loadFromStorage();
|
|
4773
|
+
}
|
|
4757
4774
|
const now = Date.now();
|
|
4758
4775
|
const result = /* @__PURE__ */ new Map();
|
|
4759
4776
|
const uncachedNames = [];
|
|
4760
4777
|
for (const name of tokenNames) {
|
|
4761
4778
|
const cached = this.cache.get(name);
|
|
4762
4779
|
if (cached && cached.expiresAt > now) {
|
|
4763
|
-
|
|
4764
|
-
result.set(name, cached.price);
|
|
4765
|
-
}
|
|
4780
|
+
result.set(name, cached.price);
|
|
4766
4781
|
} else {
|
|
4767
4782
|
uncachedNames.push(name);
|
|
4768
4783
|
}
|
|
@@ -4770,6 +4785,41 @@ var CoinGeckoPriceProvider = class {
|
|
|
4770
4785
|
if (uncachedNames.length === 0) {
|
|
4771
4786
|
return result;
|
|
4772
4787
|
}
|
|
4788
|
+
if (this.fetchPromise && this.fetchNames) {
|
|
4789
|
+
const allCovered = uncachedNames.every((n) => this.fetchNames.has(n));
|
|
4790
|
+
if (allCovered) {
|
|
4791
|
+
if (this.debug) {
|
|
4792
|
+
console.log(`[CoinGecko] Deduplicating request, reusing in-flight fetch`);
|
|
4793
|
+
}
|
|
4794
|
+
const fetched = await this.fetchPromise;
|
|
4795
|
+
for (const name of uncachedNames) {
|
|
4796
|
+
const price = fetched.get(name);
|
|
4797
|
+
if (price) {
|
|
4798
|
+
result.set(name, price);
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4801
|
+
return result;
|
|
4802
|
+
}
|
|
4803
|
+
}
|
|
4804
|
+
const fetchPromise = this.doFetch(uncachedNames);
|
|
4805
|
+
this.fetchPromise = fetchPromise;
|
|
4806
|
+
this.fetchNames = new Set(uncachedNames);
|
|
4807
|
+
try {
|
|
4808
|
+
const fetched = await fetchPromise;
|
|
4809
|
+
for (const [name, price] of fetched) {
|
|
4810
|
+
result.set(name, price);
|
|
4811
|
+
}
|
|
4812
|
+
} finally {
|
|
4813
|
+
if (this.fetchPromise === fetchPromise) {
|
|
4814
|
+
this.fetchPromise = null;
|
|
4815
|
+
this.fetchNames = null;
|
|
4816
|
+
}
|
|
4817
|
+
}
|
|
4818
|
+
return result;
|
|
4819
|
+
}
|
|
4820
|
+
async doFetch(uncachedNames) {
|
|
4821
|
+
const result = /* @__PURE__ */ new Map();
|
|
4822
|
+
const now = Date.now();
|
|
4773
4823
|
try {
|
|
4774
4824
|
const ids = uncachedNames.join(",");
|
|
4775
4825
|
const url = `${this.baseUrl}/simple/price?ids=${encodeURIComponent(ids)}&vs_currencies=usd,eur&include_24hr_change=true`;
|
|
@@ -4785,6 +4835,9 @@ var CoinGeckoPriceProvider = class {
|
|
|
4785
4835
|
signal: AbortSignal.timeout(this.timeout)
|
|
4786
4836
|
});
|
|
4787
4837
|
if (!response.ok) {
|
|
4838
|
+
if (response.status === 429) {
|
|
4839
|
+
this.extendCacheOnRateLimit(uncachedNames);
|
|
4840
|
+
}
|
|
4788
4841
|
throw new Error(`CoinGecko API error: ${response.status} ${response.statusText}`);
|
|
4789
4842
|
}
|
|
4790
4843
|
const data = await response.json();
|
|
@@ -4803,25 +4856,113 @@ var CoinGeckoPriceProvider = class {
|
|
|
4803
4856
|
}
|
|
4804
4857
|
for (const name of uncachedNames) {
|
|
4805
4858
|
if (!result.has(name)) {
|
|
4806
|
-
|
|
4859
|
+
const zeroPrice = {
|
|
4860
|
+
tokenName: name,
|
|
4861
|
+
priceUsd: 0,
|
|
4862
|
+
priceEur: 0,
|
|
4863
|
+
change24h: 0,
|
|
4864
|
+
timestamp: now
|
|
4865
|
+
};
|
|
4866
|
+
this.cache.set(name, { price: zeroPrice, expiresAt: now + this.cacheTtlMs });
|
|
4867
|
+
result.set(name, zeroPrice);
|
|
4807
4868
|
}
|
|
4808
4869
|
}
|
|
4809
4870
|
if (this.debug) {
|
|
4810
4871
|
console.log(`[CoinGecko] Fetched ${result.size} prices`);
|
|
4811
4872
|
}
|
|
4873
|
+
this.saveToStorage();
|
|
4812
4874
|
} catch (error) {
|
|
4813
4875
|
if (this.debug) {
|
|
4814
4876
|
console.warn("[CoinGecko] Fetch failed, using stale cache:", error);
|
|
4815
4877
|
}
|
|
4816
4878
|
for (const name of uncachedNames) {
|
|
4817
4879
|
const stale = this.cache.get(name);
|
|
4818
|
-
if (stale
|
|
4880
|
+
if (stale) {
|
|
4819
4881
|
result.set(name, stale.price);
|
|
4820
4882
|
}
|
|
4821
4883
|
}
|
|
4822
4884
|
}
|
|
4823
4885
|
return result;
|
|
4824
4886
|
}
|
|
4887
|
+
// ===========================================================================
|
|
4888
|
+
// Persistent Storage
|
|
4889
|
+
// ===========================================================================
|
|
4890
|
+
/**
|
|
4891
|
+
* Load cached prices from StorageProvider into in-memory cache.
|
|
4892
|
+
* Only loads entries that are still within cacheTtlMs.
|
|
4893
|
+
*/
|
|
4894
|
+
async loadFromStorage() {
|
|
4895
|
+
if (this.loadCachePromise) {
|
|
4896
|
+
return this.loadCachePromise;
|
|
4897
|
+
}
|
|
4898
|
+
this.loadCachePromise = this.doLoadFromStorage();
|
|
4899
|
+
try {
|
|
4900
|
+
await this.loadCachePromise;
|
|
4901
|
+
} finally {
|
|
4902
|
+
this.loadCachePromise = null;
|
|
4903
|
+
}
|
|
4904
|
+
}
|
|
4905
|
+
async doLoadFromStorage() {
|
|
4906
|
+
this.persistentCacheLoaded = true;
|
|
4907
|
+
if (!this.storage) return;
|
|
4908
|
+
try {
|
|
4909
|
+
const [cached, cachedTs] = await Promise.all([
|
|
4910
|
+
this.storage.get(STORAGE_KEYS_GLOBAL.PRICE_CACHE),
|
|
4911
|
+
this.storage.get(STORAGE_KEYS_GLOBAL.PRICE_CACHE_TS)
|
|
4912
|
+
]);
|
|
4913
|
+
if (!cached || !cachedTs) return;
|
|
4914
|
+
const ts = parseInt(cachedTs, 10);
|
|
4915
|
+
if (isNaN(ts)) return;
|
|
4916
|
+
const age = Date.now() - ts;
|
|
4917
|
+
if (age > this.cacheTtlMs) return;
|
|
4918
|
+
const data = JSON.parse(cached);
|
|
4919
|
+
const expiresAt = ts + this.cacheTtlMs;
|
|
4920
|
+
for (const [name, price] of Object.entries(data)) {
|
|
4921
|
+
if (!this.cache.has(name)) {
|
|
4922
|
+
this.cache.set(name, { price, expiresAt });
|
|
4923
|
+
}
|
|
4924
|
+
}
|
|
4925
|
+
if (this.debug) {
|
|
4926
|
+
console.log(`[CoinGecko] Loaded ${Object.keys(data).length} prices from persistent cache`);
|
|
4927
|
+
}
|
|
4928
|
+
} catch {
|
|
4929
|
+
}
|
|
4930
|
+
}
|
|
4931
|
+
/**
|
|
4932
|
+
* Save current prices to StorageProvider (fire-and-forget).
|
|
4933
|
+
*/
|
|
4934
|
+
saveToStorage() {
|
|
4935
|
+
if (!this.storage) return;
|
|
4936
|
+
const data = {};
|
|
4937
|
+
for (const [name, entry] of this.cache) {
|
|
4938
|
+
data[name] = entry.price;
|
|
4939
|
+
}
|
|
4940
|
+
Promise.all([
|
|
4941
|
+
this.storage.set(STORAGE_KEYS_GLOBAL.PRICE_CACHE, JSON.stringify(data)),
|
|
4942
|
+
this.storage.set(STORAGE_KEYS_GLOBAL.PRICE_CACHE_TS, String(Date.now()))
|
|
4943
|
+
]).catch(() => {
|
|
4944
|
+
});
|
|
4945
|
+
}
|
|
4946
|
+
// ===========================================================================
|
|
4947
|
+
// Rate-limit handling
|
|
4948
|
+
// ===========================================================================
|
|
4949
|
+
/**
|
|
4950
|
+
* On 429 rate-limit, extend stale cache entries so subsequent calls
|
|
4951
|
+
* don't immediately retry and hammer the API.
|
|
4952
|
+
*/
|
|
4953
|
+
extendCacheOnRateLimit(names) {
|
|
4954
|
+
const backoffMs = 6e4;
|
|
4955
|
+
const extendedExpiry = Date.now() + backoffMs;
|
|
4956
|
+
for (const name of names) {
|
|
4957
|
+
const existing = this.cache.get(name);
|
|
4958
|
+
if (existing) {
|
|
4959
|
+
existing.expiresAt = Math.max(existing.expiresAt, extendedExpiry);
|
|
4960
|
+
}
|
|
4961
|
+
}
|
|
4962
|
+
if (this.debug) {
|
|
4963
|
+
console.warn(`[CoinGecko] Rate-limited (429), extended cache TTL by ${backoffMs / 1e3}s`);
|
|
4964
|
+
}
|
|
4965
|
+
}
|
|
4825
4966
|
async getPrice(tokenName) {
|
|
4826
4967
|
const prices = await this.getPrices([tokenName]);
|
|
4827
4968
|
return prices.get(tokenName) ?? null;
|
|
@@ -4855,6 +4996,7 @@ var TokenRegistry = class _TokenRegistry {
|
|
|
4855
4996
|
refreshTimer = null;
|
|
4856
4997
|
lastRefreshAt = 0;
|
|
4857
4998
|
refreshPromise = null;
|
|
4999
|
+
initialLoadPromise = null;
|
|
4858
5000
|
constructor() {
|
|
4859
5001
|
this.definitionsById = /* @__PURE__ */ new Map();
|
|
4860
5002
|
this.definitionsBySymbol = /* @__PURE__ */ new Map();
|
|
@@ -4893,13 +5035,8 @@ var TokenRegistry = class _TokenRegistry {
|
|
|
4893
5035
|
if (options.refreshIntervalMs !== void 0) {
|
|
4894
5036
|
instance.refreshIntervalMs = options.refreshIntervalMs;
|
|
4895
5037
|
}
|
|
4896
|
-
if (instance.storage) {
|
|
4897
|
-
instance.loadFromCache();
|
|
4898
|
-
}
|
|
4899
5038
|
const autoRefresh = options.autoRefresh ?? true;
|
|
4900
|
-
|
|
4901
|
-
instance.startAutoRefresh();
|
|
4902
|
-
}
|
|
5039
|
+
instance.initialLoadPromise = instance.performInitialLoad(autoRefresh);
|
|
4903
5040
|
}
|
|
4904
5041
|
/**
|
|
4905
5042
|
* Reset the singleton instance (useful for testing).
|
|
@@ -4917,6 +5054,53 @@ var TokenRegistry = class _TokenRegistry {
|
|
|
4917
5054
|
static destroy() {
|
|
4918
5055
|
_TokenRegistry.resetInstance();
|
|
4919
5056
|
}
|
|
5057
|
+
/**
|
|
5058
|
+
* Wait for the initial data load (cache or remote) to complete.
|
|
5059
|
+
* Returns true if data was loaded, false if not (timeout or no data source).
|
|
5060
|
+
*
|
|
5061
|
+
* @param timeoutMs - Maximum wait time in ms (default: 10s). Set to 0 for no timeout.
|
|
5062
|
+
*/
|
|
5063
|
+
static async waitForReady(timeoutMs = 1e4) {
|
|
5064
|
+
const instance = _TokenRegistry.getInstance();
|
|
5065
|
+
if (!instance.initialLoadPromise) {
|
|
5066
|
+
return instance.definitionsById.size > 0;
|
|
5067
|
+
}
|
|
5068
|
+
if (timeoutMs <= 0) {
|
|
5069
|
+
return instance.initialLoadPromise;
|
|
5070
|
+
}
|
|
5071
|
+
return Promise.race([
|
|
5072
|
+
instance.initialLoadPromise,
|
|
5073
|
+
new Promise((resolve) => setTimeout(() => resolve(false), timeoutMs))
|
|
5074
|
+
]);
|
|
5075
|
+
}
|
|
5076
|
+
// ===========================================================================
|
|
5077
|
+
// Initial Load
|
|
5078
|
+
// ===========================================================================
|
|
5079
|
+
/**
|
|
5080
|
+
* Perform initial data load: try cache first, fall back to remote fetch.
|
|
5081
|
+
* After initial data is available, start periodic auto-refresh if configured.
|
|
5082
|
+
*/
|
|
5083
|
+
async performInitialLoad(autoRefresh) {
|
|
5084
|
+
let loaded = false;
|
|
5085
|
+
if (this.storage) {
|
|
5086
|
+
loaded = await this.loadFromCache();
|
|
5087
|
+
}
|
|
5088
|
+
if (loaded) {
|
|
5089
|
+
if (autoRefresh && this.remoteUrl) {
|
|
5090
|
+
this.startAutoRefresh();
|
|
5091
|
+
}
|
|
5092
|
+
return true;
|
|
5093
|
+
}
|
|
5094
|
+
if (autoRefresh && this.remoteUrl) {
|
|
5095
|
+
loaded = await this.refreshFromRemote();
|
|
5096
|
+
this.stopAutoRefresh();
|
|
5097
|
+
this.refreshTimer = setInterval(() => {
|
|
5098
|
+
this.refreshFromRemote();
|
|
5099
|
+
}, this.refreshIntervalMs);
|
|
5100
|
+
return loaded;
|
|
5101
|
+
}
|
|
5102
|
+
return false;
|
|
5103
|
+
}
|
|
4920
5104
|
// ===========================================================================
|
|
4921
5105
|
// Cache (StorageProvider)
|
|
4922
5106
|
// ===========================================================================
|
|
@@ -5246,7 +5430,7 @@ function resolveL1Config(network, config) {
|
|
|
5246
5430
|
enableVesting: config.enableVesting
|
|
5247
5431
|
};
|
|
5248
5432
|
}
|
|
5249
|
-
function resolvePriceConfig(config) {
|
|
5433
|
+
function resolvePriceConfig(config, storage) {
|
|
5250
5434
|
if (config === void 0) {
|
|
5251
5435
|
return void 0;
|
|
5252
5436
|
}
|
|
@@ -5256,7 +5440,8 @@ function resolvePriceConfig(config) {
|
|
|
5256
5440
|
baseUrl: config.baseUrl,
|
|
5257
5441
|
cacheTtlMs: config.cacheTtlMs,
|
|
5258
5442
|
timeout: config.timeout,
|
|
5259
|
-
debug: config.debug
|
|
5443
|
+
debug: config.debug,
|
|
5444
|
+
storage
|
|
5260
5445
|
};
|
|
5261
5446
|
}
|
|
5262
5447
|
function resolveGroupChatConfig(network, config) {
|
|
@@ -5273,16 +5458,6 @@ function resolveGroupChatConfig(network, config) {
|
|
|
5273
5458
|
relays: config.relays ?? [...netConfig.groupRelays]
|
|
5274
5459
|
};
|
|
5275
5460
|
}
|
|
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
|
-
}
|
|
5286
5461
|
|
|
5287
5462
|
// impl/nodejs/index.ts
|
|
5288
5463
|
function createNodeProviders(config) {
|
|
@@ -5290,21 +5465,19 @@ function createNodeProviders(config) {
|
|
|
5290
5465
|
const transportConfig = resolveTransportConfig(network, config?.transport);
|
|
5291
5466
|
const oracleConfig = resolveOracleConfig(network, config?.oracle);
|
|
5292
5467
|
const l1Config = resolveL1Config(network, config?.l1);
|
|
5293
|
-
const priceConfig = resolvePriceConfig(config?.price);
|
|
5294
5468
|
const storage = createFileStorageProvider({
|
|
5295
5469
|
dataDir: config?.dataDir ?? "./sphere-data",
|
|
5296
5470
|
...config?.walletFileName ? { fileName: config.walletFileName } : {}
|
|
5297
5471
|
});
|
|
5472
|
+
const priceConfig = resolvePriceConfig(config?.price, storage);
|
|
5298
5473
|
const ipfsSync = config?.tokenSync?.ipfs;
|
|
5299
5474
|
const ipfsTokenStorage = ipfsSync?.enabled ? createNodeIpfsStorageProvider(ipfsSync.config, storage) : void 0;
|
|
5300
5475
|
const groupChat = resolveGroupChatConfig(network, config?.groupChat);
|
|
5301
5476
|
const networkConfig = getNetworkConfig(network);
|
|
5302
5477
|
TokenRegistry.configure({ remoteUrl: networkConfig.tokenRegistryUrl, storage });
|
|
5303
|
-
const market = resolveMarketConfig(config?.market);
|
|
5304
5478
|
return {
|
|
5305
5479
|
storage,
|
|
5306
5480
|
groupChat,
|
|
5307
|
-
market,
|
|
5308
5481
|
tokenStorage: createFileTokenStorageProvider({
|
|
5309
5482
|
tokensDir: config?.tokensDir ?? "./sphere-tokens"
|
|
5310
5483
|
}),
|