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>transfers — coinexplorer</title>8<link rel="stylesheet" href="/assets/style.css">9</head>10<body>11<main>12 <div class="pagehead"><h1>Transfer explorer</h1>13 <span class="sub">browse the index chain by chain · newest first</span></div>1415 <div class="filters">16 <select id="chainSel" aria-label="Chain"></select>17 <select id="symSel"><option value="">all tokens</option></select>18 <input id="minUsd" type="number" placeholder="min USD" style="width:120px">19 <input id="addr" placeholder="filter by address (from/to)" style="flex:1;min-width:220px">20 <button class="btn" id="apply">Apply</button>21 </div>2223 <section class="card"><h2 id="tableTitle">Transfers</h2>24 <div class="scroll" style="max-height:70vh"><table>25 <thead><tr><th>block</th><th>token</th><th class="num">amount</th><th>from</th><th>to</th><th>tx</th><th>when</th></tr></thead>26 <tbody id="rows"></tbody></table></div>27 <div style="margin-top:8px"><button class="btn" id="more">Load more</button>28 <span class="sub" id="count" style="margin-left:8px"></span></div></section>29</main>3031<script type="module">32import { $, qs, j, fmtUsd, fmtN, fmtAmt, mountNav, addrLink, txLink, tokenLink, ago } from "/assets/app.js";33mountNav("Transfers");34let CHAIN = qs.get("chain") || "ethereum";35let beforeBlock = null, total = 0;3637async function load(append = false) {38 const sym = $("symSel").value, min = $("minUsd").value, addr = $("addr").value.trim();39 let rows;40 if (addr) {41 rows = (await j(`/v1/address/${encodeURIComponent(addr)}/transfers?limit=200`))42 .filter((r) => r.chain === CHAIN || !CHAIN)43 .filter((r) => !sym || r.symbol === sym)44 .filter((r) => !min || parseFloat(r.value) >= +min);45 $("more").style.display = "none";46 } else {47 let url = `/v1/${CHAIN}/transfers?limit=100`;48 if (sym) url += `&symbol=${sym}`;49 if (min) url += `&min_amount=${min}`;50 if (append && beforeBlock) url += `&before_block=${beforeBlock}`;51 rows = await j(url);52 if (rows.length) beforeBlock = rows[rows.length - 1].block;53 $("more").style.display = rows.length ? "" : "none";54 }55 total = append ? total + rows.length : rows.length;56 $("count").textContent = `${fmtN(total)} shown`;57 $("tableTitle").textContent = `Transfers — ${CHAIN}${sym ? " · " + sym : ""}${addr ? " · " + addr.slice(0, 12) + "…" : ""}`;58 const html = rows.map((r) =>59 `<tr><td>${fmtN(r.block)}</td><td>${tokenLink(r.symbol)}</td>60 <td class="num">${fmtAmt(r)}</td><td>${addrLink(r.from)}</td>61 <td>${addrLink(r.to)}</td><td>${txLink(r.chain, r.tx_hash)}</td>62 <td>${ago(r.timestamp)} ago</td></tr>`).join("");63 if (append) $("rows").insertAdjacentHTML("beforeend", html);64 else $("rows").innerHTML = html || `<tr><td colspan="7" class="empty">no matches</td></tr>`;65}6667Promise.all([j("/v1/chains"), j("/v1/tokens")]).then(([chains, tokens]) => {68 $("chainSel").innerHTML = Object.keys(chains).sort()69 .map((c) => `<option ${c === CHAIN ? "selected" : ""}>${c}</option>`).join("");70 $("symSel").innerHTML = `<option value="">all tokens</option>` +71 Object.keys(tokens).sort().map((s) => `<option>${s}</option>`).join("");72 load();73});74$("chainSel").addEventListener("change", (e) => { CHAIN = e.target.value; beforeBlock = null; load(); });75$("apply").addEventListener("click", () => { beforeBlock = null; load(); });76$("addr").addEventListener("keydown", (e) => { if (e.key === "Enter") { beforeBlock = null; load(); } });77$("more").addEventListener("click", () => load(true));78</script>79</body>80</html>81