๐Ÿ”จ Miniscript Studio

Auto compile examples

Basic examples:

Complex examples:

Auto compile examples

Basic examples:

Complex examples:

๐Ÿ”‘ Key variables

Define key variables to use names like "Alice" and "Bob". Use public keys only!

Key type generation:

No key variables defined.

๐Ÿ’พ Saved policies

No saved policies yet.

๐Ÿ“ Saved miniscript

No saved expressions yet.

๐Ÿ“˜ Policy reference

๐Ÿ“– What is Policy Language?

Policy Language is a high-level, human-readable way to describe Bitcoin spending conditions. It's designed for non-technical users to express complex logic like "Alice can spend immediately, OR Bob can spend after 1 week, OR any 2 of 3 trustees can spend." The policy compiler automatically converts these intuitive expressions into optimized miniscript and Bitcoin Script.

Key benefits: Natural language syntax โ€ข Automatic optimization โ€ข Error prevention โ€ข Supports all Bitcoin script features

๐Ÿ”‘ Authentication Functions

pk(key) - Public Key Signature

What it does: Requires a digital signature from the specified public key.
When to use: Single-user wallets, simple ownership, hot wallets.
Key formats: Variable names (Alice), hex pubkeys (02abc...), or xpub descriptors ([fingerprint/path]xpub...)
Example: pk(Alice) - Only Alice can spend
Why choose: Most efficient option (34 bytes), compiles directly to CHECKSIG.

pkh(key) - Public Key Hash (P2PKH Style)

What it does: Requires revealing the public key AND providing a signature.
When to use: Traditional Bitcoin addresses, privacy until spending, shorter scriptPubKeys.
Trade-offs: Slightly less efficient than pk() but hides the public key until spending.
Example: pkh(Alice) - Alice must reveal her key and sign
Why choose: Standard address format, privacy benefits, 20-byte hash storage.

โฐ Time-based Conditions

after(n) - Absolute Timelock

What it does: Can only spend after a specific block height or Unix timestamp.
When to use: Scheduled payments, vesting schedules, inheritance planning, time-delayed vaults.
Format: Block height (n < 500000000) or Unix timestamp (n โ‰ฅ 500000000)
Examples: after(800000) - after block 800k, after(1767225600) - after Jan 1, 2026
Why choose: Enforces global time constraints, perfect for future-dated transactions.

older(n) - Relative Timelock

What it does: Must wait n blocks after UTXO creation before spending.
When to use: Recovery paths, Lightning channels, forced hodling, vault timeouts.
Common values: 144 (1 day), 1008 (1 week), 4320 (1 month)
Example: or(pk(Alice),and(pk(Bob),older(144))) - Bob can recover after 1 day
Why choose: Creates delays relative to UTXO age, ideal for timeout-based recovery.

๐Ÿ” Hash-based Conditions

sha256(h) - SHA256 Preimage

What it does: Requires revealing data that produces the given SHA256 hash.
When to use: Atomic swaps, HTLCs, payment channels, proof of knowledge.
Format: 64 hex characters (32-byte hash)
Example: and(pk(Alice),sha256(abc123...)) - Alice + secret knowledge
Why choose: Most common hash function, widely supported, secure.

hash256(h) - Double SHA256

What it does: Requires preimage for SHA256(SHA256(data)).
When to use: Bitcoin-native hashing, compatibility with Bitcoin's block/transaction hashing.
Example: Cross-chain swaps with Bitcoin-style chains
Why choose: Standard Bitcoin hashing, resistance to length extension attacks.

hash160(h) - Bitcoin Address Hash

What it does: Requires preimage for RIPEMD160(SHA256(data)).
Format: 40 hex characters (20-byte hash)
When to use: Bitcoin address generation, compatibility with existing systems.
Why choose: Standard for Bitcoin addresses, compact 20-byte representation.

ripemd160(h) - RIPEMD160 Hash

What it does: Requires preimage for RIPEMD160 hash.
Format: 40 hex characters (20-byte hash)
When to use: Specialized use cases, rarely used alone.
Why choose: Part of Bitcoin's cryptographic suite, specific compatibility needs.

