qtc.omft.near is minted only against QTC locked on Quantus by the bridge, so its supply can never legitimately exceed issuance outside the vesting pot — 221,838 QTC today. The listing poll now reads that ceiling every five minutes through one helper, issuance_outside_pot, using the same storage reads and the same subtraction the network page uses, rescales it to the NEAR token's decimals (rounding down, so the check can only get stricter), and compares it with ft_total_supply. A breach is a logged warning and, on the market page, a sentence before any figure: unbacked, or one of the two misread. Either read failing gives no verdict rather than a false all-clear. The panel also shows the ceiling itself as "most there could be". Checked live: QTC at 0 against 221,837.97 with no breach, and the breach path forced by pointing the dev config at wNEAR (35.4M on NEAR), which logged and rendered the warning as intended. In place before QTC's announced NEAR Intents launch on 2026-10-05. Closes #36 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015pkwBsR54NRmZyU4QVhvfj
293 lines
12 KiB
TypeScript
293 lines
12 KiB
TypeScript
/**
|
||
* The chain's token as NEAR Intents settles it — or the evidence that nothing
|
||
* has settled yet.
|
||
*
|
||
* NEAR Intents has no pool and stores no price. A price exists only as the
|
||
* ratio inside a trade that executed, so that is the only figure here: no
|
||
* quote, no listing price, and no dollar value until a stablecoin pairing
|
||
* exists to derive one from. blackbeard/observer#27.
|
||
*
|
||
* The empty state is the point of the panel as much as the price is. Before a
|
||
* single swap settles, a rate for this token can still be shown elsewhere — a
|
||
* wallet's swap screen did exactly that from a constant — and the honest answer
|
||
* to "what is it worth" is the supply on NEAR, the listing, and a count of zero,
|
||
* printed with the range of blocks that count covers.
|
||
*
|
||
* A chain that tracks nothing on NEAR answers 404 `no_intents`, and the panel
|
||
* renders nothing at all.
|
||
*/
|
||
|
||
import { useEffect, useState } from 'react'
|
||
import { Link } from 'react-router-dom'
|
||
|
||
import type { IntentsPrice } from '../api/generated/IntentsPrice'
|
||
import { RequestFailed, fetchIntentsPrice } from '../api/rest'
|
||
import { ago, height as fmtHeight, price as fmtPrice, tokens } from '../lib/format'
|
||
import { useObserverInstance } from '../lib/observer-context'
|
||
import { href } from '../lib/routes'
|
||
import { tokenLabel } from '../lib/tokens'
|
||
import { Field } from './Field'
|
||
|
||
export function IntentsPanel({
|
||
chain,
|
||
decimals,
|
||
symbol,
|
||
linkMarket = false,
|
||
tiles = 4,
|
||
table = false,
|
||
}: {
|
||
chain: string
|
||
decimals: number
|
||
symbol: string
|
||
/** Point at the full market page. Set where this panel is a summary of it. */
|
||
linkMarket?: boolean
|
||
/** Counter tokens given a tile, most traded first. The rest are a table
|
||
* (`table`) or a count. A busy token trades against seventy. */
|
||
tiles?: number
|
||
/** List every priced counter below the tiles. The market page's job. */
|
||
table?: boolean
|
||
}) {
|
||
const [data, setData] = useState<IntentsPrice | null>(null)
|
||
const [absent, setAbsent] = useState(false)
|
||
const [error, setError] = useState<string | null>(null)
|
||
// Bumped by every pushed swap. The figures are re-read rather than adjusted
|
||
// here: a VWAP over the latest N trades loses its oldest trade as it gains a
|
||
// new one, and arithmetic on a copy drifts from the server's answer while
|
||
// looking authoritative — the reason the wormhole stage re-reads its stock.
|
||
const [settled, setSettled] = useState(0)
|
||
const observer = useObserverInstance()
|
||
|
||
useEffect(() => observer.onIntentsSwap(() => setSettled((n) => n + 1)), [observer])
|
||
|
||
useEffect(() => {
|
||
const controller = new AbortController()
|
||
setAbsent(false)
|
||
setError(null)
|
||
fetchIntentsPrice(chain, controller.signal)
|
||
.then(setData)
|
||
.catch((e: unknown) => {
|
||
if (controller.signal.aborted) return
|
||
if (e instanceof RequestFailed && e.code === 'no_intents') setAbsent(true)
|
||
else setError(e instanceof RequestFailed ? e.message : 'Could not reach the observer.')
|
||
})
|
||
return () => controller.abort()
|
||
}, [chain, settled])
|
||
|
||
// A different chain's figures must never stand in for this one's while it loads.
|
||
useEffect(() => setData(null), [chain])
|
||
|
||
if (absent) return null
|
||
|
||
const head = (eyebrow: string) => (
|
||
<div className="panel-head">
|
||
<h2 className="panel-title">
|
||
{linkMarket ? <Link to={href({ chain, market: 'near-intents' })}>Market</Link> : 'Market'}
|
||
</h2>
|
||
<span className="eyebrow">{eyebrow}</span>
|
||
</div>
|
||
)
|
||
|
||
if (error)
|
||
return (
|
||
<section className="panel" style={{ marginBottom: 26 }}>
|
||
{head('NEAR Intents')}
|
||
<p className="empty">{error}</p>
|
||
</section>
|
||
)
|
||
if (!data)
|
||
return (
|
||
<section className="panel" style={{ marginBottom: 26 }}>
|
||
{head('NEAR Intents')}
|
||
<p className="empty">Reading NEAR…</p>
|
||
</section>
|
||
)
|
||
|
||
const assetDecimals = data.asset_decimals ?? decimals
|
||
// The asset as NEAR names it. Normally the chain's own token; falling back to
|
||
// the chain's symbol only until the metadata has been read.
|
||
const asset = data.asset_symbol ?? symbol
|
||
const amount = (raw: string | null | undefined) =>
|
||
raw ? `${tokens(raw, assetDecimals)} ${asset}` : '—'
|
||
const listing = data.listing
|
||
const range =
|
||
data.indexed_from !== null && data.indexed_to !== null
|
||
? `NEAR blocks ${fmtHeight(data.indexed_from)}–${fmtHeight(data.indexed_to)}`
|
||
: 'nothing read yet'
|
||
const nothingBridged = listing?.total_supply === '0'
|
||
// Only a counter with known decimals has a price. NFTs and multi-tokens have
|
||
// none, and a "—" in a grid of prices reads as a price nobody could read
|
||
// rather than as what it is: a swap for something that is not a currency.
|
||
const priced = data.counters
|
||
.filter((c) => c.decimals !== null && c.vwap !== null)
|
||
.sort((a, b) => b.trades - a.trades)
|
||
const unpriced = data.counters.filter((c) => !priced.includes(c))
|
||
const unpricedTrades = unpriced.reduce((n, c) => n + c.trades, 0)
|
||
const shown = priced.slice(0, tiles)
|
||
const rest = priced.slice(tiles)
|
||
const thin = shown.some((c) => c.trades < 5)
|
||
|
||
const listingFields = (
|
||
<>
|
||
<Field
|
||
label="Bridged to NEAR"
|
||
value={amount(listing?.total_supply)}
|
||
note="the token contract's total supply"
|
||
/>
|
||
<Field
|
||
label="Inside NEAR Intents"
|
||
value={amount(listing?.in_intents)}
|
||
note="what can be traded at all"
|
||
/>
|
||
<Field
|
||
label="Listed on 1Click"
|
||
value={
|
||
listing?.oneclick_listed === undefined || listing.oneclick_listed === null
|
||
? '—'
|
||
: listing.oneclick_listed
|
||
? 'yes'
|
||
: 'no'
|
||
}
|
||
note="a listing, not a trade"
|
||
/>
|
||
<Field
|
||
label="Most there could be"
|
||
value={amount(listing?.backing_ceiling)}
|
||
note={`all the ${asset} outside the vesting pot`}
|
||
/>
|
||
</>
|
||
)
|
||
|
||
// The bridge mints only against QTC locked on the chain, so supply on NEAR
|
||
// above everything outside the vesting pot is unbacked or a misread — said
|
||
// in words, first, in every state of the panel (blackbeard/observer#36).
|
||
const breach = listing?.exceeds_backing === true && (
|
||
<p className="panel-note" role="alert">
|
||
<strong>
|
||
{asset} on NEAR exceeds all the {asset} that exists outside the vesting pot.
|
||
</strong>{' '}
|
||
{amount(listing?.total_supply)} is bridged against a most of{' '}
|
||
{amount(listing?.backing_ceiling)}. The bridge mints only against {asset} locked on the chain,
|
||
so either some of it is unbacked or one of the two figures was misread. Prices below are what
|
||
settled, but read them with that in mind.
|
||
</p>
|
||
)
|
||
|
||
if (data.counters.length === 0)
|
||
return (
|
||
<section className="panel" style={{ marginBottom: 26 }}>
|
||
{head(`NEAR Intents · ${range}`)}
|
||
{breach}
|
||
<div className="node-grid">
|
||
{listingFields}
|
||
<Field label="Settled swaps" value="0" note="in the NEAR blocks read" />
|
||
</div>
|
||
<p className="panel-note">
|
||
<strong>There is no {asset} price yet.</strong> NEAR Intents has no pool and stores no
|
||
price: a price exists only as the ratio inside a trade that has settled, and none has.{' '}
|
||
{nothingBridged &&
|
||
`No ${asset} has been bridged to NEAR, so none can have traded there. `}
|
||
A rate for {asset} shown anywhere before a trade appears here is an estimate or an offer,
|
||
not something anyone paid.
|
||
</p>
|
||
<p className="panel-note">What is read: {data.reads}</p>
|
||
</section>
|
||
)
|
||
|
||
return (
|
||
<section className="panel" style={{ marginBottom: 26 }}>
|
||
{head(`NEAR Intents · ${range}`)}
|
||
{breach}
|
||
<div className="node-grid">
|
||
{shown.map((c) => {
|
||
const unit = tokenLabel(c.counter_token, c.symbol)
|
||
return (
|
||
<Field
|
||
key={c.counter_token}
|
||
label={`${asset} in ${unit}`}
|
||
value={`${fmtPrice(c.vwap)} ${c.symbol ?? ''}`.trim()}
|
||
note={`${fmtHeight(c.trades)} trade${c.trades === 1 ? '' : 's'}${
|
||
c.taker_trades === c.trades ? '' : `, ${fmtHeight(c.taker_trades)} by users`
|
||
} · last ${fmtPrice(c.last_price)}, ${ago(c.last_at)}`}
|
||
title={c.counter_token}
|
||
/>
|
||
)
|
||
})}
|
||
{listingFields}
|
||
</div>
|
||
{table && rest.length > 0 && (
|
||
// Folded, so the chart below the figures is not pushed under a
|
||
// seventy-row table; the summary says how much is inside.
|
||
<details className="panel-note">
|
||
<summary>
|
||
{fmtHeight(rest.length)} more counter token{rest.length === 1 ? '' : 's'}
|
||
</summary>
|
||
<div className="scroll-x">
|
||
<table className="board">
|
||
<caption className="visually-hidden">Every other priced counter token</caption>
|
||
<thead>
|
||
<tr>
|
||
<th scope="col" className="left">
|
||
{asset} in
|
||
</th>
|
||
<th scope="col">Volume-weighted</th>
|
||
<th scope="col">Last</th>
|
||
<th scope="col">Trades</th>
|
||
<th scope="col" className="left">
|
||
Last traded
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{rest.map((c) => (
|
||
<tr key={c.counter_token}>
|
||
<td className="left" title={c.counter_token}>
|
||
{tokenLabel(c.counter_token, c.symbol)}
|
||
</td>
|
||
<td className="numeral">{fmtPrice(c.vwap)}</td>
|
||
<td className="numeral">{fmtPrice(c.last_price)}</td>
|
||
<td className="numeral">{fmtHeight(c.trades)}</td>
|
||
<td className="left">{ago(c.last_at)}</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</details>
|
||
)}
|
||
{(!table && rest.length > 0) || unpriced.length > 0 ? (
|
||
<p className="panel-note">
|
||
{!table && rest.length > 0 && (
|
||
<>
|
||
{fmtHeight(rest.length)} more counter token{rest.length === 1 ? '' : 's'}
|
||
{linkMarket && (
|
||
<>
|
||
{' '}
|
||
— <Link to={href({ chain, market: 'near-intents' })}>on the market page</Link>
|
||
</>
|
||
)}
|
||
.{' '}
|
||
</>
|
||
)}
|
||
{unpriced.length > 0 &&
|
||
`${fmtHeight(unpricedTrades)} trade${unpricedTrades === 1 ? '' : 's'} against ${fmtHeight(unpriced.length)} token${unpriced.length === 1 ? '' : 's'} with no price — NFTs and multi-tokens, which have no decimals to divide by — are counted but not listed.`}
|
||
</p>
|
||
) : null}
|
||
<p className="panel-note">
|
||
Each figure is volume-weighted over the latest {fmtHeight(data.window_trades)} trades
|
||
against that token at most — the ratio of the summed amounts, so a dust trade at an odd
|
||
price moves it by its size — and is in that token, not in dollars.{' '}
|
||
{thin && (
|
||
<strong>
|
||
Some of these rest on fewer than five trades; a figure from one trade is one trade.{' '}
|
||
</strong>
|
||
)}
|
||
A trade is one user's side of a swap, priced from what they gave and what they got; a
|
||
solver's routing through other tokens is never counted as a trade of its own, and
|
||
solvers trading with each other are counted but name no side. Token names are each
|
||
token's own description of itself; the contract behind a figure is in its tooltip.
|
||
</p>
|
||
<p className="panel-note">What is read: {data.reads}</p>
|
||
</section>
|
||
)
|
||
}
|