React Hooks for Ethereum providing reactive primitives for wallet connections, smart contract interactions, and blockchain data
—
EIP-5792 atomic batch transaction support for executing multiple operations as a single unit. This module provides comprehensive functionality for atomic batch transactions, allowing multiple contract calls to be executed together with guaranteed atomicity.
Hook to send multiple calls atomically using EIP-5792 batch transaction standard.
/**
* Hook to send multiple calls atomically
* @param parameters - Atomic batch calls configuration with mutation callbacks
* @returns Send calls mutation with batch transaction state
*/
function useSendCalls<config = Config, context = unknown>(
parameters?: UseSendCallsParameters<config, context>
): UseSendCallsReturnType<config, context>;
interface UseSendCallsParameters<config = Config, context = unknown> {
config?: Config | config;
mutation?: {
onMutate?: (variables: SendCallsVariables) => Promise<context> | context;
onError?: (error: SendCallsErrorType, variables: SendCallsVariables, context?: context) => Promise<void> | void;
onSuccess?: (data: SendCallsData, variables: SendCallsVariables, context?: context) => Promise<void> | void;
onSettled?: (data?: SendCallsData, error?: SendCallsErrorType, variables?: SendCallsVariables, context?: context) => Promise<void> | void;
};
}
interface UseSendCallsReturnType<config = Config, context = unknown> {
/** Send atomic batch calls */
sendCalls: (variables: SendCallsVariables, options?: SendCallsMutateOptions) => void;
/** Async version of sendCalls */
sendCallsAsync: (variables: SendCallsVariables, options?: SendCallsMutateAsyncOptions) => Promise<SendCallsData>;
/** Batch transaction data */
data?: SendCallsData;
/** Send calls error */
error: SendCallsErrorType | null;
/** Send calls status flags */
isError: boolean;
isIdle: boolean;
isPending: boolean;
isSuccess: boolean;
/** Reset send calls state */
reset: () => void;
/** Current status */
status: 'error' | 'idle' | 'pending' | 'success';
/** Additional variables */
variables?: SendCallsVariables;
}
interface SendCallsVariables {
/** Array of calls to execute atomically */
calls: Call[];
/** Batch execution capabilities */
capabilities?: WalletCapabilities;
/** Chain ID for the batch */
chainId?: number;
/** Version of the batch call standard */
version?: string;
}
interface Call {
/** Target contract address */
to?: Address;
/** Call data */
data?: Hex;
/** Value to send with call */
value?: bigint;
/** Gas limit for this call */
gas?: bigint;
/** Chain ID for this specific call */
chainId?: number;
}
interface SendCallsData {
/** Batch identifier */
id: string;
}Usage Example:
import { useSendCalls, useAccount } from "wagmi";
import { encodeFunctionData, erc20Abi } from "viem";
function BatchTransfer() {
const { address } = useAccount();
const { sendCalls, data: batchId, isPending } = useSendCalls();
const handleBatchTransfer = () => {
if (!address) return;
// Encode transfer calls
const transferCall1 = encodeFunctionData({
abi: erc20Abi,
functionName: 'transfer',
args: ['0x742d35Cc6634C0532925a3b8D', 1000000000000000000n], // 1 token
});
const transferCall2 = encodeFunctionData({
abi: erc20Abi,
functionName: 'transfer',
args: ['0x8ba1f109551bD432803012645Hac136c', 500000000000000000n], // 0.5 tokens
});
sendCalls({
calls: [
{
to: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
data: transferCall1,
},
{
to: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
data: transferCall2,
},
],
});
};
return (
<div>
<button onClick={handleBatchTransfer} disabled={!address || isPending}>
{isPending ? 'Sending Batch...' : 'Send Batch Transfers'}
</button>
{batchId && <p>Batch ID: {batchId}</p>}
</div>
);
}Hook to check the status of batched calls by their batch ID.
/**
* Hook to check status of batched calls
* @param parameters - Calls status query parameters
* @returns Current status of the batched calls
*/
function useCallsStatus<config = Config, selectData = UseCallsStatusReturnType>(
parameters: UseCallsStatusParameters<config, selectData>
): UseCallsStatusReturnType<selectData>;
interface UseCallsStatusParameters<config = Config, selectData = UseCallsStatusReturnType> {
/** Batch ID to check status for */
id: string;
/** Chain ID where batch was submitted */
chainId?: config['chains'][number]['id'];
config?: Config | config;
query?: {
enabled?: boolean;
staleTime?: number;
gcTime?: number;
refetchInterval?: number;
select?: (data: UseCallsStatusReturnType) => selectData;
};
}
interface UseCallsStatusReturnType {
/** Current batch status */
status: 'PENDING' | 'CONFIRMED' | 'FAILED';
/** Individual call receipts */
receipts?: CallReceipt[];
}
interface CallReceipt {
/** Transaction hash for this call */
transactionHash: Hash;
/** Block hash */
blockHash: Hash;
/** Block number */
blockNumber: bigint;
/** Gas used */
gasUsed: bigint;
/** Call status */
status: 'success' | 'reverted';
/** Event logs */
logs: Log[];
}Hook to wait for batched calls to complete with polling support.
/**
* Hook to wait for calls to complete
* @param parameters - Wait for calls parameters
* @returns Final status when calls complete
*/
function useWaitForCallsStatus<config = Config, selectData = UseWaitForCallsStatusReturnType>(
parameters: UseWaitForCallsStatusParameters<config, selectData>
): UseWaitForCallsStatusReturnType<selectData>;
interface UseWaitForCallsStatusParameters<config = Config, selectData = UseWaitForCallsStatusReturnType> {
/** Batch ID to wait for */
id: string;
/** Chain ID */
chainId?: config['chains'][number]['id'];
/** Polling interval in milliseconds */
pollingInterval?: number;
/** Timeout in milliseconds */
timeout?: number;
config?: Config | config;
query?: {
enabled?: boolean;
gcTime?: number;
refetchInterval?: number;
retry?: boolean | number;
select?: (data: UseWaitForCallsStatusReturnType) => selectData;
};
}
type UseWaitForCallsStatusReturnType = UseCallsStatusReturnType;Usage Example:
import { useSendCalls, useWaitForCallsStatus } from "wagmi";
function BatchWithStatus() {
const { sendCalls, data: batchId } = useSendCalls();
const {
data: status,
isLoading,
isSuccess,
isError
} = useWaitForCallsStatus({
id: batchId || '',
query: { enabled: !!batchId }
});
const handleBatch = () => {
sendCalls({
calls: [
{ to: '0x742d35Cc6634C0532925a3b8D', value: 1000000000000000000n },
{ to: '0x8ba1f109551bD432803012645Hac136c', value: 500000000000000000n },
],
});
};
return (
<div>
<button onClick={handleBatch}>Send Batch</button>
{batchId && (
<div>
<p>Batch ID: {batchId}</p>
{isLoading && <p>Waiting for confirmation...</p>}
{isSuccess && (
<div>
<p>Status: {status?.status}</p>
<p>Receipts: {status?.receipts?.length || 0}</p>
</div>
)}
{isError && <p>Batch failed</p>}
</div>
)}
</div>
);
}Hook to show calls status in the wallet UI for user visibility.
/**
* Hook to show calls status in wallet UI
* @param parameters - Show calls status configuration
* @returns Show calls status mutation
*/
function useShowCallsStatus<config = Config, context = unknown>(
parameters?: UseShowCallsStatusParameters<config, context>
): UseShowCallsStatusReturnType<config, context>;
interface UseShowCallsStatusParameters<config = Config, context = unknown> {
config?: Config | config;
mutation?: {
onMutate?: (variables: ShowCallsStatusVariables) => Promise<context> | context;
onError?: (error: ShowCallsStatusErrorType, variables: ShowCallsStatusVariables, context?: context) => Promise<void> | void;
onSuccess?: (data: ShowCallsStatusData, variables: ShowCallsStatusVariables, context?: context) => Promise<void> | void;
onSettled?: (data?: ShowCallsStatusData, error?: ShowCallsStatusErrorType, variables?: ShowCallsStatusVariables, context?: context) => Promise<void> | void;
};
}
interface UseShowCallsStatusReturnType<config = Config, context = unknown> {
/** Show calls status in wallet */
showCallsStatus: (variables: ShowCallsStatusVariables, options?: ShowCallsStatusMutateOptions) => void;
/** Async version of showCallsStatus */
showCallsStatusAsync: (variables: ShowCallsStatusVariables, options?: ShowCallsStatusMutateAsyncOptions) => Promise<ShowCallsStatusData>;
/** Show calls status data */
data?: ShowCallsStatusData;
/** Show calls status error */
error: ShowCallsStatusErrorType | null;
/** Show calls status flags */
isError: boolean;
isIdle: boolean;
isPending: boolean;
isSuccess: boolean;
/** Reset show calls status state */
reset: () => void;
/** Current status */
status: 'error' | 'idle' | 'pending' | 'success';
/** Additional variables */
variables?: ShowCallsStatusVariables;
}
interface ShowCallsStatusVariables {
/** Batch ID to show status for */
id: string;
}
type ShowCallsStatusData = boolean; // Whether wallet showed the statusHook to get wallet capabilities for batch transaction support.
/**
* Hook to get wallet capabilities
* @param parameters - Capabilities query parameters
* @returns Available wallet capabilities
*/
function useCapabilities<config = Config, selectData = UseCapabilitiesReturnType>(
parameters?: UseCapabilitiesParameters<config, selectData>
): UseCapabilitiesReturnType<selectData>;
interface UseCapabilitiesParameters<config = Config, selectData = UseCapabilitiesReturnType> {
/** Account to get capabilities for */
account?: Address;
/** Chain ID */
chainId?: config['chains'][number]['id'];
config?: Config | config;
query?: {
enabled?: boolean;
staleTime?: number;
gcTime?: number;
select?: (data: UseCapabilitiesReturnType) => selectData;
};
}
interface UseCapabilitiesReturnType {
[chainId: number]: WalletCapabilities;
}
interface WalletCapabilities {
/** Atomic batch transaction support */
atomicBatch?: {
supported: boolean;
};
/** Paymaster support */
paymasterService?: {
supported: boolean;
};
/** Session key support */
sessionKeys?: {
supported: boolean;
};
/** Additional capabilities */
[key: string]: unknown;
}Usage Example:
import { useCapabilities, useAccount } from "wagmi";
function CapabilitiesChecker() {
const { address } = useAccount();
const { data: capabilities, isLoading } = useCapabilities({
account: address,
});
if (isLoading) return <div>Checking capabilities...</div>;
return (
<div>
<h3>Wallet Capabilities</h3>
{capabilities && Object.entries(capabilities).map(([chainId, caps]) => (
<div key={chainId}>
<h4>Chain {chainId}</h4>
<p>Atomic Batch: {caps.atomicBatch?.supported ? '✅' : '❌'}</p>
<p>Paymaster: {caps.paymasterService?.supported ? '✅' : '❌'}</p>
<p>Session Keys: {caps.sessionKeys?.supported ? '✅' : '❌'}</p>
</div>
))}
</div>
);
}Hook to execute a single call (part of the batch transaction system).
/**
* Hook to execute a single call
* @param parameters - Call execution parameters
* @returns Call result
*/
function useCall<config = Config, selectData = UseCallReturnType>(
parameters: UseCallParameters<config, selectData>
): UseCallReturnType<selectData>;
interface UseCallParameters<config = Config, selectData = UseCallReturnType> {
/** Target address */
to?: Address;
/** Call data */
data?: Hex;
/** Value to send */
value?: bigint;
/** Account to call from */
account?: Address;
/** Block number to call at */
blockNumber?: bigint;
blockTag?: 'latest' | 'earliest' | 'pending' | 'safe' | 'finalized';
/** Chain ID */
chainId?: config['chains'][number]['id'];
/** Gas limit */
gas?: bigint;
/** Gas price */
gasPrice?: bigint;
/** Max fee per gas */
maxFeePerGas?: bigint;
/** Max priority fee per gas */
maxPriorityFeePerGas?: bigint;
config?: Config | config;
query?: {
enabled?: boolean;
staleTime?: number;
select?: (data: UseCallReturnType) => selectData;
};
}
type UseCallReturnType = Hex | undefined;import { useSendCalls, useCapabilities } from "wagmi";
import { encodeFunctionData, erc20Abi, parseEther } from "viem";
function MultiTokenBatch() {
const { data: capabilities } = useCapabilities();
const { sendCalls, isPending } = useSendCalls();
const canBatch = capabilities?.[1]?.atomicBatch?.supported;
const handleComplexBatch = () => {
if (!canBatch) {
alert('Wallet does not support atomic batch transactions');
return;
}
// Create multiple token operations
const approveUSDC = encodeFunctionData({
abi: erc20Abi,
functionName: 'approve',
args: ['0x7f39C581F595B53c5cb19bD0b3f8DA6c935E2Ca0', parseEther('1000')],
});
const transferDAI = encodeFunctionData({
abi: erc20Abi,
functionName: 'transfer',
args: ['0x742d35Cc6634C0532925a3b8D', parseEther('500')],
});
const transferUSDC = encodeFunctionData({
abi: erc20Abi,
functionName: 'transfer',
args: ['0x8ba1f109551bD432803012645Hac136c', 1000000000n], // 1000 USDC
});
sendCalls({
calls: [
{
to: '0xA0b86a33E6417C90CC5F6d2c4a29f9D7e5D8ecf0', // USDC
data: approveUSDC,
},
{
to: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
data: transferDAI,
},
{
to: '0xA0b86a33E6417C90CC5F6d2c4a29f9D7e5D8ecf0', // USDC
data: transferUSDC,
},
],
});
};
return (
<div>
<p>Batch Support: {canBatch ? '✅' : '❌'}</p>
<button
onClick={handleComplexBatch}
disabled={!canBatch || isPending}
>
{isPending ? 'Processing...' : 'Execute Multi-Token Batch'}
</button>
</div>
);
}import {
useSendCalls,
useCallsStatus,
useShowCallsStatus
} from "wagmi";
import { useState } from "react";
function BatchStatusMonitor() {
const [batchIds, setBatchIds] = useState<string[]>([]);
const { sendCalls } = useSendCalls({
mutation: {
onSuccess(data) {
setBatchIds(prev => [...prev, data.id]);
},
},
});
const { showCallsStatus } = useShowCallsStatus();
return (
<div>
<h3>Batch Status Monitor</h3>
<button onClick={() => sendCalls({
calls: [
{ to: '0x742d35Cc6634C0532925a3b8D', value: parseEther('0.01') }
]
})}>
Send New Batch
</button>
<div>
<h4>Active Batches ({batchIds.length})</h4>
{batchIds.map(id => (
<BatchStatusItem
key={id}
batchId={id}
onShowStatus={() => showCallsStatus({ id })}
/>
))}
</div>
</div>
);
}
function BatchStatusItem({
batchId,
onShowStatus
}: {
batchId: string;
onShowStatus: () => void;
}) {
const { data: status, isLoading } = useCallsStatus({ id: batchId });
return (
<div style={{ border: '1px solid #ccc', padding: '8px', margin: '4px' }}>
<p>ID: {batchId}</p>
<p>Status: {isLoading ? 'Loading...' : status?.status}</p>
<p>Receipts: {status?.receipts?.length || 0}</p>
<button onClick={onShowStatus}>Show in Wallet</button>
</div>
);
}type Address = `0x${string}`;
type Hash = `0x${string}`;
type Hex = `0x${string}`;
interface Log {
address: Address;
topics: readonly Hash[];
data: Hex;
blockHash?: Hash;
blockNumber?: bigint;
transactionHash?: Hash;
transactionIndex?: number;
logIndex?: number;
removed?: boolean;
}
interface SendCallsMutateOptions {
onError?: (error: Error, variables: SendCallsVariables, context?: unknown) => void;
onSuccess?: (data: SendCallsData, variables: SendCallsVariables, context?: unknown) => void;
onSettled?: (data?: SendCallsData, error?: Error, variables?: SendCallsVariables, context?: unknown) => void;
}
interface ShowCallsStatusMutateOptions {
onError?: (error: Error, variables: ShowCallsStatusVariables, context?: unknown) => void;
onSuccess?: (data: boolean, variables: ShowCallsStatusVariables, context?: unknown) => void;
onSettled?: (data?: boolean, error?: Error, variables?: ShowCallsStatusVariables, context?: unknown) => void;
}
type SendCallsErrorType = Error;
type ShowCallsStatusErrorType = Error;
interface BatchTransactionSummary {
id: string;
status: 'PENDING' | 'CONFIRMED' | 'FAILED';
callCount: number;
successCount: number;
failureCount: number;
totalGasUsed?: bigint;
blockNumber?: bigint;
}Install with Tessl CLI
npx tessl i tessl/npm-wagmi