Skip to content

Syntax Reference

Bitcoin Syntax

Examples Evaluates to
Script Fragment `2 $alice $bob $charlie 3 OP_CHECKMULTISIG` Script
Address bc1qdfac40sy0uk2g3z9g8z75z3xkrvnsjd0h0u8q5 Address
Xpub xpub661MyMwAqRbcFvXwqbgwigDczeocqeBEibKMCk… PubKey<Xpub>
Xpriv xprv9s21ZrQH143K37CBUxq5bSXTyHwKc5KwYWsm6o… SecKey<Xpriv>
WIF L4oC7AMPJkKPwuVipfFaKcJKBhsR3kmXy89f7oAFhSQSPWLNXocJ SecKey<SingleKey>
Key w/ Origin [df786318/0/15]0211fff5af35167762f616224aaa6a PubKey<SingleKey>
Key Derivation xpub661MyMwAqRbcFvXwqbgwigDczeo…/84'/0'/1'/<0;1>/* PubKey<Xpub>
Amount 1.3 BTC, 1300 bits Int
Relative Sequence Time 30 days, 2 months 2 weeks Int
Absolute Lock Time 2009-03-01T Int

Script Fragments

TBD

Addresses

Bitcoin addresses encoded in Bech32(m)/Base58Check can be written directly in Minsc code and are parsed into an Address.

$spk = 1Ai2dBSc1bmF3Nyc5VBQiNhjrtfL9SVtpw->script_pubkey;

$tx = tx[
  "inputs": [ aab9cedeca3dd53acc35e80d3e91bb89a947fae88df4c562a50b637bb4fd8f7f:1 ],
  "outputs": [
    1Ai2dBSc1bmF3Nyc5VBQiNhjrtfL9SVtpw:150000,
    bc1qdfac40sy0uk2g3z9g8z75z3xkrvnsjd0h0u8q5:20000,
  ]
];

assert::eq(typeof(1Ai2dBSc1bmF3Nyc5VBQiNhjrtfL9SVtpw), "address");
$spk = `OP_DUP OP_HASH160 <0x6a7b8abe047f2ca4444541c5ea0a26b0d93849af> OP_EQUALVERIFY OP_CHECKSIG` // script

$tx = tx[
  "version": 2,
  "inputs": [
    aab9cedeca3dd53acc35e80d3e91bb89a947fae88df4c562a50b637bb4fd8f7f:1
  ],
  "outputs": [
    `OP_DUP OP_HASH160 <0x6a7b8abe047f2ca4444541c5ea0a26b0d93849af> OP_EQUALVERIFY OP_CHECKSIG`:0.0015 BTC,
    `<0> <0x6a7b8abe047f2ca4444541c5ea0a26b0d93849af>`:0.0002 BTC
  ]
] // transaction

Keys

Xpub, Xpriv and WIF-encoded single secret keys can be written directly in Minsc code and are parsed into a PubKey/SecKey.

// Xpub (used to construct a WPKH descriptor)
$descriptor = wpkh(xpub661MyMwAqRbcFvXwqbgwigDczeocqeBEibKMCkoc31RiyB464Ybc1z8sWMnR38JdeCBJPPkSM7mKahcBX2nPX9KYVTz3cotpLmSkMxrp99L);

// Xpriv (converted into an Xpub)
$xpub = pubkey(xprv9s21ZrQH143K37CBUxq5bSXTyHwKc5KwYWsm6onCYDmiPBBDyiz3bYwvHYH9NXzsY6mDiXmhf77Ym2EJkGreLHB3s6MH5tRkfKQT9uDQ4r2);

// WIF (used to sign)
$signature = ecdsa::sign(L4oC7AMPJkKPwuVipfFaKcJKBhsR3kmXy89f7oAFhSQSPWLNXocJ, hash::sha256("Hello World"));
$descriptor = wpkh(xpub661MyMwAqRbcFvXwqbgwigDczeocqeBEibKMCkoc31RiyB464Ybc1z8sWMnR38JdeCBJPPkSM7mKahcBX2nPX9KYVTz3cotpLmSkMxrp99L) // descriptor

