Introduction
Breakwater is a Rust framework and CLI for stateful property testing of Stellar smart contracts. You declare actors (who can call), handlers (what they can call) and invariants (what must always hold). Breakwater generates randomized multi-step transaction sequences against a local Soroban Env, checks every invariant after every step, shrinks any failing sequence to its minimal form, and exports it as a reproducible Rust unit test.
It ships with two attack packs for risks that are specific to Stellar:
| Attack pack | Risk it hunts | Why it matters |
|---|---|---|
policy-bypass | Multi-step sequences that slip past OpenZeppelin smart-account policies (spending limits, context rules, signer scopes) | These policies are the safety layer for AI-agent wallets paying over x402 / MPP. A policy that holds for one transaction may fail for five. |
ttl-archival | Contract state that expires under Soroban’s TTL-based state archival while the business logic still depends on it | A one-year payment stream whose storage entry is archived at month six is a correctness bug no EVM tool knows to look for. |
Status: HackMeridian 2026 submission (Genesis path · Stellar Unlocked track). Built 25–26 October 2026 in Lisbon. See Prior work and hackathon scope.
Submission materials: website https://breakwater.belvast.de · pitch deck
docs/deck/breakwater-pitch.pdf· demo video https://vimeo.com/1239334137 (3:43).
Problem
Soroban already has good tools for three layers of contract security:
- Static analysis — Scout (CoinFabrik)
- AI review — Almanax
- Formal verification — Certora Sunbeam
What is missing is the layer in between: stateful fuzzing. The official soroban-examples repository shows how to wire up cargo-fuzz and proptest by hand, and soroban-token-fuzzer covers the standard token interface, but there is no reusable framework where a developer can declare invariants and let a tool search for the transaction sequence that breaks them. Every team that wants this rebuilds it from scratch.
Two Stellar-specific developments make the gap urgent:
- Agentic payments. x402 and MPP are live on Stellar mainnet. AI agents hold smart accounts whose on-chain policies are the only thing standing between a prompt injection and a drained wallet. Nobody has a tool that attacks those policies with sequences rather than single calls.
- State archival. Soroban storage entries have a time-to-live. Contracts that forget to extend it, or extend it on the wrong schedule, fail silently months after deployment. The standard advice is “test it by bumping the ledger sequence manually”.
Breakwater is the reusable layer for both.
How it works
%%{init: {"flowchart": {"wrappingWidth": 400}}}%%
flowchart TD
declare["<b>Declare</b><br/>actors, handlers, invariants"]
generate["<b>Generate</b><br/>random call sequences (proptest)"]
check["<b>Check</b><br/>every invariant after every step"]
shrink["<b>Shrink and export</b><br/>a minimal sequence as a Rust unit test"]
declare --> generate --> check
check -- next step --> generate
check -- violation --> shrink
- Declare. Implement the
Handlertrait for each contract call the fuzzer may make, give each one anActor, and implementInvariantfor each property that must hold. - Generate. Breakwater drives a Soroban
Env(withtestutils) usingproptest-state-machine. Each step picks a handler, an actor and arguments; ledger sequence and timestamp advance between steps so TTL and time-based logic are exercised. - Check. After every step every invariant is evaluated. Authorization runs through the real
__check_authpath for thepolicy-bypasspack so that smart-account policies are actually enforced, not mocked. - Shrink and export. A failing sequence is shrunk to the shortest reproduction and written out as a self-contained
#[test]you can drop into your crate.
A full architecture description is in docs/ARCHITECTURE.md.