apow-cli 0.3.0 → 0.3.2
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/dashboard-html.js +13 -0
- package/dist/dashboard.js +66 -12
- package/package.json +1 -1
package/dist/dashboard-html.js
CHANGED
|
@@ -8,6 +8,7 @@ function getDashboardHtml() {
|
|
|
8
8
|
<meta charset="utf-8">
|
|
9
9
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
10
10
|
<title>APoW Dashboard</title>
|
|
11
|
+
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAFNUExURQAAAABS/wBS/wBS/wBS/wBS/wBS/wBS/wBS/wBS/wBS/wBS/wBS/wBS/wBS/wBS/wBS/wBS/wBS/wBS/wBR/xNf/32n/3ql/yFp/9zo/9rm/x9n/3ym/////3mk/wBQ/yFo/9fk//r8/6fD/6jE/wBP/+3z/zZ3/zh4/+7z/wRV/yds/yxw/0F+/73S/zJ0/77T/9jl/0B9/0yF/+Xt//D1//f5/0yG/wtZ/6G//+Tt/+vx/+zy/+Dq/+Lr/+rx/6LA/wNU/xpk/xxl/5K1/+3y/0uF/xpj/5G0/xtl/yNq/zl5/9jk/7nP/ypu/yRq/7rQ/yVr/8rb/+nw//7///P3/+fv/+jv/8vb/3um//H1/wJT/5i5/1OK/yht/1SL/+/0/5e5/x1l/9Xj/6PB/z17/z58//H2/wlY/3ai/5y8/3Ge/wZW/3Sg/569/+rqE9AAAAATdFJOUwAAAitvsNz0/jma4fwbjOw3xUOydetBAAAAAWJLR0Qd6wNxkQAAAAd0SU1FB+oDERUwG5vARuoAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjYtMDMtMTdUMjE6NDg6MjIrMDA6MDDtKLHnAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDI2LTAzLTE3VDIxOjQ4OjIyKzAwOjAwnHUJWwAAACh0RVh0ZGF0ZTp0aW1lc3RhbXAAMjAyNi0wMy0xN1QyMTo0ODoyNyswMDowMJlYByMAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8AAABwklEQVQ4y4VTWVvCMBAM5T5FKKjAUkGpICoeXHKIgIrigQhe4AleKPr/H22btKT4fTgv3WYnk2R3FiERGgGMVqc3GE0mo0Gv0zLiClIgps0Wq81NYLNazAzFEEK7Y8qtwpTDrjCEwDnt/oNpJ2EIH5dBWWZZJTS4JIa4n8p7PBRD0hDOp/RZ7wxLnWIXCYyDys/O+fwUw8EIBDN1fzYAEOSot5g1iLFQ+XkfQIiWsDBIa1UJhMMqCasW6WwqgYVFlYRNh/RyzHGRAPBL0RgEI5wiokdyDZbjK6s+WEsk1mFjcyW+LNcCGcn+rSSfAkjzfBogxSczRMOITDjIbufyAIXizk6xAFDK7WbxukkmlCvVPdg/qFWrtQwPh0eVskwgR1TqxydwelZvNOpn5xCK1ivyEdIl2eZFq9CGy6trAVeX0C60bpocviR+5m2+A9BplSS0xDgfIM+UCtX1x+8gdt98kNB8fILnuL+LC4VLHen1YbfMEZRfoN+LkFLjZrFeSL0q5ePe3sHLkmbhdn8MPr+Gox4Nv38GH6TdsmEoM47+JMOoLDdmbMlyKtOqgE07Zns67xoNxsTB+X/0/h/eieP/CwWOXVaTpQ0oAAAAAElFTkSuQmCC">
|
|
11
12
|
<style>
|
|
12
13
|
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
|
13
14
|
:root{
|
|
@@ -108,6 +109,7 @@ a{color:inherit;text-decoration:none}
|
|
|
108
109
|
var prevMines = {};
|
|
109
110
|
var activeWallets = {};
|
|
110
111
|
var lastSeen = {};
|
|
112
|
+
var pollCount = 0;
|
|
111
113
|
|
|
112
114
|
function fetchJson(url) {
|
|
113
115
|
return fetch(url).then(function(r) {
|
|
@@ -151,6 +153,17 @@ a{color:inherit;text-decoration:none}
|
|
|
151
153
|
|
|
152
154
|
function updateActiveWallets(wallets) {
|
|
153
155
|
if (!wallets) return;
|
|
156
|
+
pollCount++;
|
|
157
|
+
// Skip first 2 polls to establish stable baseline (avoids false positives on load/refresh)
|
|
158
|
+
if (pollCount <= 2) {
|
|
159
|
+
for (var i = 0; i < wallets.length; i++) {
|
|
160
|
+
var w = wallets[i];
|
|
161
|
+
var totalMines = 0;
|
|
162
|
+
for (var j = 0; j < w.miners.length; j++) totalMines += Number(w.miners[j].mineCount);
|
|
163
|
+
prevMines[w.address] = totalMines;
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
154
167
|
var now = Date.now();
|
|
155
168
|
for (var i = 0; i < wallets.length; i++) {
|
|
156
169
|
var w = wallets[i];
|
package/dist/dashboard.js
CHANGED
|
@@ -193,26 +193,80 @@ function startDashboardServer(opts) {
|
|
|
193
193
|
});
|
|
194
194
|
const artCache = new Map();
|
|
195
195
|
const htmlPage = (0, dashboard_html_1.getDashboardHtml)();
|
|
196
|
+
// Response cache with TTL — always serve cached data, refresh in background
|
|
197
|
+
const responseCache = new Map();
|
|
198
|
+
const pendingFetches = new Map();
|
|
199
|
+
const CACHE_TTL = 25_000; // 25s — slightly less than client's 30s poll
|
|
200
|
+
async function cachedHandler(key, handler) {
|
|
201
|
+
const cached = responseCache.get(key);
|
|
202
|
+
const now = Date.now();
|
|
203
|
+
const isStale = !cached || (now - cached.ts > CACHE_TTL);
|
|
204
|
+
if (isStale && !pendingFetches.has(key)) {
|
|
205
|
+
// Fetch fresh data (non-blocking if we have stale data to return)
|
|
206
|
+
const fetchPromise = handler()
|
|
207
|
+
.then((result) => {
|
|
208
|
+
responseCache.set(key, { data: result, ts: Date.now() });
|
|
209
|
+
pendingFetches.delete(key);
|
|
210
|
+
return result;
|
|
211
|
+
})
|
|
212
|
+
.catch((err) => {
|
|
213
|
+
pendingFetches.delete(key);
|
|
214
|
+
if (cached)
|
|
215
|
+
return cached.data;
|
|
216
|
+
throw err;
|
|
217
|
+
});
|
|
218
|
+
pendingFetches.set(key, fetchPromise);
|
|
219
|
+
// No cached data yet — must wait for first fetch
|
|
220
|
+
if (!cached)
|
|
221
|
+
return fetchPromise;
|
|
222
|
+
}
|
|
223
|
+
// If we have a pending fetch and no cache, wait for it
|
|
224
|
+
if (!cached && pendingFetches.has(key)) {
|
|
225
|
+
return pendingFetches.get(key);
|
|
226
|
+
}
|
|
227
|
+
// Return cached data immediately (even if stale — background refresh handles it)
|
|
228
|
+
return cached ? cached.data : "{}";
|
|
229
|
+
}
|
|
230
|
+
const MULTICALL_CHUNK = 30; // max calls per multicall batch
|
|
231
|
+
async function chunkedMulticall(contracts) {
|
|
232
|
+
if (contracts.length <= MULTICALL_CHUNK) {
|
|
233
|
+
return publicClient.multicall({ contracts });
|
|
234
|
+
}
|
|
235
|
+
const results = [];
|
|
236
|
+
for (let i = 0; i < contracts.length; i += MULTICALL_CHUNK) {
|
|
237
|
+
const chunk = contracts.slice(i, i + MULTICALL_CHUNK);
|
|
238
|
+
try {
|
|
239
|
+
const chunkResults = await publicClient.multicall({ contracts: chunk });
|
|
240
|
+
results.push(...chunkResults);
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
for (let j = 0; j < chunk.length; j++) {
|
|
244
|
+
results.push({ status: "failure" });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return results;
|
|
249
|
+
}
|
|
196
250
|
async function handleWallets(fleetParam) {
|
|
197
251
|
const addresses = getAddressesForFleet(fleetParam, walletsPath);
|
|
198
252
|
if (addresses.length === 0)
|
|
199
253
|
return "[]";
|
|
200
|
-
// Phase 1: ETH balance + AGENT balance + NFT count
|
|
254
|
+
// Phase 1: ETH balance + AGENT balance + NFT count (chunked)
|
|
201
255
|
const phase1Contracts = addresses.flatMap((addr) => [
|
|
202
256
|
{ address: agentCoinAddress, abi: AgentCoinAbi, functionName: "balanceOf", args: [addr] },
|
|
203
257
|
{ address: miningAgentAddress, abi: MiningAgentAbi, functionName: "balanceOf", args: [addr] },
|
|
204
258
|
]);
|
|
205
259
|
const [balances, multicallResults] = await Promise.all([
|
|
206
260
|
Promise.all(addresses.map((addr) => publicClient.getBalance({ address: addr }).catch(() => 0n))),
|
|
207
|
-
|
|
261
|
+
chunkedMulticall(phase1Contracts),
|
|
208
262
|
]);
|
|
209
263
|
const walletInfos = addresses.map((addr, i) => ({
|
|
210
264
|
address: addr,
|
|
211
265
|
ethBalance: balances[i],
|
|
212
|
-
agentBalance: multicallResults[i * 2]
|
|
213
|
-
nftCount: Number(multicallResults[i * 2 + 1]
|
|
266
|
+
agentBalance: multicallResults[i * 2]?.result ?? 0n,
|
|
267
|
+
nftCount: Number(multicallResults[i * 2 + 1]?.result ?? 0n),
|
|
214
268
|
}));
|
|
215
|
-
// Phase 2: token IDs
|
|
269
|
+
// Phase 2: token IDs (chunked)
|
|
216
270
|
const tokenIdContracts = [];
|
|
217
271
|
const tokenIdMap = [];
|
|
218
272
|
for (let wi = 0; wi < walletInfos.length; wi++) {
|
|
@@ -230,7 +284,7 @@ function startDashboardServer(opts) {
|
|
|
230
284
|
const tokenIds = [];
|
|
231
285
|
const validTokenMap = [];
|
|
232
286
|
if (tokenIdContracts.length > 0) {
|
|
233
|
-
const tokenIdResults = await
|
|
287
|
+
const tokenIdResults = await chunkedMulticall(tokenIdContracts);
|
|
234
288
|
for (let i = 0; i < tokenIdResults.length; i++) {
|
|
235
289
|
const r = tokenIdResults[i];
|
|
236
290
|
if (r.status === "success" && r.result != null) {
|
|
@@ -249,7 +303,7 @@ function startDashboardServer(opts) {
|
|
|
249
303
|
}));
|
|
250
304
|
return JSON.stringify(wallets);
|
|
251
305
|
}
|
|
252
|
-
// Phase 3: miner stats + art
|
|
306
|
+
// Phase 3: miner stats (chunked) + art
|
|
253
307
|
const uncachedTokenIds = tokenIds.filter((id) => !artCache.has(id.toString()));
|
|
254
308
|
const FIELDS_PER_TOKEN = 5;
|
|
255
309
|
const detailContracts = tokenIds.flatMap((tokenId) => [
|
|
@@ -259,8 +313,8 @@ function startDashboardServer(opts) {
|
|
|
259
313
|
{ address: agentCoinAddress, abi: AgentCoinAbi, functionName: "tokenEarnings", args: [tokenId] },
|
|
260
314
|
{ address: miningAgentAddress, abi: MiningAgentAbi, functionName: "mintBlock", args: [tokenId] },
|
|
261
315
|
]);
|
|
262
|
-
const detailResults = await
|
|
263
|
-
// Batch art URI calls for uncached tokens —
|
|
316
|
+
const detailResults = await chunkedMulticall(detailContracts);
|
|
317
|
+
// Batch art URI calls for uncached tokens — small chunks (tokenURI returns ~41KB each)
|
|
264
318
|
const ART_CHUNK_SIZE = 2;
|
|
265
319
|
const artResults = [];
|
|
266
320
|
for (let i = 0; i < uncachedTokenIds.length; i += ART_CHUNK_SIZE) {
|
|
@@ -276,7 +330,6 @@ function startDashboardServer(opts) {
|
|
|
276
330
|
artResults.push(...chunkResults);
|
|
277
331
|
}
|
|
278
332
|
catch {
|
|
279
|
-
// Push failure entries so indexing stays aligned
|
|
280
333
|
for (let j = 0; j < chunk.length; j++) {
|
|
281
334
|
artResults.push({ status: "failure" });
|
|
282
335
|
}
|
|
@@ -392,12 +445,13 @@ function startDashboardServer(opts) {
|
|
|
392
445
|
}
|
|
393
446
|
if (pathname === "/api/wallets") {
|
|
394
447
|
const fleet = url.searchParams.get("fleet");
|
|
395
|
-
const
|
|
448
|
+
const cacheKey = `wallets:${fleet ?? "All"}`;
|
|
449
|
+
const body = await cachedHandler(cacheKey, () => handleWallets(fleet));
|
|
396
450
|
jsonResponse(res, body);
|
|
397
451
|
return;
|
|
398
452
|
}
|
|
399
453
|
if (pathname === "/api/network") {
|
|
400
|
-
const body = await handleNetwork();
|
|
454
|
+
const body = await cachedHandler("network", () => handleNetwork());
|
|
401
455
|
jsonResponse(res, body);
|
|
402
456
|
return;
|
|
403
457
|
}
|