Overview
EVM-based blockchains see high usage of ERC-20 tokens, where users often grant spend allowances (via the Approval
event).
In this Function example, we detect these approval logs and get valuable details related to the approval.
Sample Function
Below is a QuickNode Function in JavaScript Node.js v20 runtime. It:
- Collects ERC-20
Approval
logs (0x8c5be1e5…
). - Converts hex values to decimal.
- Groups logs by token address.
- Calls
name()
andsymbol()
on each token (using a minimal ABI). - Appends
tokenName
andtokenSymbol
to each approval record.
// If using CommonJS style in Node.js:
const { ethers } = require('ethers')
const HARDCODED_RPC_URL = 'https://my-eth-endpoint.quiknode.pro/SECRET/'
// The `Approval` event signature for ERC-20 tokens:
const APPROVAL_TOPIC =
'0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925'
// Minimal ERC-20 ABI (only name() and symbol())
const ERC20_ABI = [
'function name() view returns (string)',
'function symbol() view returns (string)',
]
/**
* Extract the last 40 hex characters from a padded address topic.
*/
function extractAddress(paddedAddress) {
if (typeof paddedAddress !== 'string' || paddedAddress.length < 40) {
console.error('Invalid address format:', paddedAddress)
return null
}
return '0x' + paddedAddress.slice(-40)
}
/**
* Convert a hex string to a decimal string, using BigInt.
*/
function hexToDecimal(hexValue) {
if (typeof hexValue !== 'string') {
console.error('Invalid hex value:', hexValue)
return '0'
}
hexValue = hexValue.replace(/^0x/, '')
if (hexValue === '') {
return '0'
}
try {
return BigInt('0x' + hexValue).toString()
} catch (error) {
console.error('Error converting hex to decimal:', error)
return '0'
}
}
/**
* Recursively locate Approval logs in the data structure.
* For each, store { token, owner, spender, amount, blockNumber, transactionHash }
*/
function findApprovalsInLogs(data, results) {
if (Array.isArray(data)) {
data.forEach(item => findApprovalsInLogs(item, results))
} else if (typeof data === 'object' && data !== null) {
if (data.topics && data.topics[0] === APPROVAL_TOPIC) {
const owner = extractAddress(data.topics[1])
const spender = extractAddress(data.topics[2])
const amount = hexToDecimal(data.data)
if (owner && spender) {
results.push({
token: data.address,
owner,
spender,
amount,
blockNumber: hexToDecimal(data.blockNumber),
transactionHash: data.transactionHash,
})
}
} else {
Object.values(data).forEach(val => findApprovalsInLogs(val, results))
}
}
}
/**
* fetchTokenInfo: calls name() and symbol() from ERC-20 contract.
*/
async function fetchTokenInfo(provider, tokenAddress) {
if (!ethers.isAddress(tokenAddress)) {
return { name: null, symbol: null }
}
const contract = new ethers.Contract(tokenAddress, ERC20_ABI, provider)
try {
const [name, symbol] = await Promise.all([
contract.name(),
contract.symbol(),
])
return { name, symbol }
} catch (err) {
console.error(`Error calling name()/symbol() on ${tokenAddress}`, err)
return { name: null, symbol: null }
}
}
/**
* main(params) - QuickNode Function entry point.
*
* We'll read logs from `params.data`, find approvals, fetch token name/symbol, and return a summary.
*/
async function main(params) {
console.log('DEBUG: Starting EVM token allowance checker (ethers v6.13.1)')
const logsData = params.data || []
let approvals = []
findApprovalsInLogs(logsData, approvals)
// Gather unique token addresses
const tokenAddresses = [...new Set(approvals.map(a => a.token.toLowerCase()))]
// Use ethers v6 with a hardcoded RPC provider
const provider = new ethers.JsonRpcProvider(HARDCODED_RPC_URL)
// For each token address, fetch name and symbol
let tokenInfoMap = {}
for (let addr of tokenAddresses) {
tokenInfoMap[addr] = await fetchTokenInfo(provider, addr)
}
// Attach name/symbol to each approval record
approvals.forEach(approval => {
const info = tokenInfoMap[approval.token.toLowerCase()] || {}
approval.tokenName = info.name
approval.tokenSymbol = info.symbol
})
const count = approvals.length
const message = `Found ${count} Approval events. Name/symbol fetched for ${tokenAddresses.length} tokens.`
return {
message,
approvals,
}
}
// Export for QuickNode Functions
module.exports = { main }
Replace 'https://my-eth-endpoint.quiknode.pro/SECRET/' on line 4 with your actual Ethereum mainnet RPC URL.
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": "ethereum-mainnet",
"dataset": "logs"
}'
Response
Resulting in the following response:
{
"message": "Found 26 Approval events. Fetched name/symbol for 21 unique tokens.",
"approvals": [
{
"token": "0x421b05cf5ce28cb7347e73e2278e84472f0e4a88",
"owner": "0xc57db940c5d56f8967914f19b2448a5b641f727d",
"spender": "0x000000000022d473030f116ddee9f6b43ac78ba3",
"amount": "115792089237316195423570985008687907853269984665640564025466284007913129639935",
"blockNumber": "21514446",
"transactionHash": "0x60c47303d761d904ee735f504c151a9fb037063996ff369c160af4bb5712a7f9",
"tokenName": "Sentio Protocol",
"tokenSymbol": "SEN"
},
{
"token": "0x64c5cba9a1bfbd2a5faf601d91beff2dcac2c974",
"owner": "0x3fa6db082d42b47842ecf3e89ddc79473f5645f5",
"spender": "0x000000000022d473030f116ddee9f6b43ac78ba3",
"amount": "115792089237316195423570985008687907853269984665583617695595542161914988347884",
"blockNumber": "21514446",
"transactionHash": "0xca2f1cd9b047c9b07118ce53fe0a8991808b912470a952396765711d76842fc8",
"tokenName": "Mystery",
"tokenSymbol": "MYSTERY"
},
{
"token": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"owner": "0xd4bc53434c5e12cb41381a556c3c47e1a86e80e3",
"spender": "0x836951eb21f3df98273517b7249dceff270d34bf",
"amount": "26833530624",
"blockNumber": "21514446",
"transactionHash": "0xc254e19163e7bf5ec55c1d0d315ae5e675e3a6a127a9837547dc60dc4c1f15f3",
"tokenName": "USD Coin",
"tokenSymbol": "USDC"
},
{
"token": "0x26e550ac11b26f78a04489d5f20f24e3559f7dd9",
"owner": "0x0219957fafc72f937c6366855d0b7562cf5131bf",
"spender": "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
"amount": "115792089237316195423570985008687907853269984665640564039457583747703391582002",
"blockNumber": "21514446",
"transactionHash": "0x57ed89700d6904370d3fc724f3cdf38b2a6e6c098072c366320fe0f8d30c3af3",
"tokenName": "Kekius Maximus",
"tokenSymbol": "KEKIUS"
},
{
"token": "0x6f965e544ee0be609b521aca47f16511595f0c53",
"owner": "0x4a1bee2e2d9417c3977b084f7b5eec5bac805897",
"spender": "0x80a64c6d7f12c47b7c66c5b4e20e72bc1fcd5d9e",
"amount": "115792089237316195423570985008687907853269984665640564039115768416052930243399",
"blockNumber": "21514446",
"transactionHash": "0xd63ecd08cc09e9a5eadff8d9a0c541921b379ca5db19443c22f559b5b66a7d1e",
"tokenName": "She-Dust",
"tokenSymbol": "SHEDUST"
},
{
"token": "0xae7ab96520de3a18e5e111b5eaab095312d7fe84",
"owner": "0xeb2d9f4f9a000eb173ce095369d3cb99d48a85b1",
"spender": "0xc92e8bdf79f0507f65a392b0ab4667716bfe0110",
"amount": "1022734223301193488",
"blockNumber": "21514446",
"transactionHash": "0x46ab83a9037631a8a595f58ae7522a20e3a728a27dd09c3650b13b79b9a875cf",
"tokenName": "Liquid staked Ether 2.0",
"tokenSymbol": "stETH"
},
{
"token": "0x8236a87084f8b84306f72007f36f2618a5634494",
"owner": "0xcca49c45f819912fd884b1725f4c9f669bfefbb0",
"spender": "0x34bdba9b3d8e3073eb4470cd4c031c2e39c32da8",
"amount": "307700",
"blockNumber": "21514446",
"transactionHash": "0xcdd563ad71307b516674f01defde99409550d41b08adcd4d232fa454172895a4",
"tokenName": "Lombard Staked Bitcoin",
"tokenSymbol": "LBTC"
},
{
"token": "0x9f52c8ecbee10e00d9faaac5ee9ba0ff6550f511",
"owner": "0xe83da89b0b4cdfd1c89cfb586eb3a92a9136b5ac",
"spender": "0xa2c1c1a473250094a6244f2bcf6cb51f670ad3ac",
"amount": "84000000000000000000",
"blockNumber": "21514446",
"transactionHash": "0x82e081b0b4b912394ee343d4d4164b6cb992a231ebb2e11fb1c7b0645c900b6d",
"tokenName": "Sipher Token",
"tokenSymbol": "SIPHER"
},
{
"token": "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0",
"owner": "0x37de6d5e26fd67f223dc992bf2dd400605f48c1b",
"spender": "0x881d40237659c251811cec9c364ef91dc08d300c",
"amount": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"blockNumber": "21514446",
"transactionHash": "0xe27e46db4a1feacc44606a95aba00f5a9e527aae25e9479b53f28739ca3200c5",
"tokenName": "Matic Token",
"tokenSymbol": "MATIC"
},
{
"token": "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0",
"owner": "0x37de6d5e26fd67f223dc992bf2dd400605f48c1b",
"spender": "0x881d40237659c251811cec9c364ef91dc08d300c",
"amount": "115792089237316195423570985008687907853269984665640564039156546390413129639935",
"blockNumber": "21514446",
"transactionHash": "0xb9fb983a2790ccaae756992ab1bd75a38fb205cc0c8e8997e9aa488000c78ef4",
"tokenName": "Matic Token",
"tokenSymbol": "MATIC"
},
{
"token": "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0",
"owner": "0x74de5d4fcbf63e00296fd95d33236b9794016631",
"spender": "0xd82e10b9a4107939e55fcca9b53a9ede6cf2fc46",
"amount": "115792089237316195423570985008687907853269984665640558430868373315888675748634",
"blockNumber": "21514446",
"transactionHash": "0xb9fb983a2790ccaae756992ab1bd75a38fb205cc0c8e8997e9aa488000c78ef4",
"tokenName": "Matic Token",
"tokenSymbol": "MATIC"
},
{
"token": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"owner": "0x40bb7e10b2d03d68a6c275d189a544319b40e601",
"spender": "0x000000000022d473030f116ddee9f6b43ac78ba3",
"amount": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"blockNumber": "21514446",
"transactionHash": "0xb5eede3dc185aeb5dba962c6fdd126656abe10ab261f0d2219570d208d4cb477",
"tokenName": "Wrapped Ether",
"tokenSymbol": "WETH"
},
{
"token": "0xf576e1f09e2eb4992d5ffdf68bec4ea489fa417d",
"owner": "0xf1c6b8ca186e389287353363792d77e095e73784",
"spender": "0x000000000022d473030f116ddee9f6b43ac78ba3",
"amount": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"blockNumber": "21514446",
"transactionHash": "0xf2ae76be9e241ad790d4ed8f5320e1353621d96bf798ef11d7f4ac952a877dfe",
"tokenName": "ULTI",
"tokenSymbol": "ULTI"
},
{
"token": "0xde4ee8057785a7e8e800db58f9784845a5c2cbd6",
"owner": "0x974a28f1b216f8af3b5cf20693371049dbc0cefb",
"spender": "0x1111111254eeb25477b68fb85ed929f73a960582",
"amount": "5225482945056386753",
"blockNumber": "21514446",
"transactionHash": "0xcd9dd0b89d9e866cf3567eac4993904f6d1793d1e12a9e42532d800ef6f9e174",
"tokenName": "Dexe",
"tokenSymbol": "DEXE"
},
{
"token": "0xde4ee8057785a7e8e800db58f9784845a5c2cbd6",
"owner": "0x974a28f1b216f8af3b5cf20693371049dbc0cefb",
"spender": "0x1111111254eeb25477b68fb85ed929f73a960582",
"amount": "0",
"blockNumber": "21514446",
"transactionHash": "0xb50dd5eeefeda1de636cfbd6e46b4673e98c2acbb901eb631b42508d9f8bf11a",
"tokenName": "Dexe",
"tokenSymbol": "DEXE"
},
{
"token": "0x26e550ac11b26f78a04489d5f20f24e3559f7dd9",
"owner": "0x1262656546866f1b735f8e66b8626ee466ee6b2b",
"spender": "0xc92e8bdf79f0507f65a392b0ab4667716bfe0110",
"amount": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"blockNumber": "21514446",
"transactionHash": "0xd45b907e6e0a52fc701710b3626835be32224bf61bd98703259f6ffe8362d5f5",
"tokenName": "Kekius Maximus",
"tokenSymbol": "KEKIUS"
},
{
"token": "0x8ccd897ca6160ed76755383b201c1948394328c7",
"owner": "0x203d472155ce4e27b7a50117cc1008bf18aa6dff",
"spender": "0x1231deb6f5749ef6ce6943a275a1d3e7486f4eae",
"amount": "390395764140",
"blockNumber": "21514446",
"transactionHash": "0xcf541a8388d9793432e58581d4899ebd86c19dbe0a8844afd296381d933c9ee6",
"tokenName": "Wrapped Balance AI",
"tokenSymbol": "wBAI"
},
{
"token": "0xbbcbf4dd68df01ad1aa1c568ca7a6c02787aa494",
"owner": "0x264533bd62512a9e7e791236625adbdf3e8377f3",
"spender": "0x7a250d5630b4cf539739df2c5dacb4c659f2488d",
"amount": "31960202801638",
"blockNumber": "21514446",
"transactionHash": "0xfab92da4963285b3416540e22aaa0b8a4a42ffb202cd42070d4445d965141ae3",
"tokenName": "Snax Inu",
"tokenSymbol": "Snax"
},
{
"token": "0xfd4ca4a692f14d88af3e7ae13cf00d5095213b25",
"owner": "0x5daef2e38b446920a5bc2d16a3d33aa36d271da9",
"spender": "0x0000000000001ff3684f28c67538d4d072c22734",
"amount": "733365430426900063231560",
"blockNumber": "21514446",
"transactionHash": "0x64ca739964884af4f3a2368042564a9b4c69b8277e19b12319c0c6209da24d4a",
"tokenName": "Wiskers",
"tokenSymbol": "WSKR"
},
{
"token": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"owner": "0x2aca0c7ed4d5eb4a2116a3bc060a2f264a343357",
"spender": "0x8f88ae3798e8ff3d0e0de7465a0863c9bbb577f0",
"amount": "60000000000000000",
"blockNumber": "21514446",
"transactionHash": "0xce8a81a82f91086b5a035a7245c6e2c0bfe43159cb0dbb40e483a7e86e3312b3",
"tokenName": "Wrapped Ether",
"tokenSymbol": "WETH"
},
{
"token": "0x29d5cabc57870e9a27a655594bcd08152d0a135b",
"owner": "0x44887642ddee3a01a8c89522e2262603841c67db",
"spender": "0x40aa958dd87fc8305b97f2ba922cddca374bcd7f",
"amount": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"blockNumber": "21514446",
"transactionHash": "0x20b6f2542fe9e0f43546359cf59cde36e52298819a6ef1270b4edcd2ad33b284",
"tokenName": "The Surge",
"tokenSymbol": "SURGE"
},
{
"token": "0x5d31692f98696897cb85d0188e99d484d597fe01",
"owner": "0x282b3538621091508461ecc98eaf47af14915296",
"spender": "0x40aa958dd87fc8305b97f2ba922cddca374bcd7f",
"amount": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"blockNumber": "21514446",
"transactionHash": "0x2cc71dd3845b8246ced63bc08c40cee02d80b8e4a1a4d7bbe7433a633ceafc21",
"tokenName": "Bye to cash",
"tokenSymbol": "BTC"
},
{
"token": "0x2a2550e0a75acec6d811ae3930732f7f3ad67588",
"owner": "0x8f228609eb5ee750efb00e92611aa96582decadc",
"spender": "0x40aa958dd87fc8305b97f2ba922cddca374bcd7f",
"amount": "2888983660164051964987868",
"blockNumber": "21514446",
"transactionHash": "0x17f6f00fe411bb7631af1c5be05ed90705dfe8b81b9ec68a2b9406abc81251f7",
"tokenName": "PathDao",
"tokenSymbol": "PATH"
},
{
"token": "0x20cd2e7ec8f5d8b337fe46a4f565ccef1561b9a9",
"owner": "0x4e9c34c34e9d00966648d20d2da58d07377465fc",
"spender": "0x33d933cdb77be8469c113b4c8162473b72430cc2",
"amount": "10000000000000000000000000",
"blockNumber": "21514446",
"transactionHash": "0xd25d648f5783ace261ba18260ed479771adfd7676123f9da396d8ca65cab7fe7",
"tokenName": "ESG",
"tokenSymbol": "ESG"
},
{
"token": "0x332a8ee60edff0a11cf3994b1b846bbc27d3dcd6",
"owner": "0x5e2787b3aa6ec78278df6291a5d486ff0b20c9de",
"spender": "0x888888888889758f76e7103c6cbf23abbf58f946",
"amount": "95060",
"blockNumber": "21514446",
"transactionHash": "0x36985a3aed029ef5cfc48371efdc8c48917ba314b4a98abad962af79e56da89c",
"tokenName": "PT Corn Lombard LBTC 26DEC2024",
"tokenSymbol": "PT-cornLBTC-26DEC2024"
},
{
"token": "0x0f48e776a4d983c0dddf4c0c946d66e3786f134f",
"owner": "0xccb75c1880e9d37dce0a24c633352cc1ec1a8e4d",
"spender": "0x000000000022d473030f116ddee9f6b43ac78ba3",
"amount": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"blockNumber": "21514446",
"transactionHash": "0xc9ab8551e0761b5a0384d7b61d9cdb6648c43f9643503e2cec3c22a5881828be",
"tokenName": "XETRA AI",
"tokenSymbol": "XETRA"
}
]
}
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.