Add shared LNURL types, indexing helpers, and warnings.
Introduce lnurl as a first-class mint type with probe/announcement fields and shared helpers the API and web can both rely on. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+112
-5
@@ -18,7 +18,7 @@ export type MintStatus = 'online' | 'degraded' | 'offline' | 'unknown' | 'announ
|
||||
* has to be able to hold a value it does not have a page for. Compare against
|
||||
* `ANNOUNCEMENT_KINDS` rather than switching exhaustively on this.
|
||||
*/
|
||||
export type MintType = 'cashu' | 'fedimint' | (string & {});
|
||||
export type MintType = 'cashu' | 'fedimint' | 'lnurl' | (string & {});
|
||||
|
||||
/** One item of `GET /api/mints`. */
|
||||
export interface MintListItem {
|
||||
@@ -26,7 +26,7 @@ export interface MintListItem {
|
||||
host: string;
|
||||
name: string | null;
|
||||
icon: string | null;
|
||||
/** 'cashu' or 'fedimint'. Rows written before the column existed read as 'cashu'. */
|
||||
/** 'cashu', 'fedimint' or 'lnurl'. Rows written before the column existed read as 'cashu'. */
|
||||
type: MintType;
|
||||
status: MintStatus;
|
||||
last_online: number | null;
|
||||
@@ -48,10 +48,14 @@ export type RatingDistribution = Record<'1' | '2' | '3' | '4' | '5', number>;
|
||||
/**
|
||||
* `GET /api/mints/:host`.
|
||||
*
|
||||
* The Fedimint keys are optional and absent on a Cashu mint, which is what keeps the
|
||||
* Cashu payload unchanged. Read them through `FedimintDetail` after checking `type`.
|
||||
* The Fedimint and LNURL keys are optional and absent on a Cashu mint, which is what
|
||||
* keeps the Cashu payload unchanged. Read them through `FedimintDetail` or
|
||||
* `LnurlDetail` after checking `type`.
|
||||
*/
|
||||
export interface MintDetail extends MintListItem, Partial<FedimintFields> {
|
||||
export interface MintDetail
|
||||
extends MintListItem,
|
||||
Partial<FedimintFields>,
|
||||
Partial<LnurlFields> {
|
||||
description: string | null;
|
||||
pubkey: string | null;
|
||||
info: MintInfo | null;
|
||||
@@ -100,6 +104,97 @@ export interface FedimintFields {
|
||||
/** `GET /api/mints/:host` for a Fedimint federation: the detail plus its own fields. */
|
||||
export type FedimintDetail = MintDetail & FedimintFields;
|
||||
|
||||
/**
|
||||
* What `ecosystem_json` holds for an LNURL row.
|
||||
*
|
||||
* Two sources, never mixed: `features` is what the operator *announced* on Nostr, and
|
||||
* everything under "probed" is what the mint's own endpoints said when they were last
|
||||
* reached. The kind document makes that separation normative — a prober never rewrites
|
||||
* an operator's capability list — and keeping the two in different fields is what makes
|
||||
* it impossible to do by accident.
|
||||
*
|
||||
* Every millisatoshi field is stored exactly as the wire gave it. Conversion to sats
|
||||
* happens once, at render, through `msatToSat`.
|
||||
*/
|
||||
export interface LnurlFields {
|
||||
/** The `d` tag: the mint pubkey when it has one, else the normalized host. */
|
||||
lnurl_id: string;
|
||||
/** The https base URL. `host` is the routing slug derived from it. */
|
||||
base_url: string;
|
||||
/** The `features` tag, split. The operator's claim, never edited by a probe. */
|
||||
features: string[];
|
||||
/**
|
||||
* What the last probe actually observed this mint serving.
|
||||
*
|
||||
* Kept apart from `features` above rather than merged into it, because the two are
|
||||
* different kinds of statement — a claim and an observation — and the kind document
|
||||
* makes it normative that a prober never rewrites the first. The page renders their
|
||||
* union through `displayFeatures`; the publisher signs only this one.
|
||||
*/
|
||||
observed_features: string[];
|
||||
/** The `n` tag, normalized — `bitcoin` and `mainnet` both arrive as `mainnet`. */
|
||||
network: string | null;
|
||||
/** `created_at` of the newest announcement seen. null for a seeded row. */
|
||||
announced_at: number | null;
|
||||
/** Who published that announcement. The `a` tag of a review points back at them. */
|
||||
announcer_pubkey: string | null;
|
||||
|
||||
/* ---- probed: from the mint's own endpoints ---- */
|
||||
|
||||
/**
|
||||
* The funding node's identity key, from the mint advertisement.
|
||||
*
|
||||
* Sticky once learned: a probe that finds none does not clear it, because a node
|
||||
* being unreachable for one request is not a change of identity. Its *absence from
|
||||
* the latest probe* is recorded separately, in `funding_available`.
|
||||
*/
|
||||
mint_pubkey: string | null;
|
||||
/**
|
||||
* Whether the last probe found a reachable funding source.
|
||||
*
|
||||
* null before anything has probed. false is the degraded-but-online state: the mint
|
||||
* answers, its limits are real, and `rotate`/`split`/`merge` still work, but nothing
|
||||
* moves in or out over Lightning. See NOTES-LNURL.md for why this one bit cannot
|
||||
* distinguish "never configured" from "unreachable right now", and why that is fine.
|
||||
*/
|
||||
funding_available: boolean | null;
|
||||
/** Which endpoint answered: the withdraw advertisement, or the payRequest fallback. */
|
||||
probe_endpoint: string | null;
|
||||
/**
|
||||
* Set when the host answered but with something that is not a mint advertisement.
|
||||
*
|
||||
* A distinct outcome from both online and offline, and it has to be: these endpoints
|
||||
* return HTTP 200 for their errors, so "responding" and "working" are different
|
||||
* questions. Carries the short reason, for the banner.
|
||||
*/
|
||||
invalid_reason: string | null;
|
||||
|
||||
/** Withdraw bounds, millisatoshi: what a note's value can actually be. */
|
||||
min_withdrawable_msat: number | null;
|
||||
max_withdrawable_msat: number | null;
|
||||
/** Pay bounds, millisatoshi: what a minter can actually send. Not the same numbers. */
|
||||
min_sendable_msat: number | null;
|
||||
max_sendable_msat: number | null;
|
||||
/** `Mint fees: <base>,<ppm>` from the payRequest metadata. Absent means fee-free. */
|
||||
fee_base_msat: number | null;
|
||||
fee_ppm: number | null;
|
||||
|
||||
/** The LUD-16 address, derived from `payLink` rather than the echoed identifier. */
|
||||
lightning_address: string | null;
|
||||
/** The Tor address from the one-pager, when one is advertised. */
|
||||
onion_url: string | null;
|
||||
|
||||
/** The funding node, as the mint chooses to describe it. All optional, all msat. */
|
||||
node_alias: string | null;
|
||||
node_uri: string | null;
|
||||
node_capacity_msat: number | null;
|
||||
node_channels: number | null;
|
||||
node_peers: number | null;
|
||||
}
|
||||
|
||||
/** `GET /api/mints/:host` for an LNURL mint: the detail plus its own fields. */
|
||||
export type LnurlDetail = MintDetail & LnurlFields;
|
||||
|
||||
/**
|
||||
* `GET /api/stats`.
|
||||
*
|
||||
@@ -127,6 +222,18 @@ export interface Stats {
|
||||
fedimint_announced: number;
|
||||
/** Reviews of federations (`k` = 38173), included in `reviews_total`. */
|
||||
fedimint_reviews: number;
|
||||
lnurl_total: number;
|
||||
/** LNURL mints a probe reached. Online includes the degraded-funding ones. */
|
||||
lnurl_online: number;
|
||||
lnurl_offline: number;
|
||||
/**
|
||||
* Online mints whose funding source was unreachable at the last probe: up and
|
||||
* serving, but nothing moves in or out over Lightning. A subset of `lnurl_online`,
|
||||
* never added to it.
|
||||
*/
|
||||
lnurl_degraded_funding: number;
|
||||
/** Reviews of LNURL mints (`k` = 38174), included in `reviews_total`. */
|
||||
lnurl_reviews: number;
|
||||
}
|
||||
|
||||
/** `GET /api/health`. */
|
||||
|
||||
Reference in New Issue
Block a user