Overview
Blockchain transactions being complex, having an index of human readable transaction data can come in handy.
In this Function example, we will set up a blockchain transaction Stream and translate transactions, using QuickNode Streams and Functions.
Sample Function
We will create and use a QuickNode Function as a destination for our Stream. The Function will get Transaction hashes from the Stream, send a request to Noves API, and send the translated transaction data to a webhook.
Go to QuickNode Functions editor and paste the following code
const fetch = require('node-fetch');
// API key for Noves and webhook URL for sending processed data
const NOVES_API_KEY = 'YOUR_NOVES_KEY';
const WEBHOOK_URL = 'YOUR_WEBHOOK_URL';
/**
* Translates a single transaction using the Noves API
* @param {string} txHash - The transaction hash to translate
* @returns {Object|null} - Translated transaction data or null if an error occurs
*/
async function translateTransaction(txHash) {
const url = `https://translate.noves.fi/evm/eth/tx/${txHash}?v5Format=false`;
const options = {
method: 'GET',
headers: {
accept: 'application/json',
apiKey: NOVES_API_KEY
}
};
try {
const response = await fetch(url, options);
const data = await response.json();
// Extract and return relevant information from the API response
return {
description: data.classificationData.description,
fromAddress: data.rawTransactionData.fromAddress,
toAddress: data.rawTransactionData.toAddress,
gas: data.rawTransactionData.gas,
blockNumber: data.rawTransactionData.blockNumber
};
} catch (error) {
console.error(`Error translating transaction ${txHash}:`, error);
return null;
}
}
/**
* Sends data to the specified webhook
* @param {Object} data - The data to send to the webhook
*/
async function sendToWebhook(data) {
try {
const response = await fetch(WEBHOOK_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
console.log('Data sent to webhook successfully');
} catch (error) {
console.error('Error sending data to webhook:', error);
}
}
/**
* Main function to process transaction hashes
* @param {Object} params - Input parameters from QuickNode
* @returns {Object} - Summary of processed transactions
*/
async function main(params) {
console.log('Starting main function with params:', JSON.stringify(params, null, 2));
let txHashes;
// Extract transaction hashes from the input parameters
if (typeof params === 'object' && params !== null) {
if (params.data && Array.isArray(params.data)) {
txHashes = params.data;
} else {
txHashes = params;
}
}
// Validate input
if (!Array.isArray(txHashes) || txHashes.length === 0) {
console.error('Invalid or missing transaction hashes in params:', params);
throw new Error('Invalid or missing transaction hashes in params');
}
console.log(`Processing ${txHashes.length} transaction hashes`);
const results = [];
const batchSize = 10; // Process 10 transactions at a time
// Process transactions in batches
for (let i = 0; i < txHashes.length; i += batchSize) {
const batch = txHashes.slice(i, i + batchSize);
const batchPromises = batch.map(txHash => translateTransaction(txHash));
const batchResults = await Promise.all(batchPromises);
const validResults = batchResults.filter(result => result !== null);
results.push(...validResults);
// Send batch results to webhook
if (validResults.length > 0) {
await sendToWebhook(validResults);
console.log(`Sent batch of ${validResults.length} transactions to webhook`);
}
}
// Return summary of processed transactions
return {
message: 'Transactions processed successfully',
processedTransactions: results.length,
totalTransactions: txHashes.length
};
}
module.exports = { main };
Replace YOUR_NOVES_KEY with your Noves API Key and YOUR_WEBHOOK_URL with your webhook URL. We've used webhook to demo the functionality but ideally the Function will feed into a database like Postgres.
The above script receives transaction hashes for the latest blocks from QuickNode Stream, extracts it, makes an API call to Noves API for each transaction to get translated transaction data, and sends translated transaction data to webhook.
We also have try and catch block in place to log any error so that if the delivery is hindered, we can have logs in our Streams dashboard, which can be helpful to configure retries and timeouts.
Set up a QuickNode Stream
For our example, we'll stream transaction hashes for every new Ethereum mainnet block. But, with Streams, you can do all kinds of things, from getting raw data to highly granular and enriched data; the possibilities are endless. You can check some examples in our backfill library.
The following is the Streams Filter being used:
Try it yourself by deploying the filtered Stream to your account.
function main(stream) {
var transactions = stream.data ? stream.data[0] : stream[0];
var hashes = transactions.map(transaction => transaction.hash);
return hashes;
}
Now, while selecting a destination for your QuickNode Stream, select the previously created Function as the destination for your Stream and create the Stream. Once the Stream starts streaming transaction hashes to the Function. The Function will send API requests to Noves API for each transaction and send translated transaction info to a webhook. It will look like this:
[
{
"description": "Added 33.82 WETH to a liquidity pool.",
"fromAddress": "0x28638f96ef6Bc8f0Be436A45B1fE9A397CDd68D5",
"toAddress": "0xC36442b4a4522E871399CD717aBDD847Ab11FE88",
"gas": 740962,
"blockNumber": 20844838
},
{
"description": "Called function 'buyWithEth' on contract 0xa436.",
"fromAddress": "0x62feB0006b0aB479a5e2788c704d1975a282cF2D",
"toAddress": "0xa436C9048d4927ff69943278aAe0E426f9f68755",
"gas": 211259,
"blockNumber": 20844838
},
{
"description": "Called function 'startWithdraw' on contract 0xAfa9.",
"fromAddress": "0x065CC7D59Ca3Dd39fFe0B490587a90c54F8e9023",
"toAddress": "0xAfa904152E04aBFf56701223118Be2832A4449E0",
"gas": 479556,
"blockNumber": 20844838
}
]
Learn more about QuickNode Functions.
We ❤️ Feedback!
Let us know if you have any feedback or requests for new topics. We'd love to hear from you.