Analyze ERC-20 transfers for a UTC day
Fetch ERC-20 Transfer logs for one UTC day and query the decoded rows with SQL.
Goal
Fetch ERC-20 Transfer logs for one UTC day and query the decoded rows with SQL. The example uses PENDLE on Ethereum mainnet.
Before you begin
Use an Ethereum client that serves historical logs for the requested date.
Each cell includes a saved result. Select Run all to query the provider.
1. Resolve the UTC day
Use block_range to translate 2024-01-06 into
a block interval. get_logs uses an inclusive
upper bound, so the query subtracts one from the interval end.
{"id":"resolve-day","title":"Resolve the day to blocks","code":"WITH resolved AS (\n SELECT block_range($client, '2024-01-06'::DATE) AS block_interval\n)\nSELECT\n '2024-01-06'::DATE AS utc_day,\n (block_interval).from_block AS from_block,\n (block_interval).to_block AS to_block_exclusive,\n (block_interval).to_block - 1 AS to_block_inclusive\nFROM resolved;","description":"Resolve one UTC date and produce inclusive block bounds for the log query.","resultTableName":"pendle_day_bounds"}
2. Fetch and decode transfer logs
get_logs takes the token address, Transfer event
signature, and block range. The query returns each matching log with decoded
sender, recipient, and value fields.
{"id":"decode-transfers","title":"Fetch PENDLE Transfer logs","code":"WITH resolved AS (\n SELECT block_range($client, '2024-01-06'::DATE) AS block_interval\n),\nbounds AS (\n SELECT\n (block_interval).from_block AS from_block,\n (block_interval).to_block AS to_block_exclusive,\n (block_interval).to_block - 1 AS to_block_inclusive\n FROM resolved\n)\nSELECT\n bounds.from_block AS scan_from_block,\n bounds.to_block_exclusive AS scan_to_block_exclusive,\n bounds.to_block_inclusive AS scan_to_block_inclusive,\n logs.block_number,\n logs.block_hash,\n logs.transaction_index,\n logs.transaction_hash,\n logs.log_index,\n logs.\"from\" AS sender,\n logs.\"to\" AS recipient,\n logs.value AS value_raw,\n format_units(logs.value, 18) AS pendle_amount\nFROM bounds\nCROSS JOIN get_logs(\n $client,\n '0x808507121B80c02388fAd14726482e061B8da827'::ADDRESS,\n 'event Transfer(address indexed from, address indexed to, uint256 value)',\n bounds.from_block,\n bounds.to_block_inclusive\n) AS logs\nORDER BY logs.block_number, logs.transaction_index, logs.log_index;","description":"Read and decode PENDLE Transfer logs for one UTC day.","resultTableName":"pendle_transfers"}
3. Aggregate top recipients
Group the decoded logs by recipient and rank the transferred value.
{"id":"top-recipients","title":"Rank PENDLE recipients","code":"WITH resolved AS (\n SELECT block_range($client, '2024-01-06'::DATE) AS block_interval\n),\nbounds AS (\n SELECT\n (block_interval).from_block AS from_block,\n (block_interval).to_block - 1 AS to_block_inclusive\n FROM resolved\n),\ntransfers AS (\n SELECT\n logs.\"to\" AS recipient,\n logs.value AS value_raw\n FROM bounds\n CROSS JOIN get_logs(\n $client,\n '0x808507121B80c02388fAd14726482e061B8da827'::ADDRESS,\n 'event Transfer(address indexed from, address indexed to, uint256 value)',\n bounds.from_block,\n bounds.to_block_inclusive\n ) AS logs\n)\nSELECT\n recipient,\n count(*) AS recipient_transfer_count,\n sum(value_raw) AS received_raw,\n format_units(sum(value_raw), 18) AS pendle_received\nFROM transfers\nGROUP BY recipient\nORDER BY received_raw DESC, recipient\nLIMIT 5;","description":"Group decoded logs by recipient and order them by transferred value.","resultTableName":"pendle_top_recipients"}