scriptura β from the French monnaie scripturale, "book money": money that exists as ledger entries in banks' books. This project puts those ledger entries on-chain.
An on-chain simulation of the two-tier monetary system: tokenized central bank money (wCBDC), tokenized commercial bank deposits (DEP-A / DEP-B), and a fiat-backed stablecoin (sEUR) whose reserves are those deposits β with atomic interbank settlement in central bank money, gasless EIP-712 payment intents, a Foundry invariant suite, and a role-based dashboard (client, bank operator, stablecoin issuer, central bank, observer).
Status: V1 β feature-complete, deployed & Etherscan-verified on Sepolia, validated end-to-end on the live deployment. V2+ (crisis scenarios, lender of last resort, AMM, multi-chain) is designed but deliberately not built β see Roadmap.
The public observer dashboard β the whole system at a glance, readable without connecting a wallet: monetary aggregates (M0, M1 per bank, sEUR in circulation), each bank's health badge and reserve-ratio gauge, and StableCo's live proof of reserves.
- The singleness of money. A euro at Bank A equals a euro at Bank B because every interbank transfer settles in central bank money: burn DEP-A β move wCBDC β mint DEP-B, in one transaction.
- Fractional-reserve fragility. The soft constraint (ratio breach β event, payments continue) is separated from the hard one (insufficient wCBDC β revert): bank stress is gradual and observable β HEALTHY π’ / STRESSED π / ILLIQUID π΄.
- TradFi β stablecoin contagion. sEUR is 100% backed by deposits at Bank A: freeze the bank and redemptions break while sEUR keeps circulating (the USDC/SVB 2023 pattern).
- Endogenous stablecoin supply. No admin mint exists; coverage β₯ 100% is a tested invariant and proof of reserves is a view call.
- Intermediated vs permissionless money. DEP is registry-gated and freezable; sEUR moves gasless (EIP-3009) or wallet-direct, even when the infrastructure is down.
All 9 instances deployed by one make deploy-sepolia, sources verified on Etherscan
(chain id 11155111 β see deployments/sepolia.json):
| Contract | Address | Role |
|---|---|---|
| CentralBank | 0x2fa4β¦367C |
M0 issuance, bank allowlist, ratio threshold |
| wCBDC | 0xF4dAβ¦2D05 |
Central bank money (banks only) |
| Bank A | 0x0B3Cβ¦B431 |
Reserves, client registry, freeze |
| DEP-A | 0x41E3β¦Ff23 |
Bank A's tokenized deposits |
| Bank B | 0x486Cβ¦977a |
Reserves, client registry, freeze |
| DEP-B | 0xd666β¦14a9 |
Bank B's tokenized deposits |
| SettlementEngine | 0x7838β¦5c06 |
EIP-712 intents, atomic settlement |
| StableCo | 0x5c48β¦6D24 |
Reserve vault, sEUR mint/redeem |
| sEUR | 0x4bC5β¦E797 |
Permissionless stablecoin + EIP-3009 |
Reference transactions from the end-to-end run on the live deployment:
| Flow | Tx |
|---|---|
| Interbank payment β wCBDC settles the exact amount | 0x353dβ¦bd9c |
| Cross-bank sEUR mint β burn DEP-B β wCBDC BβA β mint DEP-A to the vault β mint sEUR, one tx | 0x2479β¦6fb4 |
| Cross-bank sEUR redeem β the composed mirror (AβB) | 0x9efaβ¦6dba |
| Gasless sEUR transfer (EIP-3009, relayer pays gas) | 0x8634β¦e6d2 |
- 7 monetary invariants fuzzed with Foundry invariant testing (
fail_on_revert, production signing paths β real EIP-712 signatures, third-party submitter). - Gasless by signature, trustless by contract: a stateless relayer pays gas, the contracts re-verify everything; the engine is relay-agnostic, so censorship is operational, never contractual.
- Three anti-replay nonce namespaces, deliberately not unified (sequential engine + sequential StableCo + random EIP-3009).
- Composed cross-bank flows: a Bank B client minting sEUR moves value across all three monetary layers atomically, with coverage provably β₯ 100% at every step.
- No database anywhere: the chain is the source of truth β the relayer re-derives its state at boot, the indexer is a rebuildable projection, the frontend stores nothing.
MetaMask (client keys: EIP-712/3009 signing Β· operator direct txs)
β
βββββββββββββββββ΄βββββββββββββββββ
β Frontend β Vite + React/wagmi β :5173
ββββ¬βββββββββββββ¬βββββββββββββ¬ββββ
POST /intent β β GET /β¦ β live reads (viem)
POST /faucet β β β
βββββββββββΌβββββββ ββββΌβββββββββββ β
β Relayer β β Indexer β β
β Fastify + viem β β Ponder+Hono β β
βββββββββββ¬βββββββ ββββ²βββββββββββ β
txs β β eth_getLogsβ
βΌ β βΌ
βββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββ
β Chain (Anvil dev / Sepolia live) β source of truth β
β CentralBankββwCBDC Bank AββDEP-A Bank BββDEP-B β
β SettlementEngine StableCoββsEUR β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Prerequisites: Foundry, Node.js β₯ 20, GNU Make, MetaMask.
git clone --recurse-submodules https://github.com/TheBossMickael/scriptura
cd scriptura
cp .env.example .env
(cd relayer && npm install)
(cd indexer && npm install)
(cd frontend && npm install)The committed deployment already carries real state and history β start there. The app is
three services (relayer, indexer, front); against the live deployment you run two of
them. Set VITE_CHAIN="sepolia" in .env, then:
make front # dashboards on http://localhost:5173, live Sepolia data
CHAIN=sepolia make indexer # history walls & lists (public RPC pool preconfigured)That gives you every dashboard read-only on live on-chain data, plus direct sEUR
transfers with your own gas. The third service β the relayer, which powers every gasless
action β authenticates as the deployment's relayer EOA, whose key stays with the
operator. For the full three-service experience: run everything locally (next section,
five minutes), deploy your own Sepolia instance (make deploy-sepolia with your own
keys/RPC regenerates deployments/sepolia.json), or open an issue to ask for a live run.
The fully interactive stack β gasless payments, faucet onboarding, freezes. Nothing to
configure: the .env you copied already contains everything for local
(VITE_CHAIN="local" plus Anvil's well-known, pre-funded test keys for every role β no
real value ever touches them). Anvil, bundled with Foundry, is the supported local chain:
the genesis scripts key on its chain id (31337) and its pre-funded accounts. Four
terminals:
make anvil # 1 β local chain
make deploy-local # 2 β deploy + seed the full genesis (run once), then:
make relayer # 2 β gasless relayer on :3001
make indexer # 3 β indexer + REST API on :42069
make front # 4 β app on http://localhost:5173All three services must be up for the full experience: the relayer powers every gasless
action, the indexer powers the history sections. Point MetaMask at localhost:8545
(chain id 31337) and import a genesis client key from .env (e.g. alice1) β or connect
any wallet and use Join.
The demo is designed so a visitor can act, not just watch: connect an unknown wallet
and the observer view offers Join / get test euros β the relayer (holding a narrow
on-chain FAUCET_ROLE: register + capped credit, one-shot per address, nothing else)
onboards you as a bank client. From there everything runs gasless with your own
signatures: pay intrabank/interbank, mint and redeem sEUR (including the cross-bank
composed flow), transfer sEUR P2P. This needs the stack running (relayer + indexer +
front) β the local setup above gives you exactly that.
Each dashboard is what the app resolves for the connected account's on-chain role β the shots below were taken connected as the actual client/operator accounts, on live Sepolia data. You don't need those keys to see the same screens: the "Explore as" dropdown (top of the app) lets anyone browse every view read-only.
Client β connected as a Bank A client: balances, the bank's live health (the panic signal), gasless payment, sEUR mint/redeem and both P2P paths β then the personal history.
![]() |
![]() |
Central bank β connected as the central-bank operator: macro aggregates, per-bank
monitors, allowlist and threshold controls, the ReserveRatioBreached alert wall (note
the real breach at 9.66%) β and every interbank flow, including StableCo's composed
settlements.
![]() |
![]() |
Bank operator and StableCo β the live two-column balance sheet with its ratio gauge and freeze switch; the issuer's proof of reserves with its coverage badge.
![]() |
![]() |
Known limitations (documented, not hidden)
- Indexer history lags ~30 sβ2 min behind the head on Sepolia (public RPC pool, 15 s polling); live balances are direct RPC reads and stay instant.
- Amount inputs parse commas as en-US thousands separators:
1,500= 1500. - The relayer is a single point of failure β on purpose (real settlement systems have the same shape); mitigations and analysis in docs/threat-model.md.
- Sequential nonces allow one in-flight action per client β the UI locks buttons until confirmation.
- Testnet-grade key handling: throwaway keys in a gitignored
.env; the production posture (encrypted keystore) is documented, not implemented.
155 Foundry tests (132 unit Β· 17 integration Β· 6 invariant properties, 128 runs Γ depth 64, zero tolerated reverts) + 25 relayer and 25 frontend vitest suites β plus a full E2E user simulation, green against the live Sepolia deployment.
make test| Doc | What's inside |
|---|---|
| docs/monetary-design.md | The "why": two-tier money, singleness, hard vs soft constraints, MiCA/RTGS/unified-ledger anchors |
| docs/architecture.md | The "how": contracts, roles, flows, keys, RPC strategy β plus the phase-by-phase build history |
| docs/contracts.md | Per-contract reference; EIP-712 domains; the invariants β code β tests map |
| docs/scenarios.md | 12 numbered walk-throughs, each tied to its integration test and, where applicable, a live Sepolia tx |
| docs/threat-model.md | Powers & blast radius, attack surfaces, the assumed SPOF |
| docs/frontend.md | Role views, blocking UX, pre-simulation, chain pinning |
| docs/indexer.md | Derived-store design, REST API β and the field note on RPC getLogs caps |
| docs/project.md | The consolidated blueprint: actors, mechanisms, genesis, stack decisions, roadmap |
V1 (this release) is the deliverable. The design leaves explicit hooks for:
- V2 β crisis & liquidity: orchestrated bank-run / depeg / relayer-outage scenarios, interbank lending, refinancing + lender of last resort, SIWE-protected scenario controls.
- V3 β markets & policy: an sEUR/DEP AMM (a real market price β observable depeg), remunerated reserves, credit creation.
- V4 β multi-chain: the L1 as settlement layer, one rollup per commercial bank, cross-chain settlement via HTLCs and intents.
MIT β Β© 2026 Mickael Osorio.







