Parsing a Bitcoin Transaction

Intro

While we can verify that a transaction was mined using the clarity-bitcoin-lib contract library, we can also parse a Bitcoin transaction using Clarity directly.

If you aren't familiar with how Bitcoin transactions are encoded in raw form, take a quick look at that. The tl;dr is that all of the data from a Bitcoin transaction is encoded in hexadecimal form in a string of bytes; we can slice out pieces of that hex value to pull out all of our transaction data components.

The process to do this is relatively complex, but the clarity-bitcoin-lib provides a function called parse-tx or parse-wtx (for witness transactions) that makes this simple. All we need to do is pass in a raw transaction hex and we get back the data of the transaction, including inputs and outputs.

The current version of the clarity-bitcoin-lib is version 7. Click here for the deployed mainnet contract.

The parse-tx function of the clarity-bitcoin-lib contract looks like this:

clarity-bitcoin-lib
;; --snip--

(define-read-only (parse-tx (tx (buff 4096)))
  (let (
      (ctx {
        txbuff: tx,
        index: u0,
      })
      (parsed-version (try! (read-uint32 ctx)))
      (parsed-txins (try! (read-txins (get ctx parsed-version))))
      (parsed-txouts (try! (read-txouts (get ctx parsed-txins))))
      (parsed-locktime (try! (read-uint32 (get ctx parsed-txouts))))
    )
    ;; check if it is a non-segwit transaction?
    ;; at least check what happens
    (asserts! (is-eq (len tx) (get index (get ctx parsed-locktime)))
      (err ERR-LEFTOVER-DATA)
    )
    (ok {
      version: (get uint32 parsed-version),
      ins: (get txins parsed-txins),
      outs: (get txouts parsed-txouts),
      locktime: (get uint32 parsed-locktime),
    })
  )
)

Where txRaw is a string containing the raw transaction hex. Passing that buffer into parse-tx returns the parsed transaction object with version, inputs (ins), outputs (outs), and locktime. You can then extract whatever fields you need from that returned data.


Steps

1

Get the raw transaction hex

Obtain the raw transaction hex from a Bitcoin explorer API. This is a single hex string representing the entire transaction.

Example raw bitcoin transaction hex:

0200000000010196277c04c986c1ad78c909287fd12dba2924324699a0232e0533f46a6a3916bb0100000000ffffffff026400000000000000160014274ae586ad2035efb4c25049c155f98310d7e106ca16440000000000160014599bcef6387256c6b019030c421b4a4d382fe2600247304402204d94a1e4047ca38a450177ccb6f88585ca147f1939df343d8ac5d962c5f35bb302206f7fa42c21c47ebccdc460393d35c5dfd3b6f0a26cf10fac23d3e6fab71835c20121020cb972a66e3fb1cdcc9efcad060b4457ebec534942700d4af1c0d82a33aa13f100000000

You can paste this into a raw transaction decoder like this one to inspect the decoded fields: https://live.blockcypher.com/btc/decodetx/

2

Pass the transaction hex into Clarity-Bitcoin's parser

Convert the hex string to a Clarity buffer and pass it to the parse-tx function (via your stack.js read-only function call). In stacks.js:

If using stacks.js, pass the raw hex to your Clarity function as a Clarity typed buffer:

3

Examine and use the parsed result

parse-tx returns an object containing:

  • version

  • ins (transaction inputs)

  • outs (transaction outputs)

  • locktime

Extract whatever fields you need from that returned object.


Example Usage

Square Runes

A Clarity implementation for parsing Bitcoin Runes protocol data, allowing Stacks smart contracts to understand and react to Runes transactions on the Bitcoin chain.

Check out the project repo here.

Last updated

Was this helpful?