$xpub = xpub661MyMwAqRbcFbGeazN5xaUCXKmp1Y3nujoMuCBp6ZJhFyWNXGJJ9MGQ8otkj3MgGcpcCx7u1A88yVZ2RmmPqPuUHJ5TsD1f7G2PoFNJqLc // pubkey

$signature = 0x304402201bd3260a7e1d76160df2e458d03828a2ed4c517bc0fefbaf1d2e097a75c0f1bf022007f60ca216d20196c3a0a306e779552e4ab8b53d09a0d209089bee14d501fe12 // bytes

Keys with an origin enclosed in [] brackets are supported too:

$pk = [df786318/0/15]0211fff5af35167762f616224aaa6a426d420784ae99b618304de5b45fae12fd18;

See Key Construction for more information.

Output Amounts

Amounts can be written using the following denomination suffixes:
BTC, mBTC/uBTC, bit(s), sat(s)/satoshi(s), msat(s)

Evaluates into an Int with the amount in satoshis.

assert::eq(0.2 BTC, 20000000);
assert::eq(1 BTC, 1000 mBTC);
assert::eq(1 mBTC, 1000 bits);

$psbt = psbt[
  "inputs": [ aab9cedeca3dd53acc35e80d3e91bb89a947fae88df4c562a50b637bb4fd8f7f:1 ],
  "utxos": [ wpkh($alice/0):2 BTC ],
  "outputs": [
    1Ai2dBSc1bmF3Nyc5VBQiNhjrtfL9SVtpw:5 mBTC,
    wpkh($alice/1):2 BTC - 5 mBTC - 300 sat,
  ]
];
$psbt = psbt[
  "unsigned_tx": tx[
    "version": 2,
    "inputs": [
      aab9cedeca3dd53acc35e80d3e91bb89a947fae88df4c562a50b637bb4fd8f7f:1
    ],
    "outputs": [
      `OP_DUP OP_HASH160 <0x6a7b8abe047f2ca4444541c5ea0a26b0d93849af> OP_EQUALVERIFY OP_CHECKSIG`:0.005 BTC,
      `<0> <0xe984e78b34b183b0c1812999165ac7986ed45299>`:1.994997 BTC
    ]
  ],
  "version": 0,
  "inputs": [
    [
      "utxo": `<0> <0x8813a55e1876baedc7144daba04d703c81489183>`:2 BTC,
      "bip32_derivation": [
        [df786318/0]03e0ec52bdc52856b2144e8affa02c0182d4291586d37c84cc8350204e4d7fd09b
      ]
    ]
  ],
  "outputs": [
    [ ],
    [ "bip32_derivation": [ [df786318/1]02e4dce697a2987b086a147e1fb422cddc1d68232b94a7aaddbed1c815c78810b8 ] ]
  ]
] // psbt

Relative Sequence Time

Relative timelock durations can be written using the following time unit suffixes:
year(s), month(s), week(s), day(s), hour(s), minute(s), second(s)

Evaluates into an Int with the duration encoded as an input sequence number.

Used as the TxIn->sequence, to construct older() policies, and with OP_CSV in Script.

// Evaluates into an Int
assert::eq(100 days, 4211179);

// Used as the input sequence
$tx = tx[
  "input": [
    "prevout": aab9cedeca3dd53acc35e80d3e91bb89a947fae88df4c562a50b637bb4fd8f7f:1,
    "sequence": 1 month 10 days,
  ],
  "output": 1Ai2dBSc1bmF3Nyc5VBQiNhjrtfL9SVtpw:5000,
];

// Used with Miniscript Policy
$policy = pk($alice) && older(3 days);

