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
- ASCII string type. Example:
{
"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
- ASCII string type: Example
{
"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
- ASCII string type. Example:
{
"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
- url (string type). Example:
{
"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
}
}
}