Skip to main content

Solana Compute Block Metrics

Updated on
Jan 02, 2025

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.

Share this doc