Minsc is a high-level, domain-specific, embeddable language for Bitcoin scripting that simplifies the creation and fulfillment of complex spending conditions using an expressive pseudo-code-like syntax.
It features built-in support for Descriptors, Miniscript, Script, Transactions, PSBT, Taproot, Xpubs/Xprvs, CTV and more.
The language is dynamically typed, functional and immutable.
Note
The documentation and playground on the min.sc
website are currently outdated.
To explore some of Minsc's new abilities, check out the min.sc/v0.3
playground and the following examples:
- Simple Taproot (P2TR key-path)
- Simple Multisig (P2WSH 2-of-2)
- Co-signer with expiry (P2TR with Miniscript, Green-like)
- Multisig 3-of-3 into 2-of-3 (decays after a timeout)
- Hashed Timelock Contract (traditional HTLC)
- Recovery after a delay period (simple CSV-based, delay period since the coins last moved)
- Inheritance after a contest period (2-stage using pre-signed txs, contest delay period following the 'trigger')
- Manual Signing (P2WPKH)
- Manual Scripting & Signing (P2WSH with raw Script)
- Simple CTV Whitelist (P2TR with raw Script)
- CTV Congestion Control (payment tree expansion)
- Simplest CAT
- CTV Vault (covenant-enforced delayed withdrawal for hot/cold key security)
- Payment Pool (shared UTXO ownership with pre-signed unilateral exit)
- Fair Coin-Flip Bet (commit-reveal scheme with a security deposit)
- Lookup Tables (one-time & reusable tables, 4-bit OP_MUL)
- More scripting examples are available in the playground's default code
- Dutch Auction
- Token Sale with Royalty (recursive stateful contract, WIP code)
To learn more about the language internals, you can also check out the Minsc standard library parts implemented in Minsc:
src/stdlib/stdlib.minsc
(utilities for arrays, strings, testing and more)src/stdlib/btc.minsc
(transaction utilities, script opcodes, loop unrolling, control structures and more)src/stdlib/elements.minsc
(Elements introspection, 64-bit arithmetic and more)
Install Rust and:
$ cargo install minsc
# Execute a minsc file
$ minsc examples/htlc.minsc
# Execute from stdin
$ echo 'pk(d0de0aaeaefad02b8bdc8a01a1b8b11c696bd3d66a2c5f10780d95b7df42645c) && older(1 week)' | minsc -
# Dump AST
$ minsc examples/htlc.minsc --ast
Using the Rust API:
use minsc::eval;
let code = "pk(d0de0aaeaefad02b8bdc8a01a1b8b11c696bd3d66a2c5f10780d95b7df42645c) && older(1 week)";
let res = eval(&code).unwrap(); // a minsc::Value
println!("{}", res);
// Extract the miniscript::Policy
let policy = res.into_policy().unwrap();
Full documentation for the Rust API is available here.
Install with npm install minsc
and:
import m from 'minsc'
// A multisig between Alice and Bob
const alice_pk = 'xpub661MyMwAqRbcFjVEmr9dDxeGKJznf41v5bEd83wMwu7CJ6PFeqJk3cSECPTh6wzsh32xceVsPvBgJ1q3Cqqie2dvH9nMFdL5865WrtRNhiB'
, bob_pk = 'xpub661MyMwAqRbcFG1mzmcbw7oZss2Fn9y3d27D1KVjyKQdYGqNsZ8nSvLSexZAtkCNwvhFrAkTWAixvN9wjmnLNR22EsQczTiKccAJoLYW8CK'
const multisig = m`wsh(${alice_pk}/0/* && ${bob_pk}/0/*)`
// Generate receive address #0
const address = m`address(${multisig}/0)`
console.log(`Address: ${address}`)
// An output funding address #0
const prevout = '72877bd944be3433d5030ef102922e52f7c40de8b5ca26fa8b7c724d341e936e:1'
, amount = '0.5 BTC'
// Create PSBT
const psbt = m`psbt[
"input": [
"prevout": ${prevout},
"utxo": ${multisig}/0:${amount},
],
"outputs": [
bcrt1ql8nqx3q3v7napchr6ewy4tpyq5y08ywat84pen: 0.4 BTC,
(${multisig}/1): 0.099 BTC, // change back to multisig
],
]`
// Export PSBT for external signing
const psbt_base64 = m.base64(psbt)
// Or sign with Minsc:
const alice_sk = 'xprv9s21ZrQH143K3FQmfpccrphXmHAJFbJ4iNK2KfXkPZaDRJ477HzVVp7kM7RV3ihdLh4Wy163wJahwXcdcrpu4R6xSu6CUvKYwftQYCbowYM'
, bob_sk = 'xprv9s21ZrQH143K2mwJtk5bZyrqKqBmNhFCFoBcCw68QysefUWEL1pXu81xoeva2ZWpCjsJzzmYqph6vw6FjCMjg3q8obNzxYY9bCVgt9bKoHQ'
const signed = m`psbt::sign(${psbt}, ${[ alice_sk, bob_sk ]})`
// Finalize & Extract
const tx = m`psbt::extract(psbt::finalize(${signed}))`
console.log(m.pretty(tx))
console.log(m.bytes(tx).toString('hex'))
// Alternative style, with Minsc functions as JavaScript methods (translated into the same as above)
const address = m.address(m.wsh(m.and(m`${alice_pk}/0/1`, m`${bob_pk}/0/1`)))
const psbt = m.psbt({ inputs: [ ... ], outputs: [ ... ] })
const signed = m.psbt.sign(psbt, [ alice_sk, bob_sk ])
const tx = m.psbt.extract(m.psbt.finalize(signed))
MIT