@steerprotocol/liquidity-meter 1.0.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 +207 -0
- package/dist/adapters/onchain.d.ts +51 -0
- package/dist/adapters/onchain.js +158 -0
- package/dist/adapters/uniswapv3-subgraph.d.ts +44 -0
- package/dist/adapters/uniswapv3-subgraph.js +105 -0
- package/dist/adapters/withdraw.d.ts +14 -0
- package/dist/adapters/withdraw.js +150 -0
- package/dist/api.d.ts +72 -0
- package/dist/api.js +180 -0
- package/dist/cli/liquidity-depth.d.ts +2 -0
- package/dist/cli/liquidity-depth.js +1160 -0
- package/dist/core/depth.d.ts +48 -0
- package/dist/core/depth.js +314 -0
- package/dist/handlers/cron.d.ts +27 -0
- package/dist/handlers/cron.js +68 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/prices.d.ts +15 -0
- package/dist/prices.js +205 -0
- package/dist/wagmi/config.d.ts +2106 -0
- package/dist/wagmi/config.js +24 -0
- package/dist/wagmi/generated.d.ts +2019 -0
- package/dist/wagmi/generated.js +346 -0
- package/package.json +47 -0
package/dist/prices.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { createMemoryClient, http } from 'tevm';
|
|
3
|
+
function norm(addr) { return addr.toLowerCase(); }
|
|
4
|
+
function dexSlugFromChainId(chainId) {
|
|
5
|
+
switch (Number(chainId)) {
|
|
6
|
+
case 1: return 'ethereum';
|
|
7
|
+
case 10: return 'optimism';
|
|
8
|
+
case 56: return 'bsc';
|
|
9
|
+
case 137: return 'polygon';
|
|
10
|
+
case 8453: return 'base';
|
|
11
|
+
case 42161: return 'arbitrum';
|
|
12
|
+
case 43114: return 'avalanche';
|
|
13
|
+
case 100: return 'gnosis';
|
|
14
|
+
case 324: return 'zksync';
|
|
15
|
+
case 1101: return 'polygon-zkevm';
|
|
16
|
+
default: return undefined;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export async function fetchPricesLlama(addresses) {
|
|
20
|
+
const uniq = Array.from(new Set(addresses.map(norm)));
|
|
21
|
+
if (!uniq.length)
|
|
22
|
+
return {};
|
|
23
|
+
const coinsParam = uniq.map((a) => `ethereum:${a}`).join(',');
|
|
24
|
+
const url = `https://coins.llama.fi/prices/current/${coinsParam}`;
|
|
25
|
+
const res = await axios.get(url, { timeout: 8000, maxRedirects: 3 });
|
|
26
|
+
const coins = res.data?.coins || {};
|
|
27
|
+
const out = {};
|
|
28
|
+
for (const a of uniq) {
|
|
29
|
+
const key = `ethereum:${a}`;
|
|
30
|
+
const p = coins[key]?.price;
|
|
31
|
+
if (typeof p === 'number' && Number.isFinite(p))
|
|
32
|
+
out[a] = p;
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
export async function fetchPricesCoingecko(addresses, apiKey) {
|
|
37
|
+
const uniq = Array.from(new Set(addresses.map(norm)));
|
|
38
|
+
if (!uniq.length)
|
|
39
|
+
return {};
|
|
40
|
+
const url = 'https://api.coingecko.com/api/v3/simple/token_price/ethereum';
|
|
41
|
+
const params = { contract_addresses: uniq.join(','), vs_currencies: 'usd' };
|
|
42
|
+
const headers = apiKey ? { 'x-cg-pro-api-key': apiKey } : undefined;
|
|
43
|
+
const res = await axios.get(url, { params, headers, timeout: 8000, maxRedirects: 3 });
|
|
44
|
+
const body = res.data || {};
|
|
45
|
+
const out = {};
|
|
46
|
+
for (const a of uniq) {
|
|
47
|
+
const ent = body[a];
|
|
48
|
+
const p = ent?.usd;
|
|
49
|
+
if (typeof p === 'number' && Number.isFinite(p))
|
|
50
|
+
out[a] = p;
|
|
51
|
+
}
|
|
52
|
+
return out;
|
|
53
|
+
}
|
|
54
|
+
export async function fetchTokenUSDPrices(opts) {
|
|
55
|
+
const { addresses, source = 'auto', rpcUrl, chainId, debug = false, reserveLimit = 100 } = opts;
|
|
56
|
+
const uniq = Array.from(new Set(addresses.map(norm)));
|
|
57
|
+
const byAddress = {};
|
|
58
|
+
const coingeckoKey = process.env.COINGECKO_API_KEY;
|
|
59
|
+
async function resolveChainId(rpc) {
|
|
60
|
+
try {
|
|
61
|
+
if (!rpc)
|
|
62
|
+
return undefined;
|
|
63
|
+
const client = createMemoryClient({ fork: { transport: http(rpc), blockTag: 'latest' }, loggingLevel: 'error' });
|
|
64
|
+
if (client.tevmReady)
|
|
65
|
+
await client.tevmReady();
|
|
66
|
+
const id = await client.getChainId();
|
|
67
|
+
return id;
|
|
68
|
+
}
|
|
69
|
+
catch (_) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function fetchPricesReserve({ addresses, chainId, limit = 100 }) {
|
|
74
|
+
if (!chainId)
|
|
75
|
+
return {};
|
|
76
|
+
const url = `https://api.reserve.org/discover/dtf`;
|
|
77
|
+
const params = { chainId, limit };
|
|
78
|
+
const res = await axios.get(url, { params, timeout: 8000, maxRedirects: 3 });
|
|
79
|
+
const arr = Array.isArray(res.data) ? res.data : [];
|
|
80
|
+
const out = {};
|
|
81
|
+
const set = new Set(addresses.map(norm));
|
|
82
|
+
for (const ent of arr) {
|
|
83
|
+
const addr = norm(ent.address || '');
|
|
84
|
+
if (set.has(addr)) {
|
|
85
|
+
const p = Number(ent.price);
|
|
86
|
+
if (Number.isFinite(p))
|
|
87
|
+
out[addr] = p;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (debug)
|
|
91
|
+
console.error(`[prices] reserve: chainId=${chainId} limit=${limit} returned=${arr.length} matched=${Object.keys(out).length}`);
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
async function fetchPricesDexscreener({ addresses, chainId }) {
|
|
95
|
+
if (!chainId)
|
|
96
|
+
return {};
|
|
97
|
+
const slug = dexSlugFromChainId(chainId);
|
|
98
|
+
if (!slug)
|
|
99
|
+
return {};
|
|
100
|
+
const out = {};
|
|
101
|
+
for (const addr of addresses.map(norm)) {
|
|
102
|
+
try {
|
|
103
|
+
const url = `https://api.dexscreener.com/token-pairs/v1/${slug}/${addr}`;
|
|
104
|
+
const res = await axios.get(url, { timeout: 8000, maxRedirects: 3 });
|
|
105
|
+
const arr = Array.isArray(res.data) ? res.data : [];
|
|
106
|
+
let best = null;
|
|
107
|
+
for (const ent of arr) {
|
|
108
|
+
const base = norm(ent?.baseToken?.address || '');
|
|
109
|
+
if (base !== addr)
|
|
110
|
+
continue;
|
|
111
|
+
const liq = Number(ent?.liquidity?.usd ?? 0);
|
|
112
|
+
if (!best || liq > Number(best?.liquidity?.usd ?? 0))
|
|
113
|
+
best = ent;
|
|
114
|
+
}
|
|
115
|
+
if (best) {
|
|
116
|
+
const p = Number(best.priceUsd);
|
|
117
|
+
if (Number.isFinite(p))
|
|
118
|
+
out[addr] = p;
|
|
119
|
+
if (debug)
|
|
120
|
+
console.error(`[prices] dexscreener: addr=${addr} bestLiq=${best?.liquidity?.usd ?? 0} priceUsd=${best?.priceUsd ?? 'n/a'}`);
|
|
121
|
+
}
|
|
122
|
+
else if (debug) {
|
|
123
|
+
console.error(`[prices] dexscreener: addr=${addr} no baseToken match in ${arr.length} pairs`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
if (debug)
|
|
128
|
+
console.error(`[prices] dexscreener error for ${addr}: ${e?.message || e}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
133
|
+
async function tryLlama() {
|
|
134
|
+
try {
|
|
135
|
+
const p = await fetchPricesLlama(uniq);
|
|
136
|
+
for (const k of Object.keys(p))
|
|
137
|
+
if (byAddress[k] == null)
|
|
138
|
+
byAddress[k] = { price: p[k], source: 'llama' };
|
|
139
|
+
return Object.keys(p).length > 0;
|
|
140
|
+
}
|
|
141
|
+
catch (_) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async function tryReserve(cid) {
|
|
146
|
+
try {
|
|
147
|
+
if (!cid)
|
|
148
|
+
return false;
|
|
149
|
+
const p = await fetchPricesReserve({ addresses: uniq, chainId: cid, limit: reserveLimit });
|
|
150
|
+
for (const k of Object.keys(p))
|
|
151
|
+
if (byAddress[k] == null)
|
|
152
|
+
byAddress[k] = { price: p[k], source: 'reserve' };
|
|
153
|
+
return Object.keys(p).length > 0;
|
|
154
|
+
}
|
|
155
|
+
catch (_) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async function tryDex(cid) {
|
|
160
|
+
try {
|
|
161
|
+
if (!cid)
|
|
162
|
+
return false;
|
|
163
|
+
const p = await fetchPricesDexscreener({ addresses: uniq, chainId: cid });
|
|
164
|
+
for (const k of Object.keys(p))
|
|
165
|
+
if (byAddress[k] == null)
|
|
166
|
+
byAddress[k] = { price: p[k], source: 'dexscreener' };
|
|
167
|
+
return Object.keys(p).length > 0;
|
|
168
|
+
}
|
|
169
|
+
catch (_) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async function tryCG() {
|
|
174
|
+
try {
|
|
175
|
+
const p = await fetchPricesCoingecko(uniq, coingeckoKey);
|
|
176
|
+
for (const k of Object.keys(p))
|
|
177
|
+
if (byAddress[k] == null)
|
|
178
|
+
byAddress[k] = { price: p[k], source: 'coingecko' };
|
|
179
|
+
return Object.keys(p).length > 0;
|
|
180
|
+
}
|
|
181
|
+
catch (_) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
let cid = chainId;
|
|
186
|
+
if (!cid && (source === 'reserve' || source === 'dexscreener' || source === 'auto'))
|
|
187
|
+
cid = await resolveChainId(rpcUrl);
|
|
188
|
+
if (debug)
|
|
189
|
+
console.error(`[prices] source=${source} rpc=${rpcUrl ? 'yes' : 'no'} chainId=${cid ?? 'n/a'} addresses=${uniq.length}`);
|
|
190
|
+
if (source === 'reserve')
|
|
191
|
+
await tryReserve(cid);
|
|
192
|
+
else if (source === 'dexscreener')
|
|
193
|
+
await tryDex(cid);
|
|
194
|
+
else if (source === 'llama')
|
|
195
|
+
await tryLlama();
|
|
196
|
+
else if (source === 'coingecko')
|
|
197
|
+
await tryCG();
|
|
198
|
+
else {
|
|
199
|
+
await tryReserve(cid);
|
|
200
|
+
await tryDex(cid);
|
|
201
|
+
await tryLlama();
|
|
202
|
+
await tryCG();
|
|
203
|
+
}
|
|
204
|
+
return { byAddress };
|
|
205
|
+
}
|