React Hooks for Ethereum providing reactive primitives for wallet connections, smart contract interactions, and blockchain data
—
Event listeners and watchers for real-time blockchain data updates and contract events. This module provides comprehensive real-time data subscription functionality for monitoring blockchain state changes and contract events.
Hook to watch for new block numbers in real-time with automatic updates.
/**
* Hook to watch for new blocks
* @param parameters - Block number watching parameters
* @returns Real-time block number updates
*/
function useWatchBlockNumber<config = Config>(
parameters?: UseWatchBlockNumberParameters<config>
): UseWatchBlockNumberReturnType;
interface UseWatchBlockNumberParameters<config = Config> {
/** Chain to watch */
chainId?: config['chains'][number]['id'];
config?: Config | config;
/** Whether to watch block numbers */
enabled?: boolean;
/** Callback when block number changes */
onBlockNumber?: (blockNumber: bigint, prevBlockNumber?: bigint) => void;
/** Callback on error */
onError?: (error: Error) => void;
/** Polling interval in milliseconds (default: 4000) */
pollingInterval?: number;
/** Whether to sync connected chain */
syncConnectedChain?: boolean;
}
type UseWatchBlockNumberReturnType = void;Usage Example:
import { useWatchBlockNumber, useBlockNumber } from "wagmi";
import { useState } from "react";
function BlockNumberWatcher() {
const [latestBlock, setLatestBlock] = useState<bigint>();
const { data: currentBlock } = useBlockNumber();
useWatchBlockNumber({
onBlockNumber(blockNumber, prevBlockNumber) {
console.log('New block:', blockNumber.toString());
setLatestBlock(blockNumber);
},
onError(error) {
console.error('Block watch error:', error);
},
});
return (
<div>
<h3>Block Number Watcher</h3>
<p>Current Block: {currentBlock?.toString()}</p>
<p>Latest Watched Block: {latestBlock?.toString()}</p>
</div>
);
}Hook to watch for new blocks with full block data in real-time.
/**
* Hook to watch for new blocks
* @param parameters - Block watching parameters
* @returns Real-time block data updates
*/
function useWatchBlocks<config = Config>(
parameters?: UseWatchBlocksParameters<config>
): UseWatchBlocksReturnType;
interface UseWatchBlocksParameters<config = Config> {
/** Chain to watch */
chainId?: config['chains'][number]['id'];
config?: Config | config;
/** Block tag to watch */
blockTag?: 'latest' | 'earliest' | 'pending' | 'safe' | 'finalized';
/** Whether to watch blocks */
enabled?: boolean;
/** Whether to emit missed blocks on reconnect */
emitMissed?: boolean;
/** Whether to emit on begin */
emitOnBegin?: boolean;
/** Include full transaction data */
includeTransactions?: boolean;
/** Callback when new block arrives */
onBlock?: (block: Block, prevBlock?: Block) => void;
/** Callback on error */
onError?: (error: Error) => void;
/** Polling interval in milliseconds */
pollingInterval?: number;
/** Whether to sync connected chain */
syncConnectedChain?: boolean;
}
type UseWatchBlocksReturnType = void;
interface Block {
/** Block hash */
hash: Hash;
/** Block number */
number: bigint;
/** Parent block hash */
parentHash: Hash;
/** Timestamp */
timestamp: bigint;
/** Gas limit */
gasLimit: bigint;
/** Gas used */
gasUsed: bigint;
/** Miner/validator address */
miner: Address;
/** Transaction hashes or full transactions */
transactions: Hash[] | Transaction[];
/** Additional block properties */
difficulty?: bigint;
totalDifficulty?: bigint;
size?: bigint;
nonce?: Hex;
baseFeePerGas?: bigint;
}Hook to watch for specific contract events in real-time.
/**
* Hook to watch contract events
* @param parameters - Event watching parameters
* @returns Event watcher that triggers on new events
*/
function useWatchContractEvent<config = Config>(
parameters: UseWatchContractEventParameters<config>
): UseWatchContractEventReturnType;
interface UseWatchContractEventParameters<config = Config> {
/** Contract ABI */
abi: Abi;
/** Contract address */
address?: Address | Address[];
/** Event name to watch */
eventName?: string;
/** Event parameter filters */
args?: Record<string, unknown> | readonly unknown[];
/** Batch event handling */
batch?: boolean;
/** Chain to watch */
chainId?: config['chains'][number]['id'];
config?: Config | config;
/** Whether to watch events */
enabled?: boolean;
/** From block */
fromBlock?: bigint;
/** Event handler callback */
onLogs: (logs: Log[]) => void;
/** Error handler callback */
onError?: (error: Error) => void;
/** Polling interval in milliseconds */
pollingInterval?: number;
/** Whether to fetch past events on mount */
strict?: boolean;
/** Whether to sync connected chain */
syncConnectedChain?: boolean;
}
type UseWatchContractEventReturnType = void;
interface Log {
/** Event address */
address: Address;
/** Topics (indexed parameters) */
topics: readonly Hash[];
/** Event data */
data: Hex;
/** Block hash */
blockHash?: Hash;
/** Block number */
blockNumber?: bigint;
/** Transaction hash */
transactionHash?: Hash;
/** Transaction index */
transactionIndex?: number;
/** Log index */
logIndex?: number;
/** Whether log was removed */
removed?: boolean;
}Usage Example:
import { useWatchContractEvent } from "wagmi";
import { erc20Abi } from "viem";
import { useState } from "react";
function TokenTransferWatcher() {
const [transfers, setTransfers] = useState<Log[]>([]);
useWatchContractEvent({
abi: erc20Abi,
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
eventName: 'Transfer',
args: {
// Watch transfers from specific address
from: '0x742d35Cc6634C0532925a3b8D',
},
onLogs(logs) {
console.log('New transfer events:', logs);
setTransfers(prev => [...prev, ...logs]);
},
onError(error) {
console.error('Event watch error:', error);
},
});
return (
<div>
<h3>Token Transfer Monitor</h3>
<p>Watching {transfers.length} transfers</p>
{transfers.slice(-5).map((transfer, i) => (
<div key={i}>
<p>Block: {transfer.blockNumber?.toString()}</p>
<p>Hash: {transfer.transactionHash}</p>
</div>
))}
</div>
);
}Hook to watch for pending transactions in the mempool.
/**
* Hook to watch pending transactions
* @param parameters - Pending transaction watching parameters
* @returns Real-time pending transaction updates
*/
function useWatchPendingTransactions<config = Config>(
parameters?: UseWatchPendingTransactionsParameters<config>
): UseWatchPendingTransactionsReturnType;
interface UseWatchPendingTransactionsParameters<config = Config> {
/** Chain to watch */
chainId?: config['chains'][number]['id'];
config?: Config | config;
/** Whether to watch pending transactions */
enabled?: boolean;
/** Callback when new pending transaction */
onTransactions?: (hashes: Hash[]) => void;
/** Error handler callback */
onError?: (error: Error) => void;
/** Polling interval in milliseconds */
pollingInterval?: number;
/** Whether to sync connected chain */
syncConnectedChain?: boolean;
}
type UseWatchPendingTransactionsReturnType = void;Hook to request adding an asset (token) to the user's wallet.
/**
* Hook to request adding asset to wallet
* @param parameters - Watch asset configuration with mutation callbacks
* @returns Watch asset mutation
*/
function useWatchAsset<config = Config, context = unknown>(
parameters?: UseWatchAssetParameters<config, context>
): UseWatchAssetReturnType<config, context>;
interface UseWatchAssetParameters<config = Config, context = unknown> {
config?: Config | config;
mutation?: {
onMutate?: (variables: WatchAssetVariables) => Promise<context> | context;
onError?: (error: WatchAssetErrorType, variables: WatchAssetVariables, context?: context) => Promise<void> | void;
onSuccess?: (data: WatchAssetData, variables: WatchAssetVariables, context?: context) => Promise<void> | void;
onSettled?: (data?: WatchAssetData, error?: WatchAssetErrorType, variables?: WatchAssetVariables, context?: context) => Promise<void> | void;
};
}
interface UseWatchAssetReturnType<config = Config, context = unknown> {
/** Request to add asset to wallet */
watchAsset: (variables: WatchAssetVariables, options?: WatchAssetMutateOptions) => void;
/** Async version of watchAsset */
watchAssetAsync: (variables: WatchAssetVariables, options?: WatchAssetMutateAsyncOptions) => Promise<WatchAssetData>;
/** Watch asset data */
data?: WatchAssetData;
/** Watch asset error */
error: WatchAssetErrorType | null;
/** Watch asset status flags */
isError: boolean;
isIdle: boolean;
isPending: boolean;
isSuccess: boolean;
/** Reset watch asset state */
reset: () => void;
/** Current status */
status: 'error' | 'idle' | 'pending' | 'success';
/** Additional variables */
variables?: WatchAssetVariables;
}
interface WatchAssetVariables {
/** Asset type (currently only 'ERC20' supported) */
type: 'ERC20';
/** Token options */
options: {
/** Token contract address */
address: Address;
/** Token symbol */
symbol?: string;
/** Token decimals */
decimals?: number;
/** Token image URL */
image?: string;
};
}
type WatchAssetData = boolean; // Whether user accepted the requestUsage Example:
import { useWatchAsset } from "wagmi";
function AddTokenButton() {
const { watchAsset, isPending } = useWatchAsset();
const handleAddToken = () => {
watchAsset({
type: 'ERC20',
options: {
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
symbol: 'DAI',
decimals: 18,
image: 'https://wallet-asset-matic.s3.amazonaws.com/img/tokens/dai.svg',
},
});
};
return (
<button onClick={handleAddToken} disabled={isPending}>
{isPending ? 'Adding...' : 'Add DAI to Wallet'}
</button>
);
}import { useWatchContractEvent } from "wagmi";
import { erc20Abi } from "viem";
import { useState } from "react";
interface TokenEvent {
token: Address;
event: string;
data: Log;
timestamp: number;
}
function MultiTokenWatcher() {
const [events, setEvents] = useState<TokenEvent[]>([]);
const tokens = [
'0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
'0xA0b86a33E6417C90CC5F6d2c4a29f9D7e5D8ecf0', // USDC
'0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT
];
// Watch Transfer events for all tokens
tokens.forEach(token => {
useWatchContractEvent({
abi: erc20Abi,
address: token,
eventName: 'Transfer',
onLogs(logs) {
const tokenEvents: TokenEvent[] = logs.map(log => ({
token: token,
event: 'Transfer',
data: log,
timestamp: Date.now(),
}));
setEvents(prev => [...prev.slice(-50), ...tokenEvents]);
},
});
});
return (
<div>
<h3>Multi-Token Event Monitor</h3>
<p>Total events: {events.length}</p>
{events.slice(-10).map((event, i) => (
<div key={i} style={{ border: '1px solid #ccc', margin: '4px', padding: '8px' }}>
<p>Token: {event.token}</p>
<p>Event: {event.event}</p>
<p>Block: {event.data.blockNumber?.toString()}</p>
<p>Time: {new Date(event.timestamp).toLocaleTimeString()}</p>
</div>
))}
</div>
);
}import {
useWatchContractEvent,
useAccount,
useBalance
} from "wagmi";
import { erc20Abi } from "viem";
import { useState, useEffect } from "react";
function PortfolioTracker() {
const { address } = useAccount();
const [lastUpdate, setLastUpdate] = useState<Date>();
const [transactionCount, setTransactionCount] = useState(0);
const { data: ethBalance, refetch: refetchEth } = useBalance({
address: address!,
query: { enabled: !!address }
});
const { data: daiBalance, refetch: refetchDai } = useBalance({
address: address!,
token: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
query: { enabled: !!address }
});
// Watch for transactions affecting this address
useWatchContractEvent({
abi: erc20Abi,
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
eventName: 'Transfer',
args: {
from: address,
},
enabled: !!address,
onLogs(logs) {
console.log('Outgoing DAI transfers:', logs);
setTransactionCount(prev => prev + logs.length);
setLastUpdate(new Date());
refetchDai();
},
});
useWatchContractEvent({
abi: erc20Abi,
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
eventName: 'Transfer',
args: {
to: address,
},
enabled: !!address,
onLogs(logs) {
console.log('Incoming DAI transfers:', logs);
setTransactionCount(prev => prev + logs.length);
setLastUpdate(new Date());
refetchDai();
},
});
// Watch for new blocks to refresh ETH balance
useWatchBlockNumber({
enabled: !!address,
onBlockNumber() {
refetchEth();
},
});
if (!address) return <p>Please connect wallet</p>;
return (
<div>
<h3>Real-time Portfolio</h3>
<p>ETH Balance: {ethBalance?.formatted} {ethBalance?.symbol}</p>
<p>DAI Balance: {daiBalance?.formatted} {daiBalance?.symbol}</p>
<p>DAI Transactions Detected: {transactionCount}</p>
{lastUpdate && <p>Last Update: {lastUpdate.toLocaleTimeString()}</p>}
</div>
);
}import { useWatchContractEvent } from "wagmi";
import { useState } from "react";
import { decodeEventLog } from "viem";
function EventAnalyzer() {
const [eventStats, setEventStats] = useState<Record<string, number>>({});
useWatchContractEvent({
abi: erc20Abi,
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
onLogs(logs) {
logs.forEach(log => {
try {
const decoded = decodeEventLog({
abi: erc20Abi,
data: log.data,
topics: log.topics,
});
setEventStats(prev => ({
...prev,
[decoded.eventName]: (prev[decoded.eventName] || 0) + 1,
}));
} catch (error) {
console.error('Failed to decode event:', error);
}
});
},
});
return (
<div>
<h3>Event Statistics</h3>
{Object.entries(eventStats).map(([event, count]) => (
<p key={event}>{event}: {count}</p>
))}
</div>
);
}type Hash = `0x${string}`;
type Hex = `0x${string}`;
type Address = `0x${string}`;
interface Transaction {
hash: Hash;
blockHash?: Hash;
blockNumber?: bigint;
transactionIndex?: number;
from: Address;
to?: Address;
value: bigint;
gas: bigint;
gasPrice?: bigint;
maxFeePerGas?: bigint;
maxPriorityFeePerGas?: bigint;
input: Hex;
nonce: number;
type?: 'legacy' | 'eip2930' | 'eip1559';
}
interface Abi {
readonly [key: number]: AbiItem;
}
interface AbiItem {
type: 'function' | 'event' | 'error' | 'constructor' | 'fallback' | 'receive';
name?: string;
inputs?: AbiParameter[];
outputs?: AbiParameter[];
stateMutability?: 'pure' | 'view' | 'nonpayable' | 'payable';
anonymous?: boolean;
}
interface AbiParameter {
name: string;
type: string;
components?: AbiParameter[];
indexed?: boolean;
}
interface WatchAssetMutateOptions {
onError?: (error: Error, variables: WatchAssetVariables, context?: unknown) => void;
onSuccess?: (data: boolean, variables: WatchAssetVariables, context?: unknown) => void;
onSettled?: (data?: boolean, error?: Error, variables?: WatchAssetVariables, context?: unknown) => void;
}
type WatchAssetErrorType = Error;Install with Tessl CLI
npx tessl i tessl/npm-wagmi