Skip to main content

Find Whale Transaction

Updated on
Jan 02, 2025

Overview

Blockchain adoption has reached a point where millions of transactions occur daily. While most of these transactions are relatively small, wealthy cryptocurrency participants (whales) are also making significant moves.

In this function example, we will get transactions with native asset transfers and return the ones that exceed a given threshold..

What We Will Do

For this example, we will extract the native asset transfers above a given threshold. We will demonstrate this in two ways:

  1. A standalone Function which can be called via API, passing the transaction size as a parameter.
  2. A transformation that can be applied to a QuickNode Stream, feeding the output to the destination of your choice, block by block.

Sample Function

The following Function code extracts Ethereum whale transactions in JavaScript Node.js v20 runtime using the block dataset:

function main(params) {
// Extract block data from the params object
const block = params.data[0]

// Extract metadata
const network = params.metadata.network
const blockNo = hexToInt(block.number)

// Configure native asset details. On Ethereum, this is eth, Polygon it is matic, etc.
const asset = {
units: 'eth',
decimals: 18,
}

// Apply the minimum tx size to filter on, via the value passed in through user_data
const minTxSize_dec = params.user_data.minTxSize

// Convert the tx size to the number stored onchain, with decimal places added
const minTxSize_val = decToVal(minTxSize_dec, asset)

// Extract matching transactions
const transactions = block.transactions
const txArray_raw = transactions.filter(
obj => hexToInt(obj.value) >= minTxSize_val
)
const count = txArray_raw.length

// Decode transactions into a cleaner format
const txArray_clean = []
txArray_raw.forEach((tx, txIndex) => {
const value_clean = valToDec(hexToInt(tx.value), asset)
txArray_clean.push({
hash: tx.hash,
to: tx.to,
from: tx.from,
value: value_clean,
units: asset.units,
})
})

// While debugging, it is useful to include raw tx array, only return the necessary data in prod
const returnObj = {
block: blockNo,
network: network,
result: `${count} transaction(s) w/ value > ${minTxSize_dec} ${asset.units}`,
transactions_clean: txArray_clean,
transactions_raw: txArray_raw,
}

return returnObj
}

function hexToInt(hex) {
let decodedVal = hex.substring(2)
decodedVal = parseInt(decodedVal, 16)

return decodedVal
}

function hexToStr(hex) {
let decodedStr = hex.substring(2)
decodedStr = Buffer(decodedStr, 'hex').toString()

return decodedStr
}

function valToDec(val, asset) {
const dec = val / Math.pow(10, asset.decimals)
return dec
}

function decToVal(dec, asset) {
const val = dec * Math.pow(10, asset.decimals)
return val
}

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.
  • The user_data object can be used to pass additional parameters to the function. In this case:
    • You can send the request with a minimum transaction size (minTxSize) to filter the transactions by.
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": "ethereum-mainnet",
"dataset": "block",
"block_number": 21194293,
"user_data": {
"minTxSize": 5
}
}'

Response

Resulting in the following response:

{
"block": 21194293,
"network": "ethereum-mainnet",
"result": "3 transaction(s) w/ value > 5 eth",
"transactions_clean": [
{
"hash": "0x3008e9ae83d164e52a608b205987278245ec2874f0bd7d5fb68dd4497429dfd8",
"to": "0xb69bef6281a18c8f90eb8c04c2407f55094c95f9",
"from": "0x46340b20830761efd32832a74d7169b29feb9758",
"value": 7.14751,
"units": "eth"
},
{
"hash": "0xa2f9427b396f9b61344d44c81494a06a01c777ec32bf1143bcdb231cd8cdd062",
"to": "0x1c727a55ea3c11b0ab7d3a361fe0f3c47ce6de5d",
"from": "0xf962e467c553c2ef45d5462c52429837f4fd8c8a",
"value": 330,
"units": "eth"
},
{
"hash": "0x0d245bae645839b3cf2fabd59a01ee1a5025a7e19537755429f8bb8d09a41025",
"to": "0xfa8074696cd3bd1dacace2f25abcfa935f075e37",
"from": "0xb5d85cbf7cb3ee0d56b3bb207d5fc4b82f43f511",
"value": 10.653833003940589,
"units": "eth"
}
],
"transactions_raw": [
{
"blockHash": "0x2aa5c7b46dfa0b2698e47f9e232654b170e0229b748da0b4e615c3a6c6be115b",
"blockNumber": "0x1436635",
"from": "0x46340b20830761efd32832a74d7169b29feb9758",
"gas": "0x55730",
"gasPrice": "0x686fa4aad",
"hash": "0x3008e9ae83d164e52a608b205987278245ec2874f0bd7d5fb68dd4497429dfd8",
"input": "0x",
"nonce": "0xc2109a",
"to": "0xb69bef6281a18c8f90eb8c04c2407f55094c95f9",
"transactionIndex": "0x70",
"value": "0x63310e79d28d6000",
"type": "0x0",
"chainId": "0x1",
"v": "0x25",
"r": "0x9457b16f23678a0f72f4e1e80af256fbea1e307f7fc6f46b9cf349e48e4e506c",
"s": "0x717ccc7780e8fc95c3aa914b613af9acec5e39e17e1fbf874b0f8718737402b1"
},
{
"blockHash": "0x2aa5c7b46dfa0b2698e47f9e232654b170e0229b748da0b4e615c3a6c6be115b",
"blockNumber": "0x1436635",
"from": "0xf962e467c553c2ef45d5462c52429837f4fd8c8a",
"gas": "0x5208",
"gasPrice": "0x671ffe005",
"hash": "0xa2f9427b396f9b61344d44c81494a06a01c777ec32bf1143bcdb231cd8cdd062",
"input": "0x",
"nonce": "0xb",
"to": "0x1c727a55ea3c11b0ab7d3a361fe0f3c47ce6de5d",
"transactionIndex": "0x7f",
"value": "0x11e3ab8395c6e80000",
"type": "0x0",
"chainId": "0x1",
"v": "0x25",
"r": "0xa63913294bdaf5da7aa161aef1c06187429ecffca8a3fca92b5dd042a553e034",
"s": "0x3fab6f14508a6b13e04f8b80cab55651243712e8c9cf84d33aa72ab0237ad173"
},
{
"blockHash": "0x2aa5c7b46dfa0b2698e47f9e232654b170e0229b748da0b4e615c3a6c6be115b",
"blockNumber": "0x1436635",
"from": "0xb5d85cbf7cb3ee0d56b3bb207d5fc4b82f43f511",
"gas": "0x5208",
"gasPrice": "0x63342f948",
"maxFeePerGas": "0xc1b710800",
"maxPriorityFeePerGas": "0x3b9aca00",
"hash": "0x0d245bae645839b3cf2fabd59a01ee1a5025a7e19537755429f8bb8d09a41025",
"input": "0x",
"nonce": "0x95e8cd",
"to": "0xfa8074696cd3bd1dacace2f25abcfa935f075e37",
"transactionIndex": "0xc1",
"value": "0x93da04abb03632a5",
"type": "0x2",
"accessList": [],
"chainId": "0x1",
"v": "0x1",
"r": "0x4634e9af78659d6c65e43fe0c3640be87cfd3b77e8096bb66d66847b5909a570",
"s": "0x25e9f8a913c97d434ca2d349f8834f63bb058b54480e7bdb5b7617c32959b304",
"yParity": "0x1"
}
]
}

