How to properly encode topics for eth_getLogs
These web3.js and ethers.js scripts provide a straightforward way to generate encoded event signatures and parameters that can be utilized in the topics filter when pulling logs from an EVM-compatible chain.
const Web3 = require('web3');
// Generate event signature using keccak256 hash algorithm
function encodeEvent(event) {
const keccakHash = Web3.utils.keccak256(event);
return keccakHash;
}
// Generate 32-bytes size specification
function encodeTopic(parameter) {
const encodedParameter = Web3.utils.padLeft(parameter, 64); // 64 characters = 32 bytes encoding
return encodedParameter;
}
function main() {
// Define the event to encode
const eventToEncode = 'Transfer(address,address,uint256)';
const signature = encodeEvent(eventToEncode);
// Define the topic to encode, an Ethereum address in this case
const topicToEncode = '0xf5d500B70bfca5987A05Fb4EfcCbfa5E660a81c0';
const topic = encodeTopic(topicToEncode);
// Topics array to use in eth_getLogs in the 'topics' field of the filter
const topics = [signature, topic];
console.log('===== Topics Encoder =====');
console.log(`Event to track: ${eventToEncode}`);
console.log(`Event signature: ${signature}`);
console.log(`Encoded topic: ${topic}\n`);
console.log('This array filters ERC-20 Transfer events originating from a specific Ethereum address:');
console.log(topics);
}
main();
$ node web3
===== Topics Encoder =====
Event to track: Transfer(address,address,uint256)
Event signature: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
Encoded topic: 0x000000000000000000000000f5d500B70bfca5987A05Fb4EfcCbfa5E660a81c0
This array filters ERC-20 Transfer events originating from a specific Ethereum address:
[
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
'0x000000000000000000000000f5d500B70bfca5987A05Fb4EfcCbfa5E660a81c0'
]
When retrieving logs from an EVM-compatible blockchain, developers often use the eth_getLogs
method. One of the key parameters of this method is the topics filter, which allows you to narrow down the search to specific event logs based on their encoded signature and parameters.
This script will show you how to encode this data using web3.js and ethers.js.
web3.js:
The first step is to create a Web3
instance to be able to access the web3.js tools.
This script uses two tools from the web.js library. The keccak256
function and the padLeft
util.
ethers.js:
The first step is to create a utils
instance to be able to access the utils tools.
This script uses three tools from the ethers.js library. The keccak256
function, the toUtf8Bytes
, and the hexZeroPad
function.
const Web3 = require('web3');
The event signature is generated by hashing the event’s name and its parameter types using the Keccak-256 algorithm, also known as SHA-3. This produces a 32-byte hash value.
The event signature is used in the topics filter of the eth_getLogs
method to match the corresponding event logs on the blockchain.
By using the web3.js or ethers.js libraries, developers can easily generate these event signatures and efficiently filter event logs to extract the data they need.
// Generate event signature using keccak256 hash algorithm
function encodeEvent(event) {
const keccakHash = Web3.utils.keccak256(event);
return keccakHash;
}
Each topic in the topics filter must be a 32-bytes long string, even if the original parameter value is shorter.
To achieve this, a developer can add zeros in front of the value until it becomes a 32-bytes long string, which is equal to 64 characters (starting with 0x
).
The padLeft
in web.js and hexZeroPad
in ethers.js functions do exactly this.
// Generate 32-bytes size specification
function encodeTopic(parameter) {
const encodedParameter = Web3.utils.padLeft(parameter, 64); // 64 characters = 32 bytes encoding
return encodedParameter;
}
Within the main
function, define the event and parameters to encode, then call the functions with the event and parameter as input.
You will also find an example of a topics
array to use in the eth_getLogs
filter.
// Define the event to encode
const eventToEncode = 'Transfer(address,address,uint256)';
const signature = encodeEvent(eventToEncode);
// Define the topic to encode, an Ethereum address in this case
const topicToEncode = '0xf5d500B70bfca5987A05Fb4EfcCbfa5E660a81c0';
const topic = encodeTopic(topicToEncode);
// Topics array to use in eth_getLogs in the 'topics' field of the filter
const topics = [signature, topic];
The last part of the main function is to log the results.
console.log('===== Topics Encoder =====');
console.log(`Event to track: ${eventToEncode}`);
console.log(`Event signature: ${signature}`);
console.log(`Encoded topic: ${topic}\n`);
console.log('This array filters ERC-20 Transfer events originating from a specific Ethereum address:');
console.log(topics);
}
main();
const Web3 = require('web3');
// Generate event signature using keccak256 hash algorithm
function encodeEvent(event) {
const keccakHash = Web3.utils.keccak256(event);
return keccakHash;
}
// Generate 32-bytes size specification
function encodeTopic(parameter) {
const encodedParameter = Web3.utils.padLeft(parameter, 64); // 64 characters = 32 bytes encoding
return encodedParameter;
}
function main() {
// Define the event to encode
const eventToEncode = 'Transfer(address,address,uint256)';
const signature = encodeEvent(eventToEncode);
// Define the topic to encode, an Ethereum address in this case
const topicToEncode = '0xf5d500B70bfca5987A05Fb4EfcCbfa5E660a81c0';
const topic = encodeTopic(topicToEncode);
// Topics array to use in eth_getLogs in the 'topics' field of the filter
const topics = [signature, topic];
console.log('===== Topics Encoder =====');
console.log(`Event to track: ${eventToEncode}`);
console.log(`Event signature: ${signature}`);
console.log(`Encoded topic: ${topic}\n`);
console.log('This array filters ERC-20 Transfer events originating from a specific Ethereum address:');
console.log(topics);
}
main();
$ node web3
===== Topics Encoder =====
Event to track: Transfer(address,address,uint256)
Event signature: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
Encoded topic: 0x000000000000000000000000f5d500B70bfca5987A05Fb4EfcCbfa5E660a81c0
This array filters ERC-20 Transfer events originating from a specific Ethereum address:
[
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
'0x000000000000000000000000f5d500B70bfca5987A05Fb4EfcCbfa5E660a81c0'
]
const Web3 = require('web3');
// Generate event signature using keccak256 hash algorithm
function encodeEvent(event) {
const keccakHash = Web3.utils.keccak256(event);
return keccakHash;
}
// Generate 32-bytes size specification
function encodeTopic(parameter) {
const encodedParameter = Web3.utils.padLeft(parameter, 64); // 64 characters = 32 bytes encoding
return encodedParameter;
}
function main() {
// Define the event to encode
const eventToEncode = 'Transfer(address,address,uint256)';
const signature = encodeEvent(eventToEncode);
// Define the topic to encode, an Ethereum address in this case
const topicToEncode = '0xf5d500B70bfca5987A05Fb4EfcCbfa5E660a81c0';
const topic = encodeTopic(topicToEncode);
// Topics array to use in eth_getLogs in the 'topics' field of the filter
const topics = [signature, topic];
console.log('===== Topics Encoder =====');
console.log(`Event to track: ${eventToEncode}`);
console.log(`Event signature: ${signature}`);
console.log(`Encoded topic: ${topic}\n`);
console.log('This array filters ERC-20 Transfer events originating from a specific Ethereum address:');
console.log(topics);
}
main();
$ node web3
===== Topics Encoder =====
Event to track: Transfer(address,address,uint256)
Event signature: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
Encoded topic: 0x000000000000000000000000f5d500B70bfca5987A05Fb4EfcCbfa5E660a81c0
This array filters ERC-20 Transfer events originating from a specific Ethereum address:
[
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
'0x000000000000000000000000f5d500B70bfca5987A05Fb4EfcCbfa5E660a81c0'
]