Documentation Index
Fetch the complete documentation index at: https://seilabs-docs-evm-reference-and-sei-js-examples.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Multicall
Multicall3 lets you batch multiple read calls into a single RPC round-trip, dramatically reducing latency for dashboards, portfolio pages, and any view that needs data from multiple contracts at once.
Multicall3 is deployed at 0xcA11bde05977b3631167028862bE2a173976CA11 on Sei mainnet and testnet — the same address as Ethereum.
Batching ERC-20 Reads with viem
viem’s multicall action wraps Multicall3 automatically:
import { createPublicClient, http, parseAbi } from 'viem';
import { sei } from '@sei-js/precompiles/viem';
const client = createPublicClient({ chain: sei, transport: http() });
const ERC20_ABI = parseAbi([
'function balanceOf(address owner) view returns (uint256)',
'function symbol() view returns (string)',
'function decimals() view returns (uint8)',
]);
const tokens = [
'0xTokenA',
'0xTokenB',
'0xTokenC',
] as const;
const owner = '0xYourAddress';
// Fetch symbol, decimals, and balance for every token in one call
const results = await client.multicall({
contracts: tokens.flatMap((address) => [
{ address, abi: ERC20_ABI, functionName: 'symbol' },
{ address, abi: ERC20_ABI, functionName: 'decimals' },
{ address, abi: ERC20_ABI, functionName: 'balanceOf', args: [owner] },
]),
});
// Results are returned in the same order as the input contracts array
// Each result has { status: 'success' | 'failure', result, error }
tokens.forEach((address, i) => {
const symbol = results[i * 3];
const decimals = results[i * 3 + 1];
const balance = results[i * 3 + 2];
if (symbol.status === 'success' && balance.status === 'success') {
console.log(`${address}: ${balance.result} (${symbol.result})`);
}
});
Allowing Individual Call Failures
By default, a single failed call causes the entire batch to revert. Use allowFailure: true (the default in viem) to get partial results instead:
const results = await client.multicall({
contracts: [
{ address: '0xTokenA', abi: ERC20_ABI, functionName: 'balanceOf', args: [owner] },
{ address: '0xMaybeInvalid', abi: ERC20_ABI, functionName: 'balanceOf', args: [owner] },
],
allowFailure: true, // default — individual failures don't abort the batch
});
results.forEach((result) => {
if (result.status === 'success') {
console.log('Balance:', result.result);
} else {
console.warn('Call failed:', result.error);
}
});
Batching with ethers
ethers doesn’t have a built-in multicall action, but you can call the Multicall3 contract directly:
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com');
const MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';
const MULTICALL3_ABI = [
'function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) view returns (tuple(bool success, bytes returnData)[] returnData)',
];
const multicall = new ethers.Contract(MULTICALL3_ADDRESS, MULTICALL3_ABI, provider);
const ERC20_INTERFACE = new ethers.Interface([
'function balanceOf(address owner) view returns (uint256)',
]);
const owner = '0xYourAddress';
const tokens = ['0xTokenA', '0xTokenB', '0xTokenC'];
const calls = tokens.map((target) => ({
target,
allowFailure: true,
callData: ERC20_INTERFACE.encodeFunctionData('balanceOf', [owner]),
}));
const results = await multicall.aggregate3(calls);
results.forEach(({ success, returnData }: { success: boolean; returnData: string }, i: number) => {
if (success) {
const [balance] = ERC20_INTERFACE.decodeFunctionResult('balanceOf', returnData);
console.log(`${tokens[i]}: ${balance}`);
}
});
Wagmi
In React apps, useReadContracts handles batching automatically:
import { useReadContracts } from 'wagmi';
import { parseAbi } from 'viem';
const ERC20_ABI = parseAbi([
'function balanceOf(address owner) view returns (uint256)',
'function symbol() view returns (string)',
]);
const TOKEN_A = '0xTokenA' as const;
const TOKEN_B = '0xTokenB' as const;
const owner = '0xYourAddress' as const;
export function MultiTokenBalances() {
const { data } = useReadContracts({
contracts: [
{ address: TOKEN_A, abi: ERC20_ABI, functionName: 'symbol' },
{ address: TOKEN_A, abi: ERC20_ABI, functionName: 'balanceOf', args: [owner] },
{ address: TOKEN_B, abi: ERC20_ABI, functionName: 'symbol' },
{ address: TOKEN_B, abi: ERC20_ABI, functionName: 'balanceOf', args: [owner] },
],
});
if (!data) return null;
return (
<ul>
<li>{data[0].result}: {data[1].result?.toString()}</li>
<li>{data[2].result}: {data[3].result?.toString()}</li>
</ul>
);
}