feat: an about route, and somewhere to say thanks
Adds `/:chain/about`, linked from the header just before the connection light and the theme toggle — it is about the site rather than the chain, so it sits there instead of in the section nav, and `header: true` on its `SECTIONS` entry keeps it out while still letting the URL parse. The page says what the observer is, that it and the CUDA-optimised miner are built and freely sourced by a member of the Quantus user community rather than by the project, and takes donations in QTC. **The QR is generated in the browser from the address printed beside it, never shipped as an image.** A supplied PNG next to a typed address is two independent claims about where money goes, and nothing on the page could tell a reader if they ever stopped agreeing — a mismatch sends funds somewhere unrecoverable. Verified rather than trusted. The address was put through this crate's own SS58 decoder: checksum and network prefix both hold, and it round-trips to the same string. The QR matrix was then decoded from first principles — function-pattern map, mask 3 unapplied, codewords de-interleaved across version 4-M's two blocks — and reads back the address exactly. A note for whoever checks this again: an independent encoder produces a different matrix for the same string at every mask, because mode segmentation differs, so comparing matrices proves nothing and only a decode does. `the_donation_address_on_the_site_is_a_valid_quantus_address` reads the literal out of the frontend source and validates it, so a typo fails CI rather than costing somebody their money. Confirmed to fail on one changed character. Also on the vesting page, from verifying it live: a "claimable now" figure and a per-grant claimable column. Two grants began vesting at launch while the other forty-six wait for 2027, so 68,871 QTC is already claimable and none of it has been taken — a page that only said "first claimable" would have missed it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Who made this, and how to say thanks.
|
||||
*
|
||||
* The QR is **generated in the browser from the same string the page prints**,
|
||||
* never shipped as an image. A supplied PNG and the text beneath it are two
|
||||
* independent claims about where money should go, and nothing on the page could
|
||||
* tell you if they ever stopped agreeing — a mismatch sends funds somewhere
|
||||
* unrecoverable. Generated, they cannot disagree: there is one address, and the
|
||||
* code is a rendering of it.
|
||||
*/
|
||||
|
||||
import qrcode from 'qrcode-generator'
|
||||
|
||||
/** Where donations go. Also the string the QR is built from — see above. */
|
||||
export const DONATION_ADDRESS = 'qzkQq9ybiPm7gQR9E1ViQbW9RhqA2Rex9ZQkScU5zdJqjrre4'
|
||||
|
||||
const MINER_REPO = 'https://git.lair.cafe/quantus/miner'
|
||||
const OBSERVER_REPO = 'https://git.lair.cafe/quantus/blackbeard.observer'
|
||||
|
||||
/**
|
||||
* The address as a QR, drawn as one SVG path.
|
||||
*
|
||||
* Error-correction level `M` and an automatic version: the address is 49
|
||||
* characters, which fits comfortably and leaves the code readable at the size
|
||||
* a phone is held from a screen. Rendered as a single path of rectangles rather
|
||||
* than a grid of elements — one node instead of a thousand, and it scales to
|
||||
* whatever box the layout gives it.
|
||||
*/
|
||||
function AddressQr({ value }: { value: string }) {
|
||||
const qr = qrcode(0, 'M')
|
||||
qr.addData(value)
|
||||
qr.make()
|
||||
const count = qr.getModuleCount()
|
||||
// A four-module quiet zone, which the spec requires and scanners rely on.
|
||||
const quiet = 4
|
||||
const size = count + quiet * 2
|
||||
|
||||
let path = ''
|
||||
for (let row = 0; row < count; row += 1) {
|
||||
for (let col = 0; col < count; col += 1) {
|
||||
if (qr.isDark(row, col)) {
|
||||
path += `M${col + quiet} ${row + quiet}h1v1h-1z`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<svg
|
||||
className="qr"
|
||||
viewBox={`0 0 ${size} ${size}`}
|
||||
role="img"
|
||||
aria-label={`QR code for the donation address ${value}`}
|
||||
>
|
||||
{/* The light modules are drawn, not left transparent: a QR on a dark
|
||||
surface with a transparent background does not scan, and this site has
|
||||
a dark theme. */}
|
||||
<rect x="0" y="0" width={size} height={size} className="qr-light" />
|
||||
<path d={path} className="qr-dark" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function AboutPanel() {
|
||||
return (
|
||||
<>
|
||||
<section className="panel" style={{ marginBottom: 26 }}>
|
||||
<div className="panel-head">
|
||||
<h2 className="panel-title">About</h2>
|
||||
<span className="eyebrow">who made this</span>
|
||||
</div>
|
||||
<div className="about-prose">
|
||||
<p>
|
||||
<strong>blackbeard.observer</strong> is a mining leaderboard and block explorer for the
|
||||
Quantus network. It is built and its source provided freely by a member of the Quantus
|
||||
user community — not by the Quantus project, and with no standing behind it beyond the
|
||||
code being open and the numbers being checkable.
|
||||
</p>
|
||||
<p>
|
||||
The same author maintains a{' '}
|
||||
<a href={MINER_REPO} rel="noreferrer">
|
||||
CUDA performance-optimised miner
|
||||
</a>
|
||||
, also free and open source. Between them they are the reason a good deal of this site
|
||||
exists: a miner needs somewhere to see whether it is winning.
|
||||
</p>
|
||||
<p>
|
||||
Everything the observer shows is derived from public block headers, chain state and the
|
||||
network's own telemetry feed. The code that derives it is at{' '}
|
||||
<a href={OBSERVER_REPO} rel="noreferrer">
|
||||
git.lair.cafe/quantus/blackbeard.observer
|
||||
</a>
|
||||
, so any figure here can be traced to the read that produced it.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="panel" style={{ marginBottom: 26 }}>
|
||||
<div className="panel-head">
|
||||
<h2 className="panel-title">Donations</h2>
|
||||
<span className="eyebrow">entirely optional</span>
|
||||
</div>
|
||||
<div className="about-donate">
|
||||
<AddressQr value={DONATION_ADDRESS} />
|
||||
<div className="about-prose">
|
||||
<p>
|
||||
If the observer or the miner have been useful and you are inclined, donations toward
|
||||
development, maintenance and hosting are gratefully accepted in QTC at:
|
||||
</p>
|
||||
<p>
|
||||
<code className="about-address">{DONATION_ADDRESS}</code>
|
||||
</p>
|
||||
<p className="panel-note" style={{ padding: 0, border: 0 }}>
|
||||
Nothing here is gated behind it, and nothing about the site changes if you do. The
|
||||
code above is generated in your browser from the address printed beside it, so the two
|
||||
cannot drift apart — scan it or copy the text, whichever you trust more.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -52,7 +52,7 @@ export function SectionNav({
|
||||
>
|
||||
Genesis
|
||||
</Link>
|
||||
{SECTIONS.map((s) => (
|
||||
{SECTIONS.filter((s) => !s.header).map((s) => (
|
||||
<Link
|
||||
key={s.id}
|
||||
to={href({ chain, index: s.id })}
|
||||
|
||||
@@ -191,6 +191,18 @@ export function VestingPanel({
|
||||
return null
|
||||
}
|
||||
})()
|
||||
// Vested and not yet taken, summed across every grant. The headline figure on
|
||||
// this chain today: two grants began vesting at launch while the other
|
||||
// forty-six wait for 2027, so a page that only said "first claimable" would
|
||||
// miss that a great deal already is.
|
||||
const claimableNow = (() => {
|
||||
try {
|
||||
return data.schedules.reduce((sum, s) => sum + BigInt(s.claimable), 0n).toString()
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
})()
|
||||
|
||||
const firstCliff = data.schedules.reduce(
|
||||
(a, s) => (a === null || s.cliff < a ? s.cliff : a),
|
||||
null as string | null,
|
||||
@@ -215,6 +227,11 @@ export function VestingPanel({
|
||||
value={outstanding === null ? '—' : amount(outstanding)}
|
||||
note="scheduled less claimed"
|
||||
/>
|
||||
<Field
|
||||
label="Claimable now"
|
||||
value={claimableNow === null ? '—' : amount(claimableNow)}
|
||||
note="vested and not yet taken"
|
||||
/>
|
||||
<Field label="Grants" value={fmtHeight(data.schedules.length)} />
|
||||
<Field
|
||||
label="First claimable"
|
||||
@@ -258,6 +275,12 @@ export function VestingPanel({
|
||||
whole grant from its end, and linearly in between — so this is that arithmetic summed
|
||||
across every schedule, not a model of what somebody intends.
|
||||
</p>
|
||||
<p className="panel-note">
|
||||
The step at the left is not the whole story told small:{' '}
|
||||
<strong>the grants do not share a cliff</strong>. Read the table for which ones have
|
||||
started — a curve that rises early and then sits flat for a year is two grants vesting
|
||||
from launch while the rest wait, not a gentle beginning.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="panel" style={{ marginBottom: 26 }}>
|
||||
@@ -275,6 +298,7 @@ export function VestingPanel({
|
||||
</th>
|
||||
<th scope="col">Grant</th>
|
||||
<th scope="col">Vested</th>
|
||||
<th scope="col">Claimable</th>
|
||||
<th scope="col">Claimed</th>
|
||||
<th scope="col" className="left">
|
||||
Unlocks
|
||||
@@ -294,6 +318,7 @@ export function VestingPanel({
|
||||
</td>
|
||||
<td className="numeral">{tokens(s.total, decimals, 0)}</td>
|
||||
<td className="numeral">{tokens(s.vested, decimals, 0)}</td>
|
||||
<td className="numeral">{tokens(s.claimable, decimals, 0)}</td>
|
||||
<td className="numeral">{tokens(s.claimed, decimals, 0)}</td>
|
||||
<td className="left">{day(s.cliff)}</td>
|
||||
<td className="left">{day(s.end)}</td>
|
||||
|
||||
Reference in New Issue
Block a user