// Used with Script
$script = `$alice OP_CHECKSIGVERIFY <3 days> OP_CSV`;
$tx = tx[
  "version": 2,
  "inputs": [
    [ "prevout": aab9cedeca3dd53acc35e80d3e91bb89a947fae88df4c562a50b637bb4fd8f7f:1, "sequence": 3493888 seconds ]
  ],
  "outputs": [
    `OP_DUP OP_HASH160 <0x6a7b8abe047f2ca4444541c5ea0a26b0d93849af> OP_EQUALVERIFY OP_CHECKSIG`:0.00005 BTC
  ]
] // transaction

$policy = and(pk(xpub661MyMwAqRbcGxLpmSg6J2PK7LafNMyws1wApmxpFmqKvgfAuxe7fBvjzhsAmM3QJ9bpzBLAB4NskRYDmeYrWiRLbC4JpTYf8zahjeEn6Z8),older(4194811)) // policy

$script = `<0x022a46eb3296a185c5887405fe4ff8ff2cb5003980c624b173520cacb37e958e3d> OP_CHECKSIGVERIFY <0xfb0140> OP_CHECKSEQUENCEVERIFY` // script

Time durations are encoded in granularity of 512 seconds and are rounded up (ceil(seconds/512)), so for example 513 seconds becomes 1024 seconds.

Relative block count locks can be written using the blocks suffix.

Absolute Lock Time

Absolute datetimes can be formatted as [YYYY]-[MM]-[DD]T or [YYYY]-[MM]-[DD]T[HH]:[mm]:[ss]Z?.

Evaluates into an Int with the datetime as a Unix timestamp.

Used as the Transaction->locktime, to construct after() policies, and with OP_CLTV in Script.

// Evaluates into an Int
assert::eq(2009-03-01T, 1235865600);
assert::eq(2009-03-01T18:15:05, 1235931305);
assert::eq(2009-03-01T18:15:05Z, 1235931305);

// Used as the transaction locktime
$tx = tx[
  "locktime": 2030-01-01T,
  "input": aab9cedeca3dd53acc35e80d3e91bb89a947fae88df4c562a50b637bb4fd8f7f:1,
  "output": 1Ai2dBSc1bmF3Nyc5VBQiNhjrtfL9SVtpw:5000,
];

// Used with Miniscript Policy
$policy = pk($alice) && after(2030-01-01T);

// Used with Script
$script = `$alice OP_CHECKSIGVERIFY 2030-01-01T OP_CLTV`;
$tx = tx[
  "version": 2,
  "locktime": 2030-01-01T,
  "inputs": [
    aab9cedeca3dd53acc35e80d3e91bb89a947fae88df4c562a50b637bb4fd8f7f:1
  ],
  "outputs": [
    `OP_DUP OP_HASH160 <0x6a7b8abe047f2ca4444541c5ea0a26b0d93849af> OP_EQUALVERIFY OP_CHECKSIG`:0.00005 BTC
  ]
] // transaction

$policy = and(pk(xpub661MyMwAqRbcGxLpmSg6J2PK7LafNMyws1wApmxpFmqKvgfAuxe7fBvjzhsAmM3QJ9bpzBLAB4NskRYDmeYrWiRLbC4JpTYf8zahjeEn6Z8),after(1893456000)) // policy

$script = `<0x022a46eb3296a185c5887405fe4ff8ff2cb5003980c624b173520cacb37e958e3d> OP_CHECKSIGVERIFY <0x80d8db70> OP_CLTV` // script

Bitcoin Operators

From OperatorsBitcoin Operators, reproduced here for convenience.

