Skip to main content

Batch Transactions (Multicall)

8 mins

In this tutorial, you’ll learn how to execute multiple smart contract calls in a single transaction on Push Chain, also known as Multicall or Batch Transactions.

This is one of Push Chain’s most powerful features, letting you do multiple actions such as approvals, transfers, or any contract interactions in a single transaction.

By the end, you’ll be able to:

  • ✅ Bundle multiple contract calls into one universal transaction.
  • ✅ Execute them atomically on Push Chain (all succeed or none do).
  • ✅ Reuse the same approach for your own app logic.

Understanding Multicall

In traditional dApps, every interaction requires its own transaction — users approve, then transfer, then call another contract.

With Push Chain’s Universal Execution Account (UEA) model, you can include an array of calls in a single sendTransaction().

The SDK automatically encodes and executes them in sequence, ensuring atomicity.

Requirements: Batch transactions run from external origin chains and execute atomically on Push Chain.

Contracts Used

We’ll reuse two contracts from earlier tutorials and interact with both from an external origin chain in a single universal transaction:

Counter Contract
Counter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

contract Counter {
uint256 public countPC;
event CountIncremented(uint256 indexed countPC, address indexed caller);

function increment() public {
countPC += 1;
emit CountIncremented(countPC, msg.sender);
}

function reset() public {
countPC = 0;
}
}
Prefer to deploy your own?

Follow:

Then replace the example addresses in this tutorial with your deployments.

Build the Multicall Payload

Multicall requirements
  • Origin-only: Batch transactions are supported only from external origin chains (not native Push).
  • UEA target: The to field must be your Universal Execution Account: pushChainClient.universal.account.
  • Atomicity: If any call fails, the entire batch reverts.
// rest of the code...

// Counter ABI on Push Chain (used in tests) with an increment function
const CounterABI = [
{
inputs: [],
name: 'increment',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [],
name: 'countPC',
outputs: [
{
internalType: 'uint256',
name: '',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
];

// Counter deployed on Push Chain Testnet
const counterAddress = '0x5FbDB2315678afecb367f032d93F642f64180aa3';

// Create function call for Counter.increment()
const incrementData = PushChain.utils.helpers.encodeTxData({
abi: CounterABI,
functionName: 'increment',
});

// ERC20 ABI on Push Chain (used in tests) with a mint function
const ERC20ABI = [
{
inputs: [
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
name: 'mint',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{ name: 'account', type: 'address' },
],
name: 'balanceOf',
outputs: [
{ name: '', type: 'uint256' },
],
stateMutability: 'view',
type: 'function',
},
];

// ERC20 deployed on Push Chain Testnet
const erc20Address = '0x0165878A594ca255338adfa4d48449f69242Eb8F';

// Create function call for ERC20.mint()
const mintData = PushChain.utils.helpers.encodeTxData({
abi: ERC20ABI,
functionName: 'mint',
args: [
pushChainClient.universal.account, // recipient is the connected UEA
PushChain.utils.helpers.parseUnits('11', 18), // 11 PC in uPC (ie: 18 decimal places),
],
});

// rest of the code...

// Send batch transaction (multicall)
const batchTx = await pushChainClient.universal.sendTransaction({
to: pushChainClient.universal.account,
data: [
{to: counterAddress, value: 0n, data: incrementData},
{to: erc20Address, value: 0n, data: mintData},
]
});

// rest of the code...

Understanding Multicall Payload

In this example, we are interacting with two different contracts on Push Chain Testnet from an external origin chain in a single transaction.

Let’s break down how this transaction executes step-by-step.

  • We first construct specific function calls for each contract using the encodeTxData helper function.
  • We pass an array of function calls to the data parameter which contains to, value and data instead of a single function call.
  • We pass the Universal Account address to the to parameter instead of a contract address.
Troubleshooting

Origin-only error: Multicall works only when initiated from an external chain.
Wrong to: The batch to must be pushChainClient.universal.account.
Bad ABI/data: Ensure encodeTxData({ abi, functionName, args }) matches the contract exactly.

Interact with Multicall

Both the counter and universal ERC-20 contracts are deployed on Push Chain Testnet.

This app lets any user — whether on Ethereum, Solana, or any other external chain to increment the counter and mint $UNICORN tokens in one single transaction.

Counter Contract Address: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Demo Token ERC-20 Contract Address: 0x0165878A594ca255338adfa4d48449f69242Eb8F

Steps to interact:

  1. Connect your wallet (Ethereum, Solana, or other chains).
  2. Click the Batch Transaction button — this will atomically increment the counter and mint $UNICORN tokens.
  3. Wait for the transaction to confirm.
  4. Your Counter will be incremented and $UNICORN balance will update in the UI automatically.
  5. (Optional) Click View in Explorer to inspect the transaction on Push Chain Explorer.

Note: You can also batch approvals or swaps the same way.


💡 Tip: Why this matters
Multicall drastically simplifies UX. Instead of asking users to sign multiple actions, you combine them into one universal transaction—fewer popups, fewer confirmations, less friction.

Live Playground

REACT PLAYGROUND
LIVE APP PREVIEW

Source Code

What we Achieved

In this tutorial, we built multiple transactions into a single transaction.

  • We wrote and deployed a counter contract and ERC-20 contract.
  • We incremented counter and minted tokens, then confirmed balances via the frontend.

All with a single universal transaction from other source chains.

This forms the foundation for multi-action DeFi, gaming, and on-chain automation flows.