Skip to main content

Use Chainhooks with Bitcoin

The following guide helps you define predicates to use Chainhook with Bitcoin. The predicates are specified based on if-this, then-that constructs.

if_this Specifications

Currently, bitcoin predicates support the following if_this constructs.

Get any transaction matching a given transaction ID (txid):

  • txid mandatory argument admits:
    • 32 bytes hex encoded type.

{
"if_this": {
"scope": "txid",
"equals": "0xfaaac1833dc4883e7ec28f61e35b41f896c395f8d288b1a177155de2abd6052f"
}
}

Get any transaction matching a given OP_RETURN payload: Example: Given the following script_pubkey :

OP_RETURN
PUSHDATA(0x03)
0x616263

or 0x6a03616263 in hex, the following predicates will match the transaction above.

Get any transaction, where its OP_RETURN payload starts with a set of characters:

  • starts_with mandatory argument admits:
    • ASCII string type. Example: ab
    • hex encoded bytes. Example: 0x6162
{
"if_this": {
"scope": "outputs",
"op_return": {
"starts_with": "ab"
}
}
}

Get any transaction, where its OP_RETURN payload is equals to set of characters:

  • equals mandatory argument admits:
    • ASCII string type: Example abc
    • hex encoded bytes. Example: 0x616263
{
"if_this": {
"scope": "outputs",
"op_return": {
"equals": "0x616263"
}
}
}

Get any transaction, where its OP_RETURN payload ends with a set of characters:

  • ends_with mandatory argument admits:
    • ASCII string type. Example: bc
    • hex encoded bytes. Example: 0x6263
{
"if_this": {
"scope": "outputs",
"op_return": {
"ends_with": "0x6263"
}
}
}

Get any transaction with a p2pkh output paying a given recipient:

  • p2pkh construct admits:
    • ASCII string type. Example: "mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC"
    • hex encoded bytes type. Example: "0x76a914ee9369fb719c0ba43ddf4d94638a970b84775f4788ac"
{
"if_this": {
"scope": "outputs",
"p2pkh": {
"equals": "mr1iPkD9N3RJZZxXRk7xF9d36gffa6exNC"
}
}
}

p2pkh(Pay-to-Public-Key-Hash) is a Bitcoin transaction output script type that allows users to send funds to a recipient's hashed public key, providing security and privacy by concealing the actual public key.

Get any transaction including a p2sh output paying a given recipient p2sh construct admits:

  • p2sh construct admits:
    • string type. Example: "2MxDJ723HBJtEMa2a9vcsns4qztxBuC8Zb2"
    • hex encoded bytes type. Example: "0x76a914ee9369fb719c0ba43ddf4d94638a970b84775f4788ac"
{
"if_this": {
"scope": "outputs",
"p2sh": {
"equals": "2MxDJ723HBJtEMa2a9vcsns4qztxBuC8Zb2"
}
}
}

p2sh(Pay-to-Script-Hash) is a Bitcoin transaction output script type that enables users to send funds to a script instead of a public key, allowing for more complex transaction conditions and multi-signature addresses.

Get any transaction, including a p2wpkh output paying a given recipient:

  • p2wpkh construct admits:
    • string type. Example: "bcrt1qnxknq3wqtphv7sfwy07m7e4sr6ut9yt6ed99jg"
{
"if_this": {
"scope": "outputs",
"p2wpkh": {
"equals": "bcrt1qnxknq3wqtphv7sfwy07m7e4sr6ut9yt6ed99jg"
}
}
}

p2wpkh(Pay-to-Witness-Public-Key-Hash) is a Bitcoin transaction output script type used in Segregated Witness (SegWit) that allows for more efficient and secure transactions by separating the witness data from the transaction data and storing it in a separate block.

Get any transaction, including a p2wsh output paying a given recipient:

  • p2wsh construct admits:
    • string type. Example: "bc1qklpmx03a8qkv263gy8te36w0z9yafxplc5kwzc"
{
"if_this": {
"scope": "outputs",
"p2wsh": {
"equals": "bc1qklpmx03a8qkv263gy8te36w0z9yafxplc5kwzc"
}
}
}

p2wsh (Pay-to-Witness-Script-Hash) is a Bitcoin transaction output script type used in Segregated Witness (SegWit) that enables users to send funds to a hashed script, allowing for more complex transaction conditions and greater scalability by separating the script from the transaction data.

Wallet Descriptors provide a compact and semi-standardized method for describing how scripts and addresses within a wallet are generated. Chainhooks users that want to track addresses derived from an extended pubkey or a multisig-wallet for example, can now rely on this feature instead of defining one predicate per address. For example if we wanted to track the first 3 addressed generated by the following descriptor:

