Workspace for prototyping and exercising Simplicity-based contracts on Liquid testnet. It includes:
- A small CLI for building and broadcasting transactions
- A contracts crate housing Simplicity sources and helpers (e.g., an Options covenant contract)
- A high-level helper library to compile, run, and finalize Simplicity programs
-
crates/simplicityhl-core— High-level helpers around Simplicity on Elements/Liquid- Address derivation for P2TR Simplicity programs (
create_p2tr_address,get_p2pk_address) - Program compilation and execution (
load_program,run_program), trackers, and logging - Transaction finalization helpers to attach Simplicity witnesses (
finalize_transaction,finalize_p2pk_transaction) - Explorer utilities for Esplora broadcast/fetch with on-disk caching
- Constants for Liquid testnet (policy asset, LBTC id, genesis hash)
- Embedded
p2pk.simfprogram
- Address derivation for P2TR Simplicity programs (
-
crates/contracts— Contract templates and helpersoptions/contains a Simplicity Options contract (options.simf) based on the options whitepaper, argument builders, and witness builderssimple_storage/contains a minimal stateful storage covenant (simple_storage.simf), argument and witness buildersget_options_program,get_options_address, andfinalize_options_funding_path_transactionget_storage_compiled_program,get_storage_address, and helpers to finalize storage transactions- Bincode-based encoding of argument structs (via
simplicityhl-coreencoding feature)
-
crates/cli— Simplicity helper CLI (Liquid testnet)basiccommands: P2PK address derivation and simple LBTC/asset transfersoptionscommands: create/fund/exercise/settle/expire/cancel paths for the Options contractstoragecommands: initialize storage state and update state (mint/burn paths)- Uses a local sled store at
.cache/storefor argument persistence
-
Finalization and verification
finalize_transactionandfinalize_p2pk_transactionverify the input UTXO’s script matches the program CMR+key, build anElementsEnv, execute the program, and attach the Simplicity witness to the chosen input.
-
Ephemeral key binding
TaprootPubkeyGendeterministically binds a program’s arguments to a public key and address without holding a private key. Its string form is<seed_hex>:<xonly_pubkey>:<taproot_address>and can be re-verified later with the same arguments.
-
General
- All transactions built here are explicit (unblinded). Fee outputs are explicit LBTC. The code targets Liquid testnet (
AddressParams::LIQUID_TESTNET). - The CLI fetches UTXOs from the Liquid Testnet explorer and caches raw tx hex under
.cache/explorer/tx/. - When
--broadcastis provided, transactions are POSTed to Esplora and the txid is printed. Without it, raw hex is printed.
- All transactions built here are explicit (unblinded). Fee outputs are explicit LBTC. The code targets Liquid testnet (
-
Basic CLI (P2PK)
basic address <index>prints the derived X-only public key and its P2TR address.basic transfer-nativebuilds a 1-in/2-out LBTC spend with change + a separate fee output, then Schnorr-signs and optionally broadcasts.basic split-nativeenforces thatfirst + second + fee == input valuefor exact splits.basic transfer-assetspends an ASSET UTXO and a separate LBTC fee UTXO, returning change for both and enforcing the fee UTXO is LBTC.
-
Options contract
- Creation: mints two reissuance tokens (option/grantor), persists entropies for later reissuance, derives/stores
OptionsArguments, and prints theTaprootPubkeyGenstring for the covenant instance. - Funding: reissues both tokens forward to the covenant address, deposits LBTC collateral, returns base assets as change, and attaches Simplicity witnesses for both token inputs.
- Exercise (option-holder): burns option tokens, posts the settlement amount in target asset back to the covenant, and withdraws the proportional collateral to a P2PK recipient. Uses
fallback_locktimeandSequence::ENABLE_LOCKTIME_NO_RBFwhere required by the covenant. - Settlement (grantor): burns grantor tokens against target asset held by the covenant, forwards settlement asset, and pays fees from a P2PK LBTC UTXO.
- Expiry (grantor): after expiry, burns grantor tokens and withdraws the corresponding collateral to a P2PK recipient (fees deducted from collateral input).
- Cancellation: burns both tokens and withdraws a portion of collateral to a P2PK recipient (fees deducted from collateral input).
- The covenant expects specific output ordering and conditional change outputs; the CLI constructs outputs in that order.
- Creation: mints two reissuance tokens (option/grantor), persists entropies for later reissuance, derives/stores
-
Simple Storage
- Init: issues a slot asset and a reissuance token, binds
StorageArgumentsand persists the initial entropy for later reissuance. - Update: enforces that output[0] remains at the covenant with
SLOT_IDset toNEW_VALUE.- Burn path (decrease): burns the exact delta to OP_RETURN at index 1.
- Mint path (increase): consumes the reissuance token UTXO and enforces a covenant output at the next index.
- Only the bound owner key may authorize updates (Schnorr over
sig_all_hash). - See
crates/contracts/src/simple_storage/README.mdfor details.
- Init: issues a slot asset and a reissuance token, binds
Here are some resources to help you get started with asset swaps on Liquid:
- Learn about asset swap transactions and how to start developing smart contracts (such as options) on Liquid: https://docs.liquid.net/docs/swaps-and-smart-contracts
- A helpful article on two-step atomic swaps on the Liquid Network: https://medium.com/blockstream/liquidex-2-step-atomic-swaps-on-the-liquid-network-8a7ff6fb7aa5
This functionality completes the set of tools needed to build a DEX-like system where users can trade options.
Note: To swap two tokens for one (e.g., 1 Option Token and 1 Grantor Token for 1 LBTC), a custom Simplicity script is required.
Prerequisites
- Rust (as per crate requirements;
simplicityhl-coredeclaresrust-version = 1.90)
Setup
- In
crates/cli, create a.envfile with a 32-byte hex seed:SEED_HEX=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f - Build and view help:
cargo run -p cli -- --help
Examples
-
Address derivation:
cargo run -p cli -- basic address 0 -
Transfer LBTC (print hex or broadcast):
cargo run -p cli -- basic transfer-native \ --utxo <txid>:<vout> \ --to-address <tlq1...> \ --send-sats 150000 \ --fee-sats 500 \ --account-index 0 \ --broadcast -
Options import/export of encoded arguments:
cargo run -p cli -- options import --help cargo run -p cli -- options export --help -
Options flows (creation → funding → exercise/settlement/expiry/cancellation): see
crates/cli/README.mdfor full command lines. -
Simple Storage flows, see
crates/contracts/src/simple_storage/README.mdfor the detailed explanation.
-
Tests
- Unit tests exist in
contractsandsimplicityhl-core(including program path tests). Run:cargo test -p simplicityhl-core -p contracts
- Unit tests exist in
-
Debugging program execution
- Use
RunnerLogLevel::{Debug,Trace}and the provided trackers (DefaultTracker,DebugTracker) to observedbg!values and jet traces during execution. - Logs can be printed only if the program is successfully executed!
- Use
-
Encoding and persistence
- Enable the
encodingfeature insimplicityhl-coreto use theEncodabletrait for bincode encoding/decoding of arguments. The CLI persists encoded options arguments in a local sled store under.cache/store.
- Enable the
-
Adding a new contract
- Add a new
*.simfundercrates/contracts/<your_contract>/source_simf/. - Create argument and witness builders mirroring
options/build_arguments.rsandoptions/build_witness.rs. - Expose helpers to derive the address and finalize transactions.
- Add CLI subcommands that construct outputs in the order your covenant expects, then attach the appropriate Simplicity witness(es).
- Add a new
-
Performance and UX
- Transactions are explicit; Addresses are constantly reused; privacy is not a goal here.
- Liquid testnet only. Explicit transactions leak amounts/assets by design here.
- Ensure UTXOs spent by the CLI belong to the derived P2TR address for the chosen
account-index. - Always verify the printed addresses and parameters match your expectations before broadcasting.