WTF Ethers: 9. Event Filtering
I've been revisiting ethers.js
recently to refresh my understanding of the details and to write a simple tutorial called "WTF Ethers" for beginners.
Twitter: @0xAA_Science
Community: Website wtf.academy | WTF Solidity | discord | WeChat Group Application
All the code and tutorials are open-sourced on GitHub: github.com/WTFAcademy/WTF-Ethers
Building upon the previous lesson, let's expand our knowledge by adding event filters during the listening process to filter out transfers from specific addresses.
Refer to the ethers.js documentation for more details.
Filters
When a contract emits a log (fires an event), it can contain up to 4 indexed
items. These indexed data items are hashed and included in a Bloom filter, which is a data structure that allows for efficient filtering. Therefore, an event filter can contain up to 4
topic sets, and each topic set serves as a condition to filter target events. The rules are as follows:
- If a topic set is
null
, the log topic at that position will not be filtered, and any value will match. - If the topic set is a single value, the log topic at that position must match that value.
- If the topic set is an array, the log topic at that position must match at least one of the values in the array.
Building Filters
The contract
class in ethers.js
provides the contract.filters
method to simplify filter creation:
const filter = contract.filters.EVENT_NAME( ...args )
Here, EVENT_NAME
is the desired event name to filter and ..args
refers to the topic set/condition. Let's look at a few examples based on the previous rules.
Filtering
Transfer
events from the addressmyAddress
:contract.filters.Transfer(myAddress)
Filtering all
Transfer
events sent to the addressmyAddress
:contract.filters.Transfer(null, myAddress)
Filtering all
Transfer
events sent from the addressmyAddress
to the addressotherAddress
:contract.filters.Transfer(myAddress, otherAddress)
Filtering all
Transfer
events sent to the addressmyAddress
orotherAddress
:contract.filters.Transfer(null, [ myAddress, otherAddress ])
Listening for USDT Transfers from an Exchange
- Tracking transactions where USDT is transferred out of Binance:
Before listening to the USDT contract, we need to understand the transaction log Logs
, including the event topics
and data
. Let's find a transaction where USDT was transferred out of Binance and check its details using its hash on etherscan:
Transaction hash: 0xab1f7b575600c4517a2e479e46e3af98a95ee84dd3f46824e02ff4618523fff5
This transaction performed one action: transferred 2983.98
USDT from binance14
(Binance hot wallet) to the address 0x354de44bedba213d612e92d3248b899de17b0c58
.
Checking the event log Logs
information:
address
: USDT contract addresstopics[0]
: Event hash,keccak256("Transfer(address,address,uint256)")
topics[1]
: From address (Binance hot wallet)topics[2]
: To addressdata
: Transfer amount
Create
provider
,abi
, andUSDT
contract variables:const provider = new ethers.JsonRpcProvider(ALCHEMY_MAINNET_URL);
// Contract address
const addressUSDT = '0xdac17f958d2ee523a2206206994597c13d831ec7'
// Exchange address
const accountBinance = '0x28C6c06298d514Db089934071355E5743bf21d60'
// Build ABI
const abi = [
"event Transfer(address indexed from, address indexed to, uint value)",
"function balanceOf(address) public view returns(uint)",
];
// Build contract object
const contractUSDT = new ethers.Contract(addressUSDT, abi, provider);Retrieve the balance of USDT in the Binance hot wallet. Currently, the Binance hot wallet has over 800 million USDT.
const balanceUSDT = await contractUSDT.balanceOf(accountBinance)
console.log(`USDT balance: ${ethers.formatUnits(balanceUSDT,6)}\n`)
Create a filter to listen for USDT transfers to Binance.
// 2. Create a filter to listen for USDT transfers to the exchange
console.log("\n2. Create a filter to listen for USDT transfers to Binance")
let filterBinanceIn = contractUSDT.filters.Transfer(null, accountBinance);
console.log("Filter details:")
console.log(filterBinanceIn);
contractUSDT.on(filterBinanceIn, (res) => {
console.log('---------Listening for USDT transfers to Binance--------');
console.log(
`${res.args[0]} -> ${res.args[1]} ${ethers.formatUnits(res.args[2],6)}`
)
})Create a filter to listen for USDT transfers from Binance.
// 3. Create a filter to listen for USDT transfers from the exchange
let filterToBinanceOut = contractUSDT.filters.Transfer(accountBinance);
console.log("\n3. Create a filter to listen for USDT transfers from Binance")
console.log("Filter details:")
console.log(filterToBinanceOut);
contractUSDT.on(filterToBinanceOut, (res) => {
console.log('---------Listening for USDT transfers from Binance--------');
console.log(
`${res.args[0]} -> ${res.args[1]} ${ethers.formatUnits(res.args[2],6)}`
)
}
);
Summary
In this lecture, we introduced event filters and used them to monitor USDT
transactions related to the hot wallet of Binance exchange. You can use them to monitor any transactions you are interested in, such as discover new transactions made by smart money
, monitor which projects the NFT
experts are investing in, and so on.