An event is the chain's account of what happened; an extrinsic is the request
that caused it, and carries what no event does — who signed, what they called,
the nonce and tip, and whether it worked. A history built from events alone
omits every failed attempt and never names a submitter.
The same oracle does both. The metadata's extrinsic type carries four
parameters and the registry names all of them, including
`qp_dilithium_crypto::types::DilithiumSignatureScheme` — the part that looked
like it would need hand-written post-quantum knowledge and does not. The chain
describes its own signature scheme, so `decode_extrinsic` reads a Quantus
transaction without a line of code that knows Quantus exists.
Two facts about this chain's extrinsics, both now in CLAUDE.md. The first byte
is not the version: the top two bits are a type tag, and mainnet carries `0x84`
(signed, v4) and `0x05` (bare, v5) in the same block while the metadata declares
version 4 — check the byte against the metadata and you reject every timestamp
inherent on the chain. And a signature is 5.3 KiB, two orders of magnitude
larger than the call it authorises; it is decoded to find where the call begins
and then discarded, keeping only the scheme's name and the byte count.
Both halves of a block index in one pass, off calls already being made. Whether
a dispatch succeeded comes from the `ExtrinsicSuccess`/`ExtrinsicFailed` events
in that same decoded list — read, used, not stored.
The block page grows a table below the facts: call, signer, arguments, and the
signature's size. The account page merges extrinsics into the history it already
had, ordered by block with the request above the results it caused. `source` is
1 for an extrinsic and 0 for an event so that every part of the sort key
descends, which lets the paging cursor be a plain row comparison rather than a
mixed-direction one Postgres cannot express.
Nested calls are why this had to be generic. A `Utility::batch_all` carries whole
calls in its arguments, so a transfer's recipient can be three levels down — and
because the decoder collects account ids wherever they appear, that batched
transfer shows on the recipient's page tagged *received* even though they signed
nothing. Verified on mainnet 12,616: the batch renders both its transfers with
destinations and values, above the two `Balances::Transfer` events they produced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jp6a8EDar9ueEhAxzep4V5