How to send Batch Transactions on Ethereum using web3.js

Transactions in EVM-based blockchain networks can be batched using the web3.js library. You need to create a batch object in order to perform this task. You can have a look at the example code from the web3.js documentation.

var contract =new web3.eth.Contract(abi, address);

var batch =new web3.BatchRequest();
batch.add(web3.eth.getBalance.request('0x0000000000000000000000000000000000000000', 'latest', callback));
batch.add(contract.methods.balance(address).call.request({from: '0x0000000000000000000000000000000000000000'}, callback2));
batch.execute();

batch.add() accepts request objects and batch.execute() executes the requests in the order in which they were added.

there are a few things that you need to keep in mind if you are planning to use the web3.batchRequest for batching transactions.

  • but batch.execute() does not return a promise, hence the transaction execution will continue in the background.
  • you will get transactionHash as a return value from the callback provided by the request object.
  • But the transaction hash will be sent to you before the receipt is even generated(receipt gets generated after transaction execution).

This can cause a problem if you are planning to use this transaction receipt to retrieve the receipt using web3.eth.getTransactionHash().

In order to resolve this,

If you need to wait until batch.execute()wraps up with the transaction execution, you need to write a wrapper function that returns a promise and resolves it in the callback of the request object.

[Note]: In the example given below, we are making use of the waitForReceipt() function. It is a recursive function that calls the web3.eth.getTransactionReciept(). If the receipt is null, it recursively calls itself after a delay of 2000 milliseconds. If the receipt is not null, then a callback function is resolved.

const batchExecution = () => {
    try {
        return new Promise((resolve) => {
                batch.add( contract.methods.SomeFunc2(params).call.request(...), callback));
                batch.add( contract.methods.SomeFunc2(params)
                .call.request({from: '0x0000000000000000000000000000000000000000'}, (err,tx) => {
                        waitForReceipt(tx, (reciept) => {
                                console.log(":::::::::::::::::waitForReceipt::::::::", tx);
                  console.log(":::::::::::::::::receipt::::::::", receipt);
                  const data = {
                    success: receipt?.status
                  };
                  resolve(data);
                            };
                });
                batch.execute();
        });
    } catch(e) {
        console.log("error");
    }
}

const waitForReceipt = (tx, cb) =>{
    let reciept = web3.eth.getTransactionReciept(tx, function(err, reciept) {
        if(reciept){
            cb(reciept);
        } else {
            window.setTimeout(function () {
             waitForReceipt(tx, cb);
          }, 2000);
        }
    });
}

Thus, by utilizing promises, you can use the batch execution provided by the web3.js library in an async/await pattern. and can await until the batch is completely executed.