Operator Operands Examples
BIP 32 Derivation
/
PubKey / Int
SecKey / Int '
PubKey / *
PubKey / Array
PubKey / Hash
xpub661MyMwAqRZ90rTa…bcFvZ98rNvB/5
$alice_sk/84'/0h/0'/1/*
$alice_sk/$account'/($i+1)
$alice/<0;1>/*
$alice/hash::sha256("pay-to-contract-hash")
Execution Probability
@
Int @ Policy
Int @ Script
wsh(2@pk($alice) || 1@pk($bob))
tr[ 2@`$a OP_CHECKSIG`, 1@`$b OP_CHECKSIG` ]
Script Repetition
*
Script * Int OP_CAT*2`OP_CAT OP_CAT`
`OP_DROP 3*`OP_ROT OP_ADD` OP_SUB`
Policy Composition[1]
&& || of
Policy && Policy
Policy || Policy
Int of Array<Policy>
pk($a) && pk($b)and(pk($a), pk($b))
$pk1 || $pk2 || $pk3thresh(1, $pk1, $pk2, $pk3)
2 of [ $pk1, $pk2, $pk3 ]thresh(2, $pk1, $pk2, $pk3)
Combine PSBT
+
Psbt + Psbt $psbt1+$psbt2psbt::combine[$psbt1, $psbt2]
Taproot Tweak[2]
+
PubKey + Script
PubKey + Policy
PubKey + Array
PubKey + Hash
NUMS+`<1 year> OP_CSV`tr(NUMS, `<1 year> OP_CSV`)
$alice+[ pk($b), pk($c) ]tr($alice, [ ])
$bob+0xcb40ac3f58…tr($bob, 0xcb40ac3f58…)

Unhardened child key derivation. Can also derive SecKey, Policy, Descriptor and Arrays of derivable types.

Hardened child key derivation. Also supported using h instead of '. Only possible on SecKey.

Enables the wildcard modifier. Supports /*' and /*h. Can also be used with SecKey.

Multi-path derivation. Supported using the <M;N;K> syntax or with standard arrays: $alice/<0;1>$alice/[0,1].

Can also derive SecKey, Policy, Descriptor and Arrays of derivable types.

Unhardened child key derivation using a 256-bit hash. Can also derive SecKey, Policy, Descriptor and Arrays of derivable types.

Tweak using the script tree merkle root hash. Returns a TapInfo that supports key-path spends only.

  1. && and || support >2 branches by translating them into an N-of-N or 1-of-N thresh() policy. The Miniscript Policy standard for and()/or() only supports 2 branches.

    Compatible operands are coerced into a Policy, so for example $alice && $bobpk($alice) && pk($bob).

  2. Taproot tweak (+) returns a Descriptor<Tr> or a TapInfo, depending on whether Miniscript or raw Script was used. See tr() for more information.

Core Syntax

Identifiers

Identifiers can contain _ $ 0-9 a-z A-Z and optionally any number of :: separators.

:: is simply part of the identifier name, used to namespace related functions together.

$ is typically used to prefix variable names, but this is entirely optional.

Alphanumeric-only identifiers are limited to 25 characters, to prevent conflicts with the native Bitcoin syntax for addresses/keys and with 0x-less bytes. This limit does not apply if the identifier contain a $, _ or ::.

Functions

Named functions are defined using the fn keyword.

When the function body is a single expression, it can be written as:

fn add($a, $b) = $a + b;

The function body can also include statements and a final expression used as the return value:

fn foo($a, $b) {
  $c = $a + b;
  $c * 5 // the return value
}

There's no explicit return keyword or early return.

Anonymous functions are defined using a Rust-like |args| expr or |args| { body } syntax:

filter([ 1, 2, 3 ], |$n| $n > 2)

Default parameter values are supported, however the parser currently requires wrapping () even for some seemingly parsable expressions:

fn foo($a, $b=3, $c=false, $d="hello", $e=($b+1), $f=(-1)) =
  print($a, $b, $c, $d, $e);

Destructuring

Destructuring assignment (unpacking) is supported in variable assignment and function signatures. It works with arrays and array-like types.

[$a, [$b, $c]] = [ 1, [ 2, 3 ] ];

[$receive, $change] = wpkh($alice_sk/84'/0'/0'/<0;1>/*);

[$psbt_, $succeed, $failed] = psbt::try_sign($psbt, $sk);

fn foo([$left, $right]) = $left + $right;

If Expressions

$is_big = if $n > 5 then true else false;

$how_big = if $n > 500 then "really big"
  else if $n > 5 then "big"
  else "small";

When the if/else body includes statements, it can be written as:

$is_big = if $n > 5 {
  $r = true;
  $r
} else {
  false
};

Block Expressions

An expression that immediately evaluates the {} block body under a new child scope and returns its final expression. (Rust-like)

TAPLEAF_TAG = { $h=hash::sha256("TapLeaf"); $h+$h };

Bytes Expressions

Bytes can be constructed from hex using the 0x prefix:

0xe6343c67d813e9ae785ee0c90dae8e3b321072d4e813104e1aabc5e09ecca04b

For compatibility with the descriptors syntax, the 0x prefix is optional for lengths 20/32/33.

Base64 is supported using the 0z prefix:

0zOFlSXBVyeMFuTP5eiZnHJeNRNxneilPCCOdnQWMVU/4= // trailing '=' is optional

Comments

Standard // … for single-line comments, non-standard /** … */ for multi-line.

