Overview
Solana's high-performance blockchain generates extensive data that can be challenging to analyze.
In this Function example, we will provide comprehensive block analytics by aggregating crucial metrics that aren't directly available through standard RPC methods. This will enable developers to gain deeper insights into network activity and performance.
Sample Function
The following is the code for the Function to get the vital Solana block analytics data in JavaScript Node.js v20
runtime using the block dataset:
// Initialize the main function
function main(params) {
const dataset = params.metadata.dataset
const network = params.metadata.network
const block = params.data[0]
// Initialize counters and aggregators
let metrics = {
message: `Analysis from the ${dataset} dataset on the ${network} network.`,
blockHeight: block.blockHeight,
blockTime: block.blockTime,
// Compute units
totalComputeUnits: 0,
avgComputeUnitsPerTx: 0,
// Fee metrics
totalFees: 0,
avgFeePerTx: 0,
// Balance changes
totalBalanceChange: 0,
// Program invocations
programInvocations: {},
// Transaction metrics
successfulTxCount: 0,
failedTxCount: 0,
// Account activity
uniqueAccounts: new Set(),
uniqueSigners: new Set(),
// Write set size
totalWritableAccounts: 0,
avgWritableAccountsPerTx: 0,
}
// Process each transaction
block.transactions.forEach(tx => {
// Compute units
metrics.totalComputeUnits += tx.meta.computeUnitsConsumed
// Fees
metrics.totalFees += tx.meta.fee
// Success/Failure count
if (tx.meta.err === null) {
metrics.successfulTxCount++
} else {
metrics.failedTxCount++
}
// Balance changes
const preBalances = tx.meta.preBalances
const postBalances = tx.meta.postBalances
metrics.totalBalanceChange += postBalances.reduce(
(sum, bal, i) => sum + (bal - preBalances[i]),
0
)
// Program invocations from log messages
tx.meta.logMessages?.forEach(log => {
if (log.includes('invoke [1]')) {
const program = log.split(' ')[1]
metrics.programInvocations[program] =
(metrics.programInvocations[program] || 0) + 1
}
})
// Account activity
tx.transaction.message.accountKeys.forEach(acc => {
metrics.uniqueAccounts.add(acc.pubkey)
if (acc.signer) {
metrics.uniqueSigners.add(acc.pubkey)
}
if (acc.writable) {
metrics.totalWritableAccounts++
}
})
})
// Calculate averages
const txCount = metrics.successfulTxCount + metrics.failedTxCount
metrics.avgComputeUnitsPerTx = metrics.totalComputeUnits / txCount
metrics.avgFeePerTx = metrics.totalFees / txCount
metrics.avgWritableAccountsPerTx = metrics.totalWritableAccounts / txCount
// Convert Sets to counts
metrics.uniqueAccountCount = metrics.uniqueAccounts.size
metrics.uniqueSignerCount = metrics.uniqueSigners.size
// Clean up intermediate Set objects
delete metrics.uniqueAccounts
delete metrics.uniqueSigners
return metrics
}
Request
Request
We will invoke the function with the following cURL command. A few notes:
- Replace the YOUR_API_KEY with your own QuickNode API key - follow this guide for creating an API key.
- Replace the FUNCTION_ID with the ID of your Function - you can find this in the URL when viewing your Function in the QuickNode Dashboard.
- Use the block_number parameter to specify the block number you want to analyze. You can also omit this property for the function to run against the latest block.
curl -X POST "https://api.quicknode.com/functions/rest/v1/functions/FUNCTION_ID/call?result_only=true" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-H "x-api-key: YOUR_API_KEY" \
-d '{
"network": "solana-mainnet",
"dataset": "block",
"block_number": 288218963
}'
Response
Resulting in the following response:
{
"message": "Analysis from the block dataset on the solana-mainnet network.",
"blockHeight": 288218963,
"blockTime": 1735198645,
"totalComputeUnits": 41471436,
"avgComputeUnitsPerTx": 27833.178523489933,
"totalFees": 31947701,
"avgFeePerTx": 21441.410067114095,
"totalBalanceChange": -31947701,
"programInvocations": {
"Vote111111111111111111111111111111111111111": 905,
"ComputeBudget111111111111111111111111111111": 827,
"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P": 13,
"11111111111111111111111111111111": 394,
"AzHrwdCsEZotAjr7sjenHrHpf1ZKYoGBP6N7HVhEsyen": 1,
"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4": 168,
"DgakD8xgWC3wSjy2hnkgibo2wFRJMNkrwkk5wkpf596q": 2,
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA": 40,
"AFW9KCZtmtMWuhuLkF5mLY9wsk7SZrpZmuKijzcQ51Ni": 2,
"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8": 20,
"4pP8eDKACuV7T2rbFPE8CHxGKDYAzSdRsdMsGvz2k4oc": 2,
"HQ2UUt18uJqKaQFJhgV9zaTdQxUZjNrsKFgoEDquBkcx": 3,
"6753FE9MXMv68A5T3Tqp465MHi1wsFSw6beEUM7nPVJf": 5,
"CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK": 4,
"BSfD6SHZigAfDWSjzD5Q41jw8LmKwtmjskPH9XW1mrRW": 4,
"GDDMwNyyx8uB6zrqwBFHjLLG3TBYk2F8Az4yrQC5RzMp": 26,
"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL": 212,
"PSMxQbAoDWDbvd9ezQJgARyq6R9L5kJAasaLDVcZwf1": 2,
"PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY": 35,
"DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M": 15,
"opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb": 20,
"SoLFiHG9TfgtdUXUjWAxi3LtvYuFyDLVhBWxdMZxyCe": 14,
"MEVK7RP6dDTbxAwirSY6R5v66oS5WvzbhAQD9jpy9Lj": 4,
"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr": 6,
"TipgrjcESvvR7G4MUDiR1dWgbFqC6fprUPyZeYVDqFS": 1,
"dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH": 11,
"TT1eRKxi2Rj3oEvsFMe9W5hrcPmpXqKkNj7wC83AhXk": 5,
"8BR3zs8zSXetpnDjCtHWnkpSkNSydWb3PTTDuVKku2uu": 3,
"4MangoMjqJ2firMokCjjGgoK8d4MXcrgL7XJaL3w6fVg": 11,
"BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY": 69,
"darehJcMJFk833wzHe76pFyGPyTNzeN4yQNKTRw8wJM": 8,
"monacoUXKtUi6vKsQwaLyxmXKSievfNWEcYXTgkbCih": 24,
"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb": 1,
"NeonVMyRX5GbCrsAHnUwx1nYYoJAtskU1bWUo6JGNyG": 1,
"TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp": 2,
"ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD": 6,
"cjg3oHmg9uuPsP8D6g29NWvhySJkdYdAo9D25PRbKXJ": 10,
"TSWAPaqyCSx2KABk68Shruf4rp7CxcNi8hAsbdwmHbN": 2,
"9XFCiGhW1Pkx7rGgNcMH3S9bqDjKwgjqqM2cb2YdzpJ3": 8,
"whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc": 1,
"BobogA5N2KN2GG4XN3E3rNNRw3L8H1QPXp7QLxGrNHGM": 95,
"SAGE2HAwep459SNq61LHvjxPk4pLPEJLoMETef7f7EE": 2,
"7K3UpbZViPnQDLn2DAM853B9J5GBxd1L1rLHy4KqSmWG": 1,
"9ixSdN1CKjmHqwTessaPYdJGRMgEYCaL34oXyNjYK2mw": 4,
"BANANAjs7FJiPQqJTGFzkZJndT9o7UmKiYYGaJz6frGu": 2,
"ARBv2b5cz4hreK2Pico7jD76oYiDMP2KPhyYrYtTK7qT": 1,
"F3tYgkUtcqn6JnUska1meE2LDzwozEyh4nkH6zMJLdL5": 2,
"8V227E7nJP4y9XJY8tEnw19aiaQc2Cb9xZt1J7a3idbY": 2,
"LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo": 3,
"MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG": 1,
"13gDzEXCdocbj8iAiqrScGo47NiSuYENGsRqi3SEAwet": 1,
"L2TExMFKdjpN9kozasaurPirfHy9P8sbXoAN1qA3S95": 3,
"9m7YMBgQaxYpUwrjLvS6AA3FmGH83qXgifA5aF5VbEue": 1,
"BLZRi6frs4X4DNLw56V4EXai1b6QVESN1BhHBTYM9VcY": 1,
"9w1D9okTM8xNE7Ntb7LpaAaoLc6LfU9nHFs2h2KTpX1H": 1
},
"successfulTxCount": 1186,
"failedTxCount": 304,
"totalWritableAccounts": 7025,
"avgWritableAccountsPerTx": 4.714765100671141,
"uniqueAccountCount": 3868,
"uniqueSignerCount": 1189
}
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.