Learn more about QuickNode Functions.

Sample Stream Transformation

The following code can be used in a QuickNode Stream to filter and transform the output:

Try it yourself by deploying the filtered Stream to your account.

function main(stream) {
// Extract block data from the params object
const block = stream.data ? stream.data[0] : stream[0]

// Extract metadata
const blockNo = hexToInt(block.number)

// Configure native asset details. On Ethereum, this is eth, Polygon it is matic, etc.
const asset = {
units: 'eth',
decimals: 18,
}

// Apply the minimum tx size to filter on
const minTxSize_dec = 5

// Convert the tx size to the number stored onchain, with decimal places added
const minTxSize_val = decToVal(minTxSize_dec, asset)

// Extract matching transactions
const transactions = block.transactions
const txArray_raw = transactions.filter(
obj => hexToInt(obj.value) >= minTxSize_val
)
const count = txArray_raw.length

// Decode transactions into a cleaner format
const txArray_clean = []
txArray_raw.forEach((tx, txIndex) => {
const value_clean = valToDec(hexToInt(tx.value), asset)
txArray_clean.push({
hash: tx.hash,
to: tx.to,
from: tx.from,
value: value_clean,
units: asset.units,
})
})

// While debugging, it is useful to include raw tx array, only return the necessary data in prod
const returnObj = {
block: blockNo,
result: `${count} transaction(s) w/ value > ${minTxSize_dec} ${asset.units}`,
transactions_clean: txArray_clean,
transactions_raw: txArray_raw,
}

return returnObj
}

function hexToInt(hex) {
let decodedVal = hex.substring(2)
decodedVal = parseInt(decodedVal, 16)

return decodedVal
}

function hexToStr(hex) {
let decodedStr = hex.substring(2)
decodedStr = Buffer(decodedStr, 'hex').toString()

return decodedStr
}

function valToDec(val, asset) {
const dec = val / Math.pow(10, asset.decimals)
return dec
}

function decToVal(dec, asset) {
const val = dec * Math.pow(10, asset.decimals)
return val
}

Output

Resulting in the following output:

{
"block": 20187607,
"result": "1 transaction(s) w/ value > 5 eth",
"transactions_clean": [
{
"from": "0xbf0181f23e53a1bb6deec9c8c01b6db590da1b40",
"hash": "0xf8c2abfc4534eecec832367fa264dc97dccec1ae8d0424625c67950e230237a6",
"to": "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
"units": "eth",
"value": 10
}
],
"transactions_raw": [
{
"accessList": [],
"blockHash": "0x472b139d2c6222e289928a2ce6874850a156a5d34d4c0a64d14ece6e1667d2df",
"blockNumber": "0x13409d7",
"chainId": "0x1",
"from": "0xbf0181f23e53a1bb6deec9c8c01b6db590da1b40",
"gas": "0x32af7e",
"gasPrice": "0x11580ce6d",
"hash": "0xf8c2abfc4534eecec832367fa264dc97dccec1ae8d0424625c67950e230237a6",
"input": "0xf305d71900000000000000000000000076d713ef778acb0a8fa99574fb15a1c2786c49f50000000000000000000000000000000000000000000000008ac7230489e800000000000000000000000000000000000000000000000000008ac7230489e800000000000000000000000000000000000000000000000000008ac7230489e80000000000000000000000000000bf0181f23e53a1bb6deec9c8c01b6db590da1b4000000000000000000000000000000000000000000000000000000000667e3527",
"maxFeePerGas": "0x168fd314d",
"maxPriorityFeePerGas": "0x38f27c79",
"nonce": "0x3d",
"r": "0x734aa57549a899bd78811f28c580f18ac9fe6014907e017eee51e8dfe30977",
"s": "0x351ef09040f88aed41286a2bf9ad9d60d956bbaa03a13ae6c132470977bded1e",
"to": "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
"transactionIndex": "0x5d",
"type": "0x2",
"v": "0x0",
"value": "0x8ac7230489e80000",
"yParity": "0x0"
}
]
}

Learn more about modifying Stream output.

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