Verifying Bitcoin Transactions in Clarity
Trustlessly validate Bitcoin data on-chain.

The current version of the clarity-bitcoin-lib is version 7. Click here for the deployed mainnet contract.
Intro
One of the unique features of the Stack chain and the Clarity language is that it allows for using read-only functions to trustlessly validate on-chain Bitcoin activity from Clarity smart contracts. This enables developers to build applications that react to real Bitcoin activity — such as deposits, transfers, and proofs of inclusion — without relying on off-chain indexers or oracles. In this guide, you’ll learn how to verify Bitcoin transactions in Clarity and safely bridge Bitcoin state into on-chain logic.
Knowledge of Bitcoin state: has knowledge of the full Bitcoin state; it can trustlessly read Bitcoin transactions and state changes and execute smart contracts triggered by Bitcoin transactions. The Bitcoin read functionality helps to keep the decentralized peg state consistent with BTC locked on Bitcoin L1, amongst other things. - Stacks Whitepaper
tl;dr
We'll be using a popular smart contract
clarity-bitcoin-libto verify bitcoin transactions on-chain in Clarity.Allows contracts to enforce logic based on Bitcoin events—deposits, withdrawals, lockups, or multisig activity.
Powers designs like wrapped assets, and deposits that mint or unlock value only after provable Bitcoin transactions.
Steps
Fetch bitcoin transaction metadata: block height and header, transaction hex, and the transaction's merkle proof of inclusion.
Pass in transaction metadata as arguments to
clarity-bitcoin-libcontract'swas-tx-mined-compactread-only function.Returns
(ok <txid>)if the proof checks out and the transaction is indeed mined in the specified Bitcoin block.
Key Tools To Use
Clarity Bitcoin library: A Clarity library for parsing Bitcoin transactions and verifying Merkle proofs.
mempool.space APIs: Bitcoin explorer APIs used to fetch bitcoin transaction metadata.
Bitcoin Transaction Proof [optional]: A TypeScript library for generating Bitcoin transaction proofs, including witness data and merkle proofs.
Clarity Bitcoin Client [optional]: Clarity Bitcoin Client is an open-source TypeScript library for interacting with the
clarity-bitcoin-libcontract on Stacks.
Complete Code
If you want to jump straight to the full implementation, the complete working code used in this guide is shown below.
The below consists of the main client side code of preparing the bitcoin transaction metadata and passing them into a contract call to the clarity-bitcoin-lib contract.
Helper functions to fetch the bitcoin transaction metadata from an explorer's API.
This is a snippet of the contract function we invoke to verify a bitcoin transaction.
Walkthrough
Fetch bitcoin transaction metadata
Using mempool.space's APIs and with a bitcoin txid, fetch the required bitcoin transaction metadata needed for Clarity.
Fetches the transaction hex
Fetches the transaction merkle proof
Fetches the block header
Removes witness data from transaction hex
Are there libraries to help with fetching of the bitcoin transaction metadata?
There sure are. Here are two community-built libraries that can abstract away some of the complexities with gathering the bitcoin transaction metadata.
Bitcoin Transaction Proof - A TypeScript library for generating Bitcoin transaction proofs, including witness data and merkle proofs.
Clarity Bitcoin Client - A TypeScript library for interacting with the clarity-bitcoin-lib contract on Stacks.
Don't have a bitcoin transaction id?
Learn how to create and broadcast a bitcoin transaction on the frontend here.
Prepare metadata as Clarity arguments
Let's circle back to the was-tx-mined-compact function of the clarity-bitcoin-lib contract for a second and analyze the order/type of parameters.
You can see that it intakes the parameters with a certain typing and order:
(height uint)the block height you are looking to verify the transaction within(tx (buff 1024))the raw transaction hex of the transaction you are looking to verify(header (buff 80))the block header of the block(proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint})a merkle proof formatted as a Clarity tuple
In short, the was-tx-mined-compact function takes the block height, the transaction hex, the block header, and a merkle proof, and determines that:
the block header corresponds to the block that was mined at the given Bitcoin height
the transaction's merkle proof links it to the block header's merkle root.
The current version of the clarity-bitcoin-lib is version 7. Click here for the deployed mainnet contract.
So after you've fetched the bitcoin tx metadata and have removed the witness data from the original transaction hex, let's go ahead and prepare the metadata into it's Clarity parameter values.
What is the purpose of the merkle proof?
A Merkle proof is a mathematically efficient and compact manner to prove that a transaction is included in a block in the Bitcoin blockchain.
How transactions are combined into the Merkle root
Transactions in a block are hashed and paired, then the hashes of the pairs are hashed and paired, and so on until a single hash remains — this is called the Merkle root.
Merkle root in the block header
The Merkle root is included in the block header. By providing the hashes that lead from a transaction's hash up to the Merkle root, along with the block header, one can prove that the transaction is included in that block.
Merkle proof (Merkle path)
The hashes that connect a transaction to the Merkle root are called the Merkle proof or Merkle path. By providing the Merkle proof along with the transaction hash and block header, anyone can verify that the transaction is part of that block.
Efficient decentralized verification
This allows for efficient decentralized verification of transactions without having to download the entire blockchain. One only needs the transaction hash, Merkle proof, and block header to verify.
Why are we removing the witness data from the original tx hex?
The clarity-bitcoin-lib contract's was-tx-mined-compact function only accepts a non-witness transaction hex. Usually only legacy bitcoin transactions are deemed as non-witness transactions. The contract also has a dedicated function for bitcoin transactions with witness data called was-segwit-tx-mined-compact .
But you could still verify any transaction type in was-tx-mined-compact by simply removing the witness data from the original tx hex.
Invoke `was-tx-mined-compact` function
Construct a read-only function call, pass in the contract call options, and await the results. If the bitcoin transaction in question is indeed mined in an existing Bitcoin block, the contract will return a response that looks like:
(ok 0xfb88309b4041f76bea0c196633c768dca82bb0dd424cbfe19be38569007f92d9)
You'll see your exact bitcoin txid returned wrapped in an ok response.
That's the end-to-end flow: fetch the bitcoin transaction metadata and its merkle proof, prepare and assemble the data for the contract's read-only function call, and finally call the Clarity contract function that verifies inclusion in a Bitcoin block.
Example Usage
Here are some example projects/contracts that would leverage the clarity-bitcoin-lib contract in their own code.
Square Runes
A Clarity implementation for parsing Bitcoin Runes protocol data. Check out the project repo here.
Additional Resources
Last updated
Was this helpful?