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
1. (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.
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.
2. Send the calls
If you have your smart contract ABIs, the easiest way to send multiple calls is to use the Wagmi useWriteContracts
hook.
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>
)
}
3. 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.
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>
)
}