A DEX on Liquid where every order is a Tessera — a self-enforcing covenant.
Mosaik is a trustless, permissionless asset exchange on the Liquid Network. It has no order book, no matching engine, and no maker server. Each offer is a Tessera: a single Liquid coin whose Simplicity covenant enforces the trade terms. A market is a mosaic of these tesserae.
On Mosaik, an order isn't a promise a market maker might break — it's a covenant the coin itself keeps.
Built for the Blockstream Simplicity Bootcamp & Hackathon — Blox Space, Turin, 16–17 May 2026.
A maker wants to sell amount_A of asset A for amount_B of asset B.
Instead of posting an order to an exchange, the maker locks asset A into a
single Liquid UTXO controlled by a Tessera — a Simplicity covenant. That
covenant inspects any transaction trying to spend the coin and allows it
only if the transaction pays the maker exactly amount_B of asset B.
That one UTXO is the offer:
- Permissionless — any taker can fill it by building a transaction that pays the maker. The maker does not need to be online.
- Uncheatable — the taker cannot take asset A without paying asset B; the coin itself rejects any spend that does not. No escrow, no custody.
- Atomic — one Elements transaction moves both assets, or neither confirms.
- No infrastructure — no order book, no matching engine, no maker daemon.
The maker keeps a refund path: after a timeout the coin can be swept home — the covenant only allows a spend that returns the locked asset to the maker, so no key is needed.
The whole construction rests on transaction introspection — a coin reading
the outputs of the transaction spending it. Bitcoin Script cannot do this.
LiquiDEX-style swaps get atomicity from a SIGHASH trick; adaptor-signature
PTLCs work on plain Taproot. A covenant that enforces the counter-payment is
the thing that genuinely requires Simplicity.
- A Tessera is one offer — one covenant, one tile.
- Mosaik is the market they form — the mosaic.
(A kaleidoscope shows a mosaic — the names sit naturally inside the KaleidoSwap family.)
┌─────────────────────────────────────────────┐
│ Tessera UTXO (holds amount_A of asset A) │
│ │
│ SETTLE path: spendable by ANYONE iff the │
│ spending tx has an output paying │
│ exactly amount_B of asset B to MAKER │
│ │
│ REFUND path: after TIMEOUT, spendable by │
│ ANYONE — but only to return the locked │
│ asset to MAKER (keyless, no signature) │
└─────────────────────────────────────────────┘
maker taker
│ 1. lock asset A in the Tessera UTXO │
│ (terms baked into the tapleaf) │
│ ───────────── publish offer (outpoint) ────────► │
│ │
│ 2. build a tx: │
│ input = Tessera UTXO + own coins │
│ output = amount_B asset B → maker │
│ output = amount_A asset A → taker │
│ + fee │
│ ◄──────── 3. covenant verifies & tx confirms ─── │
The Tessera's four parameters (asset B id, amount B, maker script, timeout) are committed into the Taproot tapleaf, so they cannot be altered after the offer is published. There is no maker key — the covenant is pure transaction introspection.
mosaik/
├── crates/
│ ├── tessera/ # the Tessera Simplicity covenant
│ │ ├── contracts/ # SimplicityHL (tessera.simf) source
│ │ └── src/ # Rust: parameterise + compile the covenant
│ ├── mosaik-core/ # offers, multi-asset PSET construction, Elements RPC
│ ├── mosaik-relay/ # Nostr orderbook — publish / discover Tessera offers
│ └── mosaik-cli/ # `mosaik` CLI + the browser wallet UI server
├── webapp/index.html # the covenant DEX UI — pair filter, one wallet
├── docs/DESIGN.md # full protocol + covenant spec
├── docs/slides.html # hackathon deck
└── scripts/regtest.sh # local Elements regtest harness
- Tessera covenant compiles to a CMR; SETTLE and REFUND paths covered by execution tests.
- Enforced settlement on a Simplicity-capable
elementsd— the node executes the covenant and rejects any spend that underpays the maker. -
mosaikCLI end-to-end: make-offer → take-offer → confirmed. - Multi-asset swaps — L-BTC/asset, asset/L-BTC and asset/asset, in either direction (the covenant never inspects the locked asset).
- Nostr orderbook (
mosaik-relay) and an exchange-style browser UI — a single trading-pair filter, one user wallet that both makes and takes, and a book seeded by several independent market makers. - Adversarial "attack the covenant" path — underpay, wrong recipient, hidden maker output — all rejected by the covenant.
- Keyless REFUND — after the timeout anyone may sweep an unfilled offer, and the covenant only accepts a spend that returns the locked asset to the maker. No signature, no maker key — pure transaction introspection.
- Recursive partial-fill covenant — fund once, fill many times.
You need three things: the Rust toolchain, a Simplicity-capable elementsd,
and hal-simplicity.
The quick path — once you have the Simplicity elementsd (see below) — is the
helper script. It installs hal-simplicity, locates and verifies the node,
ad-hoc signs the binaries on macOS, and builds the workspace:
./scripts/install-deps.shOr do it by hand:
# 1. Rust (the workspace pins its toolchain via rust-toolchain.toml)
curl https://sh.rustup.rs -sSf | sh
# 2. hal-simplicity — assembles the covenant witness for a settlement
cargo install hal-simplicity # lands in ~/.cargo/bin
# 3. build the workspace
cargo buildStock Elements can fund a covenant address (a normal Taproot payment) but
cannot validate a covenant spend — leaf version 0xbe is non-standard, so a
Tessera settlement is rejected. Spending a covenant needs an elementsd built
with the Simplicity consensus rules.
The Mosaik demo was developed against the build bundled with smplx (the
Simplicity dev framework — its simplex CLI ships a matching elementsd,
elements-cli, and electrs). Either source works:
- smplx — install the
simplextoolchain and use theelementsdit bundles. See github.com/BlockstreamResearch/smplx. - Simplicity codespace — the Blockstream
simplicity-codespace
ships a Simplicity-capable
elementsdand the SimplicityHL tooling.
Put the binaries somewhere stable and point Mosaik at them — the demo expects
them under tools/ (git-ignored), but any path works via ELEMENTSD_EXEC:
mkdir -p tools
cp /path/from/smplx/{elementsd,elements-cli,electrs,simplex} tools/
# verify it is a Simplicity build (Elements Core v23.3.1, from smplx)
tools/elementsd -version | head -1macOS: a freshly downloaded
elementsdis unsigned and gets SIGKILL-ed. Ad-hoc sign each binary once:codesign -s - tools/elementsd tools/elements-cli.
hal-simplicity must be on PATH (step 2 puts it in ~/.cargo/bin), or set
HAL_SIMPLICITY=/path/to/hal-simplicity.
# 1. start a local Elements regtest with the Simplicity-capable node
export ELEMENTSD_EXEC="$PWD/tools/elementsd"
./scripts/regtest.sh up # starts elementsd + funds the treasury wallet
# 2. serve the browser wallet UI
cargo run -p mosaik-cli -- serve-wallet --port 8080
# 3. open the UI
open http://127.0.0.1:8080In the UI:
- Pick a trading pair from the filter at the top — every panel (order book, depth, settlement chart, trade tape, open orders) re-renders for it.
- Fund wallet — your wallet gets L-BTC plus the two test assets (USDT, EURx). One wallet both makes and takes.
- Post an offer — choose a side (sell/buy the base asset), set the amounts, and fund the covenant UTXO. The offer is also published to the Nostr orderbook (kind 30050).
- On an offer card: Take offer settles it via the covenant; the red Attack tests build fraudulent fills and show the covenant rejecting them; Covenant + Nostr shows the compiled SimplicityHL source, the Commitment Merkle Root, and the offer's Nostr event.
To populate the book with offers from several makers, run
./scripts/demo-seed.sh while the server is up.
Stop everything with ./scripts/regtest.sh down and pkill -f 'mosaik serve'.
The same flow without the browser:
cargo run -p mosaik-cli -- --help
cargo run -p mosaik-cli -- make-offer --amount-a 1000000 --amount-b 5000000000 \
--asset-b <usdt-asset-id> --timeout 500 > offer.json
cargo run -p mosaik-cli -- take-offer --offer offer.json
cargo run -p mosaik-cli -- serve-relay --port 7777 # local Nostr orderbookThe Simplicity contract itself is developed in
Blockstream/simplicity-codespace —
see crates/tessera/contracts/tessera.simf.
- Simplicity docs — https://docs.simplicity-lang.org
- Liquid docs — https://docs.liquid.net
- Elements — https://github.com/ElementsProject/elements
- Simplicity codespace — https://github.com/Blockstream/simplicity-codespace
- LWK walk-through — https://www.youtube.com/watch?v=C2p4jzplg4c
- Simplicity Dev TG — @simplicity_community
MIT — see LICENSE.