eth_getLogs
方法返回与给定过滤器对象匹配的所有日志数组,允许开发者直接查询历史事件数据,而无需先创建过滤器。这是检索区块链事件数据最强大和最常用的方法之一,对 dApp 开发和区块链数据分析至关重要。
此方法接受带有日志返回条件的过滤器对象,是最查询密集的 RPC 调用之一。
日志过滤器选项对象
开始搜索的区块号/标签
搜索至的区块号/标签
过滤日志的合约地址
主题过滤器数组(事件签名和索引参数)
获取日志的区块哈希(fromBlock/toBlock 的替代方案)
与过滤条件匹配的日志对象数组
此日志来源的地址
索引日志参数数组(最多 4 个主题)
非索引日志数据(十六进制编码)
包含此日志的区块号(十六进制)
创建此日志的交易哈希
交易在区块中的索引
包含此日志的区块哈希
此日志在区块中的索引位置
此日志是否因链重组而被移除
{
"jsonrpc": "2.0",
"id": 1,
"result": [
{
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x00000000000000000000000068b3465833fb72a70ecdf485e0e4c7bd8665fc45",
"0x00000000000000000000000027182842e098f60e3d576794a5bffb0777e025d3"
],
"data": "0x000000000000000000000000000000000000000000000043e1f92f1839951800",
"blockNumber": "0x10d4f40",
"transactionHash": "0x7d5b3f1d5b32c2b6da53acaace3894412f3b1ec2384a9b8e6c1a52325e880db2",
"transactionIndex": "0xc1",
"blockHash": "0xe2ccfc7b29eb6cad4bd3e55a00275a3b05d2d5f984ef63683f115e92d213d5b2",
"logIndex": "0x5a",
"removed": false
}
]
}
返回的每个日志对象包含:
true
topics
参数提供强大的过滤功能:
简单主题匹配:
"topics": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]
这匹配所有 ERC-20 Transfer 事件。
位置特定匹配(位置之间的 AND 逻辑):
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000abc123..."
]
这匹配来自特定地址的 Transfer 事件。
位置内 OR 逻辑:
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
["0x000000000000000000000000abc123...", "0x000000000000000000000000def456..."]
]
这匹配来自两个地址之一的 Transfer 事件。
使用 null
跳过位置:
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
null,
"0x000000000000000000000000xyz789..."
]
这匹配发送到特定地址的 Transfer 事件(忽略 FROM 字段)。
以下是常见用例的完整示例:
// 1. 获取特定代币的所有 ERC-20 转账事件
async function getTokenTransfers(tokenAddress, fromBlock = '0x0', toBlock = 'latest') {
// Transfer 事件签名:keccak256("Transfer(address,address,uint256)")
const transferEventSignature = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';
return await provider.send('eth_getLogs', [{
address: tokenAddress,
topics: [transferEventSignature],
fromBlock,
toBlock
}]);
}
// 2. 获取特定钱包地址的转入或转出交易
async function getUserTransfers(tokenAddress, userAddress, fromBlock = '0x0', toBlock = 'latest') {
const transferEventSignature = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';
const paddedAddress = userAddress.replace('0x', '0x000000000000000000000000');
// 主题数组匹配用户作为发送者或接收者的事件
return await provider.send('eth_getLogs', [{
address: tokenAddress,
topics: [
transferEventSignature,
[paddedAddress, null], // 从用户(或)
[paddedAddress, null] // 到用户(或)
],
fromBlock,
toBlock
}]);
}
// 3. 跟踪 NFT 铸造事件
async function getNFTMints(nftAddress, fromBlock = '0x0', toBlock = 'latest') {
// 从 0x0 地址的 Transfer 事件是铸造
const transferEventSignature = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';
const zeroAddress = '0x0000000000000000000000000000000000000000000000000000000000000000';
return await provider.send('eth_getLogs', [{
address: nftAddress,
topics: [
transferEventSignature,
zeroAddress // From 地址是零地址(铸造)
],
fromBlock,
toBlock
}]);
}
// 4. ERC-20 Transfer 事件的完整解码器
function decodeTransferEvent(log) {
// 从主题解析索引参数
const from = '0x' + log.topics[1].substring(26);
const to = '0x' + log.topics[2].substring(26);
// 从数据字段解析金额
const amount = BigInt(log.data);
return {
from,
to,
amount,
blockNumber: parseInt(log.blockNumber, 16),
txHash: log.transactionHash
};
}
高效使用 eth_getLogs
:
区块范围限制:大多数提供商限制每次请求的区块范围(例如,2000-10000 个区块)
分页策略:对于大型查询,按区块范围分页:
async function paginatedLogs(filter, maxBlocksPerPage = 2000) {
const fromBlock = parseInt(filter.fromBlock === 'earliest' ? '0x0' : filter.fromBlock, 16);
const latestBlock = parseInt(await provider.send('eth_blockNumber', []), 16);
const toBlock = filter.toBlock === 'latest' ? latestBlock : parseInt(filter.toBlock, 16);
let allLogs = [];
for (let i = fromBlock; i <= toBlock; i += maxBlocksPerPage) {
const pageToBlock = Math.min(i + maxBlocksPerPage - 1, toBlock);
const pageFilter = {
...filter,
fromBlock: '0x' + i.toString(16),
toBlock: '0x' + pageToBlock.toString(16)
};
const logs = await provider.send('eth_getLogs', [pageFilter]);
allLogs = allLogs.concat(logs);
}
return allLogs;
}
主题特异性:更具体的主题过滤器可减少数据传输和处理时间
地址过滤:查询单个合约比多个合约更高效
提供商优化:一些提供商如 Infura 有专门的日志查询端点
速率限制:此方法通常有更严格的速率限制,可能计为多个请求
缓存:考虑缓存经常访问的历史数据结果
存档节点:某些查询可能需要存档节点而非全节点
以下是用于过滤的常用事件签名:
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
(与 ERC-20 相同)0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822
0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62
0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb