Skip to main content

How to Build Your Own Indexer and API for Contract Deployment Details

Created on
Updated on
Nov 26, 2024

8 min read

Overview

Retrieving smart contract deployment details can be time-consuming. In this guide, we'll show you how to build your own indexer and API to store and retrieve smart contract deployment details using QuickNode's Streams and Key-Value Store.

Specifically, we'll teach you how to use Streams with Filters to monitor new blocks for contract deployments. You'll learn to store these deployment details in a Key-Value Store, which can be retrieved later via REST API call.

Let's get started!

What You Will Do


  • Learn about Streams and Key-Value Store
  • Create a Stream with a Filter to monitor new contract deployments
  • Store contract deployment details in Key-Value Store
  • Test the Key-Value Store API via REST API call

What You Will Need


Streams

Streams is a powerful tool that allows you to retrieve both real-time and historical blockchain data to your application of choice. Streams provides a flexible way to retrieve information from various blockchain networks and deliver it to your specified destination, such as a Webhook, Postgres database, S3 bucket, etc. More information on Streams can be found here. Also, be sure to check out our long list of Streams tutorials.

Filters

Streams includes a powerful feature called Filters, which allows you to refine the blockchain data sent from your Stream. With Filters, you can specify exactly what blockchain data you want, ensuring you only process and pay for relevant data to you. To learn more about how to effectively use Filters in your Streams, check out our comprehensive Filters documentation.

Key-Value Store

QuickNode's Key-Value Store is a fast, scalable database solution perfect for storing blockchain-related data. In this guide, we'll use it to store contract deployment details, then later we'll retrieve this data via REST API call from the Key-Value Store. Learn more about Key-Value Store in our documentation.

Create a Stream to Monitor Contract Deployments

Now, we'll walk you through creating the Stream to monitor and retrieve smart contract deployment details on the Ethereum blockchain. Specifically, we'll show you how to:


  • Set up a Stream with Filter to detect new smart contract deployments
  • Store deployment details in Key-Value Store
  • Retrieve contract deployment information by calling the Key-Value Store via REST call

Navigate to your QuickNode Dashboard and then click Streams in the left sidebar. After, click Create Stream and use the following configuration:


  • Chain and Network: Ethereum Mainnet
  • Dataset: Receipts
  • Stream name: This is a random name but can be updated if you want
  • Region: Pick a region closest to you
  • Batch size: We'll leave this at 1 in order to get real-time data but if you are backfilling you may want to test different batch numbers to increase efficiency
  • Stream Start: Latest Block (you could also configure an earlier start block if you want)
  • Stream End: Doesn't end (you could also configure an end block if you want)

Then, in the Stream payload section, choose the Modify payload before streaming option and include the code below.

function hexToDecimal(hex) {
return parseInt(hex, 16);
}

function main(stream) {
if (!stream || !stream[0]) {
return {
newContracts: [],
kvApiResults: []
};
}

var newContracts = stream[0].filter(function(receipt) {
return receipt.to === null;
}).map(function(receipt) {
return {
txHash: receipt.transactionHash,
from: receipt.from,
contractAddress: receipt.contractAddress,
blockNumber: hexToDecimal(receipt.blockNumber),
network: stream[0].network
};
});

const results = {
newContracts: newContracts,
kvApiResults: [],
};

const networkName = stream[0].network;
for (const contract of newContracts) {
const key = `${contract.contractAddress}:${networkName}`;
const item = `deploymentHash:${contract.txHash}`;

try {
const upsertResult = qnUpsertList(key, {
add_items: [item]
});
results.kvApiResults.push(upsertResult);
} catch (error) {
results.kvApiResults.push({ error: error.message });
}
}

return results;
}

Let's recap the code:


  • hexToDecimal is a helper function used to decode hexadecimal to decimal values, we'll use this for converting the block number from hex to a decimal
  • The main function contains the logic for the contract deployment tracker. For every new block sent from our Stream, we'll check the receipts, checking if the to field is null (this represents a contract deployment transaction).
    • If a contract deployment transaction is found, we'll add it's details (txHash, from, contractAddress, blockNumber, network) to a newContracts mapping
    • Next, we'll loop over the mapping of new contracts and add them to our Key-Value Store using helper commands qnUpsertList. An example of a key and item will look like this: 0x891a369c87ea9804792494b39879372e8ec87bb59401ec721d3ee4b650a1b2aa:ethereum-mainnet (e.g., contractAddress:networkName).
    • Result and error handling is included to inform us if the insertion to the Key-Value Store was valid.
tip

When working with the Key-Value Store within Streams filters, you will need to use the helper functions (e.g., qnUpsertList(), qnGetList(), qnGetAllLists(), etc.) instead of making REST API calls. More information can be found here.

We can test the filter code by specifying a Test block number, then clicking Run test. You should see a similar result to the below if you test on block 20921736.

{
"kvApiResults": [
"OK",
"OK"
],
"newContracts": [
{
"blockNumber": 20921736,
"contractAddress": "0xf26ac92ff8617622bcf77c04317c009a8134dce2",
"from": "0xf400de71bf20c529565f27aefdd80c00e579e696",
"network": "ethereum-mainnet",
"txHash": "0x891a369c87ea9804792494b39879372e8ec87bb59401ec721d3ee4b650a1b2aa"
},
{
"blockNumber": 20921736,
"contractAddress": "0x6c55f346c20ca2b0c62e30790907f0a41c978ccc",
"from": "0xf400de71bf20c529565f27aefdd80c00e579e696",
"network": "ethereum-mainnet",
"txHash": "0x43973028c8d95a3a35edb2c685786c43ab56d8dd372b5fd4add690ec7a18a74f"
}
]
}

Under the code editor, you'll see the Reorg handling section. Leave this as-is and click Next in the bottom-right to continue configuring the Stream.

In the next section, we'll set up the destination.

Set Up Destination

Since the data we need was already filtered, we don't need to do any additional processing. Therefore, we'll simply send the updates to our Webhook destination, which you can modify later if you want additional features.

Click Webhook as the destination and then fill in the following details:


  • Destination URL: This is the URL you want the Stream data sent to. For demonstration purposes, we'll set up a Webhook using https://typedwebhook.tools/
  • Payload compression: None (leave as-is)
  • Include metadata in: Header (modifiable as needed). Metadata details include information such as stream_id, batch_start_range, batch_end_range, network, etc. IMPORTANT: Currently, there is no easy way to test metadata within the Filters UI (the examples above we're taken before an update was made). As a result, you won't see Stream metadata when testing in the Filters UI. We are working on updating this to improve the metadata testing experience.
  • Timeout, Retry data fetch every, Pause stream after: Leave this as the default values

Once that data is filled in, click the Run Test button, and the Stream will send a sample of your Stream data to the URL. If you didn't change the previous Test block number, you will see the same test result sent to your Webhook; otherwise, if you're testing on a block with no new contract deployments, you will see an empty payload such as:

{
"kvApiResults": [],
"newContracts": []
}

This is normal since not every block will have a new contract deployment. Finally, click the Create a Stream button to create the Stream. Keep an eye on your Webhook; when a contract deployment is detected, you'll see a result similar to this:

typedwebhook tools result

In the next section, we'll show you how to call the Key-Value Store via REST calls to retrieve these smart contract deployment details.

Test the Key-Value Store

Ensure that the smart contract address you want details for has been deployed after your Stream was initiated (or else the Key-Value Store will be empty/undefined).

Then, to call the Key-Value Store via a REST call, we can call the /kv/rest/v1/lists/{key} REST method and do the following:

curl -X GET \
"https://api.quicknode.com/kv/rest/v1/lists/{contractAddress:network}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY"

Replace the key placeholder with the proper key (i.e., contractAddress:network). Also, include your API key in the YOUR_API_KEY field. This can be retrieved on your QuickNode dashboard by clicking your profile icon, then clicking API Keys.

The response will look something like:

{
"code": 200,
"msg": "",
"data": {
"items": [
"deploymentHash:0x3c63a1e63ad93b564db9740278bc0fb084505747c78f5981076001bdd5467b6f"
]
}
}

Note: Fetching a contract address that has not been added to the Key-Value Store will result in an undefined response.

You can now easily use this transaction hash to find out more about the transaction by either looking it up on a block explorer like Etherscan, or calling the eth_getTransactionReceipt RPC method to get the receipt of a transaction (which will include fields like logs, etc.).

Nice work! You just created a Stream to build your own indexer and API for smart contract deployments. If you want to continue building upon this logic, try the following ideas:


  1. Backfill historical blockchain data so you can eventually have a database containing all smart contract deployment history
  2. Create streams for multiple blockchains. Since we set up our key in the Key-Value Store to work with different networks, this is a perfect time to try and utilize it
  3. Use Functions to create custom APIs that access the Key-Value Store and enrich the data. For example, you could fetch the entire transaction related to a deployment hash, providing more comprehensive information about contract deployments.

Final Thoughts

Well done! This guide has demonstrated how to build an indexer and API that monitors new smart contract deployments using QuickNode's Streams.

Regardless of what you are building, we would love to hear about it. Drop us a line on Discord or Twitter and let us know what you're working on!

We ❤️ Feedback!

Let us know if you have any feedback or requests for new topics. We'd love to hear from you.

Share this guide