> For the complete documentation index, see [llms.txt](https://solieum.gitbook.io/solieum/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://solieum.gitbook.io/solieum/solana-rollups-rollapps-layer-2-solutions.md).

# Technical Architecture

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

```mermaid
flowchart TD
  U[User wallet] -->|signed Solana transaction| ADM[Admission: executed against the open block]
  ADM -->|would fail| REJ[Refused at submit, no fee, no position]
  ADM -->|succeeds| REC[Signed ordering receipt: batch and position]
  REC --> B[L2 block, sealed every 2s when there is work]
  B -->|compressed batch| DA[Data availability program on Solana]
  B -->|state root, under bond| SCC[Settlement program on Solana]
  DEP[Deposit on Solana] --> INBOX[Forced inclusion inbox]
  INBOX -->|drained and appended every block| B
  DA --> VER[Verifier re-deriving the chain]
  SCC --> VER
  VER -->|divergence| GAME[Dispute game: bisect to one step]
  GAME -->|verdict by cross-program call| SCC
  SCC -->|root final after the window| PORTAL[Bridge portal]
  PORTAL -->|proven, then the air gap| OUT[Vault pays on Solana]
```

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.

## 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.

### 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, 22,204 non-vote transactions carried **7,393 failures, every one of which paid its fee**: 33.3 percent, about 0.12 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.

### 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.

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 current chain the dispute program's enforcer address is the settlement program's dispute authority, so a verdict enforces; on the earlier chain that authority was a plain key, which is exactly why the current chain was created.
* 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.

## 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

{% stepper %}
{% step %}

## The fee leaving a transaction's payer

{% endstep %}

{% step %}

## Then, only if the transaction succeeded, one step per instruction

{% endstep %}

{% step %}

## A withdrawal, meaning the debit plus the withdrawal leaf, is one step

{% endstep %}

{% step %}

## A deposit credit from the inbox is one step

{% endstep %}

{% step %}

## A consumed forced entry is **no step**, because it changed nothing

{% endstep %}
{% endstepper %}

`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, `H("solieum-step-opaque" ‖ tag ‖ position ‖ instruction index)`, which is a value no verifier knows by construction, 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 | Status                         |
| ----------------------------------------- | -----------------: | ------------------------------ |
| System transfer between existing accounts |          19,216 CU | Verified on chain              |
| SPL token transfer                        |          20,408 CU | Verified on chain              |
| Fee step, burned                          | Single-leaf update | Verified on chain              |
| Fee step, paid to the sequencer           |    Two-leaf update | Built with the kept-fee change |
| Account creation, deletion, emptying      |  Sparse-tree paths | Landed with the sparse tree    |
| Anything through a registered program     |                    | **Opaque**                     |

Against a Solana transaction's 1,400,000 compute units, a transfer class uses about 1.4 percent. The sparse tree costs several times the sorted tree's figures and still fits.

### 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 exists as a pure crate and is wired to nothing.

* 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.

## 7. 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.

A re-derivation run over the current chain covers blocks in both settlement shapes, stepwise and combined, which any verifier must handle for good once a single combined block exists. The last published run: 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.

## 8. The gaps, stated as gaps

**The program set is not committed.** A chain executes the programs an operator names at startup, and nothing the chain publishes says what that set was. Checked rather than assumed: a registered program id has no account, no program-data account and no deploy transaction; the genesis file records the L1 program ids and no L2 set; nothing writes the set into state, so no state root covers it. An independent verifier re-deriving from published bytes must therefore be told the set out of band by the operator whose work it is checking, which is the one relationship the design exists to avoid. The current chain registers no programs, so this bites nothing today and bites the first chain that registers one. Until it is fixed, an explorer can only publish the node's own statement about a file on the operator's disk, and it does so with that caveat attached.

**Wider one-step classes.** Everything outside the classes above is opaque, including every step through a registered program.

**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](/solieum/conclusion.md), each with the condition that closes it.
