Mist Architecture & Internals
Deep dive into the Mist compilation pipeline, PVI graph model, UVL verification engine, MCE conservation analysis, and runtime execution model.
Compilation Pipeline
Mist source compiles through a 6-stage pipeline. Each stage produces verifiable intermediate artifacts:
┌─────────────────────────────────────────────────────────┐
│ mistc compile │
│ │
.mist source ─▶ Parse ─▶ Type Check ─▶ VIR Lowering ─▶ Normalize VIR │
│ │
│ ─▶ Static Analysis ─▶ MCE Conservation ─▶ Fee Calc │
│ │
│ ─▶ PVI Lowering ─▶ Bundle Generation │
│ │
└────────────────────────────────────────────── .bundle ─┘
Stage 1: Parsing
The parser converts .mist source into an AST (Abstract Syntax Tree). Mist uses an indentation-sensitive lexer (Python-style) and a recursive descent parser.
Key properties:
- Tabs are forbidden — only spaces for indentation
- No error recovery — first parse error halts compilation
- Keywords are reserved (
contract,state,function,hold,pay, etc.)
Output: Contract { name, state_decls, functions, events }
Stage 2: Type Checking
A three-pass type checker verifies type correctness:
- Pass 1 — Collect state declarations into the type environment
- Pass 2 — Collect function signatures (parameter types + return types)
- Pass 3 — Type-check function bodies, statement by statement
Gradual typing: The type system uses an Unknown type for dynamically-typed values (oracle reads, state reads, field accesses). Unknown is compatible with any concrete type, allowing oracle-dependent business logic without explicit casts.
Type lattice:
Unknown
/ | | \ \
Int Bool String Address Value
Stage 3: VIR Lowering
Converts the AST into Value IR (VIR) — an SSA-based intermediate representation with basic blocks, value IDs, and typed value operations.
Key transformations:
- Each expression becomes a fresh
ValueId require(cond)→Branch { true: continue, false: Panic }if/else→ branch to then/else blocks, both merge to a join block- Value operations (
hold,pay, etc.) become typed VIR instructions
Stage 4: VIR Normalization
Canonicalizes the VIR for deterministic hashing:
- Blocks sorted by topological order
- SSA value IDs renumbered sequentially
- The normalized VIR is SHA-256 hashed to produce the
code_hash
Stage 5: Static Analysis
Runs 10+ correctness checks on the normalized VIR:
| Check | Description | Error Code |
|---|---|---|
| No locked funds | Every hold/mint vault is consumed | MIST_E_LOCKED_FUNDS |
| Conservation | Value inputs equal outputs + fees | MIST_E_CONSERVATION |
| Split completeness | Split amounts sum to vault total | MIST_E_SPLIT_SUM |
| Mint authority | Mint operations have valid authority | MIST_E_MINT_AUTHORITY |
| Burn validity | Burns target valid vaults | MIST_E_BURN_INVALID |
| Determinism | No non-deterministic operations | MIST_E_NON_DETERMINISTIC |
| No direct mutation | Balances only change via value operations | MIST_E_DIRECT_BALANCE_MUTATION |
| No unbounded loops | Loops have provable bounds | MIST_E_UNBOUNDED_LOOP |
| Deterministic fees | Fee computation is static | MIST_E_NON_DETERMINISTIC_FEE |
Stage 5.5: MCE Conservation Analysis
The Mist Conservation Engine (MCE) performs deep value lineage analysis:
- Value Lineage Graph (VLG) — Traces every value from source to sink
- Symbolic Amount Analysis — Proves conservation symbolically when amounts aren't constant
- Linearity Check — Ensures each vault is consumed exactly once
- Runtime Assertion Injection — Adds conservation checks that trigger at execution time
- Fee Node Injection — Inserts fee deduction nodes into the VLG
Stage 6: PVI Lowering & Bundle Generation
Converts normalized VIR to a PVI graph and produces the final bundle:
{
"version": "0.1.0",
"metadata": {
"contract_name": "MyContract",
"compiler_version": "0.1.0",
"build_timestamp": "2026-04-14T12:00:00Z",
"entry_function": "init",
"fee_summary": { ... }
},
"vir": { ... },
"pvi_graph": { ... },
"code_hash": [32 bytes, SHA-256 of normalized VIR]
}
PVI Graph Model
A PVI (Proof-Verified Invocation) graph is a typed directed acyclic graph that represents all value flows in a contract execution. It is the artifact that the UVL engine verifies before settlement.
Node Types
| Node Type | Description | Example |
|---|---|---|
HOLD | Lock assets from an account into a vault | hold v = 100 asset "RIVL" from alice |
PAY | Transfer assets to an account | pay 100 asset "RIVL" to bob |
RELEASE | Return vault contents to an account | release vault v to alice |
REFUND | Return vault contents (refund semantics) | refund vault v to alice |
SPLIT | Divide vault among multiple recipients | split vault v into [80 to a, 20 to b] |
MINT | Create new asset units | mint v = 100 asset id authority owner |
BURN | Destroy asset units | burn vault v |
FEE | Protocol fee deduction (injected by compiler) | automatic |
Edge Types
Edges connect nodes and represent value flow with amounts and asset types:
HOLD(alice, 1000 RIVL)
├──[800 RIVL]──▶ PAY(bob)
└──[200 RIVL]──▶ PAY(charlie)
Graph Constraints
- Acyclicity — No cycles in the value flow graph
- Single source per vault — Each vault traces to exactly one HOLD or MINT
- Conservation per asset — For each asset type:
Σ HOLD + Σ MINT = Σ PAY + Σ RELEASE + Σ REFUND + Σ BURN + Σ FEE - No negative amounts — All edge weights are non-negative
- Complete consumption — Every HOLD/MINT node must have outgoing edges that sum to its total
Commitment Algorithm
The PVI graph is canonicalized and hashed to produce a 32-byte commitment:
PVI_commitment = Blake3(canonical_json(pvi_graph))
This commitment is embedded in the execution receipt and verified by UVL during settlement.
UVL Verification Engine
The Universal Value Ledger (UVL) is the settlement layer. It runs 14 verification checks at the commit boundary — if any check fails, the entire intent is rejected atomically.
Verification Checks
| # | Check | Error |
|---|---|---|
| 1 | PVI commitment matches embedded graph | UVL_F001 |
| 2 | Conservation of value per asset | UVL_F002 |
| 3 | No negative balances | UVL_F003 |
| 4 | No locked funds (all vaults consumed) | UVL_F004 |
| 5 | Split sums correct | UVL_F005 |
| 6 | Split rounding follows TruncateRemainderToLast | UVL_F006 |
| 7 | Mint authority valid | UVL_F007 |
| 8 | Fee quote commitment matches | UVL_F008 |
| 9 | Oracle snapshot commitment matches | UVL_F009 |
| 10 | Oracle snapshot not stale (max 256 blocks) | UVL_F010 |
| 11 | Protocol version supported | UVL_F011 |
| 12 | Compiler in allowlist | UVL_F012 |
| 13 | Host ABI commitment match | UVL_F013 |
| 14 | VRF proof valid | UVL_F014 |
Settlement Process
Intent submitted
→ Mist execution produces PVI graph + state deltas
→ Execution receipt generated with commitments
→ UVL runs 14 verification checks
→ On success: SettlementPlan with BalanceDeltas applied atomically
→ On failure: Entire intent rejected, no state changes
The settlement plan is a list of BalanceDelta { account, asset_id, delta: i128 } applied in a single atomic operation.
Atomic Rejection
UVL guarantees all-or-nothing semantics. If check #7 (mint authority) fails, it doesn't matter that checks #1-6 passed — the entire intent is rejected. No partial state changes ever persist.
Runtime Execution Model
Architecture
MistDeploy/MistCall
→ dispatch (load bundle, validate)
→ execute (interpret VIR, produce PVI instances)
→ commitments (compute receipt extension)
→ UVL verify & settle (at commit boundary)
Host Environment
The runtime receives a deterministic host environment for each execution:
| Field | Type | Description |
|---|---|---|
caller | [u8; 32] | Intent sender address |
commit_time_ms | u64 | Block commit timestamp |
oracle_prices | Map<String, u128> | Oracle spot prices (1e18 fixed-point) |
twap_rate_1e18 | u128 | Time-weighted average price |
lane_id | u32 | Execution lane |
prestate_root | [u8; 32] | Pre-execution state root |
fee_tier | u8 | Fee tier for this execution |
Host ABI (Syscalls)
Contracts interact with chain state through a fixed set of syscalls, each with a deterministic gas cost:
| Category | Operations | Gas Range |
|---|---|---|
| Storage | GET (100), SET (200), DELETE (150) | 100-200 |
| Tables | CREATE (300), GET (120), SET (220), DELETE (170), CONTAINS (80), LEN (50) | 50-300 |
| Objects | CREATE (500), READ (100), WRITE (250), TRANSFER (300), FREEZE (200), DELETE (200) | 100-500 |
| Auth | CALLER (10), HAS_CAPABILITY (50), VERIFY_SIGNATURE (1000), ISSUE_CAP (500), REVOKE_CAP (500) | 10-1000 |
| Events | EMIT (150) | 150 |
| Oracle | READ_PRICE (200), READ_TWAP (300), SNAPSHOT_COMMITMENT (50) | 50-300 |
| VRF | SEED (50), RANDOM_U64 (100), RANDOM_RANGE (100) | 50-100 |
| Crypto | HASH_BLAKE3 (100), HASH_SHA3 (150), VERIFY_SIG (1000) | 100-1000 |
Size Limits
| Limit | Value |
|---|---|
| Max key size | 256 bytes |
| Max value size | 65,536 bytes |
| Max table entries | 10,000 |
| Max events per transaction | 256 |
| Max topic size | 64 bytes |
| Max event data size | 4,096 bytes |
Contract ID Derivation
When a contract is deployed, its on-chain ID is computed deterministically:
contract_id = Blake3(
"RIVELLUM_MIST_CONTRACT_ID_V1" ‖
deployer_address ‖
contract_name ‖
code_hash ‖
commit_time_ms
)
This means the same deployer deploying the same contract at different times gets different contract IDs.
Deterministic Replay
Every execution is fully reproducible. The execution receipt contains:
- PVI commitment (Blake3 hash of canonical PVI graph)
- Fee commitment
- Oracle snapshot commitment
- Host ABI commitment (Blake3 of read set ‖ write set ‖ object ops ‖ oracle ‖ vrf proof)
Given the receipt and the original host environment, anyone can replay the execution and verify they get identical commitments:
# Export replay bundle
mistc repro-export --contract my_contract.mist --receipt <receipt_id>
# Replay and verify
mistc replay replay_bundle.json
Insolvency Handling
When a SPLIT node distributes more than the vault contains (insolvency), UVL applies a waterfall policy:
| Policy | Behavior |
|---|---|
RejectAll | Reject the entire intent (default) |
ProRata | Distribute proportionally to weight |
PriorityFirst | Fill recipients in order until exhausted |
Split Rounding
When splitting amounts that don't divide evenly, Mist uses the TruncateRemainderToLast policy: integer division truncates each share, and any remainder goes to the last recipient in the split list.
Example: splitting 100 units three ways at equal weight:
- Recipient 1: 33
- Recipient 2: 33
- Recipient 3: 34 (gets the remainder)
Oracle System
Price Format
Oracle prices use 1e18 fixed-point representation:
Actual price $60,000.50 → 60000500000000000000000 (60000.5 × 10^18)
Staleness Rules
Oracle snapshots have a maximum age of 256 blocks. If the snapshot is older, UVL rejects with UVL_F010. This ensures contract execution uses reasonably current market data.
Supported Pairs
Oracle pair format: "BASE_QUOTE" in Mist source, "BASE/QUOTE" in the runtime. The compiler translates automatically.
let btc = oracle.price("BTC_USD")
let eth = oracle.price("ETH_USD")
let rivl = oracle.price("RIVL_USD")
Compilation Output Artifacts
| File | Description |
|---|---|
<name>.bundle.json | Complete bundle (VIR + PVI + metadata + code hash) |
<name>.pvi.json | Standalone PVI graph |
<name>.analysis.json | Static analysis report |
<name>.fees.json | Fee breakdown by function |