List unspent transaction outputs by address on Bitcoin with Amazon Managed Blockchain Query

7 minute read
Content level: Intermediate
3

Retrieve a list of spent and unspent transaction outputs for your Bitcoin wallet using Amazon Managed Blockchain Query.

In order to build an application that interacts with the Bitcoin blockchain, whether it be a wallet, an Ordinals marketplace, or a BTC exchange, you must be able to reliably access the Bitcoin network. For example, you will need to read critical data from the blockchain that acts as input for properly constructed Bitcoin transactions. The most foundational of these data needs is retrieving data pertaining to transaction outputs for a given wallet from the Bitcoin network, which serves a variety of use cases. For example, retrieving unspent transaction outputs (UTXOs) to be consumed as inputs for outgoing transactions of BTC, retrieving all UTXOs for a given wallet to calculate a balance in BTC, gathering spent outputs to render a list of past Bitcoin transactions, and more.

In this post, we focus on how to use the blockchain data APIs available in Amazon Managed Blockchain (AMB) Query to provide reliable, cost-effective access to Bitcoin data at scale, including transaction output data. Furthermore, AMB Access provides serverless access to a fleet of Bitcoin nodes that you can use to broadcast your Bitcoin transactions after you have retrieved UTXOs ready to spend from AMB Query.

AMB Query provides a set of developer-friendly APIs that serve current and historical blockchain data from multiple public blockchains, including Bitcoin. With AMB Query, developers can shed the undifferentiated heavy lifting of node operations, blockchain data indexing, and ETL (extract, transform, and load), and focus their efforts on differentiated features for their application. With AMB Query, you can now use the ListFilteredTransactionEvents API to query for spent or unspent transaction outputs for an address or set of addresses on the Bitcoin network and its testnet, providing an alternative to the built-in listunspent JSON-RPC API in the Bitcoin Core node implementation.

The ListFilteredTransactionEvents API allows developers to do the following:

  • Retrieve unspent transaction outputs for a given address or set of addresses to ease the process of constructing Bitcoin transactions
  • Retrieve spent transaction output information to populate dashboards or wallet interfaces
  • Filter transaction events pertaining to unspent outputs by timestamp and finality status

If you would like to dive deeper into the fundamentals of using the unspent transaction output (UTXO) model employed by Bitcoin to compute BTC balances, you can read more in the accompanying blog post for this article, List unspent transaction outputs by address on Bitcoin with Amazon Managed Blockchain Query.

Working with the ListFilteredTransactionEvents API on AMB Query

The ListFilteredTransactionEvents API takes a list of addresses and returns any applicable Bitcoin transaction VOUT events, both spent and unspent, pertaining to those addresses. Data is returned in the following format, which provides the key properties that you may use to populate a Bitcoin transaction with any number of those unspent outputs:

HTTP/1.1 200
Content-type: application/json

{
   "events": [ 
      { 
         "blockchainInstant": { 
            "time": number
         },
         "confirmationStatus": "string",
         "contractAddress": "string",
         "eventType": "string",
         "from": "string",
         "network": "string",
         "spentVoutIndex": number,
         "spentVoutTransactionHash": "string",
         "spentVoutTransactionId": "string",
         "to": "string",
         "tokenId": "string",
         "transactionHash": "string",
         "transactionId": "string",
         "value": "string",
         "voutIndex": number,
         "voutSpent": boolean
      }
   ],
   "nextToken": "string"
}

In the preceding response format document, you will find several properties pertaining to spent and unspent transaction outputs. When retrieving spent transaction outputs, spentVoutTransactionHash, spentVoutTransactionId, and spentVoutIndex define the transaction identifiers and the array position of the spent output (vout) so you can easily determine the origin of that output. For unspent transaction outputs, the properties transactionHash, transactionId, and voutIndex define the transaction identifiers and the array position of the unspent output in the parent transaction. Additionally, the flag confirmationStatus defines whether the transaction containing the output is considered final on the Bitcoin blockchain. In AMB Query, a transaction’s finality is defined as equal to or greater than six confirmations, or blocks mined after the block in which the transaction is contained. Transactions considered final according to this criterion will be marked FINAL, whereas unconfirmed transactions will be marked NONFINAL.

To better illustrate the response from the API, the JavaScript code sample defines a simple script to retrieve a list of unspent transaction outputs for a single Bitcoin address on the Bitcoin testnet. To run these Node.js examples, the following prerequisites apply:

  • You must have node version manager (nvm) and Node.js installed on your machine. You can find installation instructions for your OS in the nvm GitHub repo.
  • Use the node --version command and confirm that you are using Node version v18.12.0 (LTS) or higher. If required, you can use the nvm install 18.12.0 command, followed by the nvm use 18.12.0 command to install.
  • The environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must contain the credentials that are associated with the account. Export these variables as strings on your client by using the following commands. Replace the values in the following code with appropriate values from the AWS Identity and Access Management (IAM) user account.

