@unicitylabs/sphere-sdk 0.1.9 → 0.2.0
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/README.md +47 -2
- package/dist/core/index.cjs +1982 -371
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +606 -55
- package/dist/core/index.d.ts +606 -55
- package/dist/core/index.js +1981 -370
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +303 -6
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +303 -6
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/browser/ipfs.cjs +4 -2
- package/dist/impl/browser/ipfs.cjs.map +1 -1
- package/dist/impl/browser/ipfs.js +4 -2
- package/dist/impl/browser/ipfs.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +322 -8
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +223 -14
- package/dist/impl/nodejs/index.d.ts +223 -14
- package/dist/impl/nodejs/index.js +322 -8
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +2214 -375
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1021 -7602
- package/dist/index.d.ts +1021 -7602
- package/dist/index.js +2203 -374
- package/dist/index.js.map +1 -1
- package/dist/l1/index.cjs.map +1 -1
- package/dist/l1/index.d.cts +7 -0
- package/dist/l1/index.d.ts +7 -0
- package/dist/l1/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -22,8 +22,10 @@ var STORAGE_KEYS_GLOBAL = {
|
|
|
22
22
|
WALLET_EXISTS: "wallet_exists",
|
|
23
23
|
/** Current active address index */
|
|
24
24
|
CURRENT_ADDRESS_INDEX: "current_address_index",
|
|
25
|
-
/**
|
|
26
|
-
ADDRESS_NAMETAGS: "address_nametags"
|
|
25
|
+
/** Nametag cache per address (separate from tracked addresses registry) */
|
|
26
|
+
ADDRESS_NAMETAGS: "address_nametags",
|
|
27
|
+
/** Active addresses registry (JSON: TrackedAddressesStorage) */
|
|
28
|
+
TRACKED_ADDRESSES: "tracked_addresses"
|
|
27
29
|
};
|
|
28
30
|
var STORAGE_KEYS_ADDRESS = {
|
|
29
31
|
/** Pending transfers for this address */
|
|
@@ -213,6 +215,19 @@ var FileStorageProvider = class {
|
|
|
213
215
|
}
|
|
214
216
|
await this.save();
|
|
215
217
|
}
|
|
218
|
+
async saveTrackedAddresses(entries) {
|
|
219
|
+
await this.set(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES, JSON.stringify({ version: 1, addresses: entries }));
|
|
220
|
+
}
|
|
221
|
+
async loadTrackedAddresses() {
|
|
222
|
+
const data = await this.get(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES);
|
|
223
|
+
if (!data) return [];
|
|
224
|
+
try {
|
|
225
|
+
const parsed = JSON.parse(data);
|
|
226
|
+
return parsed.addresses ?? [];
|
|
227
|
+
} catch {
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
230
|
+
}
|
|
216
231
|
/**
|
|
217
232
|
* Get full storage key with address prefix for per-address keys
|
|
218
233
|
*/
|
|
@@ -296,7 +311,12 @@ var FileTokenStorageProvider = class {
|
|
|
296
311
|
}
|
|
297
312
|
};
|
|
298
313
|
try {
|
|
299
|
-
const files = fs2.readdirSync(this.tokensDir).filter(
|
|
314
|
+
const files = fs2.readdirSync(this.tokensDir).filter(
|
|
315
|
+
(f) => f.endsWith(".json") && f !== "_meta.json" && f !== "_tombstones.json" && !f.startsWith("archived_") && // Skip archived tokens
|
|
316
|
+
!f.startsWith("token-") && // Skip legacy token format
|
|
317
|
+
!f.startsWith("nametag-")
|
|
318
|
+
// Skip nametag files (not tokens)
|
|
319
|
+
);
|
|
300
320
|
for (const file of files) {
|
|
301
321
|
try {
|
|
302
322
|
const basename2 = path2.basename(file, ".json");
|
|
@@ -314,6 +334,14 @@ var FileTokenStorageProvider = class {
|
|
|
314
334
|
} catch {
|
|
315
335
|
}
|
|
316
336
|
}
|
|
337
|
+
const tombstonesPath = path2.join(this.tokensDir, "_tombstones.json");
|
|
338
|
+
if (fs2.existsSync(tombstonesPath)) {
|
|
339
|
+
try {
|
|
340
|
+
const content = fs2.readFileSync(tombstonesPath, "utf-8");
|
|
341
|
+
data._tombstones = JSON.parse(content);
|
|
342
|
+
} catch {
|
|
343
|
+
}
|
|
344
|
+
}
|
|
317
345
|
return {
|
|
318
346
|
success: true,
|
|
319
347
|
data,
|
|
@@ -358,6 +386,10 @@ var FileTokenStorageProvider = class {
|
|
|
358
386
|
fs2.unlinkSync(filePath);
|
|
359
387
|
}
|
|
360
388
|
}
|
|
389
|
+
fs2.writeFileSync(
|
|
390
|
+
path2.join(this.tokensDir, "_tombstones.json"),
|
|
391
|
+
JSON.stringify(data._tombstones, null, 2)
|
|
392
|
+
);
|
|
361
393
|
}
|
|
362
394
|
return {
|
|
363
395
|
success: true,
|
|
@@ -1006,6 +1038,10 @@ function defaultUUIDGenerator() {
|
|
|
1006
1038
|
|
|
1007
1039
|
// transport/NostrTransportProvider.ts
|
|
1008
1040
|
var EVENT_KINDS = NOSTR_EVENT_KINDS;
|
|
1041
|
+
function hashAddressForTag(address) {
|
|
1042
|
+
const bytes = new TextEncoder().encode("unicity:address:" + address);
|
|
1043
|
+
return Buffer2.from(sha256(bytes)).toString("hex");
|
|
1044
|
+
}
|
|
1009
1045
|
function deriveNametagEncryptionKey(privateKeyHex) {
|
|
1010
1046
|
const privateKeyBytes = Buffer2.from(privateKeyHex, "hex");
|
|
1011
1047
|
const saltInput = new TextEncoder().encode("sphere-nametag-salt");
|
|
@@ -1414,6 +1450,28 @@ var NostrTransportProvider = class {
|
|
|
1414
1450
|
this.paymentRequestResponseHandlers.add(handler);
|
|
1415
1451
|
return () => this.paymentRequestResponseHandlers.delete(handler);
|
|
1416
1452
|
}
|
|
1453
|
+
/**
|
|
1454
|
+
* Resolve any identifier to full peer information.
|
|
1455
|
+
* Routes to the appropriate specific resolve method based on identifier format.
|
|
1456
|
+
*/
|
|
1457
|
+
async resolve(identifier) {
|
|
1458
|
+
if (identifier.startsWith("@")) {
|
|
1459
|
+
return this.resolveNametagInfo(identifier.slice(1));
|
|
1460
|
+
}
|
|
1461
|
+
if (identifier.startsWith("DIRECT:") || identifier.startsWith("PROXY:")) {
|
|
1462
|
+
return this.resolveAddressInfo(identifier);
|
|
1463
|
+
}
|
|
1464
|
+
if (identifier.startsWith("alpha1") || identifier.startsWith("alphat1")) {
|
|
1465
|
+
return this.resolveAddressInfo(identifier);
|
|
1466
|
+
}
|
|
1467
|
+
if (/^0[23][0-9a-f]{64}$/i.test(identifier)) {
|
|
1468
|
+
return this.resolveAddressInfo(identifier);
|
|
1469
|
+
}
|
|
1470
|
+
if (/^[0-9a-f]{64}$/i.test(identifier)) {
|
|
1471
|
+
return this.resolveTransportPubkeyInfo(identifier);
|
|
1472
|
+
}
|
|
1473
|
+
return this.resolveNametagInfo(identifier);
|
|
1474
|
+
}
|
|
1417
1475
|
async resolveNametag(nametag) {
|
|
1418
1476
|
this.ensureReady();
|
|
1419
1477
|
const hashedNametag = hashNametag(nametag);
|
|
@@ -1510,6 +1568,77 @@ var NostrTransportProvider = class {
|
|
|
1510
1568
|
};
|
|
1511
1569
|
}
|
|
1512
1570
|
}
|
|
1571
|
+
/**
|
|
1572
|
+
* Resolve a DIRECT://, PROXY://, or L1 address to full peer info.
|
|
1573
|
+
* Performs reverse lookup: hash(address) → query '#t' tag → parse binding event.
|
|
1574
|
+
* Works with both new identity binding events and legacy nametag binding events.
|
|
1575
|
+
*/
|
|
1576
|
+
async resolveAddressInfo(address) {
|
|
1577
|
+
this.ensureReady();
|
|
1578
|
+
const addressHash = hashAddressForTag(address);
|
|
1579
|
+
const events = await this.queryEvents({
|
|
1580
|
+
kinds: [EVENT_KINDS.NAMETAG_BINDING],
|
|
1581
|
+
"#t": [addressHash],
|
|
1582
|
+
limit: 1
|
|
1583
|
+
});
|
|
1584
|
+
if (events.length === 0) return null;
|
|
1585
|
+
const bindingEvent = events[0];
|
|
1586
|
+
try {
|
|
1587
|
+
const content = JSON.parse(bindingEvent.content);
|
|
1588
|
+
return {
|
|
1589
|
+
nametag: content.nametag || void 0,
|
|
1590
|
+
transportPubkey: bindingEvent.pubkey,
|
|
1591
|
+
chainPubkey: content.public_key || "",
|
|
1592
|
+
l1Address: content.l1_address || "",
|
|
1593
|
+
directAddress: content.direct_address || "",
|
|
1594
|
+
proxyAddress: content.proxy_address || void 0,
|
|
1595
|
+
timestamp: bindingEvent.created_at * 1e3
|
|
1596
|
+
};
|
|
1597
|
+
} catch {
|
|
1598
|
+
return {
|
|
1599
|
+
transportPubkey: bindingEvent.pubkey,
|
|
1600
|
+
chainPubkey: "",
|
|
1601
|
+
l1Address: "",
|
|
1602
|
+
directAddress: "",
|
|
1603
|
+
timestamp: bindingEvent.created_at * 1e3
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* Resolve transport pubkey (Nostr pubkey) to full peer info.
|
|
1609
|
+
* Queries binding events authored by the given pubkey.
|
|
1610
|
+
*/
|
|
1611
|
+
async resolveTransportPubkeyInfo(transportPubkey) {
|
|
1612
|
+
this.ensureReady();
|
|
1613
|
+
const events = await this.queryEvents({
|
|
1614
|
+
kinds: [EVENT_KINDS.NAMETAG_BINDING],
|
|
1615
|
+
authors: [transportPubkey],
|
|
1616
|
+
limit: 5
|
|
1617
|
+
});
|
|
1618
|
+
if (events.length === 0) return null;
|
|
1619
|
+
events.sort((a, b) => b.created_at - a.created_at);
|
|
1620
|
+
const bindingEvent = events[0];
|
|
1621
|
+
try {
|
|
1622
|
+
const content = JSON.parse(bindingEvent.content);
|
|
1623
|
+
return {
|
|
1624
|
+
nametag: content.nametag || void 0,
|
|
1625
|
+
transportPubkey: bindingEvent.pubkey,
|
|
1626
|
+
chainPubkey: content.public_key || "",
|
|
1627
|
+
l1Address: content.l1_address || "",
|
|
1628
|
+
directAddress: content.direct_address || "",
|
|
1629
|
+
proxyAddress: content.proxy_address || void 0,
|
|
1630
|
+
timestamp: bindingEvent.created_at * 1e3
|
|
1631
|
+
};
|
|
1632
|
+
} catch {
|
|
1633
|
+
return {
|
|
1634
|
+
transportPubkey: bindingEvent.pubkey,
|
|
1635
|
+
chainPubkey: "",
|
|
1636
|
+
l1Address: "",
|
|
1637
|
+
directAddress: "",
|
|
1638
|
+
timestamp: bindingEvent.created_at * 1e3
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1513
1642
|
/**
|
|
1514
1643
|
* Recover nametag for the current identity by searching for encrypted nametag events
|
|
1515
1644
|
* Used after wallet import to recover associated nametag
|
|
@@ -1553,6 +1682,63 @@ var NostrTransportProvider = class {
|
|
|
1553
1682
|
this.log("Could not decrypt nametag from any event");
|
|
1554
1683
|
return null;
|
|
1555
1684
|
}
|
|
1685
|
+
/**
|
|
1686
|
+
* Publish identity binding event on Nostr.
|
|
1687
|
+
* Without nametag: publishes base binding (chainPubkey, l1Address, directAddress).
|
|
1688
|
+
* With nametag: also publishes nametag hash, proxy address, encrypted nametag for recovery.
|
|
1689
|
+
*
|
|
1690
|
+
* Uses kind 30078 parameterized replaceable event with d=SHA256('unicity:identity:' + nostrPubkey).
|
|
1691
|
+
* Each HD address index has its own Nostr key → its own binding event.
|
|
1692
|
+
*
|
|
1693
|
+
* @returns true if successful, false if nametag is taken by another pubkey
|
|
1694
|
+
*/
|
|
1695
|
+
async publishIdentityBinding(chainPubkey, l1Address, directAddress, nametag) {
|
|
1696
|
+
this.ensureReady();
|
|
1697
|
+
if (!this.identity) {
|
|
1698
|
+
throw new Error("Identity not set");
|
|
1699
|
+
}
|
|
1700
|
+
const nostrPubkey = this.getNostrPubkey();
|
|
1701
|
+
const dTagBytes = new TextEncoder().encode("unicity:identity:" + nostrPubkey);
|
|
1702
|
+
const dTag = Buffer2.from(sha256(dTagBytes)).toString("hex");
|
|
1703
|
+
const contentObj = {
|
|
1704
|
+
public_key: chainPubkey,
|
|
1705
|
+
l1_address: l1Address,
|
|
1706
|
+
direct_address: directAddress
|
|
1707
|
+
};
|
|
1708
|
+
const tags = [
|
|
1709
|
+
["d", dTag],
|
|
1710
|
+
["t", hashAddressForTag(chainPubkey)],
|
|
1711
|
+
["t", hashAddressForTag(directAddress)],
|
|
1712
|
+
["t", hashAddressForTag(l1Address)]
|
|
1713
|
+
];
|
|
1714
|
+
if (nametag) {
|
|
1715
|
+
const existing = await this.resolveNametag(nametag);
|
|
1716
|
+
if (existing && existing !== nostrPubkey) {
|
|
1717
|
+
this.log("Nametag already taken:", nametag, "- owner:", existing);
|
|
1718
|
+
return false;
|
|
1719
|
+
}
|
|
1720
|
+
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
1721
|
+
const proxyAddr = await ProxyAddress.fromNameTag(nametag);
|
|
1722
|
+
const proxyAddress = proxyAddr.toString();
|
|
1723
|
+
const encryptedNametag = await encryptNametag(nametag, this.identity.privateKey);
|
|
1724
|
+
const hashedNametag = hashNametag(nametag);
|
|
1725
|
+
contentObj.nametag = nametag;
|
|
1726
|
+
contentObj.encrypted_nametag = encryptedNametag;
|
|
1727
|
+
contentObj.proxy_address = proxyAddress;
|
|
1728
|
+
tags.push(["t", hashedNametag]);
|
|
1729
|
+
tags.push(["t", hashAddressForTag(proxyAddress)]);
|
|
1730
|
+
}
|
|
1731
|
+
const content = JSON.stringify(contentObj);
|
|
1732
|
+
const event = await this.createEvent(EVENT_KINDS.NAMETAG_BINDING, content, tags);
|
|
1733
|
+
await this.publishEvent(event);
|
|
1734
|
+
if (nametag) {
|
|
1735
|
+
this.log("Published identity binding with nametag:", nametag, "for pubkey:", nostrPubkey.slice(0, 16) + "...");
|
|
1736
|
+
} else {
|
|
1737
|
+
this.log("Published identity binding (no nametag) for pubkey:", nostrPubkey.slice(0, 16) + "...");
|
|
1738
|
+
}
|
|
1739
|
+
return true;
|
|
1740
|
+
}
|
|
1741
|
+
/** @deprecated Use publishIdentityBinding instead */
|
|
1556
1742
|
async publishNametag(nametag, address) {
|
|
1557
1743
|
this.ensureReady();
|
|
1558
1744
|
const hashedNametag = hashNametag(nametag);
|
|
@@ -1579,6 +1765,9 @@ var NostrTransportProvider = class {
|
|
|
1579
1765
|
const compressedPubkey = getPublicKey(privateKeyHex, true);
|
|
1580
1766
|
const l1Address = publicKeyToAddress(compressedPubkey, "alpha");
|
|
1581
1767
|
const encryptedNametag = await encryptNametag(nametag, privateKeyHex);
|
|
1768
|
+
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
1769
|
+
const proxyAddr = await ProxyAddress.fromNameTag(nametag);
|
|
1770
|
+
const proxyAddress = proxyAddr.toString();
|
|
1582
1771
|
const hashedNametag = hashNametag(nametag);
|
|
1583
1772
|
const content = JSON.stringify({
|
|
1584
1773
|
nametag_hash: hashedNametag,
|
|
@@ -1588,17 +1777,20 @@ var NostrTransportProvider = class {
|
|
|
1588
1777
|
encrypted_nametag: encryptedNametag,
|
|
1589
1778
|
public_key: compressedPubkey,
|
|
1590
1779
|
l1_address: l1Address,
|
|
1591
|
-
direct_address: directAddress
|
|
1780
|
+
direct_address: directAddress,
|
|
1781
|
+
proxy_address: proxyAddress
|
|
1592
1782
|
});
|
|
1593
|
-
const
|
|
1783
|
+
const tags = [
|
|
1594
1784
|
["d", hashedNametag],
|
|
1595
1785
|
["nametag", hashedNametag],
|
|
1596
1786
|
["t", hashedNametag],
|
|
1787
|
+
["t", hashAddressForTag(directAddress)],
|
|
1788
|
+
["t", hashAddressForTag(proxyAddress)],
|
|
1597
1789
|
["address", nostrPubkey],
|
|
1598
|
-
// Extended tags for indexing
|
|
1599
1790
|
["pubkey", compressedPubkey],
|
|
1600
1791
|
["l1", l1Address]
|
|
1601
|
-
]
|
|
1792
|
+
];
|
|
1793
|
+
const event = await this.createEvent(EVENT_KINDS.NAMETAG_BINDING, content, tags);
|
|
1602
1794
|
await this.publishEvent(event);
|
|
1603
1795
|
this.log("Registered nametag:", nametag, "for pubkey:", nostrPubkey.slice(0, 16) + "...", "l1:", l1Address.slice(0, 12) + "...");
|
|
1604
1796
|
return true;
|
|
@@ -2550,6 +2742,113 @@ function createUnicityAggregatorProvider(config) {
|
|
|
2550
2742
|
}
|
|
2551
2743
|
var createUnicityOracleProvider = createUnicityAggregatorProvider;
|
|
2552
2744
|
|
|
2745
|
+
// price/CoinGeckoPriceProvider.ts
|
|
2746
|
+
var CoinGeckoPriceProvider = class {
|
|
2747
|
+
platform = "coingecko";
|
|
2748
|
+
cache = /* @__PURE__ */ new Map();
|
|
2749
|
+
apiKey;
|
|
2750
|
+
cacheTtlMs;
|
|
2751
|
+
timeout;
|
|
2752
|
+
debug;
|
|
2753
|
+
baseUrl;
|
|
2754
|
+
constructor(config) {
|
|
2755
|
+
this.apiKey = config?.apiKey;
|
|
2756
|
+
this.cacheTtlMs = config?.cacheTtlMs ?? 6e4;
|
|
2757
|
+
this.timeout = config?.timeout ?? 1e4;
|
|
2758
|
+
this.debug = config?.debug ?? false;
|
|
2759
|
+
this.baseUrl = config?.baseUrl ?? (this.apiKey ? "https://pro-api.coingecko.com/api/v3" : "https://api.coingecko.com/api/v3");
|
|
2760
|
+
}
|
|
2761
|
+
async getPrices(tokenNames) {
|
|
2762
|
+
if (tokenNames.length === 0) {
|
|
2763
|
+
return /* @__PURE__ */ new Map();
|
|
2764
|
+
}
|
|
2765
|
+
const now = Date.now();
|
|
2766
|
+
const result = /* @__PURE__ */ new Map();
|
|
2767
|
+
const uncachedNames = [];
|
|
2768
|
+
for (const name of tokenNames) {
|
|
2769
|
+
const cached = this.cache.get(name);
|
|
2770
|
+
if (cached && cached.expiresAt > now) {
|
|
2771
|
+
if (cached.price !== null) {
|
|
2772
|
+
result.set(name, cached.price);
|
|
2773
|
+
}
|
|
2774
|
+
} else {
|
|
2775
|
+
uncachedNames.push(name);
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
if (uncachedNames.length === 0) {
|
|
2779
|
+
return result;
|
|
2780
|
+
}
|
|
2781
|
+
try {
|
|
2782
|
+
const ids = uncachedNames.join(",");
|
|
2783
|
+
const url = `${this.baseUrl}/simple/price?ids=${encodeURIComponent(ids)}&vs_currencies=usd,eur&include_24hr_change=true`;
|
|
2784
|
+
const headers = { Accept: "application/json" };
|
|
2785
|
+
if (this.apiKey) {
|
|
2786
|
+
headers["x-cg-pro-api-key"] = this.apiKey;
|
|
2787
|
+
}
|
|
2788
|
+
if (this.debug) {
|
|
2789
|
+
console.log(`[CoinGecko] Fetching prices for: ${uncachedNames.join(", ")}`);
|
|
2790
|
+
}
|
|
2791
|
+
const response = await fetch(url, {
|
|
2792
|
+
headers,
|
|
2793
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
2794
|
+
});
|
|
2795
|
+
if (!response.ok) {
|
|
2796
|
+
throw new Error(`CoinGecko API error: ${response.status} ${response.statusText}`);
|
|
2797
|
+
}
|
|
2798
|
+
const data = await response.json();
|
|
2799
|
+
for (const [name, values] of Object.entries(data)) {
|
|
2800
|
+
if (values && typeof values === "object") {
|
|
2801
|
+
const price = {
|
|
2802
|
+
tokenName: name,
|
|
2803
|
+
priceUsd: values.usd ?? 0,
|
|
2804
|
+
priceEur: values.eur,
|
|
2805
|
+
change24h: values.usd_24h_change,
|
|
2806
|
+
timestamp: now
|
|
2807
|
+
};
|
|
2808
|
+
this.cache.set(name, { price, expiresAt: now + this.cacheTtlMs });
|
|
2809
|
+
result.set(name, price);
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
for (const name of uncachedNames) {
|
|
2813
|
+
if (!result.has(name)) {
|
|
2814
|
+
this.cache.set(name, { price: null, expiresAt: now + this.cacheTtlMs });
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
if (this.debug) {
|
|
2818
|
+
console.log(`[CoinGecko] Fetched ${result.size} prices`);
|
|
2819
|
+
}
|
|
2820
|
+
} catch (error) {
|
|
2821
|
+
if (this.debug) {
|
|
2822
|
+
console.warn("[CoinGecko] Fetch failed, using stale cache:", error);
|
|
2823
|
+
}
|
|
2824
|
+
for (const name of uncachedNames) {
|
|
2825
|
+
const stale = this.cache.get(name);
|
|
2826
|
+
if (stale?.price) {
|
|
2827
|
+
result.set(name, stale.price);
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
return result;
|
|
2832
|
+
}
|
|
2833
|
+
async getPrice(tokenName) {
|
|
2834
|
+
const prices = await this.getPrices([tokenName]);
|
|
2835
|
+
return prices.get(tokenName) ?? null;
|
|
2836
|
+
}
|
|
2837
|
+
clearCache() {
|
|
2838
|
+
this.cache.clear();
|
|
2839
|
+
}
|
|
2840
|
+
};
|
|
2841
|
+
|
|
2842
|
+
// price/index.ts
|
|
2843
|
+
function createPriceProvider(config) {
|
|
2844
|
+
switch (config.platform) {
|
|
2845
|
+
case "coingecko":
|
|
2846
|
+
return new CoinGeckoPriceProvider(config);
|
|
2847
|
+
default:
|
|
2848
|
+
throw new Error(`Unsupported price platform: ${String(config.platform)}`);
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
|
|
2553
2852
|
// impl/shared/resolvers.ts
|
|
2554
2853
|
function getNetworkConfig(network = "mainnet") {
|
|
2555
2854
|
return NETWORKS[network];
|
|
@@ -2598,6 +2897,19 @@ function resolveL1Config(network, config) {
|
|
|
2598
2897
|
enableVesting: config.enableVesting
|
|
2599
2898
|
};
|
|
2600
2899
|
}
|
|
2900
|
+
function resolvePriceConfig(config) {
|
|
2901
|
+
if (config === void 0) {
|
|
2902
|
+
return void 0;
|
|
2903
|
+
}
|
|
2904
|
+
return {
|
|
2905
|
+
platform: config.platform ?? "coingecko",
|
|
2906
|
+
apiKey: config.apiKey,
|
|
2907
|
+
baseUrl: config.baseUrl,
|
|
2908
|
+
cacheTtlMs: config.cacheTtlMs,
|
|
2909
|
+
timeout: config.timeout,
|
|
2910
|
+
debug: config.debug
|
|
2911
|
+
};
|
|
2912
|
+
}
|
|
2601
2913
|
|
|
2602
2914
|
// impl/nodejs/index.ts
|
|
2603
2915
|
function createNodeProviders(config) {
|
|
@@ -2605,6 +2917,7 @@ function createNodeProviders(config) {
|
|
|
2605
2917
|
const transportConfig = resolveTransportConfig(network, config?.transport);
|
|
2606
2918
|
const oracleConfig = resolveOracleConfig(network, config?.oracle);
|
|
2607
2919
|
const l1Config = resolveL1Config(network, config?.l1);
|
|
2920
|
+
const priceConfig = resolvePriceConfig(config?.price);
|
|
2608
2921
|
return {
|
|
2609
2922
|
storage: createFileStorageProvider({
|
|
2610
2923
|
dataDir: config?.dataDir ?? "./sphere-data"
|
|
@@ -2627,7 +2940,8 @@ function createNodeProviders(config) {
|
|
|
2627
2940
|
debug: oracleConfig.debug,
|
|
2628
2941
|
network
|
|
2629
2942
|
}),
|
|
2630
|
-
l1: l1Config
|
|
2943
|
+
l1: l1Config,
|
|
2944
|
+
price: priceConfig ? createPriceProvider(priceConfig) : void 0
|
|
2631
2945
|
};
|
|
2632
2946
|
}
|
|
2633
2947
|
export {
|