spb/coinexplorer Public MIT
Self-hosted, zero-API-key explorer for stablecoins and major crypto.
Python 60.3%
HTML 23.6%
JavaScript 8.1%
CSS 6.8%
SQL 1%
1<!doctype html>2<!-- Author: Simon-Pierre Boucher -->3<!-- Mail: contact@spboucher.ai -->4<html lang="en">5<head>6<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">7<title>status — coinexplorer</title>8<link rel="stylesheet" href="/assets/style.css">9</head>10<body>11<main>12 <div class="pagehead"><h1>Indexer status</h1>13 <span class="sub">per-chain progress, coverage and free-RPC endpoint health ·14 <a href="/metrics">Prometheus</a></span></div>1516 <section class="card tile"><h2>Transfers indexed</h2><div class="big" id="tTotal">—</div></section>17 <section class="card tile"><h2>Chains at head</h2><div class="big" id="tHead">—</div>18 <div class="note">lag ≤ 100 blocks</div></section>19 <section class="card tile"><h2>Healthy endpoints</h2><div class="big" id="tEp">—</div>20 <div class="note">score ≥ 0.8</div></section>21 <section class="card tile"><h2>Benched endpoints</h2><div class="big" id="tBench">—</div>22 <div class="note">currently cooling down</div></section>2324 <section class="card"><h2>Chains</h2>25 <div class="scroll" style="max-height:55vh"><table>26 <thead><tr><th>chain</th><th class="num">indexed</th><th class="num">cursor</th>27 <th class="num">head</th><th class="num">lag</th><th class="num">floor</th></tr></thead>28 <tbody id="chains"></tbody></table></div></section>2930 <section class="card"><h2>RPC endpoints</h2>31 <div class="scroll" style="max-height:55vh"><table>32 <thead><tr><th>chain</th><th>endpoint</th><th class="num">score</th><th class="num">ok</th>33 <th class="num">fail</th><th class="num">latency</th><th class="num">cooldown</th></tr></thead>34 <tbody id="rpc"></tbody></table></div></section>35</main>3637<script type="module">38import { $, j, fmtN, mountNav, chainLink } from "/assets/app.js";39mountNav("Status");4041async function load() {42 const [status, health] = await Promise.all([j("/v1/status"), j("/v1/rpc/health")]);43 $("tTotal").textContent = fmtN(Object.values(status.indexed_transfers).reduce((a, b) => a + b, 0));44 const cs = Object.entries(status.cursors);45 $("tHead").textContent = `${cs.filter(([, c]) => c.lag != null && c.lag <= 100).length}/${cs.length}`;46 const eps = Object.values(health).flat();47 $("tEp").textContent = `${eps.filter((e) => e.score >= 0.8).length}/${eps.length}`;48 $("tBench").textContent = eps.filter((e) => e.cooldown_s > 0).length;4950 $("chains").innerHTML = cs.sort((a, b) =>51 (status.indexed_transfers[b[0]] || 0) - (status.indexed_transfers[a[0]] || 0))52 .map(([c, cur]) => {53 const lag = cur.lag;54 const cls = lag == null ? "" : lag > 1000 ? "burn" : lag > 100 ? "" : "mint";55 return `<tr><td>${chainLink(c)}</td>56 <td class="num">${fmtN(status.indexed_transfers[c] || 0)}</td>57 <td class="num">${fmtN(cur.last_block)}</td>58 <td class="num">${cur.head_block ? fmtN(cur.head_block) : "—"}</td>59 <td class="num ${cls}">${lag != null ? fmtN(lag) : "—"}</td>60 <td class="num">${cur.backfill_block != null ? fmtN(cur.backfill_block) : "—"}</td></tr>`;61 }).join("");6263 $("rpc").innerHTML = Object.entries(health).flatMap(([c, list]) => list.map((e) =>64 `<tr><td>${chainLink(c)}</td>65 <td class="mono" style="max-width:none" title="${e.url}">${e.url}</td>66 <td class="num ${e.score >= 0.8 ? "mint" : e.score < 0.5 ? "burn" : ""}">${e.score}</td>67 <td class="num">${fmtN(e.ok)}</td><td class="num">${fmtN(e.fail)}</td>68 <td class="num">${e.latency_ms} ms</td>69 <td class="num">${e.cooldown_s > 0 ? e.cooldown_s + "s" : "—"}</td></tr>`)).join("");70}71load();72setInterval(load, 15_000);73</script>74</body>75</html>76