Copy the following two code snippets to your local working directory, containing the code sample (index.js) and package.json, which contains one dependency, the official AWS SDK module for AMB Query:

The following is the content of the package.json file:

{
  "name": "query-bitcoin",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@aws-sdk/client-managedblockchain-query": "^3.538.0"
  }
}

The following is the index.js code:


const { ManagedBlockchainQueryClient, ListFilteredTransactionEventsCommand } = require("@aws-sdk/client-managedblockchain-query");

const listUnspentOutputs = async (addresses) => { 
  const client = new ManagedBlockchainQueryClient({ region: "us-east-1" });
  
  const input = { // ListFilteredTransactionEventsInput
    addressIdentifierFilter: { 
      "transactionEventToAddress": [ ...addresses ]
    }, // list addresses to get events for 
    network: "BITCOIN_TESTNET", // define the network, testnet or mainnet
    sort: { 
      sortOrder: "ASCENDING",
    }, // sort order
    maxResults: Number("250"),
    "voutFilter": { 
      "voutSpent": false // retrieve only unspent outputs
   }
  };

  const command = new ListFilteredTransactionEventsCommand(input);

  try {
    const data = await client.send(command);
    console.log(data);
  } catch (error) {
    console.error(error);
  } 
}

listUnspentOutputs(['tb1qfrpw8av77mrmcf03epn5evfhv59z0t793jdh6t']) // input the Bitcoin addresses of your choice here

To use this code, run the following command from your working directory, replacing the address defined in the index.js with the addresses of your choice, if desired:

npm i && node index.js

With the filter set for unspent transactions only in the preceding code sample, the result will be similar to the following:

events: [
    {
      blockchainInstant: [Object],
      confirmationStatus: 'FINAL',
      eventType: 'BITCOIN_VOUT',
      network: 'BITCOIN_TESTNET',
      to: 'tb1qfrpw8av77mrmcf03epn5evfhv59z0t793jdh6t',
      tokenId: 'btc',
      transactionHash: '195d8c08bda48315b4ba3690264dcee36a7862f98c15d3dde9cf57bbbdf6c6ad',
      transactionId: '438961c65220ca0c9427614f9dcd2a7ca8a1e638e7702debae6aa28417045ce7',
      value: '0.00001',
      voutIndex: 0,
      voutSpent: false
    },
    {
      blockchainInstant: [Object],
      confirmationStatus: 'FINAL',
      eventType: 'BITCOIN_VOUT',
      network: 'BITCOIN_TESTNET',
      to: 'tb1qfrpw8av77mrmcf03epn5evfhv59z0t793jdh6t',
      tokenId: 'btc',
      transactionHash: 'c1f2b279e50142fbbf62282e51c3e0a2f04247173f3b9dceef15345e12aa5db2',
      transactionId: '0ec51620a146b9d3e61b35fd68c7fa3e124a1b4bd2d582177fe9529a8c59158b',
      value: '0.00001',
      voutIndex: 0,
      voutSpent: false
    },
    {
      blockchainInstant: [Object],
      confirmationStatus: 'FINAL',
      eventType: 'BITCOIN_VOUT',
      network: 'BITCOIN_TESTNET',
      to: 'tb1qfrpw8av77mrmcf03epn5evfhv59z0t793jdh6t',
      tokenId: 'btc',
      transactionHash: '347994d54743989fb65e8845d6d10b8cea87b0d4aa6c98dfcdd063735711be8a',
      transactionId: '8ce1bbf94989cc23a13d5bf2d44dbba51b2f50a4ab759cf1309e97a3eb7d5b76',
      value: '0.00001',
      voutIndex: 0,
      voutSpent: false
    },
    {
      blockchainInstant: [Object],
      confirmationStatus: 'FINAL',
      eventType: 'BITCOIN_VOUT',
      network: 'BITCOIN_TESTNET',
      to: 'tb1qfrpw8av77mrmcf03epn5evfhv59z0t793jdh6t',
      tokenId: 'btc',
      transactionHash: 'd307359bbc4d68542f2c9c2bce73094280ad304b7240715305fcb3f80323308d',
      transactionId: '17ae7be33a27ab9bee81b91428abca423824e68be8e944399809a5f0fe197c90',
      value: '0.00001545',
      voutIndex: 0,
      voutSpent: false
    }
  ]
}

In the response, you can see that the transaction identifiers (transactionHash and transactionId), value, and voutIndex are defined for each unspent output, which can serve as inputs for constructing future Bitcoin transactions.

You can modify the filters in your query to retrieve different data, such as retrieving both unspent and spent outputs, or restricting the timespan for which to retrieve data. For more information about these filters, refer to Request Body. Requests to AMB Query APIs are billed per request based on pricing buckets per million requests. You can view the latest pricing information for each API to calculate the cost of requests to AMB Query.

profile pictureAWS
EXPERT
published 5 months ago1566 views