@unicitylabs/sphere-sdk 0.1.8 → 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 +2019 -374
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +631 -58
- package/dist/core/index.d.ts +631 -58
- package/dist/core/index.js +2018 -373
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +312 -12
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +312 -12
- 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 +345 -14
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +224 -14
- package/dist/impl/nodejs/index.d.ts +224 -14
- package/dist/impl/nodejs/index.js +345 -14
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +2255 -382
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1046 -7571
- package/dist/index.d.ts +1046 -7571
- package/dist/index.js +2266 -403
- 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
|
@@ -21,8 +21,10 @@ var STORAGE_KEYS_GLOBAL = {
|
|
|
21
21
|
WALLET_EXISTS: "wallet_exists",
|
|
22
22
|
/** Current active address index */
|
|
23
23
|
CURRENT_ADDRESS_INDEX: "current_address_index",
|
|
24
|
-
/**
|
|
25
|
-
ADDRESS_NAMETAGS: "address_nametags"
|
|
24
|
+
/** Nametag cache per address (separate from tracked addresses registry) */
|
|
25
|
+
ADDRESS_NAMETAGS: "address_nametags",
|
|
26
|
+
/** Active addresses registry (JSON: TrackedAddressesStorage) */
|
|
27
|
+
TRACKED_ADDRESSES: "tracked_addresses"
|
|
26
28
|
};
|
|
27
29
|
var STORAGE_KEYS_ADDRESS = {
|
|
28
30
|
/** Pending transfers for this address */
|
|
@@ -215,6 +217,19 @@ var LocalStorageProvider = class {
|
|
|
215
217
|
await this.remove(key);
|
|
216
218
|
}
|
|
217
219
|
}
|
|
220
|
+
async saveTrackedAddresses(entries) {
|
|
221
|
+
await this.set(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES, JSON.stringify({ version: 1, addresses: entries }));
|
|
222
|
+
}
|
|
223
|
+
async loadTrackedAddresses() {
|
|
224
|
+
const data = await this.get(STORAGE_KEYS_GLOBAL.TRACKED_ADDRESSES);
|
|
225
|
+
if (!data) return [];
|
|
226
|
+
try {
|
|
227
|
+
const parsed = JSON.parse(data);
|
|
228
|
+
return parsed.addresses ?? [];
|
|
229
|
+
} catch {
|
|
230
|
+
return [];
|
|
231
|
+
}
|
|
232
|
+
}
|
|
218
233
|
// ===========================================================================
|
|
219
234
|
// Helpers
|
|
220
235
|
// ===========================================================================
|
|
@@ -1182,6 +1197,10 @@ function defaultUUIDGenerator() {
|
|
|
1182
1197
|
|
|
1183
1198
|
// transport/NostrTransportProvider.ts
|
|
1184
1199
|
var EVENT_KINDS = NOSTR_EVENT_KINDS;
|
|
1200
|
+
function hashAddressForTag(address) {
|
|
1201
|
+
const bytes = new TextEncoder().encode("unicity:address:" + address);
|
|
1202
|
+
return Buffer2.from(sha256(bytes)).toString("hex");
|
|
1203
|
+
}
|
|
1185
1204
|
function deriveNametagEncryptionKey(privateKeyHex) {
|
|
1186
1205
|
const privateKeyBytes = Buffer2.from(privateKeyHex, "hex");
|
|
1187
1206
|
const saltInput = new TextEncoder().encode("sphere-nametag-salt");
|
|
@@ -1590,6 +1609,28 @@ var NostrTransportProvider = class {
|
|
|
1590
1609
|
this.paymentRequestResponseHandlers.add(handler);
|
|
1591
1610
|
return () => this.paymentRequestResponseHandlers.delete(handler);
|
|
1592
1611
|
}
|
|
1612
|
+
/**
|
|
1613
|
+
* Resolve any identifier to full peer information.
|
|
1614
|
+
* Routes to the appropriate specific resolve method based on identifier format.
|
|
1615
|
+
*/
|
|
1616
|
+
async resolve(identifier) {
|
|
1617
|
+
if (identifier.startsWith("@")) {
|
|
1618
|
+
return this.resolveNametagInfo(identifier.slice(1));
|
|
1619
|
+
}
|
|
1620
|
+
if (identifier.startsWith("DIRECT:") || identifier.startsWith("PROXY:")) {
|
|
1621
|
+
return this.resolveAddressInfo(identifier);
|
|
1622
|
+
}
|
|
1623
|
+
if (identifier.startsWith("alpha1") || identifier.startsWith("alphat1")) {
|
|
1624
|
+
return this.resolveAddressInfo(identifier);
|
|
1625
|
+
}
|
|
1626
|
+
if (/^0[23][0-9a-f]{64}$/i.test(identifier)) {
|
|
1627
|
+
return this.resolveAddressInfo(identifier);
|
|
1628
|
+
}
|
|
1629
|
+
if (/^[0-9a-f]{64}$/i.test(identifier)) {
|
|
1630
|
+
return this.resolveTransportPubkeyInfo(identifier);
|
|
1631
|
+
}
|
|
1632
|
+
return this.resolveNametagInfo(identifier);
|
|
1633
|
+
}
|
|
1593
1634
|
async resolveNametag(nametag) {
|
|
1594
1635
|
this.ensureReady();
|
|
1595
1636
|
const hashedNametag = hashNametag(nametag);
|
|
@@ -1633,15 +1674,17 @@ var NostrTransportProvider = class {
|
|
|
1633
1674
|
const bindingEvent = events[0];
|
|
1634
1675
|
try {
|
|
1635
1676
|
const content = JSON.parse(bindingEvent.content);
|
|
1677
|
+
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
1678
|
+
const proxyAddr = await ProxyAddress.fromNameTag(nametag);
|
|
1679
|
+
const proxyAddress = proxyAddr.toString();
|
|
1636
1680
|
if (content.public_key && content.l1_address) {
|
|
1637
|
-
const l3Address = `PROXY:${hashedNametag}`;
|
|
1638
1681
|
return {
|
|
1639
1682
|
nametag,
|
|
1640
1683
|
transportPubkey: bindingEvent.pubkey,
|
|
1641
1684
|
chainPubkey: content.public_key,
|
|
1642
1685
|
l1Address: content.l1_address,
|
|
1643
1686
|
directAddress: content.direct_address || "",
|
|
1644
|
-
proxyAddress
|
|
1687
|
+
proxyAddress,
|
|
1645
1688
|
timestamp: bindingEvent.created_at * 1e3
|
|
1646
1689
|
};
|
|
1647
1690
|
}
|
|
@@ -1649,14 +1692,13 @@ var NostrTransportProvider = class {
|
|
|
1649
1692
|
const pubkeyTag = bindingEvent.tags.find((t) => t[0] === "pubkey");
|
|
1650
1693
|
const l1Tag = bindingEvent.tags.find((t) => t[0] === "l1");
|
|
1651
1694
|
if (pubkeyTag?.[1] && l1Tag?.[1]) {
|
|
1652
|
-
const l3Address = `PROXY:${hashedNametag}`;
|
|
1653
1695
|
return {
|
|
1654
1696
|
nametag,
|
|
1655
1697
|
transportPubkey: bindingEvent.pubkey,
|
|
1656
1698
|
chainPubkey: pubkeyTag[1],
|
|
1657
1699
|
l1Address: l1Tag[1],
|
|
1658
1700
|
directAddress: "",
|
|
1659
|
-
proxyAddress
|
|
1701
|
+
proxyAddress,
|
|
1660
1702
|
timestamp: bindingEvent.created_at * 1e3
|
|
1661
1703
|
};
|
|
1662
1704
|
}
|
|
@@ -1668,17 +1710,90 @@ var NostrTransportProvider = class {
|
|
|
1668
1710
|
l1Address: "",
|
|
1669
1711
|
// Cannot derive without 33-byte pubkey
|
|
1670
1712
|
directAddress: "",
|
|
1671
|
-
proxyAddress
|
|
1713
|
+
proxyAddress,
|
|
1672
1714
|
timestamp: bindingEvent.created_at * 1e3
|
|
1673
1715
|
};
|
|
1674
1716
|
} catch {
|
|
1717
|
+
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
1718
|
+
const proxyAddr = await ProxyAddress.fromNameTag(nametag);
|
|
1675
1719
|
return {
|
|
1676
1720
|
nametag,
|
|
1677
1721
|
transportPubkey: bindingEvent.pubkey,
|
|
1678
1722
|
chainPubkey: "",
|
|
1679
1723
|
l1Address: "",
|
|
1680
1724
|
directAddress: "",
|
|
1681
|
-
proxyAddress:
|
|
1725
|
+
proxyAddress: proxyAddr.toString(),
|
|
1726
|
+
timestamp: bindingEvent.created_at * 1e3
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
/**
|
|
1731
|
+
* Resolve a DIRECT://, PROXY://, or L1 address to full peer info.
|
|
1732
|
+
* Performs reverse lookup: hash(address) → query '#t' tag → parse binding event.
|
|
1733
|
+
* Works with both new identity binding events and legacy nametag binding events.
|
|
1734
|
+
*/
|
|
1735
|
+
async resolveAddressInfo(address) {
|
|
1736
|
+
this.ensureReady();
|
|
1737
|
+
const addressHash = hashAddressForTag(address);
|
|
1738
|
+
const events = await this.queryEvents({
|
|
1739
|
+
kinds: [EVENT_KINDS.NAMETAG_BINDING],
|
|
1740
|
+
"#t": [addressHash],
|
|
1741
|
+
limit: 1
|
|
1742
|
+
});
|
|
1743
|
+
if (events.length === 0) return null;
|
|
1744
|
+
const bindingEvent = events[0];
|
|
1745
|
+
try {
|
|
1746
|
+
const content = JSON.parse(bindingEvent.content);
|
|
1747
|
+
return {
|
|
1748
|
+
nametag: content.nametag || void 0,
|
|
1749
|
+
transportPubkey: bindingEvent.pubkey,
|
|
1750
|
+
chainPubkey: content.public_key || "",
|
|
1751
|
+
l1Address: content.l1_address || "",
|
|
1752
|
+
directAddress: content.direct_address || "",
|
|
1753
|
+
proxyAddress: content.proxy_address || void 0,
|
|
1754
|
+
timestamp: bindingEvent.created_at * 1e3
|
|
1755
|
+
};
|
|
1756
|
+
} catch {
|
|
1757
|
+
return {
|
|
1758
|
+
transportPubkey: bindingEvent.pubkey,
|
|
1759
|
+
chainPubkey: "",
|
|
1760
|
+
l1Address: "",
|
|
1761
|
+
directAddress: "",
|
|
1762
|
+
timestamp: bindingEvent.created_at * 1e3
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
/**
|
|
1767
|
+
* Resolve transport pubkey (Nostr pubkey) to full peer info.
|
|
1768
|
+
* Queries binding events authored by the given pubkey.
|
|
1769
|
+
*/
|
|
1770
|
+
async resolveTransportPubkeyInfo(transportPubkey) {
|
|
1771
|
+
this.ensureReady();
|
|
1772
|
+
const events = await this.queryEvents({
|
|
1773
|
+
kinds: [EVENT_KINDS.NAMETAG_BINDING],
|
|
1774
|
+
authors: [transportPubkey],
|
|
1775
|
+
limit: 5
|
|
1776
|
+
});
|
|
1777
|
+
if (events.length === 0) return null;
|
|
1778
|
+
events.sort((a, b) => b.created_at - a.created_at);
|
|
1779
|
+
const bindingEvent = events[0];
|
|
1780
|
+
try {
|
|
1781
|
+
const content = JSON.parse(bindingEvent.content);
|
|
1782
|
+
return {
|
|
1783
|
+
nametag: content.nametag || void 0,
|
|
1784
|
+
transportPubkey: bindingEvent.pubkey,
|
|
1785
|
+
chainPubkey: content.public_key || "",
|
|
1786
|
+
l1Address: content.l1_address || "",
|
|
1787
|
+
directAddress: content.direct_address || "",
|
|
1788
|
+
proxyAddress: content.proxy_address || void 0,
|
|
1789
|
+
timestamp: bindingEvent.created_at * 1e3
|
|
1790
|
+
};
|
|
1791
|
+
} catch {
|
|
1792
|
+
return {
|
|
1793
|
+
transportPubkey: bindingEvent.pubkey,
|
|
1794
|
+
chainPubkey: "",
|
|
1795
|
+
l1Address: "",
|
|
1796
|
+
directAddress: "",
|
|
1682
1797
|
timestamp: bindingEvent.created_at * 1e3
|
|
1683
1798
|
};
|
|
1684
1799
|
}
|
|
@@ -1726,6 +1841,63 @@ var NostrTransportProvider = class {
|
|
|
1726
1841
|
this.log("Could not decrypt nametag from any event");
|
|
1727
1842
|
return null;
|
|
1728
1843
|
}
|
|
1844
|
+
/**
|
|
1845
|
+
* Publish identity binding event on Nostr.
|
|
1846
|
+
* Without nametag: publishes base binding (chainPubkey, l1Address, directAddress).
|
|
1847
|
+
* With nametag: also publishes nametag hash, proxy address, encrypted nametag for recovery.
|
|
1848
|
+
*
|
|
1849
|
+
* Uses kind 30078 parameterized replaceable event with d=SHA256('unicity:identity:' + nostrPubkey).
|
|
1850
|
+
* Each HD address index has its own Nostr key → its own binding event.
|
|
1851
|
+
*
|
|
1852
|
+
* @returns true if successful, false if nametag is taken by another pubkey
|
|
1853
|
+
*/
|
|
1854
|
+
async publishIdentityBinding(chainPubkey, l1Address, directAddress, nametag) {
|
|
1855
|
+
this.ensureReady();
|
|
1856
|
+
if (!this.identity) {
|
|
1857
|
+
throw new Error("Identity not set");
|
|
1858
|
+
}
|
|
1859
|
+
const nostrPubkey = this.getNostrPubkey();
|
|
1860
|
+
const dTagBytes = new TextEncoder().encode("unicity:identity:" + nostrPubkey);
|
|
1861
|
+
const dTag = Buffer2.from(sha256(dTagBytes)).toString("hex");
|
|
1862
|
+
const contentObj = {
|
|
1863
|
+
public_key: chainPubkey,
|
|
1864
|
+
l1_address: l1Address,
|
|
1865
|
+
direct_address: directAddress
|
|
1866
|
+
};
|
|
1867
|
+
const tags = [
|
|
1868
|
+
["d", dTag],
|
|
1869
|
+
["t", hashAddressForTag(chainPubkey)],
|
|
1870
|
+
["t", hashAddressForTag(directAddress)],
|
|
1871
|
+
["t", hashAddressForTag(l1Address)]
|
|
1872
|
+
];
|
|
1873
|
+
if (nametag) {
|
|
1874
|
+
const existing = await this.resolveNametag(nametag);
|
|
1875
|
+
if (existing && existing !== nostrPubkey) {
|
|
1876
|
+
this.log("Nametag already taken:", nametag, "- owner:", existing);
|
|
1877
|
+
return false;
|
|
1878
|
+
}
|
|
1879
|
+
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
1880
|
+
const proxyAddr = await ProxyAddress.fromNameTag(nametag);
|
|
1881
|
+
const proxyAddress = proxyAddr.toString();
|
|
1882
|
+
const encryptedNametag = await encryptNametag(nametag, this.identity.privateKey);
|
|
1883
|
+
const hashedNametag = hashNametag(nametag);
|
|
1884
|
+
contentObj.nametag = nametag;
|
|
1885
|
+
contentObj.encrypted_nametag = encryptedNametag;
|
|
1886
|
+
contentObj.proxy_address = proxyAddress;
|
|
1887
|
+
tags.push(["t", hashedNametag]);
|
|
1888
|
+
tags.push(["t", hashAddressForTag(proxyAddress)]);
|
|
1889
|
+
}
|
|
1890
|
+
const content = JSON.stringify(contentObj);
|
|
1891
|
+
const event = await this.createEvent(EVENT_KINDS.NAMETAG_BINDING, content, tags);
|
|
1892
|
+
await this.publishEvent(event);
|
|
1893
|
+
if (nametag) {
|
|
1894
|
+
this.log("Published identity binding with nametag:", nametag, "for pubkey:", nostrPubkey.slice(0, 16) + "...");
|
|
1895
|
+
} else {
|
|
1896
|
+
this.log("Published identity binding (no nametag) for pubkey:", nostrPubkey.slice(0, 16) + "...");
|
|
1897
|
+
}
|
|
1898
|
+
return true;
|
|
1899
|
+
}
|
|
1900
|
+
/** @deprecated Use publishIdentityBinding instead */
|
|
1729
1901
|
async publishNametag(nametag, address) {
|
|
1730
1902
|
this.ensureReady();
|
|
1731
1903
|
const hashedNametag = hashNametag(nametag);
|
|
@@ -1752,6 +1924,9 @@ var NostrTransportProvider = class {
|
|
|
1752
1924
|
const compressedPubkey = getPublicKey(privateKeyHex, true);
|
|
1753
1925
|
const l1Address = publicKeyToAddress(compressedPubkey, "alpha");
|
|
1754
1926
|
const encryptedNametag = await encryptNametag(nametag, privateKeyHex);
|
|
1927
|
+
const { ProxyAddress } = await import("@unicitylabs/state-transition-sdk/lib/address/ProxyAddress");
|
|
1928
|
+
const proxyAddr = await ProxyAddress.fromNameTag(nametag);
|
|
1929
|
+
const proxyAddress = proxyAddr.toString();
|
|
1755
1930
|
const hashedNametag = hashNametag(nametag);
|
|
1756
1931
|
const content = JSON.stringify({
|
|
1757
1932
|
nametag_hash: hashedNametag,
|
|
@@ -1761,17 +1936,20 @@ var NostrTransportProvider = class {
|
|
|
1761
1936
|
encrypted_nametag: encryptedNametag,
|
|
1762
1937
|
public_key: compressedPubkey,
|
|
1763
1938
|
l1_address: l1Address,
|
|
1764
|
-
direct_address: directAddress
|
|
1939
|
+
direct_address: directAddress,
|
|
1940
|
+
proxy_address: proxyAddress
|
|
1765
1941
|
});
|
|
1766
|
-
const
|
|
1942
|
+
const tags = [
|
|
1767
1943
|
["d", hashedNametag],
|
|
1768
1944
|
["nametag", hashedNametag],
|
|
1769
1945
|
["t", hashedNametag],
|
|
1946
|
+
["t", hashAddressForTag(directAddress)],
|
|
1947
|
+
["t", hashAddressForTag(proxyAddress)],
|
|
1770
1948
|
["address", nostrPubkey],
|
|
1771
|
-
// Extended tags for indexing
|
|
1772
1949
|
["pubkey", compressedPubkey],
|
|
1773
1950
|
["l1", l1Address]
|
|
1774
|
-
]
|
|
1951
|
+
];
|
|
1952
|
+
const event = await this.createEvent(EVENT_KINDS.NAMETAG_BINDING, content, tags);
|
|
1775
1953
|
await this.publishEvent(event);
|
|
1776
1954
|
this.log("Registered nametag:", nametag, "for pubkey:", nostrPubkey.slice(0, 16) + "...", "l1:", l1Address.slice(0, 12) + "...");
|
|
1777
1955
|
return true;
|
|
@@ -2776,6 +2954,113 @@ async function readFileAsUint8Array(file) {
|
|
|
2776
2954
|
return new Uint8Array(buffer);
|
|
2777
2955
|
}
|
|
2778
2956
|
|
|
2957
|
+
// price/CoinGeckoPriceProvider.ts
|
|
2958
|
+
var CoinGeckoPriceProvider = class {
|
|
2959
|
+
platform = "coingecko";
|
|
2960
|
+
cache = /* @__PURE__ */ new Map();
|
|
2961
|
+
apiKey;
|
|
2962
|
+
cacheTtlMs;
|
|
2963
|
+
timeout;
|
|
2964
|
+
debug;
|
|
2965
|
+
baseUrl;
|
|
2966
|
+
constructor(config) {
|
|
2967
|
+
this.apiKey = config?.apiKey;
|
|
2968
|
+
this.cacheTtlMs = config?.cacheTtlMs ?? 6e4;
|
|
2969
|
+
this.timeout = config?.timeout ?? 1e4;
|
|
2970
|
+
this.debug = config?.debug ?? false;
|
|
2971
|
+
this.baseUrl = config?.baseUrl ?? (this.apiKey ? "https://pro-api.coingecko.com/api/v3" : "https://api.coingecko.com/api/v3");
|
|
2972
|
+
}
|
|
2973
|
+
async getPrices(tokenNames) {
|
|
2974
|
+
if (tokenNames.length === 0) {
|
|
2975
|
+
return /* @__PURE__ */ new Map();
|
|
2976
|
+
}
|
|
2977
|
+
const now = Date.now();
|
|
2978
|
+
const result = /* @__PURE__ */ new Map();
|
|
2979
|
+
const uncachedNames = [];
|
|
2980
|
+
for (const name of tokenNames) {
|
|
2981
|
+
const cached = this.cache.get(name);
|
|
2982
|
+
if (cached && cached.expiresAt > now) {
|
|
2983
|
+
if (cached.price !== null) {
|
|
2984
|
+
result.set(name, cached.price);
|
|
2985
|
+
}
|
|
2986
|
+
} else {
|
|
2987
|
+
uncachedNames.push(name);
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
if (uncachedNames.length === 0) {
|
|
2991
|
+
return result;
|
|
2992
|
+
}
|
|
2993
|
+
try {
|
|
2994
|
+
const ids = uncachedNames.join(",");
|
|
2995
|
+
const url = `${this.baseUrl}/simple/price?ids=${encodeURIComponent(ids)}&vs_currencies=usd,eur&include_24hr_change=true`;
|
|
2996
|
+
const headers = { Accept: "application/json" };
|
|
2997
|
+
if (this.apiKey) {
|
|
2998
|
+
headers["x-cg-pro-api-key"] = this.apiKey;
|
|
2999
|
+
}
|
|
3000
|
+
if (this.debug) {
|
|
3001
|
+
console.log(`[CoinGecko] Fetching prices for: ${uncachedNames.join(", ")}`);
|
|
3002
|
+
}
|
|
3003
|
+
const response = await fetch(url, {
|
|
3004
|
+
headers,
|
|
3005
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
3006
|
+
});
|
|
3007
|
+
if (!response.ok) {
|
|
3008
|
+
throw new Error(`CoinGecko API error: ${response.status} ${response.statusText}`);
|
|
3009
|
+
}
|
|
3010
|
+
const data = await response.json();
|
|
3011
|
+
for (const [name, values] of Object.entries(data)) {
|
|
3012
|
+
if (values && typeof values === "object") {
|
|
3013
|
+
const price = {
|
|
3014
|
+
tokenName: name,
|
|
3015
|
+
priceUsd: values.usd ?? 0,
|
|
3016
|
+
priceEur: values.eur,
|
|
3017
|
+
change24h: values.usd_24h_change,
|
|
3018
|
+
timestamp: now
|
|
3019
|
+
};
|
|
3020
|
+
this.cache.set(name, { price, expiresAt: now + this.cacheTtlMs });
|
|
3021
|
+
result.set(name, price);
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
for (const name of uncachedNames) {
|
|
3025
|
+
if (!result.has(name)) {
|
|
3026
|
+
this.cache.set(name, { price: null, expiresAt: now + this.cacheTtlMs });
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
if (this.debug) {
|
|
3030
|
+
console.log(`[CoinGecko] Fetched ${result.size} prices`);
|
|
3031
|
+
}
|
|
3032
|
+
} catch (error) {
|
|
3033
|
+
if (this.debug) {
|
|
3034
|
+
console.warn("[CoinGecko] Fetch failed, using stale cache:", error);
|
|
3035
|
+
}
|
|
3036
|
+
for (const name of uncachedNames) {
|
|
3037
|
+
const stale = this.cache.get(name);
|
|
3038
|
+
if (stale?.price) {
|
|
3039
|
+
result.set(name, stale.price);
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
return result;
|
|
3044
|
+
}
|
|
3045
|
+
async getPrice(tokenName) {
|
|
3046
|
+
const prices = await this.getPrices([tokenName]);
|
|
3047
|
+
return prices.get(tokenName) ?? null;
|
|
3048
|
+
}
|
|
3049
|
+
clearCache() {
|
|
3050
|
+
this.cache.clear();
|
|
3051
|
+
}
|
|
3052
|
+
};
|
|
3053
|
+
|
|
3054
|
+
// price/index.ts
|
|
3055
|
+
function createPriceProvider(config) {
|
|
3056
|
+
switch (config.platform) {
|
|
3057
|
+
case "coingecko":
|
|
3058
|
+
return new CoinGeckoPriceProvider(config);
|
|
3059
|
+
default:
|
|
3060
|
+
throw new Error(`Unsupported price platform: ${String(config.platform)}`);
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
|
|
2779
3064
|
// impl/shared/resolvers.ts
|
|
2780
3065
|
function getNetworkConfig(network = "mainnet") {
|
|
2781
3066
|
return NETWORKS[network];
|
|
@@ -2824,6 +3109,19 @@ function resolveL1Config(network, config) {
|
|
|
2824
3109
|
enableVesting: config.enableVesting
|
|
2825
3110
|
};
|
|
2826
3111
|
}
|
|
3112
|
+
function resolvePriceConfig(config) {
|
|
3113
|
+
if (config === void 0) {
|
|
3114
|
+
return void 0;
|
|
3115
|
+
}
|
|
3116
|
+
return {
|
|
3117
|
+
platform: config.platform ?? "coingecko",
|
|
3118
|
+
apiKey: config.apiKey,
|
|
3119
|
+
baseUrl: config.baseUrl,
|
|
3120
|
+
cacheTtlMs: config.cacheTtlMs,
|
|
3121
|
+
timeout: config.timeout,
|
|
3122
|
+
debug: config.debug
|
|
3123
|
+
};
|
|
3124
|
+
}
|
|
2827
3125
|
function resolveArrayConfig(defaults, replace, additional) {
|
|
2828
3126
|
if (replace) {
|
|
2829
3127
|
return replace;
|
|
@@ -2891,6 +3189,7 @@ function createBrowserProviders(config) {
|
|
|
2891
3189
|
const oracleConfig = resolveOracleConfig(network, config?.oracle);
|
|
2892
3190
|
const l1Config = resolveL1Config(network, config?.l1);
|
|
2893
3191
|
const tokenSyncConfig = resolveTokenSyncConfig(network, config?.tokenSync);
|
|
3192
|
+
const priceConfig = resolvePriceConfig(config?.price);
|
|
2894
3193
|
return {
|
|
2895
3194
|
storage: createLocalStorageProvider(config?.storage),
|
|
2896
3195
|
transport: createNostrTransportProvider({
|
|
@@ -2911,6 +3210,7 @@ function createBrowserProviders(config) {
|
|
|
2911
3210
|
}),
|
|
2912
3211
|
tokenStorage: createIndexedDBTokenStorageProvider(),
|
|
2913
3212
|
l1: l1Config,
|
|
3213
|
+
price: priceConfig ? createPriceProvider(priceConfig) : void 0,
|
|
2914
3214
|
tokenSyncConfig
|
|
2915
3215
|
};
|
|
2916
3216
|
}
|