[{"data":1,"prerenderedAt":4},["ShallowReactive",2],{"cNXhnGu68C":3},"\u003Cdiv align=\"center\">\n  \u003Cpicture>\n    \u003Csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://github.com/solana-foundation/leanprover-solanalib/raw/main/docs/assets/banner-github-dark.png\">\n    \u003Csource media=\"(prefers-color-scheme: light)\" srcset=\"https://github.com/solana-foundation/leanprover-solanalib/raw/main/docs/assets/banner-github-light.png\">\n    \u003Cimg alt=\"Solanalib\" width=\"100%\" src=\"https://github.com/solana-foundation/leanprover-solanalib/raw/main/docs/assets/banner-github-light.png\">\n  \u003C/picture>\n\u003C/div>\n\n\u003Cp align=\"center\">\n  \u003Ca href=\"https://github.com/solana-foundation/leanprover-solanalib/actions/workflows/ci.yml\">\u003Cimg alt=\"CI\" src=\"https://github.com/solana-foundation/leanprover-solanalib/actions/workflows/ci.yml/badge.svg\">\u003C/a>\n  \u003Ca href=\"https://github.com/solana-foundation/leanprover-solanalib/actions/workflows/docs.yml\">\u003Cimg alt=\"Docs\" src=\"https://github.com/solana-foundation/leanprover-solanalib/actions/workflows/docs.yml/badge.svg\">\u003C/a>\n  \u003Ca href=\"LICENSE\">\u003Cimg alt=\"License: Apache-2.0\" src=\"https://img.shields.io/badge/license-Apache--2.0-blue\">\u003C/a>\n\u003C/p>\n\n**A [Lean 4](https://lean-lang.org/) library of formal models and verified theorems for Solana programs.** \n\nStatus: **purely experimental — exploring framework shape.**\n\nThe ambition is to be for Solana what [Mathlib](https://leanprover-community.github.io/mathlib4_docs/) is for mathematics — a shared, well-documented foundation that downstream verification work can build on without redefining the same primitives every time. Programs written in [Anchor](https://www.anchor-lang.com/), [Pinocchio](https://github.com/anza-xyz/pinocchio), or hand-rolled, are intended to be modelled against `Solanalib`'s formal primitives and proven correct.\n\n📚 **Generated docs:** \u003Chttps://solana-foundation.github.io/leanprover-solanalib/>\n\n---\n\n## Why\n\nSolana programs run with real assets at stake. Bugs in lending invariants, vesting curves, AMM math, PDA derivation, or token-supply accounting cost users — and code review is fundamentally limited as a defence: it scans for known patterns and misses what reviewers don't think to look for.\n\nFormal verification flips the model. Instead of *reading* code looking for bugs, you *prove* the code matches a specification — a precise mathematical statement of intended behaviour. If the proof type-checks, the property holds *for every possible input*, not just the cases the reviewer happened to consider.\n\nThe bottleneck has historically been the cost of specs: writing them takes deep expertise, and every protocol has to reinvent the same primitives. `Solanalib` aims to lower that cost by providing the primitives — accounts, instructions, PDAs, common financial shapes — as reusable Lean definitions with theorems already proven about them.\n\nThe role this library plays in the stack:\n\n```\n┌──────────────────────────────────────────────────────────────┐\n│  ON-CHAIN: Rust/Anchor/Pinocchio → compiled sBPF bytecode     │\n└───────────────┬──────────────────────────────┬───────────────┘\n                │ source-level                  │ artifact-level\n┌───────────────▼──────────────────────────────▼───────────────┐\n│  SPEC: Solanalib formal models in Lean 4                      │\n│    · high-level layer  — accounts, instructions, finance …    │\n│    · Solanalib.SBPF    — the sBPF ISA semantics: the machine  │\n│                          the deployed bytecode actually runs  │\n└──────────────────────────────────────────────────────────────┘\n```\n\nSolanalib lives entirely at the spec layer — it doesn't run on-chain. But it now models *both* ends of the refinement: the high-level shapes a protocol's logic is proven against, **and** the sBPF instruction-set semantics those programs compile down to. That lower anchor (`Solanalib.SBPF`) is a Lean port of the [OOPSLA 2025 Isabelle/HOL formalisation](https://dl.acm.org/doi/10.1145/3720414), validated against a reference VM (see [`spinoza`](https://github.com/lgalabru/spinoza), the companion harness). The eventual goal is an end-to-end chain from deployed bytecode up to protocol invariants.\n\n---\n\n## Architecture\n\nSix peer top-level folders, each a distinct layer:\n\n```\nSolanalib/\n├── Init.lean              ← Mathlib linter activation (imports-only)\n├── Primitives/            ← atomic on-chain types\n│   ├── Pubkey.lean             ByteArray wrapper, DecidableEq\n│   ├── Lamports.lean           UInt64 (= Lamports) + Nat (= LamportsUnchecked)\n│   └── Time.lean               UInt64 (= Timestamp) + Nat (= TimestampUnchecked)\n├── Numeric/               ← fixed-point + arithmetic infrastructure\n│   └── Fraction.lean           Q68.60 (Nat-backed spec; Fraction128 future)\n├── Account/               ← Solana account model\n│   ├── Basic.lean              5-field on-chain Account + credit/debit\n│   └── Transfer.lean           Lamport transfer + conservation theorem\n├── Instruction/           ← Solana instruction model\n│   └── Basic.lean              Instruction + AccountMeta + signers/writables\n├── Finance/               ← domain abstractions for DeFi shapes\n│   ├── Decay.lean                  WindowedDecay bundled structure\n│   ├── Growth.lean                 GrowthCurve (windowed dual of WindowedDecay)\n│   ├── LinearDecay.lean            first concrete decay shape\n│   ├── MonotoneSequence.lean       unbounded monotone-up shape\n│   ├── CompoundInterest.lean       discrete compounding as a MonotoneSequence\n│   └── WithdrawalCap.lean          stateful sliding-window rate limiter\n└── SBPF/                  ← the sBPF instruction-set semantics (machine layer)\n    ├── CommType.lean               BitVec machine words (U4 … U128) + byte (de)ser\n    ├── Syntax.lean                 BpfInstruction (22 forms) + registers/ops/version\n    ├── Value.lean                  CompCert-style memory value\n    ├── Memory.lean                 byte-addressable memory + loadv / storev\n    ├── Decoder.lean                bytecode → BpfInstruction (full opcode table)\n    ├── State.lean                  registers, call stack, BpfState result\n    ├── Interpreter.lean            step + fuel-bounded bpfInterp\n    └── Verifier.lean               verifyInstr + step-safety theorem (Lemma 6.4)\n```\n\nThe boundaries are deliberate:\n\n- **`Primitives/`** are atomic types (no operations beyond the obvious). When we add `Slot`, `Epoch`, `Hash`, they live here.\n- **`Numeric/`** is the numeric backbone — every fixed-point or refined-int type belongs here, separate from domain code so it stays reusable.\n- **`Account/`**, **`Instruction/`** model the Solana runtime data shapes 1:1 with the real Rust crates (`solana-pubkey 4.2`, `solana-account 4.3`, `solana-instruction 3.4`).\n- **`Finance/`** is the first domain layer — financial shapes that recur across DeFi: decay curves, growth curves, and (future) AMM math, lending invariants, oracle aggregation.\n- **`SBPF/`** is the machine layer — a faithful Lean port of the [OOPSLA 2025](https://dl.acm.org/doi/10.1145/3720414) Isabelle/HOL sBPF semantics (all 22 instruction forms, the decoder, the small-step interpreter, and the verifier). Unlike the `Nat`-backed spec layers above, it models bit-precise machine words with `BitVec` (signed division, sign-extension, shifts), because the bytecode it describes is bit-precise. It is the concrete machine the high-level shapes ultimately refine down to.\n\nThe high-level layers depend only on the ones above them plus `Init.lean`; `SBPF/` is self-contained (it models the machine, not the protocol). The result: replacing or refining a lower layer doesn't ripple — e.g. promoting `Fraction` to a bounded `Fraction128` won't force `Finance/Decay.lean` to change.\n\n---\n\n## Design patterns\n\nThese are the patterns the codebase has converged on. They're documented in detail in [`skills/lean-best-practices/SKILL.md`](skills/lean-best-practices/SKILL.md).\n\n### 1. **Bundled structures, not type-classes**, for domain shapes\n\nWhen a concept has several properties that must hold together (e.g. \"a function that decays from peak to zero, monotonically, in a window\"), we use a **bundled structure** with the function and proofs embedded:\n\n```lean\nstructure WindowedDecay where\n  tBegin tEnd peak : Nat\n  apply : Nat → Nat\n  bounded : ∀ t, apply t ≤ peak\n  at_begin : tBegin \u003C tEnd → apply tBegin = peak\n  at_end : apply tEnd = 0\n  antitone_in_window : …\n```\n\nConcrete shapes provide a `toX` constructor that constructs the bundle by proving each obligation:\n\n```lean\ndef LinearDecay.toWindowedDecay (tBegin tEnd peak : Nat) : WindowedDecay := { … }\n```\n\nGeneric theorems are methods on the structure (`d.complementary`, `d.complementary_le_peak`). No type-class elaboration surprises; matches Mathlib's `OrderHom`, `MulHom`, `LinearMap`.\n\n### 2. **Bounded types live alongside unbounded spec aliases**\n\nSolana fields are `u64` on-chain; mathematical reasoning wants unbounded `Nat`. We expose both:\n\n```lean\nnotation \"Lamports\"          => UInt64   -- strict on-chain shape\nnotation \"LamportsUnchecked\" => Nat      -- spec layer; omega-friendly\n```\n\nOperations on `Lamports` carry overflow / underflow preconditions in their type signatures:\n\n```lean\ndef Account.credit (a : Account) (amount : Lamports)\n    (h : a.lamports.toNat + amount.toNat \u003C UInt64.size) : Account := …\n```\n\nTheorems are typically stated at the `.toNat` level (so `omega` does the arithmetic) with bridge lemmas like `Account.credit_lamports_toNat` connecting the two.\n\n### 3. **`@[ext]` on every structure, `@[simp]` on every projection**\n\nLets `ext; simp` close most structural-equality proofs uniformly. Skim any test file for examples.\n\n### 4. **Layered model: spec layer in `Nat`, bounded refinement separate**\n\n`Fraction = Q68.60` is currently `Nat`-backed at the spec layer for arithmetic clarity. The bounded `Fraction128` refinement (matching the on-chain `u128` shape) lives in a separate type with `.toNat` bridge lemmas — same architectural pattern as `Lamports` / `LamportsUnchecked`. This follows the [seL4](https://sel4.systems/) / [CompCert](https://compcert.org/) discipline: prove on the abstract model, refine to the bounded representation as a separate step.\n\n---\n\n## Quick-start\n\nYou'll need:\n\n- [`elan`](https://github.com/leanprover/elan), the Lean toolchain manager (rustup-equivalent). Install once:\n  ```sh\n  curl -sSf https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh | sh\n  ```\n- [`just`](https://github.com/casey/just) as the command runner: `brew install just`.\n\nThen, in this directory:\n\n```sh\njust setup        # ~5 min: fetches Mathlib + downloads cached oleans\njust build        # ~10 s warm: type-checks the library + all theorems\njust test         # runs regression tests in SolanalibTest/\njust docs-open    # builds the docs site + opens .lake/build/doc/index.html\n```\n\nCI runs the same `just build` + `just test` + `scripts/lint-style.sh` flow on every push. Warning gate fails any build that emits a `warning: Solanalib*/` line.\n\n---\n\n## What's in it today\n\nInventory of the verified surface as of this commit:\n\n| Module | Definitions | Theorems |\n|---|---|---|\n| `Primitives.Lamports` | `Lamports`, `LamportsUnchecked`, `Lamports.perSol` | — |\n| `Primitives.Pubkey` | `Pubkey` | — |\n| `Numeric.Fraction` | `Fraction`, `scale`, `zero`, `one`, `fromNat`, `toFloor`, `add`, `sub`, `mul`, `div`, `ofLamports`, `toLamports?`, order instances | 14 (monoid, ordering, round-trip, sub/add cancel, div_one) |\n| `Account.Basic` | `Account`, `credit`, `debit` | 12 (10 `@[simp]` projection + 2 `.toNat` bridge) |\n| `Account.Transfer` | `TransferResult`, `transfer` | 1 (`transfer_preserves_total` — lamport conservation) |\n| `Instruction.Basic` | `Instruction`, `AccountMeta`, `Instruction.signers`, `.writables` | — |\n| `Finance.Decay` | `WindowedDecay`, `WindowedDecay.complementary` | 4 (inherited via composition) |\n| `Primitives.Time` | `Timestamp`, `TimestampUnchecked` | — |\n| `Finance.LinearDecay` | `LinearDecay.value`, `LinearDecay.toWindowedDecay` | 4 (P1–P3b: bounded, antitone, value-at-begin, value-at-end) |\n| `Finance.MonotoneSequence` | `MonotoneSequence` | 2 (`apply_zero_le`, `apply_le_of_le`) |\n| `Finance.CompoundInterest` | `balance`, `toMonotoneSequence` | 5 (boundary, unit-multiplier identity, one-step monotonicity, multi-step monotonicity, balance ≥ principal) |\n| `Finance.WithdrawalCap` | `WithdrawalCap`, `Invariant`, `IsElapsed`, `remaining`, `TryAdd`, `tryAdd` | 5 (`remaining_le_capacity`, `tryAdd_preserves_invariant`, `tryAdd_rejects_over_cap_midwindow` + `_at_reset`, `interval_reset_idempotent`) |\n| `Finance.Growth` | `GrowthCurve`, `WindowedDecay.toComplementaryGrowthCurve` | 4 (embedded; inherited at constructor time) |\n| `SBPF.{CommType,Syntax,Value,Memory}` | `BitVec` words, `BpfInstruction` (22 forms), `Val`, `loadv`/`storev` | data layer |\n| `SBPF.Decoder` | `decode`, `findInstr` (full opcode table) | round-trip tests |\n| `SBPF.Interpreter` | `step`, `bpfInterp` + per-class evaluators | differential testing (below) |\n| `SBPF.Verifier` | `verifyInstr`, `step_ne_err` (Lemma 6.4) | 9 |\n\nRun `scripts/coverage.sh` for the live def/theorem tally.\n\nThe two layers are validated differently, on purpose. The **high-level / finance** shapes carry per-definition theorems — a high theorem-to-def ratio there is the signal that we're proving properties faster than we add surface. The **`SBPF/` machine layer** is mostly executable definitions (the interpreter is a faithful model, not a thing to prove theorems *about* per se); its correctness is established two ways instead: (1) the `step_ne_err` **safety theorem** — a verifier-accepted instruction never faults to the `err` state ([Lemma 6.4 of the paper](https://dl.acm.org/doi/10.1145/3720414)) — and (2) **differential testing** against a reference sBPF VM via [`spinoza`](https://github.com/lgalabru/spinoza), which has run tens of thousands of randomized programs through both this Lean interpreter and the VM with zero divergences.\n\n---\n\n## Roadmap\n\nListed roughly in order of how foundational they are; not all need to be done before the library is useful, but all are flagged as known gaps.\n\n### Numeric foundation\n\n- [ ] **`Fraction.mul_assoc` error bound.** Truncating `bits / scale` after each multiplication introduces ≤ 1 ULP of error; left-associative and right-associative multiplications can differ by `(a + c) / scale` in the worst case. The theorem statement is straightforward; the proof needs careful `Nat` lemmas about double-truncation.\n- [ ] **`Fraction128` bounded refinement.** Mirror of the `Lamports` / `LamportsUnchecked` story: a `UInt128`-backed `Fraction128` plus `.toNat` bridge. Lean 4 doesn't have native `UInt128`; modelled as `Fin (2^128)` or a `(lo, hi)` `UInt64` pair.\n- [ ] **`Fraction.divCeil`** — round-up variant for slippage / collateralisation calculations.\n- [ ] **Pow / `Real.exp` correspondence** — for compound interest, we'll want a theorem bounding `pow_fraction r n` against the mathematical `e^(r·n)`. Requires importing more of Mathlib.\n\n### Domain primitives\n\n- [x] **`Finance.CompoundInterest`** — landed: discrete-compounding `balance` consuming a `Fraction` multiplier, bundled into a `MonotoneSequence`. Open follow-up: truncated-Taylor approximation matching Kamino's `approximate_compounded_interest`, plus an error-bound theorem connecting the approximation to the exact compounding modelled here.\n- [x] **`Finance.WithdrawalCap`** — landed: stateful sliding-window rate limiter; four theorems pin the bug classes auditors check for (saturation, invariant escape, off-by-one on the boundary, missed accumulator reset on interval rollover).\n- [ ] **`Finance.AMM`** — constant-product (`x · y = k`), then weighted pools, then concentrated liquidity. `IsAMM` becomes a `class` (this is one of the places where typeclass dispatch genuinely earns its keep — multiple instance types share an interface).\n- [ ] **`Finance.Lending`** — collateralisation-ratio invariants, liquidation conditions, interest accrual.\n- [ ] **`Primitives.Slot`, `Primitives.Epoch`** — `UInt64`-backed clock primitives.\n- [ ] **`Primitives.Hash`** — 32-byte cryptographic hash with collision-resistance as an axiom (for unsigned theorems) and refinement to specific hash functions (SHA-256, Keccak) later.\n\n### Solana semantics\n\n- [ ] **`Pda`** — `find_program_address` + the determinism theorem. Hit by every Solana program.\n- [ ] **`Sysvar.Clock`, `Sysvar.Rent`** — read-only on-chain state most programs depend on.\n- [ ] **`ProgramResult`** — `Ok` / `Err` with the standard Solana error codes.\n- [ ] **`Transaction`** — list of instructions + signers + recent blockhash.\n- [ ] **`CPI`** — cross-program-invocation semantics; invariants spanning a CPI call.\n\n### Bridges and verification\n\n- [x] **sBPF semantics in Lean (`Solanalib.SBPF`).** Landed: a Lean port of the [OOPSLA 2025 Isabelle/HOL formalisation](https://dl.acm.org/doi/10.1145/3720414) — all 22 instruction forms, the decoder, the small-step interpreter, and the verifier with the `step_ne_err` safety theorem (Lemma 6.4). Validated against a reference VM through the [`spinoza`](https://github.com/lgalabru/spinoza) harness (`spinoza validate`), which differentially tests the Lean interpreter against an executable sBPF VM; `spinoza lift` emits a deployed program's `.text` as an importable `BpfBin` term.\n- [ ] **Refinement from `SBPF/` up to the spec layers.** The pieces exist at both ends; the open work is the connecting theorem — e.g. a lifted program's `bpfInterp` result preserves a `Finance.WithdrawalCap` invariant. Also outstanding from the port: the x86-64 JIT correspondence, and reconciling the PQR high-multiply edge case against a second oracle (agave `solana-sbpf`).\n- [ ] **Aeneas integration (source-level path).** The complement to the artifact-level sBPF route above: pick one pure-math Rust function (e.g. `kfarms::get_withdrawal_penalty_bps`), run `cargo charon --preset=aeneas`, run `aeneas -backend lean`, import the generated Lean, prove `theorem rust_impl_refines_spec`. If this works end-to-end on one realistic module, we have a defensible refinement story for the algorithmic core of Solana programs.\n\n### Library hygiene\n\n- [ ] **`@[simps]` integration.** Currently we hand-write projection lemmas — Mathlib's `@[simps]` macro auto-generates them. Saves boilerplate as the codebase grows.\n- [ ] **`shake` integration.** Mathlib's unused-imports detector. Deferred because the current `notation`-heavy style doesn't play well with shake's import-usage analysis; revisit when we have ≥ 10 leaf modules.\n- [ ] **Mathlib version-bump automation.** `update.yml` workflow currently runs `lake update` monthly; add a follow-on step that bumps `lean-toolchain` to whatever Mathlib pins.\n\n---\n\n## Contributing\n\nThe skill at [`skills/lean-best-practices/SKILL.md`](skills/lean-best-practices/SKILL.md) is the canonical guide — file template, naming conventions, the `abbrev` / `notation` / `UInt64` trade-offs, common errors, the bundled-structure pattern, and the numeric-bridge discipline. Read it before adding a new primitive.\n\nIn short, the workflow for adding a new domain primitive looks like:\n\n1. Decide the file path under `Solanalib/\u003CConcept>/`. New top-level concept ↔ new top-level folder.\n2. Write the file using the template in the skill (copyright header, then imports, then `/-! … -/` module docstring, then namespace).\n3. Add the file to `Solanalib.lean`.\n4. Run `just build` locally. **Always verify locally before pushing** — CI cycles are ~2 minutes vs ~10 seconds locally.\n5. For load-bearing API, add a regression test under `SolanalibTest/\u003CConcept>/\u003CAspect>Test.lean` and import it from `SolanalibTest.lean`.\n6. Run `just test` and `scripts/lint-style.sh`.\n\nConventions worth knowing before writing the first proof:\n\n- Use `notation` over `abbrev` for type synonyms over `Nat` / `Int` (`omega` doesn't unfold `abbrev`).\n- Tag every `structure` with `@[ext]`, every projection lemma with `@[simp]`.\n- For bounded `UInt64` / `UInt128` reasoning, bridge to `Nat` via `.toNat` — `omega` doesn't reason about `UInt64` directly.\n- Bundled structures > type-classes for domain shapes with concrete parameters.\n- The first commit should follow the existing pattern (no AI-attribution trailer, copyright `(c) \u003CYEAR> Solana Foundation`, `Authors: Solanalib Contributors`).\n\n---\n\n## License\n\nApache-2.0. See [`LICENSE`](LICENSE).\n",1782661969672]