import * as React from "react"; import { CoreShellDiagram } from "@/src/components/notes/CoreShellDiagram"; import { NoteFigure, NoteList, NoteParagraph, NoteSection, } from "@/src/components/notes/NoteProse"; import type { Note } from "./types"; const Body: React.FC = () => ( <> I am building a liquidation system that runs across 7 chains. It is written in Rust, with Solidity executors on chain. It is built to run unattended, which changes how much you can rely on manual checking: almost all of the confidence has to come from tests that run before the code does. The architecture that makes this workable is a functional core with imperative shells. The shape A detector per chain indexes borrower positions and computes health factors. That work is split in two. The math lives in a pure domain crate: health factor, fixed-point arithmetic, and a closed-form optimum that is checked against an exact swap engine. The crate has no network, no clock, no randomness and no global state. Everything arrives as arguments and leaves as return values. Around that core sit thin shells. A shell reads state from a chain, converts it into plain data, calls the core, and acts on the answer. It contains almost no decisions, so there is little in it that can be wrong in an interesting way. The on-chain side The executor contract does one job in one transaction. It takes a flash loan, liquidates the position, swaps the collateral, repays the loan, and reverts if the trade would not be profitable. Because the transaction is atomic, a failed attempt leaves nothing behind except the gas for the attempt. No own capital is at risk per attempt, which is a property of the design rather than of careful operation. The same idea applies on chain as off chain: the contract is a small, closed piece of logic with explicit inputs. The off-chain core decides whether to try; the contract has the final say. Testing the edge, not the middle Fixed-point arithmetic has cliffs. Past a certain input size a multiplication no longer fits in the integer type. If the code wraps around, a hugely healthy position can suddenly look unhealthy, or the other way round, and nothing crashes to tell you. One spec in the domain crate tests the exact overflow cliff of the health factor. It pins the behavior one step below the cliff and at the cliff, and requires the result to saturate instead of wrapping. This kind of case is where an optimizing refactor or a generated change breaks things silently, and it is trivial to write because the core is a pure function: call it with the boundary values and compare the result. Why the split pays off
  • The core is testable without a network. Specs run fast, with no node, no fork and no flaky timing. That makes spec-first practical for the part with the most arithmetic.
  • Mutation testing can prove the specs. Pure functions give mutation tools clean targets. If a mutant of the health factor survives, a spec is missing, and the gate says so. That is a stronger statement than a coverage percentage.
  • Shells are proven by end-to-end specs. They are not pure, so they are not tested like the core. They are exercised as a whole against the behavior they are meant to produce, and because they hold so little logic, those specs stay small.
  • Where it hurts The discipline has a cost. You have to keep deciding where a line of code belongs, and the temptation to put a small calculation in the shell because it is right there is constant. Every such shortcut moves logic into the part that is hardest to test. The gates described in the note on mechanical spec-first enforcement help here: a gate that forbids methods, mutable references and unwrap pushes the code toward the style the core depends on. The pattern is not specific to this project. Any system where money, permissions or irreversible actions sit behind a thin layer of I/O gains from keeping the decision logic pure and the I/O boring. ); export const functionalCoreImperativeShell: Note = { slug: "functional-core-imperative-shell-multi-chain", title: "Functional core, imperative shell in a multi-chain system", summary: "How a pure Rust core, thin shells and an atomic on-chain executor make a 7-chain liquidation system testable.", publishedOn: "2026-10-02", readingMinutes: 4, tags: ["architecture", "rust", "web3"], Body, };