๐Ÿ”— Logic Operators

and(X,Y) - Both Conditions Required

What it does: Both X AND Y must be satisfied to spend.
When to use: Multi-signature wallets, complex authorization, dual requirements.
Optimization tip: Order matters - put the more likely to fail condition first.
Example: and(pk(Alice),pk(Bob)) - requires both signatures
Why choose: Clear intent, automatic optimization to and_v() miniscript.

or(X,Y) - Either Condition Works

What it does: Either X OR Y can satisfy (or both).
When to use: Backup paths, multiple spending methods, recovery options.
Auto-optimization: Compiler chooses optimal miniscript combinator (or_b, or_c, or_d, or_i).
Example: or(pk(Alice),and(pk(Bob),older(144))) - Alice immediately OR Bob after delay
Why choose: Flexible spending paths, compiler handles complexity.

thresh(k,X,Y,Z,...) - K-of-N Threshold

What it does: Exactly k out of n conditions must be satisfied.
When to use: Flexible multisig, mixed condition types, complex governance.
Advantage: More flexible than multi() - works with any policy fragments, not just keys.
Examples: thresh(2,pk(Alice),pk(Bob),pk(Charlie)) for 2-of-3, thresh(2,pk(Alice),older(100),sha256(...))
Why choose: Ultimate flexibility, can combine different condition types.

โšก Optimization Features

n@ - Probability Weights

What it does: Indicates relative probability/frequency of use for optimization.
When to use: When you know which spending path will be used most often.
Benefit: Compiler optimizes for smaller, cheaper transactions on common paths.
Examples:
โ€ข or(99@pk(Alice),pk(Bob)) - Alice spends 99% of the time
โ€ข or(9@pk(Alice),pk(Bob)) - Alice 9x more likely than Bob
โ€ข thresh(2,10@pk(Alice),10@pk(Bob),1@pk(Charlie)) - Charlie rarely participates
How it works: Only relative ratios matter, any scale works (1-10, percentages, weights).

Important Constraints

  • Satisfiability: All policies must be satisfiable (no impossible conditions like and(pk(A),0))
  • Script Limits: Maximum 520 bytes for P2SH, 10,000 bytes for P2WSH, no limit for Taproot
  • Time Format: Block heights (small numbers like 144), Unix timestamps (large numbers like 1767225600)
  • Key Format: Variable names (Alice), hex pubkeys (02abc...), or descriptors ([fingerprint/path]xpub...)
  • Compilation: Policy โ†’ Miniscript โ†’ Bitcoin Script (automatic optimization at each stage)

Reference: https://bitcoin.sipa.be/miniscript/

๐Ÿ“š Miniscript reference

โš™๏ธ What is Miniscript?

Miniscript is a structured representation of Bitcoin Script that's designed to be analyzable, composable, and optimizable. While Policy is human-readable, Miniscript is the precise, unambiguous intermediate language that compiles directly to Bitcoin Script. It explicitly defines how Bitcoin opcodes combine, ensuring scripts are secure, efficient, and predictable.

Key features: Precise script control โ€ข Fragment composability โ€ข Size/cost analysis โ€ข Security guarantees โ€ข Optimization opportunities

๐Ÿ”‘ Key-based Fragments

pk(key) - Simple Public Key Check

What it does: Requires a signature from the specified public key.
Script: <pubkey> CHECKSIG
When to use: Single-user wallets, simple ownership where one person controls funds.
Example: pk(Alice) - Only Alice can spend
Why choose this: Most efficient for single-key spending. Minimal script size (34 bytes for compressed key).

pkh(key) - Public Key Hash

What it does: Requires revealing the public key AND a signature. Key must hash to the given hash160.
Script: DUP HASH160 <hash> EQUALVERIFY CHECKSIG
When to use: Traditional Bitcoin addresses (P2PKH style), when you want shorter scriptPubKeys.
Example: pkh(Alice) - Alice must reveal her key and sign
Why choose this: Shorter scriptPubKey (only 20-byte hash stored), privacy until spending, standard address format.

pk_k(key) - Raw Key on Stack

