Ethers 102

1. StaticCall
2. Identify ERC721 Contracts
3. Encode Calldata
4. Batch Wallet Generation
5. Batch Transfer
6. Batch Collect
7. MerkleTree Script
8. Digital Signature Script
9. Listen to Mempool
10. Decode Transaction
11. Vanity Address Generator
12. Read Any Data
13. Front-running Script
14. Identify ERC20 Contracts
15. Flashbots
16. EIP712 Signature Script
.
Listen to Mempool

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


In this lesson, we will learn how to read transactions from the mempool.

MEV

MEV (Maximal Extractable Value) is a fascinating topic. Most people are unfamiliar with it as it did not exist before the invention of blockchain that supports smart contracts. It's a feast for searchers, a friend of miners, and a nightmare for retail investors.

In blockchain, miners/validators can profit by packing, excluding, or reordering transactions in the blocks they generate, and MEV is a metric that measures this profit.

Mempool

Before a user's transaction is included in the Ethereum blockchain by miners/validators, all transactions gather in the Mempool. Miners also search for transactions with high fees in the Mempool to prioritize packaging and maximize their profits. Generally, transactions with higher gas prices are more likely to be packaged.

At the same time, some MEV bots also search for profitable trades in the mempool. For example, a swap transaction with a high slippage may be subject to sandwich attacks: the bot adjusts the gas price to insert a buy order before the transaction, and then sends a sell order afterward, effectively selling the tokens at a higher price to the user (front-running).

Mempool

Listening to the Mempool

You can use the Provider class provided by ethers.js to listen to pending transactions in the mempool:

provider.on("pending", listener)

Mempool Listening Script

Below is a script that listens to the mempool.

  1. Create the provider and wallet. This time, we will use a WebSocket Provider for more persistent transaction listening. Thus, we need to change the url to wss.

    console.log("\n1. Connect to wss RPC")
    const INFURA_MAINNET_WSSURL = 'wss://mainnet.infura.io/ws/v3/8b9750710d56460d940aeff47967c4ba';
    const provider = new ethers.WebSocketProvider(INFURA_MAINNET_WSSURL);

Infura wss

  1. Because there are many pending transactions in the mempool, sometimes hundreds per second, it is easy to reach the request limit of free RPC nodes. Therefore, we need to throttle the request frequency using throttle.

    function throttle(fn, delay) {
        let timer;
        return function(){
            if(!timer) {
                fn.apply(this, arguments)
                timer = setTimeout(()=>{
                    clearTimeout(timer)
                    timer = null
                },delay)
            }
        }
    }
  2. Listen to the pending transactions in the mempool and print the transaction hash.

    let i = 0
    provider.on("pending", async (txHash) => {
        if (txHash && i < 100) {
            // Print txHash
            console.log(`[${(new Date).toLocaleTimeString()}] Listening to Pending Transaction ${i}: ${txHash} \r`);
            i++
            }
    });

    Get pending transaction hash

  3. Get the transaction details using the hash of the pending transaction. We can see that the transaction has not been included in a block yet, so its blockHash, blockNumber, and transactionIndex are all empty. However, we can retrieve information such as the sender address from, gas price gasPrice, target address to, amount of ether sent value, transaction data data, etc. Bots utilize this information for MEV mining.

    let j = 0
    provider.on("pending", throttle(async (txHash) => {
        if (txHash && j >= 100) {
            // Get transaction details
            let tx = await provider.getTransaction(txHash);
            console.log(`\n[${(new Date).toLocaleTimeString()}] Listening to Pending Transaction ${j}: ${txHash} \r`);
            console.log(tx);
            j++
            }
    }, 1000));

    Get transaction details

Summary

In this lesson, we briefly introduced MEV and mempool, and wrote a script to listen to pending transactions in the mempool.

PreviousNext