|
1 | 1 | ## Simplicity Contracts |
| 2 | + |
| 3 | +Workspace for prototyping and exercising Simplicity-based contracts on Liquid testnet. It includes: |
| 4 | +- A small CLI for building and broadcasting transactions |
| 5 | +- A contracts crate housing Simplicity sources and helpers (e.g., an Options covenant contract) |
| 6 | +- A high-level helper library to compile, run, and finalize Simplicity programs |
| 7 | + |
| 8 | +## Workspace structure |
| 9 | + |
| 10 | +- `crates/simplicityhl-core` — High-level helpers around Simplicity on Elements/Liquid |
| 11 | + - Address derivation for P2TR Simplicity programs (`create_p2tr_address`, `get_p2pk_address`) |
| 12 | + - Program compilation and execution (`load_program`, `run_program`), trackers, and logging |
| 13 | + - Transaction finalization helpers to attach Simplicity witnesses (`finalize_transaction`, `finalize_p2pk_transaction`) |
| 14 | + - Explorer utilities for Esplora broadcast/fetch with on-disk caching |
| 15 | + - Constants for Liquid testnet (policy asset, LBTC id, genesis hash) |
| 16 | + - Embedded `p2pk.simf` program |
| 17 | + |
| 18 | +- `crates/contracts` — Contract templates and helpers |
| 19 | + - `options/` contains a Simplicity Options contract (`options.simf`), argument builders, and witness builders |
| 20 | + - `get_options_program`, `get_options_address`, and `finalize_options_funding_path_transaction` |
| 21 | + - Bincode-based encoding of argument structs (via `simplicityhl-core` encoding feature) |
| 22 | + |
| 23 | +- `crates/cli` — Simplicity helper CLI (Liquid testnet) |
| 24 | + - `basic` commands: P2PK address derivation and simple LBTC/asset transfers |
| 25 | + - `options` commands: create/fund/exercise/settle/expire/cancel paths for the Options contract |
| 26 | + - Uses a local sled store at `.cache/store` for argument persistence |
| 27 | + |
| 28 | +### Note |
| 29 | + |
| 30 | +- Finalization and verification |
| 31 | + - `finalize_transaction` and `finalize_p2pk_transaction` verify the input UTXO’s script matches the program CMR+key, build an `ElementsEnv`, execute the program, and attach the Simplicity witness to the chosen input. |
| 32 | + |
| 33 | +- Ephemeral key binding |
| 34 | + - `TaprootPubkeyGen` deterministically 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. |
| 35 | + |
| 36 | +## Expected behavior |
| 37 | + |
| 38 | +- General |
| 39 | + - All transactions built here are explicit (unblinded). Fee outputs are explicit LBTC. The code targets Liquid testnet (`AddressParams::LIQUID_TESTNET`). |
| 40 | + - The CLI fetches UTXOs from the Liquid Testnet explorer and caches raw tx hex under `.cache/explorer/tx/`. |
| 41 | + - When `--broadcast` is provided, transactions are POSTed to Esplora and the txid is printed. Without it, raw hex is printed. |
| 42 | + |
| 43 | +- Basic CLI (P2PK) |
| 44 | + - `basic address <index>` prints the derived X-only public key and its P2TR address. |
| 45 | + - `basic transfer-native` builds a 1-in/2-out LBTC spend with change + a separate fee output, then Schnorr-signs and optionally broadcasts. |
| 46 | + - `basic split-native` enforces that `first + second + fee == input value` for exact splits. |
| 47 | + - `basic transfer-asset` spends an ASSET UTXO and a separate LBTC fee UTXO, returning change for both and enforcing the fee UTXO is LBTC. |
| 48 | + |
| 49 | +- Options contract |
| 50 | + - Creation: mints two reissuance tokens (option/grantor), persists entropies for later reissuance, derives/stores `OptionsArguments`, and prints the `TaprootPubkeyGen` string for the covenant instance. |
| 51 | + - 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. |
| 52 | + - 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_locktime` and `Sequence::ENABLE_LOCKTIME_NO_RBF` where required by the covenant. |
| 53 | + - Settlement (grantor): burns grantor tokens against target asset held by the covenant, forwards settlement asset, and pays fees from a P2PK LBTC UTXO. |
| 54 | + - Expiry (grantor): after expiry, burns grantor tokens and withdraws the corresponding collateral to a P2PK recipient (fees deducted from collateral input). |
| 55 | + - Cancellation: burns both tokens and withdraws a portion of collateral to a P2PK recipient (fees deducted from collateral input). |
| 56 | + - The covenant expects specific output ordering and conditional change outputs; the CLI constructs outputs in that order. |
| 57 | + |
| 58 | +## Getting started |
| 59 | + |
| 60 | +Prerequisites |
| 61 | +- Rust (as per crate requirements; `simplicityhl-core` declares `rust-version = 1.90`) |
| 62 | + |
| 63 | +Setup |
| 64 | +1. In `crates/cli`, create a `.env` file with a 32-byte hex seed: |
| 65 | + ``` |
| 66 | + SEED_HEX=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f |
| 67 | + ``` |
| 68 | +2. Build and view help: |
| 69 | + ``` |
| 70 | + cargo run -p cli -- --help |
| 71 | + ``` |
| 72 | + |
| 73 | +Examples |
| 74 | +- Address derivation: |
| 75 | + ``` |
| 76 | + cargo run -p cli -- basic address 0 |
| 77 | + ``` |
| 78 | +- Transfer LBTC (print hex or broadcast): |
| 79 | + ``` |
| 80 | + cargo run -p cli -- basic transfer-native \ |
| 81 | + --utxo <txid>:<vout> \ |
| 82 | + --to-address <tlq1...> \ |
| 83 | + --send-sats 150000 \ |
| 84 | + --fee-sats 500 \ |
| 85 | + --account-index 0 \ |
| 86 | + --broadcast |
| 87 | + ``` |
| 88 | +- Options import/export of encoded arguments: |
| 89 | + ``` |
| 90 | + cargo run -p cli -- options import --help |
| 91 | + cargo run -p cli -- options export --help |
| 92 | + ``` |
| 93 | +- Options flows (creation → funding → exercise/settlement/expiry/cancellation): see `crates/cli/README.md` for full command lines. |
| 94 | + |
| 95 | +## Development tips |
| 96 | + |
| 97 | +- Tests |
| 98 | + - Unit tests exist in `contracts` and `simplicityhl-core` (including program path tests). Run: |
| 99 | + ``` |
| 100 | + cargo test -p simplicityhl-core -p contracts |
| 101 | + ``` |
| 102 | +
|
| 103 | +- Debugging program execution |
| 104 | + - Use `RunnerLogLevel::{Debug,Trace}` and the provided trackers (`DefaultTracker`, `DebugTracker`) to observe `dbg!` values and jet traces during execution. |
| 105 | + - **Logs can be printed only if the program is successfully executed!** |
| 106 | +
|
| 107 | +- Encoding and persistence |
| 108 | + - Enable the `encoding` feature in `simplicityhl-core` to use the `Encodable` trait for bincode encoding/decoding of arguments. The CLI persists encoded options arguments in a local sled store under `.cache/store`. |
| 109 | +
|
| 110 | +- Adding a new contract |
| 111 | + 1. Add a new `*.simf` under `crates/contracts/<your_contract>/source_simf/`. |
| 112 | + 2. Create argument and witness builders mirroring `options/build_arguments.rs` and `options/build_witness.rs`. |
| 113 | + 3. Expose helpers to derive the address and finalize transactions. |
| 114 | + 4. Add CLI subcommands that construct outputs in the order your covenant expects, then attach the appropriate Simplicity witness(es). |
| 115 | +
|
| 116 | +- Performance and UX |
| 117 | + - Transactions are explicit; Addresses are constantly reused; privacy is not a goal here. |
| 118 | +
|
| 119 | +## Safety and network notes |
| 120 | +
|
| 121 | +- Liquid testnet only. Explicit transactions leak amounts/assets by design here. |
| 122 | +- Ensure UTXOs spent by the CLI belong to the derived P2TR address for the chosen `account-index`. |
| 123 | +- Always verify the printed addresses and parameters match your expectations before broadcasting. |
0 commit comments