- Both HTTP batch requests and multicall contracts can bundle multiple calls to reduce client-server overhead and improve response times.
- In the tests, HTTP batch requests often slightly outperformed multicall contracts, but both outperformed sending multiple single requests by a significant margin.
- Batch requests still count each call against your RPC usage, whereas multicall typically counts as a single request. However, multicall requires extra contract deployment and can lead to “request timeout” or “response size too big” errors for more complex calls.
- The best choice depends on your specific use cases, desired simplicity, and performance requirements.
Main article
This article is about two approaches many developers believe can help them save on RPC consumptions: batch request and multicall smart contract. In this article, we will explore how to use them and compare their performances.HTTP batch request
HTTP batch request is a feature most Ethereum clients support, for example, Geth. With batch requests enabled, multiple HTTP requests can be packaged into one single request and sent to the server. Server process this bulk request and returns a bulk result. All of these are done in a single round trip. This feature can be useful for reducing the load on the server and improving the performance to a certain extent.How to implement
To implement an HTTP batch request, just send a request with a payload containing multiple request objects in an array like below:Important: Response order is not guaranteedAccording to the JSON-RPC 2.0 specification, the server may return batch responses in any order. Always use the
id
field to match requests with responses, not the array position.This is especially important when using ethers.js v5, which had an issue that incorrectly relied on response ordering. If you’re using ethers.js v5 and experiencing ordering issues, upgrade to ethers.js v6 where this has been fixed. See ethers.js issue #3431 for details.Web3.js install instructions
Runnpm i web3
to install web3.js. The code in this guide is compatible with web3.js V4
.getBalances
function creates a new BatchRequest
object using web3.BatchRequest()
.
The function then loops through each address in the addresses
array and creates a new request to get the balance of that address using web3.eth.getBalance.request()
. It adds each request to the batch using batch.add()
.
Finally, the function executes the batch request using batch.execute()
. When executed, the requests in the batch are sent to the Ethereum network simultaneously, and the callback functions are executed when the responses are received.
Multicall contract
A multicall contract is a smart contract that takes in the function call objects as parameters and executes them together. A developer can use the multicall contract as a proxy to call other contracts on Ethereum. The implementation of a multicall contract is, in fact, very simple: it leverages Solidity’s call function to broadcast contract calls. This is a sample implementation of multicall’s aggregate function:Call
, calls each one, and returns an array of the results along with the block number in which the function was called. It is designed to be used as a general-purpose aggregator for calling other contracts on the Ethereum blockchain.
How to implement
Anyone can deploy their own multicall contract. In this article, we leverage MakerDAO’s multicall contract on the Ethereum mainnet; which is deployed at 0xeefBa1e63905eF1D7ACbA5a8513c70307C1cE441. Below is an example calling the smart contract with MakerDAO’s helper library multicall.js; it essentially does the same thing as the previous example:main
function iterates through each address in the addressArr
array and creates a call object for each address. These call objects use the multicall library to retrieve the ether balance for each address.
Once all of the call objects have been created and pushed to the calls
array, the multicall library’s aggregate
function is called with the array of call objects and the configuration object. This function aggregates the results of all of the calls into a single object, which is stored in the result
variable.
Finally, the code logs the “result” to the console and calculates the time it took to receive the “result”, which is also logged to the console.
You will need to install the multicall.js library to run this code.
Performance comparison
In this section, we compare the performance of 3 different approaches:- Sending multiple HTTP requests in parallel
- Sending a batch HTTP request
- Using a multicall contract
- Getting account balance
- Calling a smart contract
Getting account balance for 30 distinct addresses
The testing script for batch requests and multicall contract is already included in the previous sections. Below is the code for sending multiple HTTP requests in parallel:Result
Parallel single requests | Batch request | Multicall | |
---|---|---|---|
Round 1 | 1.789 | 1.49 | 1.447 |
Round 2 | 1.896 | 1.159 | 1.54 |
Round 3 | 2.337 | 1.113 | 2.132 |
Round 4 | 2.942 | 1.224 | 1.609 |
Round 5 | 1.638 | 1.602 | 2.012 |
Getting the owners of BAYC tokens
Below are the testing scripts using web3.js for making smart contract calls. The tests are based on an ERC-721 standard methodownerOf
from BAYC’s smart contract.
Sending multiple HTTP requests in parallel:
Result
Parallel single requests | Batch request | Multicall | |
---|---|---|---|
Round 1 | 1.693 | 1.931 | 1.878 |
Round 2 | 1.717 | 1.592 | 1.195 |
Round 3 | 1.712 | 1.617 | 2.183 |
Round 4 | 2.103 | 1.589 | 1.3 |
Round 5 | 2.785 | 1.416 | 1.429 |
Common questions
If I package 100 requests into a single batch request, does that count as 1 request or 100 requests on Chainstack?
If I package 100 requests into a single batch request, does that count as 1 request or 100 requests on Chainstack?
As an RPC provider, Chainstack counts “request” as RPC calls. After a server receives an HTTP batch request, it “unpacks” the request and processes the calls separately. So from the server’s point of view, 1 batch request of 100 calls consumes 100 requests instead of 1.
Check the Understanding your request consumption page on Chainstack support docs for more details.
If I package 100 calls into a single multicall request, does that count as 1 request or 100 requests?
If I package 100 calls into a single multicall request, does that count as 1 request or 100 requests?
In this case, even though it is a very heavy call, it counts as a single request.
Is there any hard limit for the number of calls to multicall contracts?
Is there any hard limit for the number of calls to multicall contracts?
The BAYC testing script stops working with 1,040 calls.