Skip to content

Indexing & Verification

How an indexer discovers, orders and validates Sigil records.

Proposed

An indexer turns chain history into Saelis state. It is where the protocol's rules are actually executed, so its design determines whether two parties can independently arrive at the same answer.

Pipeline

A conforming indexer is a deterministic pipeline:

StageInputOutput
SourceElysium RPCBlocks with full transactions and receipts
ExtractTransactionsCandidate payloads, according to the transport
DecodePayloadsParsed envelopes, or rejection reasons
OrderEnvelopesEnvelopes in canonical order
ValidateOrdered envelopesOutcomes under the envelope and standard rules
ApplyValid operationsSigils, references, assemblies and standard state
ServeStateAn API for applications

Every stage except Source and Serve must be a pure function of its input. Given the same blocks, the output must be identical.

Scanning blocks

The following processes a block range with viem. It is real, runnable code for the Source and Extract stages under the calldata candidate transport.

TypeScript
import type { Block, Transaction } from 'viem'
import { publicClient } from './lib/client'
import { parseSigil } from './parse-sigil'

export async function scanRange(from: bigint, to: bigint) {
  const candidates = []

  for (let number = from; number <= to; number++) {
    const block = await publicClient.getBlock({ blockNumber: number, includeTransactions: true })

    for (const tx of block.transactions as Transaction[]) {
      const envelope = parseSigil(tx.input)
      if (!envelope) continue

      const receipt = await publicClient.getTransactionReceipt({ hash: tx.hash })
      if (receipt.status !== 'success') continue

      candidates.push({
        id: tx.hash,
        creator: tx.from,
        block: block.number,
        index: receipt.transactionIndex,
        envelope,
      })
    }
  }

  return candidates
}

At 100 ms per block, Elysium produces about 864,000 blocks per day. Production indexers should request blocks concurrently in bounded batches, use a dedicated archive endpoint and persist progress so that scanning can resume.

Canonical ordering

Candidates are sorted by (block, index) and, if multiple Sigils per transaction are permitted, by their intra-transaction index. Validation then proceeds strictly in that order, because the validity of an operation can depend on every operation before it.

Validation

Validation is the Saelis-specific core and the least settled part of the design. The shape is fixed, even though the rules are not:

validate.ts

Draft Example — Not Finalized

TypeScript
// Pseudocode. The rules referenced here are drafts.
for (const candidate of sortByPosition(candidates)) {
  const envelopeResult = validateEnvelope(candidate.envelope)
  if (!envelopeResult.ok) {
    record(candidate, envelopeResult.reason)
    continue
  }

  const standard = standards.get(candidate.envelope.std ?? '')
  if (!standard) {
    record(candidate, 'valid:uninterpreted')
    continue
  }

  const outcome = standard.apply(state, candidate)
  record(candidate, outcome)
}

Finality

Elysium's sequencer confirms transactions quickly, while settlement to HyperEVM happens later. Before a block is final it can, in principle, be reorganized.

The Elysium testnet RPC supports the standard safe and finalized block tags, which lag behind latest:

TypeScript
import { publicClient } from './lib/client'

const [latest, finalized] = await Promise.all([
  publicClient.getBlock({ blockTag: 'latest' }),
  publicClient.getBlock({ blockTag: 'finalized' }),
])

console.log(latest.number - finalized.number) // blocks not yet final

An indexer can either process only finalized blocks, accepting a delay, or process latest blocks and roll back state when a reorganization is detected by a change in a previously seen block hash. Many indexers do both, exposing a provisional and a final view.

Verification

Indexer output is derived data. Users and applications should be able to verify it:

  • Spot checks. Any Sigil returned by an indexer can be checked by fetching its transaction directly and re-parsing it, as shown in Reading Sigils.
  • State commitments. An indexer can publish a hash of its state at a block height. Two indexers that agree on the hash agree on the entire state.
  • Test vectors. Conformance is demonstrated by reproducing the published test vectors for each standard exactly.