Skip to content

kaleidoswap/mosaik

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mosaik

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.


The idea

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.

Why this needs Simplicity

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.

Mosaik & Tessera

  • 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.)


How it works

        ┌─────────────────────────────────────────────┐
        │  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.


Repository layout

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

Status

  • 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.
  • mosaik CLI 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.

Install

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.sh

Or 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 build

Simplicity-capable elementsd

Stock 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:

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 -1

macOS: a freshly downloaded elementsd is 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.

Run

# 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:8080

In the UI:

  1. 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.
  2. Fund wallet — your wallet gets L-BTC plus the two test assets (USDT, EURx). One wallet both makes and takes.
  3. 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).
  4. 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'.

CLI

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 orderbook

The Simplicity contract itself is developed in Blockstream/simplicity-codespace — see crates/tessera/contracts/tessera.simf.

References

License

MIT — see LICENSE.

About

Mosaik — a DEX on Liquid where every order is a Tessera, a self-enforcing Simplicity covenant

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors