For the best experience, use on a desktop browser

๐Ÿ”จ 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.

โฐ 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/
BIP-379: https://github.com/bitcoin/bips/blob/master/bip-0379.md

๐Ÿ’ก 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.

๐Ÿ”‘ Key management & extraction:

Use the ๐Ÿ”‘ button to automatically detect keys in your expression and convert them to reusable variables. Works for hex values too. Also works from error messages - click "extract keys" from the error message itself to auto generate them. Detects public keys, xpubs, tpubs, and x-only keys. If an example fails to compile, go to "Key variables" section and use "Restore defaults" to reset Alice, Bob, Charlie, etc.

๐Ÿ“ 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:

Paste Bitcoin Script hex/ASM into the "Script HEX" or "Script ASM" fields to lift them to miniscript. Use โฌ†๏ธ to lift miniscript to policy for analysis.

๐Ÿ” Analyze:

View detailed policy analysis - spending paths, keys, timelocks, security info and more.

๐ŸŒ 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. Three formats are available in Settings:
โ€ข URL encoded: Shortest URLs, expression only. Not all platforms show a valid link
โ€ข Base64: Compact encoding, expression only. Good for platforms like Twitter
โ€ข JSON with custom keys: Includes your custom variables for sharing complete policies

๐Ÿƒ 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.

๐Ÿ“Š Spending analysis:

View detailed cost analysis, satisfaction paths, and weight units for fee estimation in the compilation results.

๐Ÿท๏ธ HD wallet descriptors:

Full support for xpub/tpub with wildcards (/*). Index field appears automatically for derivation paths.

๐ŸŒณ Taproot branches:

For Policies with Taproot OR conditions, use the branch selector to choose which path to compile and compare costs.

โš™๏ธ 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 encoded: Shortest URLs, expression only without custom variables. Easy to read but not all platforms show a valid link.
โ€ข Base64: Compact base64 encoding of expression only, without custom variables. Good for platforms that don't handle the url encoding well (Twitter).
โ€ข JSON with custom keys: Base64-encoded JSON that includes your custom variables. Good for sharing new policies with new variables.

๐Ÿ‘๏ธ 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.