Technical Architecture
The components, node roles, measured metrics and data structures, then how Solieum works in full: sequencing and admission, data availability, settlement, the state commitment and its versions, the trace a dispute is about, the one-step verifier and its classes, the unfinished binding of each step to a signed transaction, and the gaps that remain open.
Everything here is implemented and deployed on Solana devnet unless the text says otherwise. Where a figure appears, the thing that measured it is named. Where something is not built, it says so in the same sentence rather than in a footnote.
The life of a transaction
Nothing about a transaction is final until its root is, and the only thing that makes a root final is time passing without a successful challenge.
The components at a glance
The classic description of a rollup names four parts: an operator that orders and executes, a state commitment chain, a fraud proof system, and a data availability layer. Solieum has each of them, plus three more that a working system turned out to need. Every on-chain row is a program deployed on Solana devnet.
| Role | Solieum component | Runs | What it does |
| Rollup operator | Sequencer node, solieum-node run | Off chain | Admits and executes transactions, signs ordering receipts, seals a block every 2 seconds when there is work, publishes it and settles it |
| State commitment chain | Settlement program, solieum-scc | Solana | Holds each state root under bond, runs the challenge window, finalizes, and reclaims rent |
| Data availability layer | Data-availability program | Solana | Opens a batch with its commitment, takes its chunks, and seals only if the bytes fold to that commitment |
| Fraud proof system | Dispute game program | Solana | Bisects a disputed trace and verifies the final single step from Merkle witnesses |
| Forced inclusion | Inbox program | Solana | Queues a transaction under an on-chain deadline the sequencer must honour |
| Bridge | Bridge portal program | Solana | Holds the vault, takes deposits as inbox entries, pays withdrawals proven against final roots, and carries the guardian's air gap |
| Sequencer accountability | Sequencer bond program | Solana | Bonds the receipt-signing key and slashes it on a proven double assignment |
| Independent verification | solieum-node verify | Off chain | Re-derives the chain from Solana and challenges a divergence under bond |
What each program exposes
| Program | Instructions |
| Settlement | initialize, submit_root, open_challenge, resolve_challenge, finalize, claim_bond, close_finalized_root |
| Data availability | initialize, open_batch, post_chunk, seal_batch, abandon_batch, close_batch |
| Inbox | initialize, enqueue, mark_included, claim_fee, reclaim_fee |
| Dispute game | open_dispute, bisect, select, and one one_step_verify_* instruction per witnessed class |
| Bridge portal | initialize, deposit, deposit_token, refund_deposit, prove_withdrawal, finalize_withdrawal and their token forms, set_pause, initialize_guard, set_guardian, deny_root, reopen_withdrawal, initialize_limits, set_limits |
| Sequencer bond | initialize, register, top_up, request_unbond, withdraw, migrate, slash_equivocation |
These are the instructions in the current source. The programs deployed for gamma predate the newest of them: the bridge limits and the bond migration are built and not deployed.
Node roles
There is no validator set, and there will not be one: finality is Solana's. The off-chain roles are these.
| Role | What it is | What it is trusted with |
| Sequencer | The single node holding the chain's receipt key | Liveness and ordering, bounded by receipts, the bond and forced inclusion. Until section 7 lands, also the one party that could commit a forged step |
| Verifier | Anyone running solieum-node verify --watch against their own Solana providers | Nothing: it trusts Solana and its own readers, which must agree |
| RPC gateway | A small allowlist, bridge/gateway/server.mjs, in front of the node | It is the only public JSON-RPC surface. It forwards only the bridge's methods, answers the browser pre-flight the node does not, refuses batches, caps bodies at 16 KB and rate-limits per address. The node itself listens on loopback only |
| Explorer | Served read-only by the node | Nothing beyond the node it reads |
| Fast-exit provider | Anyone fronting withdrawals for a spread | It takes the window's risk; the user takes none |
Key performance metrics
Every figure here was measured or is a named constant, and none is a projection. The throughput row is absent on purpose: a rollup on Solana is bounded by what it costs to publish data, which section 2 derives rather than asserts.
| Metric | Value | Source |
| Block cadence | 2 s, and only when there is work | Node parameter |
| L2 block to root committed on Solana | median 5.5 s over 64 blocks, beta chain, 10 September 2026 | Timing harness on devnet; timing varies between runs on identical configuration, so treat it as a snapshot |
| Failed transactions in blocks | none: a failing transaction is refused at admission, free | ADR-0014 |
| Challenge window | 432,000 slots, 48 h at the 400 ms target | Settlement constant |
| Canonical withdrawal | window plus a 24 h air gap, about three days | Named constants; the devnet portal runs a 1 h delay |
| One-step verification, transfers | 19,216 CU system, 20,408 CU SPL token | Execution harness against the compiled program |
| One-step verification, token classes | 352,129 to 619,729 CU, within a 700,000 CU allowance | Execution harness |
| Batch compression | 53 percent of raw on modelled traffic, 79 percent on recorded mainnet transactions | Repository codec |
| Data-availability cost | 280 lamports per L2 transaction on a 4,000-transfer batch | Derived from the compression figure |
| User fee | 5,000 lamports, in SOL | Wire constant |
| Settlement cost per block | 3,291,520 lamports sunk, plus a 100,000 lamport bond returned on finality | Node measurement |
1. Sequencing and admission
Solieum launches with a single operator sequencer, stated plainly as a liveness and ordering monopoly. Four mechanisms make that survivable rather than custodial, and all four ship with it rather than after it.
Signed ordering receipts
Every accepted transaction gets a receipt fixing its hash, batch, position and L2 slot, signed before execution. Two receipts assigning the same position to different transactions, or a published batch that contradicts a held receipt, is a contradiction the holder can prove.
This matters because the proof system cannot help here. Reordering valid transactions produces a perfectly valid state: in the implementation's own test, shuffling a batch of transfers reaches a byte-identical state root. The fraud proof sees nothing wrong because nothing about the state is wrong. The receipt is the control.
A bond behind those receipts
A separate Solana program holds a bond posted by the receipt-signing key, not the settlement payer, because slashing has to hit the identity that made the promise. The bond account is derived from that key's public key.
The judgement on chain is the protocol's own judgement: the program calls the same prove_double_assign function the test suite runs, with both signatures verified by Solana's Ed25519 precompile in the same transaction. The program reads the precompile's instruction data through the instructions sysvar to learn which triples were verified. A transaction whose precompile instruction fails never executes, so a triple present in that data is a verified one. Only triples inline in the precompile instruction are accepted, and only instructions before the slash instruction are read.
| Parameter | Devnet value |
| Minimum bond | 0.1 SOL |
| Reporter's share | 50 percent, remainder to the incinerator |
| Unbonding delay | 604,800 seconds, intended to exceed the challenge window |
Reporting is permissionless and there is no allowlist. A slashed bond account keeps slashed = true along with the batch and position it was slashed for, permanently and readably, so a slashed key cannot quietly re-register. On devnet this is exercised rather than asserted: the sequencer is bonded, and a throwaway key was made to sign two receipts for one position and was slashed for it, with the second attempt refused as already slashed.
What is not yet slashable on chain is batch contradiction, where the published batch does not honour a receipt. That needs a one-position inclusion proof against the root's step tree, which the trace design below now makes possible. It is recorded as the next step rather than implied to exist.
A receipt names its chain. A receipt used to sign only its order hash, batch, position and slot, so one key sequencing two chains would produce two valid receipts for the same position that the proof could not tell apart from cheating. Receipts now carry the chain's genesis hash inside the signed bytes, 85 bytes in all, and the equivocation proof refuses a pair that names different chains. A receipt signed before the change names no chain and still verifies under its original 53-byte layout, so old block logs replay unchanged. The bond is bound to one chain the same way, and an existing bond migrates once to say which.
Both rebuilt programs are not yet deployed. The bond program on devnet still judges the old layout, so an equivocation between new receipts is provable today but not yet slashable there. Until the upgrade, the operating rule carries it: one sequencer key per chain, checked by a preflight before any node starts.
Execution at admission
A transaction is executed when it is submitted, against the exact position it will occupy. One that would fail is refused there: the caller gets the program's own error and logs back over JSON-RPC, and nothing else happens. No fee, no position, no receipt, no published bytes.
This is possible because three properties hold, none of them added for this purpose. One process answers submission and produces blocks. The position is fixed by the signed receipt before execution, so a transaction's predecessors are known when it arrives and cannot change. And nothing is ever inserted in front: forced-inclusion entries are appended, never spliced.
The motivation is measurable. Across a self-taken sample of 39 finalized Solana mainnet blocks in one hour on 15 September 2026, 15,459 non-vote transactions carried 4,100 failures, every one of which paid its fee: 26.5 percent, about 0.09 SOL in an hour buying nothing. That is not a defect in Solana. It is the necessary consequence of an open fee market where you build against state you can see and an unknown leader executes hundreds of milliseconds later. Solieum can know the outcome in advance, so it does.
Real execution still happens at block production, from the real store, in the same order, at the same timestamp. The admission gate is a refusal in front of an unchanged path, not a second execution engine.
Two further limits sit in the same gate and are off by default: a per-payer submission rate and a per-transaction compute ceiling. Both refuse before anything executes, so a refused submission costs the node nothing, and both must be set before any endpoint is exposed publicly.
Launch scope
Admission can refuse on a second ground: not that a transaction would fail, but that the chain could not defend it in a dispute. With --launch-scope, the node runs the block's own step recorder over the admission execution and refuses any transaction containing a step no verifier class covers, before it is ordered and without charging it. It is the same classifier that judges a sealed block, so admission and the dispute game cannot disagree about what is defensible.
A forced-inclusion entry is never refused, because a slot was promised. An out-of-scope forced entry is consumed as a no-op instead: its position kept, nothing executed. Every verifier then re-judges each no-op at its own position from its own bytes, and a valid, in-scope transaction consumed without executing is reported as censorship. That is an alarm distinct from a divergence, because the roots still match.
Launch scope is off by default and off on gamma, the chain running now, which carries a token program whose creation steps are opaque. Nothing on chain penalises a false no-op yet; the alarm is all there is.
Forced inclusion
A transaction submitted directly to a Solana program must be included within a deadline fixed on chain from the cluster clock at submission, which nobody can edit afterwards.
- Submitting takes no permission and never touches the sequencer's key.
- If the transaction is ignored past its deadline, you reclaim your fee yourself, without the operator's cooperation. Censoring costs the operator and costs you nothing.
- A late inclusion is stamped late permanently rather than tidied up.
- An entry left past its deadline while the operator keeps posting batches makes the chain stop deriving: a fault naming the offending batch and the skipped entry, with everything before it still standing.
- The transaction bytes live on the queue entry itself rather than a hash kept elsewhere, because a sequencer can always claim it never received bytes it cannot be shown to have. A commitment nobody can open is not a forced transaction.
- A junk, unsigned or unpayable entry is still consumed and still counted, published as a record of its own. Otherwise anyone could freeze the chain for the price of one fee.
Forced inclusion promises a slot, not a successful transaction. Measured: a transfer submitted only to Solana landed in the next block, and three adversarial entries, junk, an unfunded signer, and a valid transfer behind them, were all consumed with balances changed by exactly the valid one.
Ordering policy, and MEV
Today's rule is first-come, first-served by admission, and it is checkable because of the receipts. A priority-fee auction and an encrypt-until-ordered scheme remain on the table. Whichever is chosen is published in a form checkable against published batch data, because "we order fairly" is not a policy.
On MEV: a sequencer that can see transactions before ordering them can extract value from that position. Saying "we do not do this" is not a control. The control is either a policy checkable from outside or an architecture where contents are hidden until order is fixed. Solieum states which one it is using and will not claim front-running resistance without the second.
Failover and determinism
A replacement sequencer rebuilds exact state from published data with nothing handed over by the previous operator. The node exercises this on every restart: it keeps no state snapshot, re-executes the published bytes, and refuses to start if any block fails to reproduce its committed root.
Hand-off without equivocation. Signed receipts make two receipts for one position a slashable offence, and the rule does not care why they exist, so two honest nodes under one key would slash themselves. The node therefore refuses to sign when its next batch is behind the next batch the data-availability program will open, which is how far any node has carried that identity: a standby started from a stale copy is refused by name before its first receipt. A datadir rebuilt from published bytes resumes its batch counter from the log rather than from zero.
What this does not close is two nodes at the same height at the same time. The structural fix, claiming each batch on Solana before signing anything in it, costs one more Solana transaction and one round trip per block, and is the owner's decision (ADR-0020). Until then the rule is one key, one running node.
Underneath sits a determinism charter with no exceptions: no floats, no map-order dependence, no wall clock, and byte-identical state roots across machines, enforced by a harness that replays identical blocks on different hosts. A fraud proof is worthless if two honest nodes disagree.
2. Data availability
This is the quietest component and the one that decides whether Solieum is a Layer 2 at all. If batch data is withheld, nobody can recompute the state, nobody can prove a root wrong, nobody can construct a withdrawal proof, and what is left is a system where you trust the operator.
The mechanism. A batch is opened with its commitment, its compressed chunks are posted in Solana transactions, and sealing succeeds only if the published bytes fold to the commitment declared at open. An opened batch that cannot be sealed can be abandoned by its poster. A sealed one never can, and its sequence number is never freed, so published history is not rewritable.
Compression. Repeated account keys dominate raw transaction bytes, so the compression ratio sets cost per transaction directly.
| Sample | Raw | Compressed | Ratio |
| 4,000 transfers over 128 recurring accounts | 416,000 bytes | 222,768 bytes | 53 percent |
| Twelve recorded mainnet transactions sharing almost no accounts | 79 percent |
Compression pays for repetition, so the second figure is the floor rather than the target, and the codec is a plain general-purpose compressor with no dictionary priming or signature-aware encoding. A production codec should beat both. On devnet the first block's batch was 226 published bytes in a single chunk, one posting transaction, visible on Solana.
Settlement cadence. A block's four settlement instructions were originally four confirmed writes. They are now sent as one Solana transaction where the payload fits, with the stepwise loop kept for multi-chunk and resumed publishes, which is what makes a partial publish resumable.
| Path | Result, L2 block to root on devnet |
| Stepwise, four writes | 13.2 to 29.7 s across two samples |
| Combined, one write | 7.4 to 17.8 s, mean 11.3 |
| Combined with a resend policy, 64 runs | median 5.5 s; 58 of 64 between 4.0 and 6.1 s; three near 16 s |
Read those as one measurement each, not as a clean multiple: the two stepwise samples are the same code and span 13 to 30 seconds by themselves, so devnet variance alone is wide. The last row also carries a second change, a policy that resends an unconfirmed settlement every 2 seconds inside a 45 second deadline, so the two cannot be separated from these runs. The median moved from 5.35 to 5.50 between the 56th and 64th run, which is why this paper quotes "about five seconds" and not a tenth of a second.
Retention and archive. Data must remain available for longer than any withdrawal path, because a withdrawal that becomes unprovable is a permanent loss rather than a degraded experience. Today the ledger holds the posting transactions and the node keeps an append-only log of every published batch. A verifier can be pointed at that archive when a pruned endpoint no longer carries the chunks, and the archive is not trusted: its bytes are decompressed, decoded, their deposits matched against inbox entries read under quorum, and replayed to a root that must equal the one on chain. Only the cheap pre-fold against the published commitment is lost, and a byte-level failure from an archive is reported as unverifiable rather than as a divergence, because without the published bytes nothing says which side lied.
Sampling by light clients is planned and will be published with its security parameters rather than asserted, because sampling is only a guarantee alongside erasure coding and enough independent samplers.
3. Settlement
The settlement program holds the sequence of state roots.
- A proposer commits a root under bond, one root per block.
- A root finalizes only if its parent is final and its own window has elapsed unchallenged.
- A challenged root cannot finalize, with no time-based escape. Once a challenge is open the game gates finality and the window no longer does, so a halt cannot finalize a root out from under a live dispute.
- Withdrawals verify against final roots only.
- Challenge verdicts enter only by cross-program call from the dispute program at its pinned address. A direct key cannot inject one. On the chain running now, gamma, the dispute program's enforcer address is the settlement program's dispute authority, so a verdict enforces. On the first devnet chain that authority was a plain key, so a verdict there did not.
- The clock is the on-chain Clock sysvar, injected into the core as a parameter. The core has no way to read a clock of its own.
The challenge window is 432,000 slots, 48 hours at the 400 millisecond target. It is fixed at chain initialization and there is no setter, so the constant governs the chain it was created with and changes nothing already running.
Rent reclamation. Root and batch records are Solana accounts, and their rent is the dominant cost of running the chain. Instructions to close both after a retention period are built and gated by tests, and neither is deployed.
| Retention | Value | Why |
| Finalized roots | 1,000 newer final roots and 6,480,000 slots, about thirty days | Closing a root ends the ability to prove new withdrawals against it, so retention is a proving deadline. A count alone shrinks as cadence rises: a thousand roots is six weeks at the current pace and under two hours at a five-second block. |
| Sealed batches | 3,240,000 slots, about fifteen days from the seal | Many times the challenge window, so no batch closes while its block is disputable, and shorter than the root retention, so batches close before the roots whose archived bytes are replayed against them. |
Closing is permissionless with the destination pinned to the recorded proposer or poster, so a sender can only pay the fee, never redirect the refund. The archive had to come first: closing a batch destroys the commitment published chunks fold to, so a verifier could not begin on that block without somewhere else to read the bytes.
4. The state commitment
State is committed as a sparse Merkle tree of depth 256, where an account's leaf address is the bits of its key, so a key pins its own position. Accounts and withdrawal leaves live in the same tree.
The shape is chosen for one reason: it makes insertion witnessable. A sorted-leaf tree, which the earlier chain used, has smaller proofs and handles in-place mutation perfectly, but inserting a key at sorted position p shifts every leaf at or after p and changes the pairing and promotion structure of the whole right portion of the tree, up to O(n) internal nodes. No bounded witness can express that, so account creation could not be disputed on chain and had to be deferred to a placeholder.
On the sparse tree, creation is an ordinary update at a determined leaf, and non-inclusion is provable, which insertion witnesses need: you cannot prove you may create an account without first proving it is absent.
| Property | Value |
| Depth | 256, keyed by the hash of the public key |
| Proof size | A 256-bit bitmap plus the non-empty siblings only: 321 bytes and 8 siblings over a 256-account chain |
| Cost | 257 hash calls per proof; 1,020 for a two-account witnessed step |
| Public inputs | No leaf count: a key pins its own position, so the parameter a caller could get wrong no longer exists |
An all-zero subtree hashes to an empty tag, so two trees with identical contents cannot hold different roots.
The commitment version
What an account leaf or a step leaf commits is consensus: change it and every root moves. So genesis records a state-commitment version inside the genesis hash, and a node opens only the versions it implements, refusing any other by name rather than diverging at replay.
| Version | What it changed |
| 2 | Account data committed by its page-tree root, so a slice of an account can be proven without carrying the whole account. Gamma, the chain running now, is at 2 |
| 3 | An opaque instruction step binds its program, its account list and its data, so the root records what ran even where no verifier can re-execute it |
| 4, 7 to 10 | Token operations become witnessed one-steps, one class per version, listed in section 6 |
| 5, 6 | Retired. Their initialisation leaf did not bind the instruction tag. They are refused by name rather than reinterpreted, because an older binary could still cut a chain at those numbers with the unsound leaf |
| 11 | Reserved for binding each step to a signed transaction, section 7 |
One binary serves every version it opens. A chain below a class's version keeps that step as the opaque leaf it always was, byte for byte, so the running fleet replays unchanged while a freshly cut chain gets the new classes. A new leaf shape is always a new version, never a quiet change to an existing one.
5. What a dispute is actually about
You cannot re-run the batch. A Solana transaction has a bounded compute budget and a batch of thousands of transactions exceeds it by orders of magnitude. Any fraud proof design that says "re-execute and compare" has not been costed.
So the dispute is about a trace, not a block. A trace is num_steps state commitments where roots[0] is the pre-state and roots[T] is the post-state. The two parties bisect it until one step remains, and a one-step verifier decides that step from Merkle witnesses alone.
What counts as a step
- The fee leaving a transaction's payer.
- Then, only if the transaction succeeded, one step per instruction.
- A withdrawal, meaning the debit plus the withdrawal leaf, is one step.
- A deposit credit from the inbox is one step.
- A consumed forced entry is no step, because it changed nothing.
roots[k] is the state-tree root after k steps.
The step tree
transactions_root is not a flat hash of transaction hashes. It is a tree over the steps, with leaf key H("solieum-ix" ‖ i). For a step inside a verifier class, the leaf value is exactly what the verifier recomputes, for example H("solieum-transfer" ‖ from ‖ to ‖ lamports). Every other step is committed opaque under its own domain. Up to version 2 that leaf binds only the step's position and instruction index; from version 3 it also binds the program id and hashes of the account list and the instruction data, so the root records what ran even where nothing can re-execute it. Either way it is a value no verifier recomputes, so an opaque step can never be mistaken for a witnessable one.
Before this existed, the node committed a flat hash and a root could be challenged but never defended: no bisection had an honest midpoint and no witness could bind an instruction to the batch.
The node defends its own roots
At its move it bisects with roots[mid]. On a unit range over a witnessable step it sends the witnessed one-step with the witness built from that step's pre-state. On a unit range over an opaque step it reports the root as undefendable and lets the timeout run. It refuses to play at all from a trace whose endpoints do not reproduce the claim. Every block carries a defence record saying how many steps it has, how many are witnessed, how many opaque, and which is the first opaque one, so the boundary is published per block rather than discovered in a dispute.
The arithmetic that sizes the window
A dispute is 2 × ceil(log2(steps)) + 1 moves.
| Response window per move | Worst-case game for a 131,072-step trace | Against a 48-hour window |
| 1,500 slots, 600 s, the enforced floor | 5.8 hours | clears by more than 8x |
| 1 hour | 35 hours | clears by about 1.4x |
131,072 steps is the ceiling the devnet proposer bond implies, and 35 moves is what that costs. A dispute that could not finish inside the window is refused at open rather than allowed to make the window decorative. The round length is the parameter with the least slack and should not be lengthened without re-running this arithmetic.
Deadlines count slots, not seconds, so a halted chain cannot time out an honest party.
6. The one-step verifier, and why it has classes
The constraint nobody can design around
A one-step verifier receives the disputed instruction and the accounts it touches as Merkle witnesses. Those accounts are L2 accounts. They do not exist on Solana, they have no addresses the runtime knows, and no program owns them there.
Therefore the verifier cannot invoke the program being disputed. There is nothing to call: invocation needs real accounts owned by real programs, and a witness is bytes with a proof. Every instruction class must have its semantics re-implemented inside the verifier program. This is not a limitation of the implementation. It follows from what a fraud proof is.
The consequence: classes, explicitly, one at a time
Each class is its own instruction on the dispute program, with the class boundary enforced in code rather than guessed. The token parser, for instance, rejects delegated, frozen, uninitialized and wrapped-SOL accounts rather than improvising semantics it does not implement. Anything outside a supported class returns StepNotExecutable, a rejection and never a default.
Every class carries a differential test against the real program. For SPL tokens the harness executes the batch with the actual SPL Token program and then plays an honest dispute game that can only end in a proposer win if the re-implementation reproduces the real program byte for byte. A one-unit divergence flips the verdict and fails the gate. Re-implementation risk is managed by test, not by assertion.
| Class | Cost | Chain needs commitment version |
| System transfer between existing accounts | 19,216 CU | any running chain |
| SPL token transfer | 20,408 CU | any running chain |
| Fee step, burned or paid to the sequencer | one or two leaves | any running chain |
| Account creation, deletion and emptying | sparse-tree paths | any running chain |
| Withdrawal and deposit credit | any running chain | |
| SPL MintTo | 619,729 CU | 4 |
| SPL InitializeMint2 | 352,129 CU | 7 |
| SPL InitializeAccount3 | 378,868 CU | 7 |
| SPL Burn | 619,714 CU | 8 |
| SPL SetAuthority | 364,673 to 371,731 CU | 9 |
| SPL Approve | 371,338 CU | 10 |
| Multi-signer and Checked token variants, and every step through any other program | Opaque |
Every class fits the 700,000 compute units a witnessed step is allowed. The token classes witness only the single-signer spelling that can succeed on this runtime. InitializeMint2 and InitializeAccount3 are the only initialisation spellings covered, because the older ones read a rent account the Layer 2 never populates and cannot succeed here, and each class binds its instruction tag so a verifier refuses any other. The InitializeMint2 figure predates that tag binding and has not been re-measured. Gamma is at version 2, so none of the token classes applies to it: a token moves under proof there, but is created under trust.
These classes are not yet sound against a dishonest proposer. Each checks that its step sits under the transactions root the proposer submitted, and that root is the proposer's own argument, bound to no signed transaction. A witnessed class therefore proves that a step was executed correctly, not that anyone asked for it. Section 7 describes the gap and what is being built to close it.
The end state, named so it is not mistaken for the present
General one-step verification, meaning any instruction of any deployed program, requires executing SBF bytecode on chain against witnessed accounts. That interpreter now runs inside the dispute-game program as its own instruction, which has not been deployed and is wired to no game: a dispute cannot yet reach it. Since the first build it has gained calls and returns inside the class, the runtime's entry state and memory map pinned by differential tests against the real loader, and a read-back of the machine a program exits in.
- The machine's root commits registers, program counter, memory and the halt flag, which is the state a bisection descends into. One step leaves the machine untouched on any fault.
- The program is committed as a Merkle tree over its instruction stream, so a witness carries one instruction and its path: sixteen hashes for a 65,536-instruction program, about nineteen hashes for a whole verification, against the 700,000 compute units a witnessed step is allowed. That is how the ELF requirement is met without putting an ELF in a transaction. The commitment binds the instruction's index, the program's instruction count, so a prover cannot re-aim a jump by claiming a different length, and its padding leaves.
- Memory is a page tree of 32-byte pages at depth 59, covering the whole 64-bit address space so real addresses sit where they actually are with no region map to get wrong. A proof is 59 hashes, about 9,000 compute units, so a load and a store together cost roughly 18,000 of the 700,000.
- That puts aligned 8-byte loads and stores inside the class. Alignment is the safety property: an aligned 8-byte access always lies inside one page, so a verifier holding one witnessed page has provably seen the whole access. Unaligned accesses fault rather than being split across a page nobody proved, and narrower forms stay out because sub-word semantics are exactly the kind of thing that becomes a consensus bug when guessed.
- It is a class, not an emulator, and that is deliberate. Division, modulo, 32-bit arithmetic and syscalls fault rather than being approximated, because the interpreter and the node's execution must agree exactly. A witness that does not hold up decides nothing rather than deciding against the proposer, so an unsupported step stays opaque exactly as it is today.
One finding from surveying the runtime the node actually runs: account data is never copied into the program input buffer, because direct mapping is enabled. Each account's data becomes its own memory region aliasing the real bytes. The good news is that the page tree committing account data is already the right commitment for those regions, so account data is not committed twice. The consequence is that a program call's memory commitment must be composed, a header buffer plus each account's existing data root, rather than one flat tree that would copy every account into a second commitment and then have to keep the two agreeing.
What the class can and cannot reach. Real programs make system calls and cross-program calls constantly; SPL Token's own paths do, and so does every metadata program. So the interpreter can verify individual instructions inside its class, but it will never run a real token program to its exit, which is why the token operations in the table above are covered by hand-written classes instead. A witness is about 1,060 bytes, which does not fit one Solana transaction beside everything else and has to span two.
7. Binding a step to a signed transaction
This is the most important open problem in the design, and it was found in the design's own review on 15 September 2026.
The gap
A one-step verifier checks that a disputed step sits under the settled root's transactions root, then re-executes it. But that root is supplied by the proposer, it is a tree over derived steps rather than over signed transactions, and nothing on chain checks a user's signature. So a dishonest proposer could commit a step no user signed, a withdrawal draining an account, a transfer, a MintTo inflating supply, and win the dispute over it. An honest verifier would see the divergence and challenge; bisection would narrow to the forged step; and the one-step would confirm it, because the step is internally valid. The deposit credit is the one class that is safe today, because it reads its input from the Solana inbox rather than from the proposer.
What stands in for the missing binding is entirely outside the proof: one operator holds the only sequencer key, withdrawals wait out the window and the air gap, the vault pays only what it holds, and no running chain carries value. That is an acceptable posture for a single-operator test network. It is not one for an open sequencer set, an untrusted proposer, a chain holding value, or mainnet, and the fix is required before any of those.
The design, ADR-0021
- The settled root names a sealed data-availability batch and a transaction root. The transaction root is a binary Merkle tree over the batch's signed transactions, in order, with the count bound in. Settlement refuses a root whose named batch is not sealed, and derivation rebuilds the transaction root from the published bytes and diverges if it does not match.
- Derivation refuses what admission refuses. A bad signature, a blockhash outside the recent window, or a reused signature is consumed as a no-op at its position, using the same validation admission uses, so an honest verifier diverges from a proposer who executed it.
- The one-step re-checks the same facts on chain: the signature through Solana's Ed25519 precompile, the blockhash against a committed window, and one-time use against committed signature status. That is too large to sit beside a witness in one transaction, so it spans several.
- The step is derived from the signed bytes rather than asserted by the proposer, and each step stands for one transaction position, once and in order.
- Witnessed classes cover single-instruction transactions only. For those, the instruction succeeding is the transaction succeeding. A transaction with several instructions stays opaque until the interpreter answers multi-instruction atomicity.
- A forced-inclusion transaction binds to the inbox entry it was drained from, not to the batch.
What is built
| Part | State |
| Transaction root computed at production over the published sequencer transactions | In the node |
| Derivation refuses a bad signature, a stale blockhash or a reused signature | In the node |
| A forced transaction published with its inbox index, faulting the proposer if no inbox entry backs it | In the node |
| Settlement commits the batch sequence and transaction root into the block id, and refuses an unsealed batch | In the settlement program source |
| verify checks the committed transaction root against the published blob | In the node |
| Signature, blockhash-window and signature-status checks in the one-step, on chain | Not built |
| Every witnessed class reworked onto the bound witness | Not built |
| Activation at commitment version 11 | Not built |
Everything built is gated at commitment version 11. Gamma is at version 2, so none of it is active on any chain, and the forged-step gap is open everywhere. New witnessed classes are paused until the bound witness shape is settled, so that each is built once, in its final form.
8. Independent verification
The watchtower runs as one command. It re-derives the chain from Solana through its own RPC readers, which must agree with each other, verifies each new sealed batch as it lands, checks that bridged supply still fits the vault, posts alarms to a URL as well as the terminal, and can open a challenge under bond on a real divergence.
Agree, diverge and not-yet-derivable are kept distinct, so withheld data or a lying reader is never mistaken for fraud. It has been drilled against a deliberately poisoned provider.
Two further checks sit beside the watchtower. verify-program compares a program's deployed bytes on Solana with a local build, byte for byte, allowing for the loader's header and zero padding while catching anything appended; gamma's settlement and dispute programs have each matched a clean build. And every no-op in a block is re-judged at its own position, so a valid transaction the sequencer consumed without executing is reported as censorship rather than passing silently.
A re-derivation run over the beta chain, before it was stopped, covered blocks in both settlement shapes, stepwise and combined, which any verifier must handle for good once a single combined block exists: 33 blocks re-derived from Solana, roots matching their records, settlement head equal to the last re-derived root, no divergences.
What is not done: nobody outside the operator runs one. That is the gap between a proof system that exists and a proof system that protects.
9. The gaps, stated as gaps
Forged steps. Section 7. Until each step is bound to a signed transaction, finality on a user-signed step rests on trusting the single operator.
The program set is committed in genesis, and the running chain does not use it. Genesis can now commit each Layer 2 program as its id and a hash of its bytes, inside the genesis hash, and a node or a verifier refuses to run under any other set (ADR-0019). A verifier then learns the set from the chain instead of from the operator it is checking. But gamma was cut before this and commits no programs, and it runs the SPL Token program under an explicit --uncommitted-programs flag, so on gamma the set is still the operator's word. A chain meant to run programs has to be cut with them committed, and a deploy path by transaction is still not built.
Wider one-step classes. Everything outside the classes in section 6 is opaque, including every multi-instruction transaction and every step through a program the classes do not cover.
The step count is not bound into the root record. A challenger who names a step count larger than the block's can drive the game to a position no witness covers. The node plays anyway and says so; the fix is for the program to refuse a claim of the wrong length.
Outside verifiers, audits, rent reclamation in production, a composed fee, a staked sequencer set, and validity proofs as a second gate are all covered in Status and Roadmap, each with the condition that closes it.
10. Reference: data structures and implementation
These are the shapes the system commits to, copied from source. Field names are the code's; comments are added here.
The block header a root commits to, in solieum-core:
A settled root on Solana, in the settlement program:
The chain's settlement state, trimmed to the fields that decide behaviour:
A published batch, in the data-availability program:
A forced-inclusion entry, in the inbox program:
A proven withdrawal, in the bridge portal:
An ordering receipt, in solieum-receipt:
Byte layouts that two implementations must agree on exactly:
Integers are little-endian throughout, and each domain tag keeps one kind of value from ever being read as another.
Implementation map
The running system is one node binary and six on-chain programs, built from these shared crates:
| Crate | Role | Built into |
| solieum-core | The settlement state machine, pure and deterministic | node, settlement, dispute game |
| solieum-sequencer | The ordering machine | node |
| solieum-receipt | Signed receipts and equivocation proofs | node, sequencer bond |
| solieum-sparse-tree | The state commitment tree | node, dispute game |
| solieum-pagetree | Account data committed so one page can be proven alone | node, dispute game |
| solieum-compress | The batch compressor | node |
| solieum-dispute | The interactive dispute game's rules | node, dispute game |
| solieum-sbf | The one-step SBF interpreter class | node, dispute game |
| solieum-state-tree | The earlier sorted-leaf tree, kept for its proofs | dispute game |
Several more crates model individual rules purely, the wire batch format, the canonical chain rule and the divergence alarm among them, and are exercised by the execution harness. Two are wired to nothing: a bridge accounting core and an enshrined order-book core.
Inside the node, the modules that carry the design are admission for execution at admission, trace for the step recorder and its classifier, l1 for settlement and dispute play, verify for re-derivation, wire for transaction validation, persist for the published record, and reproduce for byte-for-byte checks of deployed programs.