For the complete documentation index, see llms.txt. This page is also available as Markdown.

How to Use Multicall3

Multicall3 batches multiple operations into a single transaction with atomic execution.

Multicall3 batches multiple operations into a single transaction with atomic execution using bsc_private_tx to get MEV protection with internal fee payment.

This is very good for most use cases, e.g., DEX swaps, token purchases, etc but consumes a lot of gas. If any part fails, the entire transaction reverts.

How it works

This is how Multicall3 works:

spinner

Prerequisites

You must have the following:

  • Node.js v18 or later installed

  • A funded BSC wallet with sufficient BNB for transactions and gas

  • GetBlock's BSC Accelerated Dedication Node

Reference Addresses

Address
Purpose

0xcA11bde05977b3631167028862bE2a173976CA11

Multicall3 contract

0x6374Ca2da5646C73Eb444aB99780495d61035f9b

Gateway fee recipient

Example

1

Set up the project

mkdir multicall3-example
cd multicall3-example
npm init -y
npm install ws ethers dotenv
mkdir multicall3-example
cd multicall3-example
yarn init -y
yarn ws ethers
2

Create a new file named index.js. This is where you will make your first call.

3

Set the ES module "type": "module" in your package.json.

4

Create .env file and add the following:

ACCESS_TOKEN=your-accelerated-node-endpoint
RPC_URL=your-normal-bsc-node-endppoint //e.g https
PRIVATE_KEY=your-wallet-private-key
5

Add the following code to index.js:

The following example demonstrates a simple BNB transfer with a priority fee:

import WebSocket from "ws"; 
import { ethers } from "ethers";
import "dotenv/config";
const PRIVATE_KEY = process.env.PRIVATE_KEY
const RPC_URL = process.env.RPC_URL;

// Constants
const MULTICALL3 = "0xcA11bde05977b3631167028862bE2a173976CA11";
const BLOXROUTE_FEE = "0x6374Ca2da5646C73Eb444aB99780495d61035f9b";

const MULTICALL_ABI = [
  "function aggregate3Value(tuple(address target, bool allowFailure, uint256 value, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[])",
];

async function sendPrivateTxWithTip() {
  const provider = new ethers.JsonRpcProvider(RPC_URL);
  const wallet = new ethers.Wallet(PRIVATE_KEY, provider);

  console.log("Wallet:", wallet.address);

  // Define amounts
  const tipAmount = ethers.parseEther("0.0001"); // 0.0001 BNB tip
  const transferAmount = ethers.parseEther("0.001"); // Your actual transfer
  const totalValue = tipAmount + transferAmount;

  // Build Multicall3 calls
  const calls = [
    {
      target: BLOXROUTE_FEE, // Tip payment FIRST
      allowFailure: false,
      value: tipAmount,
      callData: "0x", // Empty = simple transfer
    },
    {
      target: process.env.TO_ADDRESS, // Your actual transaction
      allowFailure: false,
      value: transferAmount,
      callData: "0x", // Or your contract calldata
    },
  ];
  // Encode Multicall3 call
  const multicall = new ethers.Contract(MULTICALL3, MULTICALL_ABI, wallet);
  const data = multicall.interface.encodeFunctionData("aggregate3Value", [
    calls,
  ]);
  // Get nonce
  const nonce = await provider.getTransactionCount(wallet.address);
  // Sign transaction
  const signedTx = await wallet.signTransaction({
    nonce: nonce,
    to: MULTICALL3,
    value: totalValue,
    data: data,
    gasPrice: ethers.parseUnits("3", "gwei"),
    gasLimit: 150000, // Higher for Multicall3
    chainId: 56,
  });
  const signedTxNoPrefix = signedTx.startsWith("0x")
  ? signedTx.slice(2)
  : signedTx;

  console.log("Transaction built:");
  console.log("  Tip:", ethers.formatEther(tipAmount), "BNB");
  console.log("  Transfer:", ethers.formatEther(transferAmount), "BNB");
  console.log("  Total:", ethers.formatEther(totalValue), "BNB");

  // Send via bsc_private_tx
  const ws = new WebSocket(process.env.ACCESS_TOKEN);

  ws.on("open", () => {
    const request = {
      jsonrpc: "2.0",
      id: 1,
      method: "bsc_private_tx",
      params: {
        transaction: signedTxNoPrefix,
        mev_builders: ["all"],
      },
    };

    console.log("\nSending private TX with tip...");
    ws.send(JSON.stringify(request));
  });

  ws.on("message", (data) => {
    const response = JSON.parse(data);

    if (response.result) {
      console.log("\n✅ Transaction submitted!");
      console.log("TX Hash:", response.result.txHash);
      console.log(
        "BSCScan:",
        `https://bscscan.com/tx/${response.result.txHash}`,
      );
    } else {
      console.error("\n❌ Error:", response.error);
    }

    ws.close();
  });
}
sendPrivateTxWithTip().catch(console.error);
6

Run the code using this command:

node index.js
7

Sample response

Wallet: 0xD1AF2dAc.....
Transaction built:
  Tip: 0.0001 BNB
  Transfer: 0.001 BNB
  Total: 0.0011 BNB
Sending private TX with tip...
 Transaction submitted!
TX Hash: 61bd625be986bb45e9fb495a5dba7bf39e00fd6871176971663436465311c970
BSCScan: https://bscscan.com/tx/61bd625be986bb45e9fb495a5dba7bf39e00fd6871176971663436465311c970

Extension sample

You can extend this code for a DEX swap by encoding the router call and including it as the second Multicall3 call:

Troubleshooting

Problem
Solution

"Multicall3: call failed"

  • Check you have enough BNB for all calls + gas

  • Increase gas limit to 200000

  • Verify target addresses are correct

TX not included

  • Increase tip amount

  • Check if you're rate limited

  • Verify TX is valid (simulate first)

Low priority despite tip

  • Increase tip amount

  • Check builder status

  • Try different builders: mev_builders: ["48club", "bloxroute"]

Conclusion

This is the simplest method for adding tips to private transactions: batch multiple operations into a single transaction with atomic execution. This is very good and highly recommended for day-to-day activities like sending transactions or DEX swaps.

Last updated

Was this helpful?