0
# Real-time Data Watching
1
2
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.
3
4
## Capabilities
5
6
### useWatchBlockNumber
7
8
Hook to watch for new block numbers in real-time with automatic updates.
9
10
```typescript { .api }
11
/**
12
* Hook to watch for new blocks
13
* @param parameters - Block number watching parameters
14
* @returns Real-time block number updates
15
*/
16
function useWatchBlockNumber<config = Config>(
17
parameters?: UseWatchBlockNumberParameters<config>
18
): UseWatchBlockNumberReturnType;
19
20
interface UseWatchBlockNumberParameters<config = Config> {
21
/** Chain to watch */
22
chainId?: config['chains'][number]['id'];
23
config?: Config | config;
24
/** Whether to watch block numbers */
25
enabled?: boolean;
26
/** Callback when block number changes */
27
onBlockNumber?: (blockNumber: bigint, prevBlockNumber?: bigint) => void;
28
/** Callback on error */
29
onError?: (error: Error) => void;
30
/** Polling interval in milliseconds (default: 4000) */
31
pollingInterval?: number;
32
/** Whether to sync connected chain */
33
syncConnectedChain?: boolean;
34
}
35
36
type UseWatchBlockNumberReturnType = void;
37
```
38
39
**Usage Example:**
40
41
```typescript
42
import { useWatchBlockNumber, useBlockNumber } from "wagmi";
43
import { useState } from "react";
44
45
function BlockNumberWatcher() {
46
const [latestBlock, setLatestBlock] = useState<bigint>();
47
const { data: currentBlock } = useBlockNumber();
48
49
useWatchBlockNumber({
50
onBlockNumber(blockNumber, prevBlockNumber) {
51
console.log('New block:', blockNumber.toString());
52
setLatestBlock(blockNumber);
53
},
54
onError(error) {
55
console.error('Block watch error:', error);
56
},
57
});
58
59
return (
60
<div>
61
<h3>Block Number Watcher</h3>
62
<p>Current Block: {currentBlock?.toString()}</p>
63
<p>Latest Watched Block: {latestBlock?.toString()}</p>
64
</div>
65
);
66
}
67
```
68
69
### useWatchBlocks
70
71
Hook to watch for new blocks with full block data in real-time.
72
73
```typescript { .api }
74
/**
75
* Hook to watch for new blocks
76
* @param parameters - Block watching parameters
77
* @returns Real-time block data updates
78
*/
79
function useWatchBlocks<config = Config>(
80
parameters?: UseWatchBlocksParameters<config>
81
): UseWatchBlocksReturnType;
82
83
interface UseWatchBlocksParameters<config = Config> {
84
/** Chain to watch */
85
chainId?: config['chains'][number]['id'];
86
config?: Config | config;
87
/** Block tag to watch */
88
blockTag?: 'latest' | 'earliest' | 'pending' | 'safe' | 'finalized';
89
/** Whether to watch blocks */
90
enabled?: boolean;
91
/** Whether to emit missed blocks on reconnect */
92
emitMissed?: boolean;
93
/** Whether to emit on begin */
94
emitOnBegin?: boolean;
95
/** Include full transaction data */
96
includeTransactions?: boolean;
97
/** Callback when new block arrives */
98
onBlock?: (block: Block, prevBlock?: Block) => void;
99
/** Callback on error */
100
onError?: (error: Error) => void;
101
/** Polling interval in milliseconds */
102
pollingInterval?: number;
103
/** Whether to sync connected chain */
104
syncConnectedChain?: boolean;
105
}
106
107
type UseWatchBlocksReturnType = void;
108
109
interface Block {
110
/** Block hash */
111
hash: Hash;
112
/** Block number */
113
number: bigint;
114
/** Parent block hash */
115
parentHash: Hash;
116
/** Timestamp */
117
timestamp: bigint;
118
/** Gas limit */
119
gasLimit: bigint;
120
/** Gas used */
121
gasUsed: bigint;
122
/** Miner/validator address */
123
miner: Address;
124
/** Transaction hashes or full transactions */
125
transactions: Hash[] | Transaction[];
126
/** Additional block properties */
127
difficulty?: bigint;
128
totalDifficulty?: bigint;
129
size?: bigint;
130
nonce?: Hex;
131
baseFeePerGas?: bigint;
132
}
133
```
134
135
### useWatchContractEvent
136
137
Hook to watch for specific contract events in real-time.
138
139
```typescript { .api }
140
/**
141
* Hook to watch contract events
142
* @param parameters - Event watching parameters
143
* @returns Event watcher that triggers on new events
144
*/
145
function useWatchContractEvent<config = Config>(
146
parameters: UseWatchContractEventParameters<config>
147
): UseWatchContractEventReturnType;
148
149
interface UseWatchContractEventParameters<config = Config> {
150
/** Contract ABI */
151
abi: Abi;
152
/** Contract address */
153
address?: Address | Address[];
154
/** Event name to watch */
155
eventName?: string;
156
/** Event parameter filters */
157
args?: Record<string, unknown> | readonly unknown[];
158
/** Batch event handling */
159
batch?: boolean;
160
/** Chain to watch */
161
chainId?: config['chains'][number]['id'];
162
config?: Config | config;
163
/** Whether to watch events */
164
enabled?: boolean;
165
/** From block */
166
fromBlock?: bigint;
167
/** Event handler callback */
168
onLogs: (logs: Log[]) => void;
169
/** Error handler callback */
170
onError?: (error: Error) => void;
171
/** Polling interval in milliseconds */
172
pollingInterval?: number;
173
/** Whether to fetch past events on mount */
174
strict?: boolean;
175
/** Whether to sync connected chain */
176
syncConnectedChain?: boolean;
177
}
178
179
type UseWatchContractEventReturnType = void;
180
181
interface Log {
182
/** Event address */
183
address: Address;
184
/** Topics (indexed parameters) */
185
topics: readonly Hash[];
186
/** Event data */
187
data: Hex;
188
/** Block hash */
189
blockHash?: Hash;
190
/** Block number */
191
blockNumber?: bigint;
192
/** Transaction hash */
193
transactionHash?: Hash;
194
/** Transaction index */
195
transactionIndex?: number;
196
/** Log index */
197
logIndex?: number;
198
/** Whether log was removed */
199
removed?: boolean;
200
}
201
```
202
203
**Usage Example:**
204
205
```typescript
206
import { useWatchContractEvent } from "wagmi";
207
import { erc20Abi } from "viem";
208
import { useState } from "react";
209
210
function TokenTransferWatcher() {
211
const [transfers, setTransfers] = useState<Log[]>([]);
212
213
useWatchContractEvent({
214
abi: erc20Abi,
215
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
216
eventName: 'Transfer',
217
args: {
218
// Watch transfers from specific address
219
from: '0x742d35Cc6634C0532925a3b8D',
220
},
221
onLogs(logs) {
222
console.log('New transfer events:', logs);
223
setTransfers(prev => [...prev, ...logs]);
224
},
225
onError(error) {
226
console.error('Event watch error:', error);
227
},
228
});
229
230
return (
231
<div>
232
<h3>Token Transfer Monitor</h3>
233
<p>Watching {transfers.length} transfers</p>
234
{transfers.slice(-5).map((transfer, i) => (
235
<div key={i}>
236
<p>Block: {transfer.blockNumber?.toString()}</p>
237
<p>Hash: {transfer.transactionHash}</p>
238
</div>
239
))}
240
</div>
241
);
242
}
243
```
244
245
### useWatchPendingTransactions
246
247
Hook to watch for pending transactions in the mempool.
248
249
```typescript { .api }
250
/**
251
* Hook to watch pending transactions
252
* @param parameters - Pending transaction watching parameters
253
* @returns Real-time pending transaction updates
254
*/
255
function useWatchPendingTransactions<config = Config>(
256
parameters?: UseWatchPendingTransactionsParameters<config>
257
): UseWatchPendingTransactionsReturnType;
258
259
interface UseWatchPendingTransactionsParameters<config = Config> {
260
/** Chain to watch */
261
chainId?: config['chains'][number]['id'];
262
config?: Config | config;
263
/** Whether to watch pending transactions */
264
enabled?: boolean;
265
/** Callback when new pending transaction */
266
onTransactions?: (hashes: Hash[]) => void;
267
/** Error handler callback */
268
onError?: (error: Error) => void;
269
/** Polling interval in milliseconds */
270
pollingInterval?: number;
271
/** Whether to sync connected chain */
272
syncConnectedChain?: boolean;
273
}
274
275
type UseWatchPendingTransactionsReturnType = void;
276
```
277
278
### useWatchAsset
279
280
Hook to request adding an asset (token) to the user's wallet.
281
282
```typescript { .api }
283
/**
284
* Hook to request adding asset to wallet
285
* @param parameters - Watch asset configuration with mutation callbacks
286
* @returns Watch asset mutation
287
*/
288
function useWatchAsset<config = Config, context = unknown>(
289
parameters?: UseWatchAssetParameters<config, context>
290
): UseWatchAssetReturnType<config, context>;
291
292
interface UseWatchAssetParameters<config = Config, context = unknown> {
293
config?: Config | config;
294
mutation?: {
295
onMutate?: (variables: WatchAssetVariables) => Promise<context> | context;
296
onError?: (error: WatchAssetErrorType, variables: WatchAssetVariables, context?: context) => Promise<void> | void;
297
onSuccess?: (data: WatchAssetData, variables: WatchAssetVariables, context?: context) => Promise<void> | void;
298
onSettled?: (data?: WatchAssetData, error?: WatchAssetErrorType, variables?: WatchAssetVariables, context?: context) => Promise<void> | void;
299
};
300
}
301
302
interface UseWatchAssetReturnType<config = Config, context = unknown> {
303
/** Request to add asset to wallet */
304
watchAsset: (variables: WatchAssetVariables, options?: WatchAssetMutateOptions) => void;
305
/** Async version of watchAsset */
306
watchAssetAsync: (variables: WatchAssetVariables, options?: WatchAssetMutateAsyncOptions) => Promise<WatchAssetData>;
307
/** Watch asset data */
308
data?: WatchAssetData;
309
/** Watch asset error */
310
error: WatchAssetErrorType | null;
311
/** Watch asset status flags */
312
isError: boolean;
313
isIdle: boolean;
314
isPending: boolean;
315
isSuccess: boolean;
316
/** Reset watch asset state */
317
reset: () => void;
318
/** Current status */
319
status: 'error' | 'idle' | 'pending' | 'success';
320
/** Additional variables */
321
variables?: WatchAssetVariables;
322
}
323
324
interface WatchAssetVariables {
325
/** Asset type (currently only 'ERC20' supported) */
326
type: 'ERC20';
327
/** Token options */
328
options: {
329
/** Token contract address */
330
address: Address;
331
/** Token symbol */
332
symbol?: string;
333
/** Token decimals */
334
decimals?: number;
335
/** Token image URL */
336
image?: string;
337
};
338
}
339
340
type WatchAssetData = boolean; // Whether user accepted the request
341
```
342
343
**Usage Example:**
344
345
```typescript
346
import { useWatchAsset } from "wagmi";
347
348
function AddTokenButton() {
349
const { watchAsset, isPending } = useWatchAsset();
350
351
const handleAddToken = () => {
352
watchAsset({
353
type: 'ERC20',
354
options: {
355
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
356
symbol: 'DAI',
357
decimals: 18,
358
image: 'https://wallet-asset-matic.s3.amazonaws.com/img/tokens/dai.svg',
359
},
360
});
361
};
362
363
return (
364
<button onClick={handleAddToken} disabled={isPending}>
365
{isPending ? 'Adding...' : 'Add DAI to Wallet'}
366
</button>
367
);
368
}
369
```
370
371
## Advanced Watching Patterns
372
373
### Multi-Contract Event Aggregator
374
375
```typescript
376
import { useWatchContractEvent } from "wagmi";
377
import { erc20Abi } from "viem";
378
import { useState } from "react";
379
380
interface TokenEvent {
381
token: Address;
382
event: string;
383
data: Log;
384
timestamp: number;
385
}
386
387
function MultiTokenWatcher() {
388
const [events, setEvents] = useState<TokenEvent[]>([]);
389
390
const tokens = [
391
'0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
392
'0xA0b86a33E6417C90CC5F6d2c4a29f9D7e5D8ecf0', // USDC
393
'0xdac17f958d2ee523a2206206994597c13d831ec7', // USDT
394
];
395
396
// Watch Transfer events for all tokens
397
tokens.forEach(token => {
398
useWatchContractEvent({
399
abi: erc20Abi,
400
address: token,
401
eventName: 'Transfer',
402
onLogs(logs) {
403
const tokenEvents: TokenEvent[] = logs.map(log => ({
404
token: token,
405
event: 'Transfer',
406
data: log,
407
timestamp: Date.now(),
408
}));
409
setEvents(prev => [...prev.slice(-50), ...tokenEvents]);
410
},
411
});
412
});
413
414
return (
415
<div>
416
<h3>Multi-Token Event Monitor</h3>
417
<p>Total events: {events.length}</p>
418
{events.slice(-10).map((event, i) => (
419
<div key={i} style={{ border: '1px solid #ccc', margin: '4px', padding: '8px' }}>
420
<p>Token: {event.token}</p>
421
<p>Event: {event.event}</p>
422
<p>Block: {event.data.blockNumber?.toString()}</p>
423
<p>Time: {new Date(event.timestamp).toLocaleTimeString()}</p>
424
</div>
425
))}
426
</div>
427
);
428
}
429
```
430
431
### Real-time Portfolio Tracker
432
433
```typescript
434
import {
435
useWatchContractEvent,
436
useAccount,
437
useBalance
438
} from "wagmi";
439
import { erc20Abi } from "viem";
440
import { useState, useEffect } from "react";
441
442
function PortfolioTracker() {
443
const { address } = useAccount();
444
const [lastUpdate, setLastUpdate] = useState<Date>();
445
const [transactionCount, setTransactionCount] = useState(0);
446
447
const { data: ethBalance, refetch: refetchEth } = useBalance({
448
address: address!,
449
query: { enabled: !!address }
450
});
451
452
const { data: daiBalance, refetch: refetchDai } = useBalance({
453
address: address!,
454
token: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
455
query: { enabled: !!address }
456
});
457
458
// Watch for transactions affecting this address
459
useWatchContractEvent({
460
abi: erc20Abi,
461
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
462
eventName: 'Transfer',
463
args: {
464
from: address,
465
},
466
enabled: !!address,
467
onLogs(logs) {
468
console.log('Outgoing DAI transfers:', logs);
469
setTransactionCount(prev => prev + logs.length);
470
setLastUpdate(new Date());
471
refetchDai();
472
},
473
});
474
475
useWatchContractEvent({
476
abi: erc20Abi,
477
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
478
eventName: 'Transfer',
479
args: {
480
to: address,
481
},
482
enabled: !!address,
483
onLogs(logs) {
484
console.log('Incoming DAI transfers:', logs);
485
setTransactionCount(prev => prev + logs.length);
486
setLastUpdate(new Date());
487
refetchDai();
488
},
489
});
490
491
// Watch for new blocks to refresh ETH balance
492
useWatchBlockNumber({
493
enabled: !!address,
494
onBlockNumber() {
495
refetchEth();
496
},
497
});
498
499
if (!address) return <p>Please connect wallet</p>;
500
501
return (
502
<div>
503
<h3>Real-time Portfolio</h3>
504
<p>ETH Balance: {ethBalance?.formatted} {ethBalance?.symbol}</p>
505
<p>DAI Balance: {daiBalance?.formatted} {daiBalance?.symbol}</p>
506
<p>DAI Transactions Detected: {transactionCount}</p>
507
{lastUpdate && <p>Last Update: {lastUpdate.toLocaleTimeString()}</p>}
508
</div>
509
);
510
}
511
```
512
513
### Event Log Analyzer
514
515
```typescript
516
import { useWatchContractEvent } from "wagmi";
517
import { useState } from "react";
518
import { decodeEventLog } from "viem";
519
520
function EventAnalyzer() {
521
const [eventStats, setEventStats] = useState<Record<string, number>>({});
522
523
useWatchContractEvent({
524
abi: erc20Abi,
525
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI
526
onLogs(logs) {
527
logs.forEach(log => {
528
try {
529
const decoded = decodeEventLog({
530
abi: erc20Abi,
531
data: log.data,
532
topics: log.topics,
533
});
534
535
setEventStats(prev => ({
536
...prev,
537
[decoded.eventName]: (prev[decoded.eventName] || 0) + 1,
538
}));
539
} catch (error) {
540
console.error('Failed to decode event:', error);
541
}
542
});
543
},
544
});
545
546
return (
547
<div>
548
<h3>Event Statistics</h3>
549
{Object.entries(eventStats).map(([event, count]) => (
550
<p key={event}>{event}: {count}</p>
551
))}
552
</div>
553
);
554
}
555
```
556
557
## Common Types
558
559
```typescript { .api }
560
type Hash = `0x${string}`;
561
type Hex = `0x${string}`;
562
type Address = `0x${string}`;
563
564
interface Transaction {
565
hash: Hash;
566
blockHash?: Hash;
567
blockNumber?: bigint;
568
transactionIndex?: number;
569
from: Address;
570
to?: Address;
571
value: bigint;
572
gas: bigint;
573
gasPrice?: bigint;
574
maxFeePerGas?: bigint;
575
maxPriorityFeePerGas?: bigint;
576
input: Hex;
577
nonce: number;
578
type?: 'legacy' | 'eip2930' | 'eip1559';
579
}
580
581
interface Abi {
582
readonly [key: number]: AbiItem;
583
}
584
585
interface AbiItem {
586
type: 'function' | 'event' | 'error' | 'constructor' | 'fallback' | 'receive';
587
name?: string;
588
inputs?: AbiParameter[];
589
outputs?: AbiParameter[];
590
stateMutability?: 'pure' | 'view' | 'nonpayable' | 'payable';
591
anonymous?: boolean;
592
}
593
594
interface AbiParameter {
595
name: string;
596
type: string;
597
components?: AbiParameter[];
598
indexed?: boolean;
599
}
600
601
interface WatchAssetMutateOptions {
602
onError?: (error: Error, variables: WatchAssetVariables, context?: unknown) => void;
603
onSuccess?: (data: boolean, variables: WatchAssetVariables, context?: unknown) => void;
604
onSettled?: (data?: boolean, error?: Error, variables?: WatchAssetVariables, context?: unknown) => void;
605
}
606
607
type WatchAssetErrorType = Error;
608
```