Skip to content

Batch Transactions

With Smart Wallet, you can send multiple onchain calls in a single transaction. Doing so improves the UX of multi-step interactions by reducing them to a single click. A common example of where you might want to leverage batch transactions is an ERC-20 approve followed by a swap.

You can submit batch transactions by using new wallet_sendCalls RPC, defined here.

Using Wagmi

(Optional) Check for atomic batching support

Smart Wallet will submit multiple calls as part of a single transaction. However, if your app supports other wallets, and you want to check that multiple calls will be submitted atomically (in a single transaction), check the wallet's capabilities.

App.tsx
import { useCapabilities } from 'wagmi/experimental'
 
function App() {
  const { data: capabilities } = useCapabilities() 
{
84532: {
atomicBatch: {
supported: true,
},
}
}
return <div /> }

The useCapabilities method will return, per chain, the capabilities that the connected wallet supports. If the connected wallet supports atomic batching, it will return an atomicBatch capability with a supported field equal to true for each chain it supports atomic batching on.

Send the calls

If you have your smart contract ABIs, the easiest way to send multiple calls is to use the Wagmi useWriteContracts hook.

App.tsx
import { useAccount } from 'wagmi'
import { useWriteContracts } from 'wagmi/experimental'
 
const abi = [
  {
    stateMutability: 'nonpayable',
    type: 'function',
    inputs: [{ name: 'to', type: 'address' }],
    name: 'safeMint',
    outputs: [],
  }
] as const
 
function App() {
  const account = useAccount()
  const { writeContracts } = useWriteContracts() 
 
  const handleMint = () => {
    writeContracts({ 
      contracts: [ 
        { 
          address: "0x119Ea671030FBf79AB93b436D2E20af6ea469a19", 
          abi, 
          functionName: "safeMint", 
          args: [account.address], 
        }, 
        { 
          address: "0x119Ea671030FBf79AB93b436D2E20af6ea469a19", 
          abi, 
          functionName: "safeMint", 
          args: [account.address], 
        } 
      ], 
    }) 
  }
 
  return ( 
    <div>
      <button onClick={handleMint}>Mint</button>
    </div>
  )
}

Check on the status of your calls

The useWriteContracts hook returns an object with a data field. This data is a call bundle identifier. Use the Wagmi useCallsStatus hook with this identifier to check on the status of your calls.

This will return a PENDING or CONFIRMED status along with a subset of a transaction receipt.

App.tsx
import { useAccount } from 'wagmi'
import { useWriteContracts, useCallsStatus } from 'wagmi/experimental'
 
const abi = [
  {
    stateMutability: 'nonpayable',
    type: 'function',
    inputs: [{ name: 'to', type: 'address' }],
    name: 'safeMint',
    outputs: [],
  }
] as const
 
function App() {
  const account = useAccount()
  const { data: id, writeContracts } = useWriteContracts()
  const { data: callsStatus } = useCallsStatus({ 
    id: id as string, 
    query: { 
      enabled: !!id, 
      // Poll every second until the calls are confirmed
      refetchInterval: (data) =>
        data.state.data?.status === "CONFIRMED" ? false : 1000, 
    }, 
  }); 
{
status: 'CONFIRMED',
receipts: [
{
logs: [
{
address: '0x...',
topics: [
'0x...'
],
data: '0x...'
},
],
status: 'success',
blockHash: '0x...',
blockNumber: 122414523n,
gasUsed: 390000n,
transactionHash: '0x...'
}
]
}
const handleMint = () => { writeContracts({ contracts: [ { address: "0x...", abi, functionName: "safeMint", args: [account.address], }, { address: "0x...", abi, functionName: "safeMint", args: [account.address], } ], }) } return ( <div> <button onClick={handleMint}>Mint</button> {callsStatus && <div> Status: {callsStatus.status}</div>} </div> ) }