Yes, two opening *. This is because /* conflicts with the wildcard modifier syntax for keys.

Syntactic Sugar

Array Call

Syntax Example
Array Call Function [ ...Any ] f[ A, B ]f([ A, B ])

When a function is called with a single argument that is a literal [ … ] array, the () may be omitted.

sum[ 4, 5, 10 ] // → sum([ 4, 5, 10 ])

psbt[
  "unsigned_tx": tx[
    "input": 331d3f920595ba389c35e3ecdd91ca85ae24313501d44401567f7ddc626e926a:1,
    "output": tb1qxtympy2gdtwm87t0t5xdgklac66a73a6pqx26r:0.01 BTC,
  ],
  "utxos": [ wpkh($alice/5):0.011 BTC ],
]
tr[ `$alice OP_CHECKSIG`, `$bob OP_CHECKSIG` ]

Pipe Call

Syntax Example
Pipe Call Any | Function ( ...Any )
Any | Function [ ...Any ]
A | f(B, C)f(A, B, C)
A | f[B, C]f(A, [B, C])

Alternative function calling syntax, convenient for chaining.

$paid_to_addr = $tx->outputs
  | filter(|$out| $out->script_pubkey == tb1qqecgzhegje3vuu6wazq9amkznj52jkdjad70cg->script_pubkey)
  | map(|$out| $out->amount)
  | sum();

// → sum(map(filter($tx->outputs, |$out| $out->script_pubkey == …), |$out| $out->amount))
psbt::create($tx)
  | psbt::update[ "inputs": [ 0: [ "utxo": $utxo ] ] ]
  | psbt::sign($bob_sk)
  | psbt::finalize()
  | psbt::extract()

// → psbt::extract(psbt::finalize(psbt::sign(psbt::update(psbt::create($tx), […])), $bob_sk))

The second example could actually be written much more simply: psbt::sign_finalize([ "unsigned_tx": $tx, "utxos": [ $utxo ] ], $bob_sk)

Colon Tuple

From OperatorsColon Tuple, reproduced here for convenience.

Operator Operands Example
Colon Tuple : Any : Any A:B[ A, B ]

The colon operator provides syntactic sugar for constructing 2-tuple arrays, using A:B as shorthand for [ A, B ].

It is commonly used in Minsc to represent simple 2-field data structures, such as:

  • OutPoint as a txid:vout tuple

    5e876d922f10e1b7382c72b850f1796d5259c62b72266fb995368ec859afb07e:1
    // → [ 5e876d922f10e1b7382c72b8…, 1 ]
    
  • TxOut as a scriptPubKey:amount tuple

    wpkh($alice):1.5 BTC // → [ wpkh($alice), 150000000 ]
    

It is also used for constructing "object"-like tagged arrays of key-value pairs. For example:

$tx = tx([
  "version": 2,
  "locktime": 1 day,
  "inputs": [
    [
      "prevout": 5e876d922f10e1b7382c72b850f1796d5259c62b72266fb995368ec859afb07e:0,
      "witness": [ 0x010203f0e0d0 ],
    ]
  ],
  "outputs": [
    wpkh($alice):1.5 BTC,
    bc1qu6p58lnd32acn0rpzkevk76c7g9aant6k4s2ly:9000,
  ]
]);
$tx = tx([
  [ "version", 2 ],
  [ "locktime", 4194473 ],
  [
    "inputs",
    [
      [
        [ "prevout", [ 0x5e876d922f10e1b7382c72b850f1796d5259c62b72266fb995368ec859afb07e, 0 ] ],
        [ "witness", [ 0x010203f0e0d0 ] ],
      ]
    ],
  ],
  [
    "outputs",
    [
      [ wpkh($alice), 150000000 ],
      [ bc1qu6p58lnd32acn0rpzkevk76c7g9aant6k4s2ly, 9000 ],
    ]
  ]
]);

Unlike all other operators in Minsc, the colon operator is right-associative. This makes line 6 in the last example parse as [ "prevout", [ 5e876d922f…, 0 ] ] rather than [ [ "prevout", 5e876d922f ], 0 ].

When 2-tuple arrays are formatted as strings (e.g. through str() or on the web playground), there's a heuristic that decides whether to format them as [A,B] or A:B. It should typically get it right, but it could be surprising. For example, 1:2 is re-formatted as [1,2] .

Standards Compatibility

Minsc implements several syntax features and coercions that aim to maximize compatibility with standardized string representations of common Bitcoin data structures, including Bitcoin addresses, Xpubs, Xprivs, WIFs, single keys, Miniscript policies, descriptor keys and (some) descriptors.

This enables seamless copy-paste of existing string representations into Minsc code, without requiring any changes. Some caveats apply.

0x-less Bytes

For compatibility with the BIP 380 Key Expression syntax for single public keys and with the Miniscript Policy syntax for hashes, the 0x prefix is optional for bytes of length 20, 32 or 33.

wpkh(02372b2c6b9452181083a4777bf6ec6cbf4f363d25c1a4ee9fd646f5a90ef2e2aa)
// → wpkh(0x02372b2c6b9452181083a4777bf6ec6cbf4f363d25c1a4ee9fd646f5a90ef2e2aa)

Aside from compatibility, this is also commonly used with txids and txid:vot outpoints.

For example (together with the colon operator): 331d3f920595ba389c35e3ecdd91ca85ae24313501d44401567f7ddc626e926a:1

<A;B;…> Arrays

For compatibility with the BIP 389 syntax for multi-path keys and descriptors, <A;B;> is supported as an alternative array construction syntax equivalent to [ A, B, ].

wpkh(xpub68hYLoZ9YjcBZuLp4uq1…EuJzCq/<0;1>/*)
// → wpkh(xpub68hYLoZ9YjcBZuLp4uq1…EuJzCq/[0,1]/*)

Only works with two or more array elements.

{A,B} Arrays

For compatibility with the BIP 386 syntax for Taproot tr() descriptors with binary script trees, {A,B} is supported as an alternative array construction syntax equivalent to [ A, B ].

tr(xpub661MyMwAqRbcGxLpmSg6…J2PK7La,{pk(xpub661MyMwAqRbcFmLNCYgX…GZtNT5a),pk(xpub661MyMwAqRbcFaqugwCZA…how4KY)})
// → tr(xpub661MyMwAqRbcGxLpmSg6J…J2PK7La,[pk(xpub661MyMwAqRbcFmLNCYgXG…GZtNT5a),pk(xpub661MyMwAqRbcFaqugwCZA…how4KY)])

Only works with exactly two array elements.