wpkh(tprv8ZgxMBicQKsPePxn6j3TjvB2MBzQkuhGgc6oRh2WZancZQgxktcnjZJ44XdsRiw3jNkbVTK9JW6KFHvnRKgAMtSyuBevMJprSkZ4PTfmTgV/84'/1'/0'/0/*)

which reads: describe a P2WPKH output with the specified extended public key, and produces these BIP84 addresses:

bcrt1qzy2rdyvu8c57qd8exyyp0mw7dk5drsu9ewzdsu
bcrt1qsfsjnagr29m8h3a3vdand2s85cg4cefkcwk2fy
bcrt1qauewfytqe5mtr0xwp786r6fl39kmum2lr65kmj

The following predicate should be defined:

{
"if_this": {
"scope": "outputs",
"descriptor": {
"expression": "wpkh(tprv8ZgxMBicQKsPePxn6j3TjvB2MBzQkuhGgc6oRh2WZancZQgxktcnjZJ44XdsRiw3jNkbVTK9JW6KFHvnRKgAMtSyuBevMJprSkZ4PTfmTgV/84'/1'/0'/0/*)",
"range": [0,3]
}
}
}

Get any Bitcoin transaction, including a Block commitment. Broadcasted payloads include Proof of Transfer reward information:

{
"if_this": {
"scope": "stacks_protocol",
"operation": "block_committed"
}
}

Proof of Transfer(PoT) is a blockchain consensus mechanism where participants prove ownership of assets outside the blockchain from another network and transfer them to the target blockchain to validate and secure transactions without relying solely on computational work or stake ownership.

Get any transaction, including a key registration operation:

{
"if_this": {
"scope": "stacks_protocol",
"operation": "leader_registered"
}
}

Get any transaction, including an STX transfer operation: // Coming soon

{
"if_this": {
"scope": "stacks_protocol",
"operation": "stx_transferred"
}
}

Get any transaction, including an STX lock operation: // Coming soon

{
"if_this": {
"scope": "stacks_protocol",
"operation": "stx_locked"
}
}

Get any transaction including a new Ordinal inscription (inscription revealed and transferred)

{
"if_this": {
"scope": "ordinals_protocol",
"operation": "inscription_feed"
}
}

then_that Constructs

The following then_that constructs are supported:

HTTP Post block/transaction payload to a given endpoint:

  • http_post construct admits:
    • url (string type). Example: http://localhost:3000/api/v1/wrapBtc
    • authorization_header (string type). Secret to add to the request authorization header when posting payloads

{
"then_that": {
"http_post": {
"url": "http://localhost:3000/api/v1/wrapBtc",
"authorization_header": "Bearer cn389ncoiwuencr"
}
}
}

Append events to a file through the filesystem. Convenient for local tests:

  • file_append construct admits:
    • path (string type). Path to the file on disk.
{
"then_that": {
"file_append": {
"path": "/tmp/events.json",
}
}
}

Additional configuration knobs available

The following additional configurations can be used to improve the performance of Chainhook by preventing a full scan of the blockchain:

  • Ignore any block before the given block: "start_block": 101

  • Ignore any block after the given block: "end_block": 201

  • Stop evaluating chainhook after a given number of occurrences found: "expire_after_occurrence": 1

  • Don't include proofs: "include_proof": false

  • Don't include Bitcoin transaction inputs in the payload: "include_inputs": false

  • Don't include Bitcoin transaction outputs in the payload: "include_outputs": false

  • Don't include Bitcoin transaction witnesses in the payload: "include_witness": false

Example predicate definition to post first five transfers

Retrieve and HTTP Post to http://localhost:3000/api/v1/wrapBtc the five first transfers to the p2wpkh bcrt1qnxk...yt6ed99jg address of any amount, occurring after block height 10200.

NOTE:

The start_block is mandatory to post events using http_post then-that predicate.

{
"chain": "bitcoin",
"uuid": "1",
"name": "Wrap BTC",
"version": 1,
"networks": {
"testnet": {
"if_this": {
"scope": "outputs",
"p2wpkh": {
"equals": "bcrt1qnxknq3wqtphv7sfwy07m7e4sr6ut9yt6ed99jg"
}
},
"then_that": {
"http_post": {
"url": "http://localhost:3000/api/v1/transfers",
"authorization_header": "Bearer cn389ncoiwuencr"
}
},
"start_block": 10200,
"expire_after_occurrence": 5, // only prints the first 5 characters
}
}
}

Example predicate with multiple networks

A specification file can also include different networks. In this case, the Chainhook will select the predicate corresponding to the network it was launched against. In the below example, the testnet and mainnet are two different networks.

{
"chain": "bitcoin",
"uuid": "1",
"name": "Wrap BTC",
"version": 1,
"networks": {
"testnet": {
"if_this": {
"scope": "ordinals_protocol",
"operation": "inscription_feed"
},
"then_that": {
"http_post": {
"url": "http://localhost:3000/api/v1/ordinals",
"authorization_header": "Bearer cn389ncoiwuencr"
}
},
"start_block": 10200,
},
"mainnet": {
"if_this": {
"scope": "ordinals_protocol",
"operation": "inscription_feed"
},
"then_that": {
"http_post": {
"url": "http://my-protocol.xyz/api/v1/ordinals",
"authorization_header": "Bearer cn389ncoiwuencr"
}
},
"start_block": 90232,
}

}
}