@unicitylabs/sphere-sdk 0.3.7 → 0.3.9
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/connect/index.cjs +770 -0
- package/dist/connect/index.cjs.map +1 -0
- package/dist/connect/index.d.cts +312 -0
- package/dist/connect/index.d.ts +312 -0
- package/dist/connect/index.js +747 -0
- package/dist/connect/index.js.map +1 -0
- package/dist/core/index.cjs +90 -2502
- 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 +86 -2498
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/connect/index.cjs +271 -0
- package/dist/impl/browser/connect/index.cjs.map +1 -0
- package/dist/impl/browser/connect/index.d.cts +137 -0
- package/dist/impl/browser/connect/index.d.ts +137 -0
- package/dist/impl/browser/connect/index.js +248 -0
- package/dist/impl/browser/connect/index.js.map +1 -0
- 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/connect/index.cjs +372 -0
- package/dist/impl/nodejs/connect/index.cjs.map +1 -0
- package/dist/impl/nodejs/connect/index.d.cts +178 -0
- package/dist/impl/nodejs/connect/index.d.ts +178 -0
- package/dist/impl/nodejs/connect/index.js +333 -0
- package/dist/impl/nodejs/connect/index.js.map +1 -0
- 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 +232 -2513
- 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 +228 -2506
- package/dist/index.js.map +1 -1
- package/package.json +31 -1
|
@@ -1239,6 +1239,7 @@ declare const createUnicityOracleProvider: typeof createUnicityAggregatorProvide
|
|
|
1239
1239
|
* Platform-independent abstraction for fetching token market prices.
|
|
1240
1240
|
* Does not extend BaseProvider — stateless HTTP client with internal caching.
|
|
1241
1241
|
*/
|
|
1242
|
+
|
|
1242
1243
|
/**
|
|
1243
1244
|
* Supported price provider platforms
|
|
1244
1245
|
*/
|
|
@@ -1283,7 +1284,7 @@ interface PriceProvider {
|
|
|
1283
1284
|
/**
|
|
1284
1285
|
* Get price for a single token
|
|
1285
1286
|
* @param tokenName - Token name (e.g., 'bitcoin')
|
|
1286
|
-
* @returns Token price or null
|
|
1287
|
+
* @returns Token price (zero-price entry for tokens not listed on the platform), or null on network error with no cache
|
|
1287
1288
|
*/
|
|
1288
1289
|
getPrice(tokenName: string): Promise<TokenPrice | null>;
|
|
1289
1290
|
/**
|
|
@@ -1364,15 +1365,6 @@ interface BasePriceConfig {
|
|
|
1364
1365
|
/** Enable debug logging */
|
|
1365
1366
|
debug?: boolean;
|
|
1366
1367
|
}
|
|
1367
|
-
/**
|
|
1368
|
-
* Base market module configuration
|
|
1369
|
-
*/
|
|
1370
|
-
interface BaseMarketConfig {
|
|
1371
|
-
/** Market API base URL (default: https://market-api.unicity.network) */
|
|
1372
|
-
apiUrl?: string;
|
|
1373
|
-
/** Request timeout in ms (default: 30000) */
|
|
1374
|
-
timeout?: number;
|
|
1375
|
-
}
|
|
1376
1368
|
/**
|
|
1377
1369
|
* Base providers result
|
|
1378
1370
|
* Common structure for all platforms
|
|
@@ -1388,13 +1380,6 @@ interface BaseProviders {
|
|
|
1388
1380
|
price?: PriceProvider;
|
|
1389
1381
|
}
|
|
1390
1382
|
|
|
1391
|
-
interface MarketModuleConfig {
|
|
1392
|
-
/** Market API base URL (default: https://market-api.unicity.network) */
|
|
1393
|
-
apiUrl?: string;
|
|
1394
|
-
/** Request timeout in ms (default: 30000) */
|
|
1395
|
-
timeout?: number;
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
1383
|
/** IPFS storage provider configuration */
|
|
1399
1384
|
interface IpfsStorageConfig {
|
|
1400
1385
|
/** Gateway URLs for HTTP API (defaults to Unicity dedicated nodes) */
|
|
@@ -1487,8 +1472,6 @@ interface NodeProvidersConfig {
|
|
|
1487
1472
|
enabled?: boolean;
|
|
1488
1473
|
relays?: string[];
|
|
1489
1474
|
} | boolean;
|
|
1490
|
-
/** Market module configuration. true = enable with defaults, object = custom config */
|
|
1491
|
-
market?: BaseMarketConfig | boolean;
|
|
1492
1475
|
}
|
|
1493
1476
|
interface NodeProviders {
|
|
1494
1477
|
storage: StorageProvider;
|
|
@@ -1503,8 +1486,6 @@ interface NodeProviders {
|
|
|
1503
1486
|
ipfsTokenStorage?: TokenStorageProvider<TxfStorageDataBase>;
|
|
1504
1487
|
/** Group chat config (resolved, for passing to Sphere.init) */
|
|
1505
1488
|
groupChat?: GroupChatModuleConfig | boolean;
|
|
1506
|
-
/** Market module config (resolved, for passing to Sphere.init) */
|
|
1507
|
-
market?: MarketModuleConfig | boolean;
|
|
1508
1489
|
}
|
|
1509
1490
|
/**
|
|
1510
1491
|
* Create all Node.js providers with default configuration
|
|
@@ -1239,6 +1239,7 @@ declare const createUnicityOracleProvider: typeof createUnicityAggregatorProvide
|
|
|
1239
1239
|
* Platform-independent abstraction for fetching token market prices.
|
|
1240
1240
|
* Does not extend BaseProvider — stateless HTTP client with internal caching.
|
|
1241
1241
|
*/
|
|
1242
|
+
|
|
1242
1243
|
/**
|
|
1243
1244
|
* Supported price provider platforms
|
|
1244
1245
|
*/
|
|
@@ -1283,7 +1284,7 @@ interface PriceProvider {
|
|
|
1283
1284
|
/**
|
|
1284
1285
|
* Get price for a single token
|
|
1285
1286
|
* @param tokenName - Token name (e.g., 'bitcoin')
|
|
1286
|
-
* @returns Token price or null
|
|
1287
|
+
* @returns Token price (zero-price entry for tokens not listed on the platform), or null on network error with no cache
|
|
1287
1288
|
*/
|
|
1288
1289
|
getPrice(tokenName: string): Promise<TokenPrice | null>;
|
|
1289
1290
|
/**
|
|
@@ -1364,15 +1365,6 @@ interface BasePriceConfig {
|
|
|
1364
1365
|
/** Enable debug logging */
|
|
1365
1366
|
debug?: boolean;
|
|
1366
1367
|
}
|
|
1367
|
-
/**
|
|
1368
|
-
* Base market module configuration
|
|
1369
|
-
*/
|
|
1370
|
-
interface BaseMarketConfig {
|
|
1371
|
-
/** Market API base URL (default: https://market-api.unicity.network) */
|
|
1372
|
-
apiUrl?: string;
|
|
1373
|
-
/** Request timeout in ms (default: 30000) */
|
|
1374
|
-
timeout?: number;
|
|
1375
|
-
}
|
|
1376
1368
|
/**
|
|
1377
1369
|
* Base providers result
|
|
1378
1370
|
* Common structure for all platforms
|
|
@@ -1388,13 +1380,6 @@ interface BaseProviders {
|
|
|
1388
1380
|
price?: PriceProvider;
|
|
1389
1381
|
}
|
|
1390
1382
|
|
|
1391
|
-
interface MarketModuleConfig {
|
|
1392
|
-
/** Market API base URL (default: https://market-api.unicity.network) */
|
|
1393
|
-
apiUrl?: string;
|
|
1394
|
-
/** Request timeout in ms (default: 30000) */
|
|
1395
|
-
timeout?: number;
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
1383
|
/** IPFS storage provider configuration */
|
|
1399
1384
|
interface IpfsStorageConfig {
|
|
1400
1385
|
/** Gateway URLs for HTTP API (defaults to Unicity dedicated nodes) */
|
|
@@ -1487,8 +1472,6 @@ interface NodeProvidersConfig {
|
|
|
1487
1472
|
enabled?: boolean;
|
|
1488
1473
|
relays?: string[];
|
|
1489
1474
|
} | boolean;
|
|
1490
|
-
/** Market module configuration. true = enable with defaults, object = custom config */
|
|
1491
|
-
market?: BaseMarketConfig | boolean;
|
|
1492
1475
|
}
|
|
1493
1476
|
interface NodeProviders {
|
|
1494
1477
|
storage: StorageProvider;
|
|
@@ -1503,8 +1486,6 @@ interface NodeProviders {
|
|
|
1503
1486
|
ipfsTokenStorage?: TokenStorageProvider<TxfStorageDataBase>;
|
|
1504
1487
|
/** Group chat config (resolved, for passing to Sphere.init) */
|
|
1505
1488
|
groupChat?: GroupChatModuleConfig | boolean;
|
|
1506
|
-
/** Market module config (resolved, for passing to Sphere.init) */
|
|
1507
|
-
market?: MarketModuleConfig | boolean;
|
|
1508
1489
|
}
|
|
1509
1490
|
/**
|
|
1510
1491
|
* 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 {
|
|
@@ -3114,6 +3117,7 @@ async function loadIpnsModule() {
|
|
|
3114
3117
|
async function createSignedRecord(keyPair, cid, sequenceNumber, lifetimeMs = DEFAULT_LIFETIME_MS) {
|
|
3115
3118
|
const { createIPNSRecord, marshalIPNSRecord } = await loadIpnsModule();
|
|
3116
3119
|
const record = await createIPNSRecord(
|
|
3120
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3117
3121
|
keyPair,
|
|
3118
3122
|
`/ipfs/${cid}`,
|
|
3119
3123
|
sequenceNumber,
|
|
@@ -4855,26 +4859,37 @@ var CoinGeckoPriceProvider = class {
|
|
|
4855
4859
|
timeout;
|
|
4856
4860
|
debug;
|
|
4857
4861
|
baseUrl;
|
|
4862
|
+
storage;
|
|
4863
|
+
/** In-flight fetch promise for deduplication of concurrent getPrices() calls */
|
|
4864
|
+
fetchPromise = null;
|
|
4865
|
+
/** Token names being fetched in the current in-flight request */
|
|
4866
|
+
fetchNames = null;
|
|
4867
|
+
/** Whether persistent cache has been loaded into memory */
|
|
4868
|
+
persistentCacheLoaded = false;
|
|
4869
|
+
/** Promise for loading persistent cache (deduplication) */
|
|
4870
|
+
loadCachePromise = null;
|
|
4858
4871
|
constructor(config) {
|
|
4859
4872
|
this.apiKey = config?.apiKey;
|
|
4860
4873
|
this.cacheTtlMs = config?.cacheTtlMs ?? 6e4;
|
|
4861
4874
|
this.timeout = config?.timeout ?? 1e4;
|
|
4862
4875
|
this.debug = config?.debug ?? false;
|
|
4876
|
+
this.storage = config?.storage ?? null;
|
|
4863
4877
|
this.baseUrl = config?.baseUrl ?? (this.apiKey ? "https://pro-api.coingecko.com/api/v3" : "https://api.coingecko.com/api/v3");
|
|
4864
4878
|
}
|
|
4865
4879
|
async getPrices(tokenNames) {
|
|
4866
4880
|
if (tokenNames.length === 0) {
|
|
4867
4881
|
return /* @__PURE__ */ new Map();
|
|
4868
4882
|
}
|
|
4883
|
+
if (!this.persistentCacheLoaded && this.storage) {
|
|
4884
|
+
await this.loadFromStorage();
|
|
4885
|
+
}
|
|
4869
4886
|
const now = Date.now();
|
|
4870
4887
|
const result = /* @__PURE__ */ new Map();
|
|
4871
4888
|
const uncachedNames = [];
|
|
4872
4889
|
for (const name of tokenNames) {
|
|
4873
4890
|
const cached = this.cache.get(name);
|
|
4874
4891
|
if (cached && cached.expiresAt > now) {
|
|
4875
|
-
|
|
4876
|
-
result.set(name, cached.price);
|
|
4877
|
-
}
|
|
4892
|
+
result.set(name, cached.price);
|
|
4878
4893
|
} else {
|
|
4879
4894
|
uncachedNames.push(name);
|
|
4880
4895
|
}
|
|
@@ -4882,6 +4897,41 @@ var CoinGeckoPriceProvider = class {
|
|
|
4882
4897
|
if (uncachedNames.length === 0) {
|
|
4883
4898
|
return result;
|
|
4884
4899
|
}
|
|
4900
|
+
if (this.fetchPromise && this.fetchNames) {
|
|
4901
|
+
const allCovered = uncachedNames.every((n) => this.fetchNames.has(n));
|
|
4902
|
+
if (allCovered) {
|
|
4903
|
+
if (this.debug) {
|
|
4904
|
+
console.log(`[CoinGecko] Deduplicating request, reusing in-flight fetch`);
|
|
4905
|
+
}
|
|
4906
|
+
const fetched = await this.fetchPromise;
|
|
4907
|
+
for (const name of uncachedNames) {
|
|
4908
|
+
const price = fetched.get(name);
|
|
4909
|
+
if (price) {
|
|
4910
|
+
result.set(name, price);
|
|
4911
|
+
}
|
|
4912
|
+
}
|
|
4913
|
+
return result;
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
const fetchPromise = this.doFetch(uncachedNames);
|
|
4917
|
+
this.fetchPromise = fetchPromise;
|
|
4918
|
+
this.fetchNames = new Set(uncachedNames);
|
|
4919
|
+
try {
|
|
4920
|
+
const fetched = await fetchPromise;
|
|
4921
|
+
for (const [name, price] of fetched) {
|
|
4922
|
+
result.set(name, price);
|
|
4923
|
+
}
|
|
4924
|
+
} finally {
|
|
4925
|
+
if (this.fetchPromise === fetchPromise) {
|
|
4926
|
+
this.fetchPromise = null;
|
|
4927
|
+
this.fetchNames = null;
|
|
4928
|
+
}
|
|
4929
|
+
}
|
|
4930
|
+
return result;
|
|
4931
|
+
}
|
|
4932
|
+
async doFetch(uncachedNames) {
|
|
4933
|
+
const result = /* @__PURE__ */ new Map();
|
|
4934
|
+
const now = Date.now();
|
|
4885
4935
|
try {
|
|
4886
4936
|
const ids = uncachedNames.join(",");
|
|
4887
4937
|
const url = `${this.baseUrl}/simple/price?ids=${encodeURIComponent(ids)}&vs_currencies=usd,eur&include_24hr_change=true`;
|
|
@@ -4897,6 +4947,9 @@ var CoinGeckoPriceProvider = class {
|
|
|
4897
4947
|
signal: AbortSignal.timeout(this.timeout)
|
|
4898
4948
|
});
|
|
4899
4949
|
if (!response.ok) {
|
|
4950
|
+
if (response.status === 429) {
|
|
4951
|
+
this.extendCacheOnRateLimit(uncachedNames);
|
|
4952
|
+
}
|
|
4900
4953
|
throw new Error(`CoinGecko API error: ${response.status} ${response.statusText}`);
|
|
4901
4954
|
}
|
|
4902
4955
|
const data = await response.json();
|
|
@@ -4915,25 +4968,113 @@ var CoinGeckoPriceProvider = class {
|
|
|
4915
4968
|
}
|
|
4916
4969
|
for (const name of uncachedNames) {
|
|
4917
4970
|
if (!result.has(name)) {
|
|
4918
|
-
|
|
4971
|
+
const zeroPrice = {
|
|
4972
|
+
tokenName: name,
|
|
4973
|
+
priceUsd: 0,
|
|
4974
|
+
priceEur: 0,
|
|
4975
|
+
change24h: 0,
|
|
4976
|
+
timestamp: now
|
|
4977
|
+
};
|
|
4978
|
+
this.cache.set(name, { price: zeroPrice, expiresAt: now + this.cacheTtlMs });
|
|
4979
|
+
result.set(name, zeroPrice);
|
|
4919
4980
|
}
|
|
4920
4981
|
}
|
|
4921
4982
|
if (this.debug) {
|
|
4922
4983
|
console.log(`[CoinGecko] Fetched ${result.size} prices`);
|
|
4923
4984
|
}
|
|
4985
|
+
this.saveToStorage();
|
|
4924
4986
|
} catch (error) {
|
|
4925
4987
|
if (this.debug) {
|
|
4926
4988
|
console.warn("[CoinGecko] Fetch failed, using stale cache:", error);
|
|
4927
4989
|
}
|
|
4928
4990
|
for (const name of uncachedNames) {
|
|
4929
4991
|
const stale = this.cache.get(name);
|
|
4930
|
-
if (stale
|
|
4992
|
+
if (stale) {
|
|
4931
4993
|
result.set(name, stale.price);
|
|
4932
4994
|
}
|
|
4933
4995
|
}
|
|
4934
4996
|
}
|
|
4935
4997
|
return result;
|
|
4936
4998
|
}
|
|
4999
|
+
// ===========================================================================
|
|
5000
|
+
// Persistent Storage
|
|
5001
|
+
// ===========================================================================
|
|
5002
|
+
/**
|
|
5003
|
+
* Load cached prices from StorageProvider into in-memory cache.
|
|
5004
|
+
* Only loads entries that are still within cacheTtlMs.
|
|
5005
|
+
*/
|
|
5006
|
+
async loadFromStorage() {
|
|
5007
|
+
if (this.loadCachePromise) {
|
|
5008
|
+
return this.loadCachePromise;
|
|
5009
|
+
}
|
|
5010
|
+
this.loadCachePromise = this.doLoadFromStorage();
|
|
5011
|
+
try {
|
|
5012
|
+
await this.loadCachePromise;
|
|
5013
|
+
} finally {
|
|
5014
|
+
this.loadCachePromise = null;
|
|
5015
|
+
}
|
|
5016
|
+
}
|
|
5017
|
+
async doLoadFromStorage() {
|
|
5018
|
+
this.persistentCacheLoaded = true;
|
|
5019
|
+
if (!this.storage) return;
|
|
5020
|
+
try {
|
|
5021
|
+
const [cached, cachedTs] = await Promise.all([
|
|
5022
|
+
this.storage.get(STORAGE_KEYS_GLOBAL.PRICE_CACHE),
|
|
5023
|
+
this.storage.get(STORAGE_KEYS_GLOBAL.PRICE_CACHE_TS)
|
|
5024
|
+
]);
|
|
5025
|
+
if (!cached || !cachedTs) return;
|
|
5026
|
+
const ts = parseInt(cachedTs, 10);
|
|
5027
|
+
if (isNaN(ts)) return;
|
|
5028
|
+
const age = Date.now() - ts;
|
|
5029
|
+
if (age > this.cacheTtlMs) return;
|
|
5030
|
+
const data = JSON.parse(cached);
|
|
5031
|
+
const expiresAt = ts + this.cacheTtlMs;
|
|
5032
|
+
for (const [name, price] of Object.entries(data)) {
|
|
5033
|
+
if (!this.cache.has(name)) {
|
|
5034
|
+
this.cache.set(name, { price, expiresAt });
|
|
5035
|
+
}
|
|
5036
|
+
}
|
|
5037
|
+
if (this.debug) {
|
|
5038
|
+
console.log(`[CoinGecko] Loaded ${Object.keys(data).length} prices from persistent cache`);
|
|
5039
|
+
}
|
|
5040
|
+
} catch {
|
|
5041
|
+
}
|
|
5042
|
+
}
|
|
5043
|
+
/**
|
|
5044
|
+
* Save current prices to StorageProvider (fire-and-forget).
|
|
5045
|
+
*/
|
|
5046
|
+
saveToStorage() {
|
|
5047
|
+
if (!this.storage) return;
|
|
5048
|
+
const data = {};
|
|
5049
|
+
for (const [name, entry] of this.cache) {
|
|
5050
|
+
data[name] = entry.price;
|
|
5051
|
+
}
|
|
5052
|
+
Promise.all([
|
|
5053
|
+
this.storage.set(STORAGE_KEYS_GLOBAL.PRICE_CACHE, JSON.stringify(data)),
|
|
5054
|
+
this.storage.set(STORAGE_KEYS_GLOBAL.PRICE_CACHE_TS, String(Date.now()))
|
|
5055
|
+
]).catch(() => {
|
|
5056
|
+
});
|
|
5057
|
+
}
|
|
5058
|
+
// ===========================================================================
|
|
5059
|
+
// Rate-limit handling
|
|
5060
|
+
// ===========================================================================
|
|
5061
|
+
/**
|
|
5062
|
+
* On 429 rate-limit, extend stale cache entries so subsequent calls
|
|
5063
|
+
* don't immediately retry and hammer the API.
|
|
5064
|
+
*/
|
|
5065
|
+
extendCacheOnRateLimit(names) {
|
|
5066
|
+
const backoffMs = 6e4;
|
|
5067
|
+
const extendedExpiry = Date.now() + backoffMs;
|
|
5068
|
+
for (const name of names) {
|
|
5069
|
+
const existing = this.cache.get(name);
|
|
5070
|
+
if (existing) {
|
|
5071
|
+
existing.expiresAt = Math.max(existing.expiresAt, extendedExpiry);
|
|
5072
|
+
}
|
|
5073
|
+
}
|
|
5074
|
+
if (this.debug) {
|
|
5075
|
+
console.warn(`[CoinGecko] Rate-limited (429), extended cache TTL by ${backoffMs / 1e3}s`);
|
|
5076
|
+
}
|
|
5077
|
+
}
|
|
4937
5078
|
async getPrice(tokenName) {
|
|
4938
5079
|
const prices = await this.getPrices([tokenName]);
|
|
4939
5080
|
return prices.get(tokenName) ?? null;
|
|
@@ -4967,6 +5108,7 @@ var TokenRegistry = class _TokenRegistry {
|
|
|
4967
5108
|
refreshTimer = null;
|
|
4968
5109
|
lastRefreshAt = 0;
|
|
4969
5110
|
refreshPromise = null;
|
|
5111
|
+
initialLoadPromise = null;
|
|
4970
5112
|
constructor() {
|
|
4971
5113
|
this.definitionsById = /* @__PURE__ */ new Map();
|
|
4972
5114
|
this.definitionsBySymbol = /* @__PURE__ */ new Map();
|
|
@@ -5005,13 +5147,8 @@ var TokenRegistry = class _TokenRegistry {
|
|
|
5005
5147
|
if (options.refreshIntervalMs !== void 0) {
|
|
5006
5148
|
instance.refreshIntervalMs = options.refreshIntervalMs;
|
|
5007
5149
|
}
|
|
5008
|
-
if (instance.storage) {
|
|
5009
|
-
instance.loadFromCache();
|
|
5010
|
-
}
|
|
5011
5150
|
const autoRefresh = options.autoRefresh ?? true;
|
|
5012
|
-
|
|
5013
|
-
instance.startAutoRefresh();
|
|
5014
|
-
}
|
|
5151
|
+
instance.initialLoadPromise = instance.performInitialLoad(autoRefresh);
|
|
5015
5152
|
}
|
|
5016
5153
|
/**
|
|
5017
5154
|
* Reset the singleton instance (useful for testing).
|
|
@@ -5029,6 +5166,53 @@ var TokenRegistry = class _TokenRegistry {
|
|
|
5029
5166
|
static destroy() {
|
|
5030
5167
|
_TokenRegistry.resetInstance();
|
|
5031
5168
|
}
|
|
5169
|
+
/**
|
|
5170
|
+
* Wait for the initial data load (cache or remote) to complete.
|
|
5171
|
+
* Returns true if data was loaded, false if not (timeout or no data source).
|
|
5172
|
+
*
|
|
5173
|
+
* @param timeoutMs - Maximum wait time in ms (default: 10s). Set to 0 for no timeout.
|
|
5174
|
+
*/
|
|
5175
|
+
static async waitForReady(timeoutMs = 1e4) {
|
|
5176
|
+
const instance = _TokenRegistry.getInstance();
|
|
5177
|
+
if (!instance.initialLoadPromise) {
|
|
5178
|
+
return instance.definitionsById.size > 0;
|
|
5179
|
+
}
|
|
5180
|
+
if (timeoutMs <= 0) {
|
|
5181
|
+
return instance.initialLoadPromise;
|
|
5182
|
+
}
|
|
5183
|
+
return Promise.race([
|
|
5184
|
+
instance.initialLoadPromise,
|
|
5185
|
+
new Promise((resolve) => setTimeout(() => resolve(false), timeoutMs))
|
|
5186
|
+
]);
|
|
5187
|
+
}
|
|
5188
|
+
// ===========================================================================
|
|
5189
|
+
// Initial Load
|
|
5190
|
+
// ===========================================================================
|
|
5191
|
+
/**
|
|
5192
|
+
* Perform initial data load: try cache first, fall back to remote fetch.
|
|
5193
|
+
* After initial data is available, start periodic auto-refresh if configured.
|
|
5194
|
+
*/
|
|
5195
|
+
async performInitialLoad(autoRefresh) {
|
|
5196
|
+
let loaded = false;
|
|
5197
|
+
if (this.storage) {
|
|
5198
|
+
loaded = await this.loadFromCache();
|
|
5199
|
+
}
|
|
5200
|
+
if (loaded) {
|
|
5201
|
+
if (autoRefresh && this.remoteUrl) {
|
|
5202
|
+
this.startAutoRefresh();
|
|
5203
|
+
}
|
|
5204
|
+
return true;
|
|
5205
|
+
}
|
|
5206
|
+
if (autoRefresh && this.remoteUrl) {
|
|
5207
|
+
loaded = await this.refreshFromRemote();
|
|
5208
|
+
this.stopAutoRefresh();
|
|
5209
|
+
this.refreshTimer = setInterval(() => {
|
|
5210
|
+
this.refreshFromRemote();
|
|
5211
|
+
}, this.refreshIntervalMs);
|
|
5212
|
+
return loaded;
|
|
5213
|
+
}
|
|
5214
|
+
return false;
|
|
5215
|
+
}
|
|
5032
5216
|
// ===========================================================================
|
|
5033
5217
|
// Cache (StorageProvider)
|
|
5034
5218
|
// ===========================================================================
|
|
@@ -5358,7 +5542,7 @@ function resolveL1Config(network, config) {
|
|
|
5358
5542
|
enableVesting: config.enableVesting
|
|
5359
5543
|
};
|
|
5360
5544
|
}
|
|
5361
|
-
function resolvePriceConfig(config) {
|
|
5545
|
+
function resolvePriceConfig(config, storage) {
|
|
5362
5546
|
if (config === void 0) {
|
|
5363
5547
|
return void 0;
|
|
5364
5548
|
}
|
|
@@ -5368,7 +5552,8 @@ function resolvePriceConfig(config) {
|
|
|
5368
5552
|
baseUrl: config.baseUrl,
|
|
5369
5553
|
cacheTtlMs: config.cacheTtlMs,
|
|
5370
5554
|
timeout: config.timeout,
|
|
5371
|
-
debug: config.debug
|
|
5555
|
+
debug: config.debug,
|
|
5556
|
+
storage
|
|
5372
5557
|
};
|
|
5373
5558
|
}
|
|
5374
5559
|
function resolveGroupChatConfig(network, config) {
|
|
@@ -5385,16 +5570,6 @@ function resolveGroupChatConfig(network, config) {
|
|
|
5385
5570
|
relays: config.relays ?? [...netConfig.groupRelays]
|
|
5386
5571
|
};
|
|
5387
5572
|
}
|
|
5388
|
-
function resolveMarketConfig(config) {
|
|
5389
|
-
if (!config) return void 0;
|
|
5390
|
-
if (config === true) {
|
|
5391
|
-
return { apiUrl: DEFAULT_MARKET_API_URL };
|
|
5392
|
-
}
|
|
5393
|
-
return {
|
|
5394
|
-
apiUrl: config.apiUrl ?? DEFAULT_MARKET_API_URL,
|
|
5395
|
-
timeout: config.timeout
|
|
5396
|
-
};
|
|
5397
|
-
}
|
|
5398
5573
|
|
|
5399
5574
|
// impl/nodejs/index.ts
|
|
5400
5575
|
function createNodeProviders(config) {
|
|
@@ -5402,21 +5577,19 @@ function createNodeProviders(config) {
|
|
|
5402
5577
|
const transportConfig = resolveTransportConfig(network, config?.transport);
|
|
5403
5578
|
const oracleConfig = resolveOracleConfig(network, config?.oracle);
|
|
5404
5579
|
const l1Config = resolveL1Config(network, config?.l1);
|
|
5405
|
-
const priceConfig = resolvePriceConfig(config?.price);
|
|
5406
5580
|
const storage = createFileStorageProvider({
|
|
5407
5581
|
dataDir: config?.dataDir ?? "./sphere-data",
|
|
5408
5582
|
...config?.walletFileName ? { fileName: config.walletFileName } : {}
|
|
5409
5583
|
});
|
|
5584
|
+
const priceConfig = resolvePriceConfig(config?.price, storage);
|
|
5410
5585
|
const ipfsSync = config?.tokenSync?.ipfs;
|
|
5411
5586
|
const ipfsTokenStorage = ipfsSync?.enabled ? createNodeIpfsStorageProvider(ipfsSync.config, storage) : void 0;
|
|
5412
5587
|
const groupChat = resolveGroupChatConfig(network, config?.groupChat);
|
|
5413
5588
|
const networkConfig = getNetworkConfig(network);
|
|
5414
5589
|
TokenRegistry.configure({ remoteUrl: networkConfig.tokenRegistryUrl, storage });
|
|
5415
|
-
const market = resolveMarketConfig(config?.market);
|
|
5416
5590
|
return {
|
|
5417
5591
|
storage,
|
|
5418
5592
|
groupChat,
|
|
5419
|
-
market,
|
|
5420
5593
|
tokenStorage: createFileTokenStorageProvider({
|
|
5421
5594
|
tokensDir: config?.tokensDir ?? "./sphere-tokens"
|
|
5422
5595
|
}),
|