feat(release): ship QIP-55 explorer and default v3 testnet - #190
Merged
Merged
Conversation
GET and HEAD responses now send Access-Control-Allow-Origin: * so the read API is usable from any browser origin. POST endpoints keep the explorer-origin allowlist (plus browser-extension schemes and the CORS_ALLOW_ORIGINS env override): /contract/explain is Anthropic-billed and /contract/verify plus /contract/call are abuse-sensitive, so third-party pages must not fire them from visitors' browsers. Vary: Origin is set unconditionally so shared caches never serve an ACAO-less copy to a CORS request. Claude-Session: https://claude.ai/code/session_015ADBggzM8n5R9gqApZodPv
Hand-written OpenAPI 3.1 spec (app/lib/openapi.json) covering all 43 backend routes plus /search and /faucet/claim, served at /openapi.json with open CORS. The /api-explorer page is rebuilt from the spec: every endpoint server-rendered and crawlable (the old accordions unmounted all content), anchors and a sticky tag nav, parameter tables, curl examples with copy buttons, live try-it links for GETs, accurate rate limit and CORS documentation, and a client-side filter. Documented pagination defaults, cache TTLs, and encoding conventions were verified against the Go handlers. scripts/check-api-docs-drift.mjs diffs the spec against the Go route registrations and runs in CI, so the docs can no longer drift silently (7 endpoints were missing from the old page). Claude-Session: https://claude.ai/code/session_015ADBggzM8n5R9gqApZodPv
…rite New /learn section: 12 plain-English guides across three paths (QRL 2.0 basics, using the explorer, hands-on tutorials for MyQRLWallet, QuantaPool staking, QuantaSwap atomic swaps, QRC20 creation, and contract deploy plus verify). Every product claim was grounded in the corresponding source repo and adversarially fact-checked. Shared article components emit TechArticle and BreadcrumbList JSON-LD; the index, sidebar group, footer links, sitemap rows, and layout hasPart entries are driven by a single article registry. The FAQ is rewritten as a pure server component with native details/summary so answers are finally in the served HTML (the old headlessui accordion unmounted them), gains FAQPage JSON-LD, and corrects a factual error: QRL 2.0 signatures are ML-DSA-87, the old copy said SPHINCS+. The faucet button label and address error copy now use Quanta unit phrasing. Claude-Session: https://claude.ai/code/session_015ADBggzM8n5R9gqApZodPv
Learn section, OpenAPI-driven API docs, server-rendered FAQ, method-split CORS
…racing Restore private internal transaction tracing and remove the duplicate transaction confirmation badge.
Restore the on-box syncer lag check and alert when its height query becomes unusable.
Adds a visual order book page backed by a new read-only market-data endpoint. No request parameter can change the venue, symbol, or upstream: the QRLUSDT spot market is fixed in code. Backend: - marketdata: MEXC client fetching depth, recent trades, and the 24h ticker in parallel, with a bounded HTTP client (per-phase timeouts, per-endpoint body caps, redirects rejected so the fixed upstream cannot redirect elsewhere). All decimals are validated and normalized; trades with a null id get a deterministic sha256 synthetic id, which is the live path since MEXC's public /trades returns null ids. - GET /market/orderbook behind a 3s TTL. Upstream failures are cached as values for the same window so a MEXC 429 or 5xx cannot turn sequential visitors into a new three-request burst; failures respond 502 with Retry-After. - cache: add GetOrComputeContext, the cancelable-wait counterpart of GetOrCompute. Canceling one waiter never cancels the shared computation. Frontend: - /orderbook renders grouped depth, recent executions, and a play-by-play feed over an arena visualization driven by the live snapshot. - Sidebar entry, sitemap entry, OpenAPI spec, and schema.org WebPage node for the new route. - Move the JSON-LD script out of <head> into <body>. Next's metadata pipeline owns <head>, and a manual head child in that stream can cause a hydration mismatch in development. Search engines accept structured data in either section. Verified against the live MEXC feed end to end: go test, jest 148/148, tsc --noEmit, eslint, and next build all pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rolls up collected public executions into buy/sell volume per trade-size band, a net-inflow series over a selectable window, and five days of daily net inflow. Built venue-first rather than around one exchange, since more venues are planned. Adding one means implementing marketdata.Venue and appending it in DefaultRegistry; storage, API, and UI need no further wiring. Backend: - marketdata.Venue + Registry: a venue owns its id, symbol, quote asset, size thresholds, and fetchers. MEXC becomes the first implementation. - collector package polls each venue's tape every 60s and upserts it. The poll interval is far shorter than the tape's span (~8h for QRLUSDT), so a missed poll, a restart, or an hours-long outage loses no trades. Rows are keyed <venueId>:<tradeId>, making collection idempotent and safe to run from more than one process. - Collection runs in backendAPI because the read API only fetches when a visitor arrives: an endpoint computing a 24h rollup on demand would have nothing to compute from on a quiet site. - GET /market/fundflow?venue=&window=, cached 30s per venue and window. Windows are an allowlist; unknown venue or window is a 400 naming the accepted values. - marketTrades is created eagerly with its indexes and a 45-day TTL. createIndexes skips collections that do not exist yet, so a collection first written at runtime would otherwise stay unindexed until some later restart happened to find it populated. - Size bands are ZondScan's own classification by quote notional, sized from the observed tape. The API reports the thresholds so the UI can state them, and a row keeps the band it was classified into at write time, so retuning the constants cannot redraw history. Frontend: - Diverging column charts: 24px cap, 4px rounded data-end square at the baseline, hairline grid, selective direct labels, hover and focus tooltips, and an sr-only table twin. - The buy/sell green and red pair measures dE 6.5 for deuteranopia, which is legal only with secondary encoding, so sign is carried three further ways: direction from the zero baseline, an explicit +/- on every value, and text labels. The pair is kept because the rest of the page already binds those hues to buy and sell. - Coverage is surfaced rather than hidden. Venues expose no historical trade tape, so history accumulates forward from first collection and a partly covered window says so instead of reading as an absence of flow. Verified against the live MEXC feed: a real DNS timeout mid-run was logged, skipped, and recovered on the next tick with no trades lost. go vet, 8 Go packages, jest 175/175, tsc, eslint, next build, and the API docs drift check all pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A UI review found the page had accumulated seams from being built in layers. Fixes the drift and one real rendering bug. Rendering bug: the two fund-flow charts shared a fixed 720x220 viewBox scaled by w-full, so their height AND their label type size were both derived from container width. The per-step chart sits in a half-width grid column and the daily chart spans the card, so a matched pair rendered at different sizes. Both now render in pixel space against the measured width, giving a fixed 220px height and identical type at any width. Axis labels now shift to an edge anchor only when the centred text would actually spill outside the plot. Anchoring by index detached the first and last labels from their columns whenever bands were wide, which was every low-count chart including the five daily bars. Consistency with the rest of the site: - Vertical rhythm moves to mb-6/mt-6 between stacked sections, matching gas, validators, and richlist. - The arena gets the same bordered header strip and card padding as every other block on the page, so it stops reading as a separate object glued on top. - The fund-flow window selector reuses the arena's pill chrome. Two controls doing the same job had nothing in common. - Table headers adopt the site's header type (11px, tracking-[0.12em], font-medium) in place of a 10px tracking-wider style invented for this page. The values are copied rather than the table-header utility applied, since that utility forces px-6 and text-left and would break these dense right-aligned tables. - Headline figures use the mono/tabular treatment the stat cards use, and both error-card titles settle on text-xl. - Motion toggle drops to py-1.5 to match the buttons beside it. - The arena background image had sizes="70vw" while rendering full width at every breakpoint, so it was served under-resolved. jest 178/178, tsc, eslint, and next build all pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The /gas charts have never had data. UpdateGasHistory scanned blocks oldest-first, capped at 5000, starting from an empty collection's highwater of -1. On a chain at block ~202k with 60s slots, blocks 0-4999 are around 140 days old, so the retention sweep at the end of the same run deleted every row the run had just written. The highwater returned to -1 and the next run repeated the identical batch, twice an hour, forever. Scanning newest-first keeps written rows inside the retention window, which is what lets the highwater advance. The scan limit is raised to cover the 7-day retention horizon at the current 60s slot time so a cold start fills the whole 7d view in one pass. Adds integration coverage for the deadlock. The regression test seeds more ancient blocks than one scan can hold, which is the condition the bug needs: a first version seeded only 120 blocks and passed against the broken code, because a single oldest-first scan still reached the recent ones. The seed size now derives from the scan limit so the test stays honest if that constant is retuned. A second test asserts the sweep still removes rows past retention, so the fix cannot be faked by never deleting. Verified both directions against a live MongoDB: the regression test fails on the old ordering with "first run stored nothing; every written row was swept as too old", and passes on the fix with the highwater advancing and holding across runs. Note: the chain is currently idle, so the chart will render a truthful flat line at zero rather than a message about missing history. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reported from the live order book: the grouped depth ladder showed "13.93" and "92.06" beside "1,152" and "13,454", so the column read as broken. formatQrlQuantity picked its precision per value (value < 100 ? 2 : 0), so whether a cell grew decimals depended on that cell alone. Any bucket that happened to fall under 100 QRL sprouted two decimals while its neighbours stayed whole. Precision now follows the column's purpose rather than the individual value. Depth ladder and other aggregates round to whole QRL: a bucket is a sum, and its fraction is noise beside the total. Below 1 QRL the fraction is the only information present, so it is kept, and dust that would round to a misleading "0" renders as "<0.01" instead. The trade tape gets its own formatter at a fixed two decimals. Rounding an individual execution would misstate it, since 7.75 QRL is not 8, and fixing the precision keeps that column one shape at every magnitude too. The live book happens to hold no sub-100 buckets right now, so the tests pin the exact values from the report rather than relying on a snapshot that no longer reproduces it. jest 186/186, tsc, eslint, next build all pass. Reported-by: ChillerID Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two things from the same report. Depth. The ladder was not truncating a deep book, the backend was only fetching a shallow one: 100 levels a side yielded about 10 grouped buckets per side at the 0.01 grouping, fewer than the 12 rows the ladder already displayed. QRLUSDT actually rests around 340 bids and 1025 asks, so the fetch moves to 500 a side, which covers the entire bid book and the part of the ask book anyone reads. The far tail is parasitic (a bid near zero, asks into the millions) but the ladder walks outward from the touch, so it only appears if a reader asks for enough rows to reach it. With real depth behind it the ladder gains a Rows control (12/25/50) and a 0.1 grouping. Both were pointless before: at 100 levels there was nothing further to reveal, and 0.1 collapsed each side to two rows. The ladder's depth is now built separately from the arena's levels. They shared one constant, so more rows would have meant more markers on a field that crowds long before a table does. Select popups. A native option list is drawn by the OS on its own light surface and does not inherit the dark theme the closed control wears, so the site's near-white text was landing on a white popup and the list was close to unreadable. Options now carry their own background and colour. This is site-wide rather than per page: the converter, verify-contract, and fund-flow selects had the same problem. jest 187/187, tsc, eslint, next build, go vet, go test, and the API docs drift check all pass. Reported-by: ChillerID Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The arena only ever animated one element. Every marker positioned itself with the SVG transform attribute, which CSS cannot transition, so on each 3s poll the whole field jumped. The ball moved smoothly because it alone used a style transform. Marker size had the same problem: radius and sprite dimensions were attributes, so they snapped too. Markers now carry position and scale in one CSS transform and glide on the same 900ms easing as the ball. Geometry is drawn at a base size and scaled, so both move together. transform-origin is pinned to 0 0 on everything that scales. SVG defaults to transform-box: view-box with a 50% 50% origin that resolves to the centre of the viewBox rather than the element, which translate-only transforms never notice. Left at the default, markers would grow towards mid-field and the ripple would fly across the pitch. Three nested groups so the animations compose rather than overwrite: the outer owns the position transform, the middle fades new buckets in, and the inner runs an idle bob. The bob matters most here, because a quiet book left the field motionless between polls. Its timing is seeded from the bucket price rather than the array index: a bucket keeps its React key across polls, so its animation keeps running, and rewriting the delay mid-flight whenever its rank shifted would jump it a frame. Executed plays now send a ripple from the ball, coloured by direction and neutral for a flat play, matching the play feed's existing convention. Also corrects aria-pressed on the Motion toggle, which was inverted against its own label and against every sibling toggle on the page. It predates this change but was inconsequential until motion became real. Reviewed with headless repros in Chrome 151 and Firefox 152 measuring getBoundingClientRect across the exact nesting used here, confirming both the transform-origin handling and that px in these transforms resolves to SVG user-space units. WebKit is unverified. Compositor layer promotion for the idle loop is unprofiled and is the open question if the field ever feels heavy. jest 187/187, tsc, eslint, next build all pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(learn): link QRL Ecosystem Index and Quantum Readiness Index Add a Further reading section to the /learn index with cards for qrlecosystem.com (community project directory) and qrindex.org (independent blockchain quantum-readiness index), plus inline references from the what-is-qrl-2 and why-post-quantum articles. Claude-Session: https://claude.ai/code/session_013DTfZj6whJh28h4UvjfH8d * refactor(learn): address code-review findings on external resources Extract a shared LearnCard used by both the article grid and the new Further reading grid, mark external cards with the standard ArrowTopRightOnSquareIcon plus sr-only new-tab text, derive the domain caption from the href instead of hand-maintaining it, make the section blurb count-independent, and soften the volatile qrindex.org tier claims to a neutral description. Claude-Session: https://claude.ai/code/session_013DTfZj6whJh28h4UvjfH8d
New /learn article on validity proofs, why the QRL 2.0 virtual machine verifies hash-based STARKs, the QuantaStark measurements on local 64-byte networks and the layer 2 arithmetic derived from them; matching FAQ entry; wider basics blurb. Review findings applied in the second commit. https://claude.ai/code/session_011eAxRYrxF7ruiQDme9tYHx
Adds /staking-calculator, an estimator that runs the qrysm base-reward formula (sqrt(total stake) issuance, Altair duty weights, integer square root) against live validator stats and circulating supply. Inputs: own validators, network validators, uptime, network participation, priority fee, supply, price. Outputs gross/net/effective APR, real yield after issuance dilution, duty breakdown, payout schedule, and the dilution curve against network size. Real yield is share-of-supply growth, so it reads 0% at a 100% staking ratio by construction; the page explains that and also reports the operator's share of new issuance and of the 105M-cap emission pool (QIP-016 policy, not enforced by the testnet issuance formula yet). Wires the page into the sidebar, footer, and sitemap. Unit tests pin the consensus constants, the published ROI band, and the dilution cases. Claude-Session: https://claude.ai/code/session_01DVxL6ZPjibv7uH2tj5nvsK
Preserve exact transaction amounts while keeping list columns readable.
feat(ui): add horizontal navigation and explorer preferences
fix(ui): expand phone navigation below the header
…culator) into the 64-byte port
QRL Testnet v3 in the explorer network menu now opens v3.zondscan.com instead of showing a disabled Upcoming badge. The network catalog carries an href per network; the build's own network is selected with NEXT_PUBLIC_EXPLORER_NETWORK (default testnet-v2) and marked active, other live networks render as links in the desktop menu and the phone navigation, and their badge reads Devnet in all four locales. Claude-Session: https://claude.ai/code/session_01FNiKphhAvq1TjaQ1ESiEBP
go-qrl reports a revert without return data as JSON-RPC error -32000 "execution reverted" and only uses code 3 when revert data is present. Contract type detection accepted code 3 alone, so every contract without ERC-165 (each plain ERC-20) turned the supportsInterface probe into a retryable error, the block's companion processing failed, and the syncer retried that block forever. Found on the 64-byte devnet at block 704. The classifier now accepts both forms; state and transport failures stay retryable. Tests cover the classifier table and the detection fall-through. Claude-Session: https://claude.ai/code/session_01FNiKphhAvq1TjaQ1ESiEBP
The three-segment QIP-55 fingerprint wrapped the home page's latest transactions onto two rows. Compact lists go back to the two-segment form; detail pages keep the fingerprint through AddressFingerprint. Claude-Session: https://claude.ai/code/session_01FNiKphhAvq1TjaQ1ESiEBP
…network The header subtitle, the gas price tooltip and the pending transaction page's chain id were fixed to QRL Testnet v2 (0x539). They now follow NEXT_PUBLIC_EXPLORER_NETWORK, so the v3 explorer reads QRL Testnet v3 and reports chain 0x301825. Claude-Session: https://claude.ai/code/session_01FNiKphhAvq1TjaQ1ESiEBP
QRL Testnet v3 in the explorer network menu now opens v3.zondscan.com instead of showing a disabled Upcoming badge. The network catalog carries an href per network; the build's own network is selected with NEXT_PUBLIC_EXPLORER_NETWORK (default testnet-v2) and marked active, other live networks render as links in the desktop menu and the phone navigation, and their badge reads Devnet in all four locales. Claude-Session: https://claude.ai/code/session_01FNiKphhAvq1TjaQ1ESiEBP
fix(ui): align explorer labels with the selected network
feat(explorer): release QIP-55 as the default testnet
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Validation
Release notes
This PR prepares source for the coordinated v3 cutover. Deployment must retain the v3 database and the existing v3 explorer alias, configure the canonical explorer origin for signer-bound challenges, and qualify the faucet against the selected chain. The current deployed v3 archive has no Git metadata and differs from this candidate in receipt handling and dependency files, so production source equivalence is not claimed.
The production browser suite passed without the transient router-initialization error observed under the development server. Live deployment and v2 retirement are coordinated separately.