TL;DR¶
WAVE is a Radiant naming primitive (alice.rxd → wallet address).
pyrxd has ~80% of the building blocks already; full support is gated on
a real consumer surfacing. Until then, do not invest design effort.
Update 2026-09-24 (#728). The
WaveAttrsshape this record asks for was added in #102, and it put the QUALIFIED name inattrs.name("alice.rxd") with the top-levelnameempty. Photonic puts the bare label there (attrs.name = "alice",attrs.domain = "rxd") and the qualified name at the top level (createWaveNameMetadata,packages/lib/src/wave.ts), and RXinDexer’svalidate_wave_namerefuses the.— so by the indexer’s source no claimbuild_wave_metadatabuilt through 0.24.0 from a qualified name was registered. (Given a bare label,"alice", it wroteattrs.name = "alice", which does registeralice.rxd; and hand-built CBOR of the right shape could always be revealed throughprepare_wave_reveal.) This is the silent-divergence risk described below, arriving by a different route.The “byte-equivalence test against Photonic-emitted mainnet tokens” named under Why deferred exists only in part, and the difference matters.
tests/test_wave_claim_registers_with_the_indexer.pyshows that pyrxd writes the FIELD SET and values ofcreateWaveNameMetadata, and that its bytes equal those of a mainnet claim the public indexer resolves. That claim was not encoded by Photonic: it is canonical CBOR with nodesc, while Photonic’scbor-xwrites 16-bit map headers in object order and its register page always setsdesc. No Photonic-emitted claim has been compared byte for byte. The inspector classifier andWaveResolveralso exist now, and every claim pyrxd writes is held to one rule (src/pyrxd/glyph/wave_rules.py). The sections below are the record as written on 2026-05-13.Update 2026-09-25 (registration fee). Item 4 below calls Photonic’s pricing tiers “advisory”. That is true of consensus and of RXinDexer’s claim path, which registers a name whatever it pays, but it is not what the protocol says: WAVE-Protocol’s
ANNOUNCEMENT.md(c05b8e7a, lines 51-52) makes registration “a one-time, length-based fee” and renewal a payment of “the name’s registration price to the protocol treasury”, which RXinDexer does check (_maybe_process_renewal,wave_index.py:581-642atca8a6a4e). The maintainer’s decision: pyrxd pays the fee by default, as Photonic’s register page does, to the treasury Photonic and RXinDexer both name. The reveal builders return the fee output for the caller to add, funded from a wallet input at reveal time as Photonic funds it, andpyrxd glyph mint-nftpays it if an indexer (one server’swave.check_availableanswer) reports no confirmed registration of the name;pay_registration_fee=Falseis the only way out. See the “WAVE registration fee” paragraph in §8.1 ofdocs/reference/glyph-token-protocol-spec.mdandsrc/pyrxd/glyph/wave_rules.py.
What WAVE actually is (verified 2026-05-13)¶
Two specs exist under the “WAVE” name:
Shape A (whitepaper) |
Shape B (live) |
|
|---|---|---|
Source |
|
|
Tx shape |
38 outputs (1 claim NFT + 37 prefix-tree branches) |
2 outputs (vanilla mutable NFT — 63-byte singleton + 174-byte MUT contract) |
Protocol id |
n/a |
CBOR |
Name storage |
implicit in tree position |
CBOR |
Uniqueness |
on-chain (prefix-tree covenant) |
off-chain (RXinDexer picks first-confirmed) |
Front-running |
possible (no consensus rule) |
mitigated (indexer skips mempool WAVE claims) |
Mainnet status |
no contracts deployed |
live since block 425046, mainnet genesis |
Photonic Wallet emits shape B. Indexers (RXinDexer) recognize shape B. Shape A is paper-only. pyrxd should treat shape B as the protocol of record; implementing shape A would produce a token nobody can resolve.
What pyrxd already has¶
GlyphProtocol.WAVE = 11(enum value).GlyphBuilder.prepare_wave_reveal()— delegates toprepare_mutable_reveal(), which is shape-B-compatible at the script level.Test fixtures in
tests/test_mut_container_wave_builders.py.
What pyrxd does NOT have¶
A
WaveAttrsCBOR shape that matches Photonic’sattrs.{name, domain, target, target_type}. Currentprepare_wave_revealonly looks at the top-level CBORnamefield, not atattrs.name. This is the silent-divergence risk: a pyrxd-minted WAVE token would have a name that RXinDexer cannot find, because the indexer reads fromattrs.nameperRXinDexer/electrumx/server/wave_index.py.Inspector classifier for
p: [2, 5, 11].GlyphInspector.find_glyphshas no WAVE branch — WAVE tokens are currently classified as plain mutable NFTs.Resolver client.
name → addresslookup requires RXinDexer’s REST endpoint (/wave/resolve/{name}). pyrxd has no HTTP indexer client; only the ElectrumX socket client.Name validator. Allowed character set is
^[a-z0-9]([a-z0-9-]{1,61}[a-z0-9])?$(Photonic), 3–63 chars. No reserved-name policy on chain; pricing tiers exist in Photonic source but are advisory.
Why deferred¶
pyrxd ships SDK primitives, not finished apps. The gating signal for investing in WAVE is one concrete pyrxd consumer that needs WAVE:
Resolution caller: a wallet or pay-link tool that has to look up
alice.rxd→ address. Requires (3) above plus an RXinDexer client.Mint caller: a tool that lets users claim names from pyrxd. Requires (1) and (2), plus a
WaveAttrsbyte-equivalence test against Photonic-emitted mainnet tokens.
Neither consumer exists today. Implementing WAVE pre-emptively risks:
Wire-format drift with Photonic (the M1 covenant-rejection bug class — see
dmint-v1-mint-shape-mismatch.mdanddmint-v1-mint-scriptsig-divergence.md).Locking pyrxd to one indexer before alternatives exist.
Building shape A (the whitepaper) by mistake, which nobody else implements.
When to revisit¶
Trigger conditions, in priority order:
A downstream pyrxd consumer (
radiant-pay,radiant-ledger-app, or similar) opens an issue asking forresolve_wave_name(name) → address.The user (or another contributor) needs to mint a name from a pyrxd-based tool.
REP-3011 is finalised and converges shape A and shape B at the script level — would force a redesign anyway.
If (1) or (2): the right next step is /workflows:brainstorm against
the actual consumer’s API needs, then a plan doc, then implementation.
Plan to add (1) WaveAttrs CBOR shape, (2) inspector classifier, and
(3) WaveResolver HTTP client as three small PRs, in that order.
If (3): redesign decision — likely a major-version bump.
References¶
Live Photonic source:
github.com/Radiant-Core/Photonic-Wallet/blob/main/packages/lib/src/wave.tsRXinDexer wave index:
github.com/Radiant-Core/RXinDexer/blob/main/electrumx/server/wave_index.pyWAVE whitepaper (shape A):
github.com/RadiantBlockchain-Community/WAVEMainnet WAVE genesis: txid
115e62d96f44402c448bf76d4ca403188733b902ab0b7703d9f36333178afda4vout 0, block 425046pyrxd existing wave builder:
src/pyrxd/glyph/builder.py(search forprepare_wave_reveal)