Two ways to index a Substrate chain
Dynamic metadata. Connect to a node, fetch its metadata at run time, decode blocks by name: "give me the Transfer event of pallet Balances". Flexible — survives runtime upgrades without recompiling — and unchecked: a name that does not exist simply never matches.
Generated types. Run subxt codegen against the metadata to produce Rust types for every pallet, call, event and storage item. Rigid — recompile per upgrade — and checked: a name that does not exist is a compile error.
What EmpooScan chose and what it cost
EmpooScan's indexer (~4,900 lines of Rust on subxt) uses dynamic metadata. The September 2026 audit found ten (pallet, event) pairs it listened for that do not exist in the runtime:
| Indexer expected | Runtime has |
|---|---|
MevProtection.CommitSubmitted / Revealed | TxCommitted / TxRevealed |
MevProtection.ValidatorSlashed | — |
BlobStorage.BlobExpired | BlobPruned |
AiServing.InferenceFulfilled / InferenceFailed | InferenceCompleted / PruebaInvalida |
Uniques.CollectionCreated / Minted | Created / Issued (the runtime uses Parity's pallet, not the local crate) |
IdentitySsi.DidUpdated | — |
None fired, ever. With generated types, all ten would have been compile errors.
Three more findings
- 58 fields decoded with
{:?}. The indexer formatted SCALE values with Rust's debug formatter in 46 places; what sits in the database isValue { value: Composite(…) }text, not addresses. The correct decoder existed a few functions away. on_initialize/on_finalizeevents invisible. The listener lived inside the per-extrinsic loop; state-rent charges and blob pruning never reached the database.- No
Balances.Transferhandler; EVM transactions attributed toSystem/Root.
The repair
- Use the existing address decoder everywhere a field is stored.
- Generate types from metadata per runtime version, so names are checked at compile time; keep a dynamic fallback only for pallets the indexer deliberately treats generically.
- Move event listening outside the extrinsic loop.
- Add
Balances.TransferandEthereum.transacthandlers. - Put the database somewhere that does not go silent on a quota.
For anyone indexing EmpoorioChain
The metadata changes in every runtime upgrade (eight in ten days in September). Whichever approach you take, pin it to a spec version and re-check after each upgrade. The exchange-integration guide's advice — run your own indexer for deposits — stands until EmpooScan's repair lands.
The partnership this replaces
There is no data partner. There is one explorer, audited by its own ecosystem, with a written repair list.
Based on the EmpooScan audit (2026-09-02).


