Documentation Index
Fetch the complete documentation index at: https://seilabs-docs-clarify-association-method-availability.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Error Handling
Most transaction failures fall into three categories: contract reverts, wallet rejections, and RPC errors. This page shows how to decode each type and respond correctly.
Contract Reverts
When a transaction reverts, the error contains the revert reason if the contract provides one.
import { ContractFunctionRevertedError, BaseError } from 'viem';
try {
await client.simulateContract({
address: TOKEN,
abi: ERC20_ABI,
functionName: 'transfer',
args: ['0xRecipient', 1_000_000n],
account,
});
} catch (err) {
if (err instanceof BaseError) {
const revert = err.walk((e) => e instanceof ContractFunctionRevertedError);
if (revert instanceof ContractFunctionRevertedError) {
console.error('Revert reason:', revert.data?.errorName);
// e.g. 'ERC20InsufficientBalance'
}
}
}
Simulating Before Sending
Always simulate write calls before submitting them. Simulation runs the call against current chain state and surfaces reverts before you spend gas:
import { ContractFunctionRevertedError, BaseError } from 'viem';
async function safeWrite(params: Parameters<typeof walletClient.writeContract>[0]) {
// 1. Simulate — reverts here cost no gas
const { request } = await client.simulateContract(params);
// 2. Execute only if simulation passes
return walletClient.writeContract(request);
}
Wallet Rejections
Users can reject signature and transaction requests. These rejections have a predictable error code:
import { UserRejectedRequestError } from 'viem';
try {
await walletClient.sendTransaction({ to: '0xRecipient', value: 1n });
} catch (err) {
if (err instanceof UserRejectedRequestError) {
// User cancelled — don't show an error, just reset UI state
console.log('User rejected the request');
}
}
Insufficient Funds
import { InsufficientFundsError } from 'viem';
try {
await walletClient.sendTransaction({ to: '0xRecipient', value: parseEther('9999') });
} catch (err) {
if (err instanceof InsufficientFundsError) {
console.error('Not enough SEI for this transaction');
}
}
Classifying Any Error
A utility to handle the most common cases in one place:
import {
BaseError,
ContractFunctionRevertedError,
UserRejectedRequestError,
InsufficientFundsError,
} from 'viem';
function classifyError(err: unknown): string {
if (err instanceof UserRejectedRequestError) return 'rejected';
if (err instanceof InsufficientFundsError) return 'insufficient_funds';
if (err instanceof BaseError) {
const revert = err.walk((e) => e instanceof ContractFunctionRevertedError);
if (revert instanceof ContractFunctionRevertedError) {
return `revert:${revert.data?.errorName ?? 'unknown'}`;
}
}
return 'unknown';
}
RPC Errors and Retries
Transient RPC failures (network timeouts, rate limits) can be retried. Don’t retry on contract reverts or user rejections — those are deterministic.
async function withRetry<T>(fn: () => Promise<T>, maxAttempts = 3): Promise<T> {
let lastError: unknown;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (err: any) {
// Don't retry user rejections or contract reverts
if (
err instanceof UserRejectedRequestError ||
err?.code === 4001 ||
err?.code === 'CALL_EXCEPTION'
) {
throw err;
}
lastError = err;
if (attempt < maxAttempts) {
await new Promise((r) => setTimeout(r, 500 * attempt)); // backoff
}
}
}
throw lastError;
}
// Usage
const balance = await withRetry(() =>
client.readContract({ address: TOKEN, abi: ERC20_ABI, functionName: 'balanceOf', args: [owner] })
);