What it does: Like pk() but expects the key already on stack (doesn't push it).
Script: CHECKSIG (key must be on stack already)
When to use: Advanced scripts where key comes from elsewhere (e.g., from a hash preimage).
Example: Used internally by compiler for optimizations
Why choose this: Saves bytes when key is already available, used in complex nested structures.

pk_h(key) - Hashed Key Check

What it does: Like pkh() but doesn't duplicate the key (expects it on stack).
Script: HASH160 <hash> EQUALVERIFY CHECKSIG
When to use: Internal optimization when key is already available.
Example: Used in nested constructs by compiler
Why choose this: Saves a DUP operation when key is already on stack.

0 - Always False

What it does: Pushes false (0) onto the stack.
Script: 0
When to use: Creating provably unspendable branches or testing.
Example: or_i(pk(Alice),0) - Only Alice can spend
Why use: Explicit false conditions, often for disabled branches.

1 - Always True

What it does: Pushes true (1) onto the stack.
Script: 1
When to use: Creating always satisfiable conditions or testing.
Example: and_v(1,pk(Alice)) - Effectively just requires Alice
Why use: Placeholder conditions or always-satisfied requirements.

โฐ Time-based Fragments

after(n) - Absolute Timelock

What it does: Can only spend after block height or Unix timestamp n.
Script: <n> CHECKLOCKTIMEVERIFY
When to use: Scheduled payments, vesting, inheritance planning, time-delayed vaults.
Example: and_v(v:pk(Alice),after(1767225600)) - Alice can spend after Jan 1, 2026
Why choose this: Enforces spending cannot happen before specific time. Uses transaction nLockTime field.

older(n) - Relative Timelock

What it does: Must wait n blocks after UTXO creation before spending.
Script: <n> CHECKSEQUENCEVERIFY
When to use: Lightning channels, vault recovery paths, forced hodling, gradual release.
Example: or(pk(Alice),and(pk(Bob),older(144))) - Bob can spend after 1 day
Why choose this: Creates delay relative to when UTXO was created. Perfect for timeout-based recovery.

๐Ÿ” Hash-based Fragments

sha256(hash) - SHA256 Preimage

What it does: Requires revealing data that hashes to the given SHA256 hash.
Script: SIZE 32 EQUALVERIFY SHA256 <hash> EQUAL
When to use: Atomic swaps, HTLCs, proof of knowledge, cross-chain bridges.
Example: and(pk(Alice),sha256(abc123...)) - Alice + secret
Why choose this: Most common hash function in Bitcoin, 32-byte preimages, widely supported.

hash256(hash) - Double SHA256

What it does: Requires preimage for SHA256(SHA256(data)).
Script: SIZE 32 EQUALVERIFY HASH256 <hash> EQUAL
When to use: Bitcoin-native hashing (used for block headers, txids).
Example: Cross-chain swaps with Bitcoin-style chains
Why choose this: Standard Bitcoin hashing, slightly more secure against length extension.

hash160(hash) - RIPEMD160(SHA256)

What it does: Requires preimage for RIPEMD160(SHA256(data)).
Script: SIZE 32 EQUALVERIFY HASH160 <hash> EQUAL
When to use: Bitcoin address-style hashing, 20-byte hashes for smaller scripts.
Example: and(pk(Alice),hash160(6c60f404...)) - Common in 2FA setups
Why choose this: Smaller hash (20 bytes), used in Bitcoin addresses, good for space optimization.

ripemd160(hash) - RIPEMD160 Hash

What it does: Requires preimage for RIPEMD160(data).
Script: SIZE 32 EQUALVERIFY RIPEMD160 <hash> EQUAL
When to use: Alternative hash function, legacy compatibility.
Example: Rarely used, mainly for specific protocols
Why choose this: Different hash function for diversity, 20-byte output.

๐Ÿ”€ OR Combinators - Choosing the Right One

or_i(X,Y) - If/Else Branch Selection

What it does: Spender chooses branch with 1 (left) or 0 (right) on stack.
Script: IF <X> ELSE <Y> ENDIF
When to use: Two distinct spending paths, clean branch selection, vault tiers.
Example: or_i(pk(Alice),pk(Bob)) - Alice OR Bob can spend
Why choose or_i: Most readable, explicit branch choice, good for user-facing options. Spender decides upfront which path to take.

or_d(X,Y) - Try Left First, Fallback to Right

What it does: Tries X first; if X leaves 0, then tries Y.
Script: <X> IFDUP NOTIF <Y> ENDIF
When to use: Primary path with automatic fallback, signature-or-timeout patterns.
Example: or_d(pk(Alice),and(pk(Bob),older(144))) - Alice primary, Bob backup
Why choose or_d: Automatic fallback without explicit choice. Left branch "dissatisfies" gracefully to allow right branch.

or_c(X,Y) - Optimized for Signatures

What it does: Specialized OR for pk/pkh combinations.
Script: <X> NOTIF <Y> ENDIF
When to use: When left side is a signature check that can be "dissatisfied".
Example: Used internally for optimizing signature-based ORs
Why choose or_c: Most efficient for signature-based options. Compiler often chooses this automatically.

or_b(X,Y) - Boolean OR Logic

What it does: Evaluates both branches, combines with boolean OR.
Script: <X> <Y> BOOLOR
When to use: When you need both conditions evaluated (rare).
Example: Complex boolean logic, both sides must execute
Why choose or_b: Both branches always execute. Usually inefficient but sometimes necessary for specific logic.

๐Ÿ”— AND Combinators - Combining Conditions

and_v(X,Y) - Verify Then Execute

What it does: X must be true (verified), then Y is evaluated.
Script: <X> VERIFY <Y>
When to use: Most common AND. Signature + timelock, key + hash, any two conditions.
Example: and_v(v:pk(Alice),older(144)) - Alice after 1 day
Why choose and_v: Clean, efficient, X doesn't leave anything on stack. Use when X is a "check" condition.

and_b(X,Y) - Boolean AND

What it does: Both return boolean values, combined with AND.
Script: <X> <Y> BOOLAND
When to use: When both sides produce boolean (0/1) results.
Example: Combining hash checks or boolean conditions
Why choose and_b: Both sides leave values on stack to be combined. Less common than and_v.

and_n(X,Y) - Conditional AND

What it does: Uses NOTIF pattern for conditional execution.
Script: <X> NOTIF 0 ELSE <Y> ENDIF
When to use: Internal optimization, rarely hand-written.
Example: Compiler-generated for specific patterns
Why choose and_n: Special cases where this pattern is more efficient.

๐ŸŽฏ Threshold & Multisig

thresh(k,Xโ‚,...,Xโ‚™) - K-of-N Threshold

What it does: Exactly k of the n conditions must be satisfied.
Script: Complex combination of ADD and EQUAL checks
When to use: Flexible multisig, mixed conditions (keys + timelocks + hashes).
Example: thresh(2,pk(A),pk(B),pk(C)) - Any 2 of 3 signers
Why choose thresh: Most flexible threshold. Can mix different condition types, not just keys.

multi(k,keyโ‚,...) - Classic Multisig

What it does: Traditional Bitcoin multisig with CHECKMULTISIG.
Script: <k> <key1> ... <keyn> <n> CHECKMULTISIG
When to use: Legacy compatibility, up to 3-of-3 in P2SH.
Example: multi(2,Alice,Bob,Charlie)
Why choose multi: Most efficient for pure key-based multisig. Limited to 20 keys max.

multi_a(k,keyโ‚,...) - Taproot Multisig

What it does: Modern multisig using CHECKSIGADD (Taproot only).
Script: Series of CHECKSIGADD operations
When to use: Taproot scripts, more than 20 keys, batch validation.
Example: multi_a(11,key1,...,key20) - 11-of-20
Why choose multi_a: No key limit, more efficient in Taproot, better for large multisigs.

๐ŸŽญ Wrappers - Modifying Behavior

v: - Verify Wrapper

What it does: Adds VERIFY, expression must be true or script fails.
Script: <expr> VERIFY
When to use: Converting expressions for use in and_v, ensuring conditions are met.
Example: and_v(v:pk(Alice),older(144))
Why use: Makes expression "unit type" - doesn't leave value on stack.

a: - Alt Stack Wrapper

What it does: Temporarily stores value on alt stack.
Script: TOALTSTACK ... FROMALTSTACK
When to use: Complex scripts needing temporary storage, threshold operations.
Example: thresh(2,pk(A),a:pk(B),a:pk(C))
Why use: Prevents stack interference in complex evaluations.

s: - Swap Wrapper

What it does: Swaps top two stack elements.
Script: SWAP <expr>
When to use: Reordering stack for specific operations.
Example: Internal compiler optimization
Why use: Stack management in complex scripts.

c: - Checksig Wrapper

What it does: Adds CHECKSIG to a key.
Script: <key> CHECKSIG
When to use: Converting raw keys to signature checks.
Example: or_c(pk(A),v:pk(B)) uses this internally
Why use: Type conversion for certain combinators.

d: - Dup-If Wrapper

What it does: Duplicates if non-zero.
Script: DUP IF ... ENDIF
When to use: or_d constructions, conditional duplication.
Example: Used in or_d patterns
Why use: Enables dissatisfaction in OR branches.

t: - True/False Wrapper

What it does: Converts to 1 if true, 0 if false.
Script: IF 1 ELSE 0 ENDIF
When to use: Normalizing boolean values.
Example: t:or_i(pk(A),pk(B))
Why use: Ensures consistent 0/1 output.

j: - Size Check Wrapper

What it does: Adds SIZE 0NOTEQUAL check (non-zero size).
Script: SIZE 0NOTEQUAL IF <expr> ENDIF
When to use: Ensuring input has non-zero size before evaluation.
Example: j:pk(Alice) - Requires non-empty signature
Why use: Malleability protection, ensuring witness data present.

n: - Nonzero Wrapper

What it does: Converts any non-zero value to 1.
Script: DUP 0NOTEQUAL
When to use: Normalizing non-zero values to boolean true.
Example: n:older(100) - Ensures boolean output
Why use: Type normalization for certain operations.

๐Ÿš€ Advanced Patterns

andor(X,Y,Z) - Optimized (X AND Y) OR Z

What it does: If X is satisfied, require Y; otherwise require Z.
Script: Optimized combination avoiding redundant checks
When to use: "If condition then require Y, else require Z" patterns.
Example: andor(pk(Alice),pk(Bob),and(pk(Charlie),older(144)))
Why choose andor: More efficient than or(and(X,Y),Z) for this specific pattern.

๐Ÿ’ก Design Patterns & Best Practices

Common Patterns

  • Hot Wallet + Cold Backup: or(pk(Hot),and(pk(Cold),older(1008)))
  • 2FA with Timeout: and(pk(User),or(pk(Service),older(52560)))
  • Escrow: or(and(pk(Buyer),pk(Seller)),pk(Arbiter))
  • HTLC: or(and(pk(Recipient),sha256(H)),and(pk(Sender),older(144)))
  • Inheritance: or(pk(Owner),and(pk(Heir),after(1798761600)))
  • Vault: or(and(pk(Hot),pk(Cold)),and(pk(Recovery),older(4320)))

Choosing Fragments

  • Keys: Use pk() for simplicity, pkh() for privacy/compatibility
  • Time: Use after() for absolute dates, older() for relative delays
  • OR Logic: Use or_i for clear choices, or_d for primary/backup
  • AND Logic: Use and_v for most cases, others for optimization
  • Threshold: Use thresh for mixed conditions, multi for pure multisig
  • Wrappers: Let compiler add them, use v: when needed for and_v

Note: Public keys must be 33-byte compressed hex strings (66 characters) for Legacy and Segwit v0, or 32-byte X-only keys (64 characters) for Taproot. The compiler supports Legacy (p2SH), Segwit v0 (p2WSH), and Taproot contexts.

Reference: https://bitcoin.sipa.be/miniscript/

๐Ÿ’ก Tips & Quick Help
๐Ÿท๏ธ Show key names:

Use the ๐Ÿท๏ธ button to toggle between showing full public keys and variable names (Alice, Bob, etc.) in your expressions. Makes complex policies much easier to read and understand.

๐Ÿ”‘ Missing or corrupted key variables?

If one of the examples fails to complie with "pubkey string should be 66 or 130 digits" (or similar), or ๐Ÿท๏ธ show keys doesn't work properly, go to "Key variables" section and use "Clear all" then "Restore defaults" to reset Alice, Bob, Charlie, etc. You can delete only the problematic key and "Restore defaults".

๐Ÿ”‘ Extract keys to variables:

Use the ๐Ÿ”‘ button to automatically detect keys in your expression and convert them to reusable variables. Detects public keys, xpubs, tpubs, and x-only keys. For each key, choose the appropriate type (compressed, x-only, xpub, tpub). Suggests meaningful names (Alice, Bob, Charlie) and prevents duplicates.

๐Ÿ“ Format expressions:

Use the ๐Ÿ“ button to format long policy, miniscript expressions and scripts with proper indentation. Makes complex nested expressions easier to read and understand. Note: formatting is automatically cleaned during compilation to reflect the actual expression being compiled.

๐Ÿงน Clean up text:

The ๐Ÿงน button removes extra whitespace and formatting from pasted expressions that prevent them from compiling successfully.

๐Ÿ’พ Save/Load expressions:

Save frequently used policies/expressions with the ๐Ÿ’พ Save button. Saved items appear in their respective sections for quick reloading.

๐ŸŽฒ Generate test keys:

Use the ๐ŸŽฒ Generate button in Key variables to create random test keys appropriate for your selected script context (xpub, compressed for Legacy/Segwit, X-only for Taproot).

๐Ÿ”„ Context auto-detection:

Script context automatically switches based on key types: compressed keys (66 chars) โ†’ Legacy/Segwit, X-only keys (64 chars) โ†’ Taproot.

โฌ†๏ธ Lift (Reverse Engineering):

Paste Bitcoin Script hex/ASM into the "Script HEX" or "Script ASM" fields to automatically lift them back to miniscript. Every lift also shows the policy equivalent. You can also lift miniscript expression to policy.

๐ŸŒ Network Support:

Use the ๐ŸŒ button to toggle between mainnet and testnet address generation. Testnet keys (tpub) automatically generate testnet addresses, mainnet keys (xpub) generate mainnet addresses.

๐Ÿ“‹ Local storage:

All data (keys, saved expressions) persists locally in your browser. No server - everything stays private. Use public keys only!

๐Ÿ”— Share your work:

Use the ๐Ÿ”— button above any expression to generate a shareable URL that includes your policy/miniscript and all key variables. Perfect for collaboration or saving your work. Choose between URL encoding and compact JSON format in Settings below.

๐Ÿƒ Auto compile examples:

When loading an example or a saved expression, it can be automatically compiled to show results immediately. This behavior can be toggled on/off in the Settings section below. When off, expressions load but wait for manual compilation.

โš™๏ธ Settings
๐ŸŽจ Theme:

Choose between dark and light theme. Overrides system preference.

๐Ÿƒ Auto compile examples:

Automatically compile policies and miniscripts when loading from examples or saved expressions. When disabled, expressions load but require manual compilation.

๐Ÿ“– Show descriptions:

Display helpful descriptions when clicking example buttons. Turn off if you're a miniscript master who doesn't like explanations. ๐Ÿง™โ€โ™‚๏ธ

๐Ÿ’ก Show editor tips:

Show helpful tips and instructions in empty editors. Turn off for a cleaner interface with just "Enter your policy/miniscript here..." instead of detailed guidance.

๐Ÿ”— Share format:

Choose how shared URLs are formatted. URL encoding preserves special characters in readable format and is better for simple expressions. JSON creates base64-encoded URLs that include all state.

๐Ÿ‘๏ธ Hide corner buttons (web only):

Hide the GitHub and beer donation buttons in the top corners for a cleaner interface. Corner buttons are automatically hidden on mobile devices.

๐ŸŒณ Tree structure display:

Choose how to display the tree structure in compilation results. Script Compilation shows opcodes and detailed structure, Visual Hierarchy shows a clean graphical tree, or hide it completely.