1- import { Interface , LogDescription } from '@ethersproject/abi' ;
2- import { getAddress } from '@ethersproject/address' ;
3- import { keccak256 } from '@ethersproject/keccak256' ;
41import {
5- Formatter ,
2+ createPublicClient ,
3+ formatLog ,
4+ getAddress ,
5+ http ,
6+ keccak256 ,
67 Log ,
7- Provider ,
8- StaticJsonRpcProvider
9- } from '@ethersproject/providers' ;
10- import { toUtf8Bytes } from '@ethersproject/strings' ;
8+ parseEventLogs ,
9+ ParseEventLogsReturnType ,
10+ PublicClient ,
11+ RpcLog ,
12+ stringToBytes
13+ } from 'viem' ;
1114import { getRangeHint } from './helpers' ;
1215import { Block , CustomJsonRpcError , EventsData , Writer } from './types' ;
1316import { CheckpointRecord } from '../../stores/checkpoints' ;
@@ -24,17 +27,20 @@ type GetLogsBlockRangeFilter = {
2427 toBlock : number ;
2528} ;
2629
30+ /**
31+ * Timeout for client requests in milliseconds.
32+ * This timeout is also used when fetching latest blocks in getLogs.
33+ */
34+ const CLIENT_TIMEOUT = 5 * 1000 ;
35+
2736const MAX_BLOCKS_PER_REQUEST = 10000 ;
2837
2938export class EvmProvider extends BaseProvider {
30- private readonly provider : Provider ;
31- /**
32- * Formatter instance from ethers.js used to format raw responses.
33- */
34- private readonly formatter = new Formatter ( ) ;
39+ private readonly client : PublicClient ;
40+
3541 private readonly writers : Record < string , Writer > ;
3642 private sourceHashes = new Map < string , string > ( ) ;
37- private logsCache = new Map < number , Log [ ] > ( ) ;
43+ private logsCache = new Map < bigint , Log [ ] > ( ) ;
3844
3945 constructor ( {
4046 instance,
@@ -46,9 +52,12 @@ export class EvmProvider extends BaseProvider {
4652 } ) {
4753 super ( { instance, log, abis } ) ;
4854
49- this . provider = new StaticJsonRpcProvider (
50- this . instance . config . network_node_url
51- ) ;
55+ this . client = createPublicClient ( {
56+ transport : http ( instance . config . network_node_url , {
57+ timeout : CLIENT_TIMEOUT
58+ } )
59+ } ) ;
60+
5261 this . writers = writers ;
5362 }
5463
@@ -57,76 +66,78 @@ export class EvmProvider extends BaseProvider {
5766 }
5867
5968 async getNetworkIdentifier ( ) : Promise < string > {
60- const result = await this . provider . getNetwork ( ) ;
61- return `evm_${ result . chainId } ` ;
69+ const chainId = await this . client . getChainId ( ) ;
70+
71+ return `evm_${ chainId } ` ;
6272 }
6373
6474 async getLatestBlockNumber ( ) : Promise < number > {
65- return this . provider . getBlockNumber ( ) ;
75+ const blockNumber = await this . client . getBlockNumber ( ) ;
76+
77+ return Number ( blockNumber ) ;
6678 }
6779
6880 async getBlockHash ( blockNumber : number ) {
69- const block = await this . provider . getBlock ( blockNumber ) ;
81+ const block = await this . client . getBlock ( {
82+ blockNumber : BigInt ( blockNumber )
83+ } ) ;
84+
7085 return block . hash ;
7186 }
7287
73- async processBlock ( blockNum : number , parentHash : string | null ) {
88+ async processBlock ( blockNumber : number , parentHash : string | null ) {
7489 let block : Block | null = null ;
7590 let eventsData : EventsData ;
7691
7792 const skipBlockFetching = this . instance . opts ?. skipBlockFetching ?? false ;
7893 const hasPreloadedBlockEvents =
79- skipBlockFetching && this . logsCache . has ( blockNum ) ;
94+ skipBlockFetching && this . logsCache . has ( BigInt ( blockNumber ) ) ;
8095
8196 try {
8297 if ( ! hasPreloadedBlockEvents ) {
83- block = await this . provider . getBlock ( blockNum ) ;
98+ block = await this . client . getBlock ( {
99+ blockNumber : BigInt ( blockNumber )
100+ } ) ;
84101 }
85- } catch ( e ) {
86- this . log . error (
87- { blockNumber : blockNum , err : e } ,
88- 'getting block failed... retrying'
89- ) ;
90- throw e ;
102+ } catch ( err ) {
103+ this . log . error ( { blockNumber, err } , 'getting block failed... retrying' ) ;
104+ throw err ;
91105 }
92106
93107 if ( ! hasPreloadedBlockEvents && block === null ) {
94- this . log . info ( { blockNumber : blockNum } , 'block not found' ) ;
108+ this . log . info ( { blockNumber } , 'block not found' ) ;
95109 throw new BlockNotFoundError ( ) ;
96110 }
97111
98112 try {
99113 eventsData = await this . getEvents ( {
100- blockNumber : blockNum ,
114+ blockNumber : BigInt ( blockNumber ) ,
101115 blockHash : block ?. hash ?? null
102116 } ) ;
103- } catch ( e : unknown ) {
104- if ( e instanceof CustomJsonRpcError && e . code === - 32000 ) {
105- this . log . info ( { blockNumber : blockNum } , 'block events not found' ) ;
117+ } catch ( err : unknown ) {
118+ if ( err instanceof CustomJsonRpcError && err . code === - 32000 ) {
119+ this . log . info ( { blockNumber } , 'block events not found' ) ;
106120 throw new BlockNotFoundError ( ) ;
107121 }
108122
109- this . log . error (
110- { blockNumber : blockNum , err : e } ,
111- 'getting events failed... retrying'
112- ) ;
113- throw e ;
123+ this . log . error ( { blockNumber, err } , 'getting events failed... retrying' ) ;
124+ throw err ;
114125 }
115126
116127 if ( block && parentHash && block . parentHash !== parentHash ) {
117- this . log . error ( { blockNumber : blockNum } , 'reorg detected' ) ;
128+ this . log . error ( { blockNumber } , 'reorg detected' ) ;
118129 throw new ReorgDetectedError ( ) ;
119130 }
120131
121- await this . handleBlock ( blockNum , block , eventsData ) ;
132+ await this . handleBlock ( blockNumber , block , eventsData ) ;
122133
123134 if ( block ) {
124- await this . instance . setBlockHash ( blockNum , block . hash ) ;
135+ await this . instance . setBlockHash ( blockNumber , block . hash ) ;
125136 }
126137
127- await this . instance . setLastIndexedBlock ( blockNum ) ;
138+ await this . instance . setLastIndexedBlock ( blockNumber ) ;
128139
129- return blockNum + 1 ;
140+ return blockNumber + 1 ;
130141 }
131142
132143 private async handleBlock (
@@ -160,7 +171,7 @@ export class EvmProvider extends BaseProvider {
160171 ) {
161172 this . log . debug ( { txId } , 'handling transaction' ) ;
162173
163- const helpers = await this . instance . getWriterHelpers ( ) ;
174+ const helpers = this . instance . getWriterHelpers ( ) ;
164175
165176 if ( this . instance . config . tx_fn ) {
166177 await this . writers [ this . instance . config . tx_fn ] ( {
@@ -184,7 +195,10 @@ export class EvmProvider extends BaseProvider {
184195 ) ;
185196
186197 for ( const event of logs ) {
187- const handler = globalEventHandlers [ event . topics [ 0 ] ] ;
198+ const eventHash = event . topics [ 0 ] ;
199+ if ( ! eventHash ) continue ;
200+
201+ const handler = globalEventHandlers [ eventHash ] ;
188202 if ( ! handler ) continue ;
189203
190204 this . log . info (
@@ -228,11 +242,15 @@ export class EvmProvider extends BaseProvider {
228242 'found contract event'
229243 ) ;
230244
231- let parsedEvent : LogDescription | undefined ;
245+ let parsedEvent : ParseEventLogsReturnType [ number ] | undefined ;
232246 if ( source . abi && this . abis ?. [ source . abi ] ) {
233- const iface = new Interface ( this . abis [ source . abi ] ) ;
234247 try {
235- parsedEvent = iface . parseLog ( log ) ;
248+ const parsedLogs = parseEventLogs ( {
249+ abi : this . abis [ source . abi ] ,
250+ logs : [ log ]
251+ } ) ;
252+ parsedEvent =
253+ parsedLogs [ 0 ] as ParseEventLogsReturnType [ number ] ;
236254 } catch ( err ) {
237255 this . log . warn (
238256 {
@@ -298,7 +316,7 @@ export class EvmProvider extends BaseProvider {
298316 blockNumber
299317 } : {
300318 blockHash : string | null ;
301- blockNumber : number ;
319+ blockNumber : bigint ;
302320 } ) : Promise < EventsData > {
303321 let isPreloaded = false ;
304322 let events : Log [ ] = [ ] ;
@@ -320,6 +338,8 @@ export class EvmProvider extends BaseProvider {
320338 return {
321339 isPreloaded,
322340 events : events . reduce ( ( acc , event ) => {
341+ if ( event . transactionHash === null ) return acc ;
342+
323343 if ( ! acc [ event . transactionHash ] ) acc [ event . transactionHash ] = [ ] ;
324344
325345 acc [ event . transactionHash ] = acc [ event . transactionHash ] . concat ( event ) ;
@@ -350,7 +370,10 @@ export class EvmProvider extends BaseProvider {
350370 topics ?: ( string | string [ ] ) [ ] ;
351371 } = { } ;
352372
373+ let signal : AbortSignal | undefined ;
374+
353375 if ( 'blockHash' in filter ) {
376+ signal = AbortSignal . timeout ( CLIENT_TIMEOUT ) ;
354377 params . blockHash = filter . blockHash ;
355378 }
356379
@@ -372,6 +395,7 @@ export class EvmProvider extends BaseProvider {
372395
373396 const res = await fetch ( this . instance . config . network_node_url , {
374397 method : 'POST' ,
398+ signal,
375399 headers : {
376400 'Content-Type' : 'application/json'
377401 } ,
@@ -397,9 +421,7 @@ export class EvmProvider extends BaseProvider {
397421 ) ;
398422 }
399423
400- return Formatter . arrayOf ( this . formatter . filterLog . bind ( this . formatter ) ) (
401- json . result
402- ) ;
424+ return json . result . map ( ( log : RpcLog ) => formatLog ( log ) ) ;
403425 }
404426
405427 async getLogs (
@@ -496,6 +518,8 @@ export class EvmProvider extends BaseProvider {
496518 } ) ;
497519
498520 for ( const log of events ) {
521+ if ( log . blockNumber === null ) continue ;
522+
499523 if ( ! this . logsCache . has ( log . blockNumber ) ) {
500524 this . logsCache . set ( log . blockNumber , [ ] ) ;
501525 }
@@ -504,14 +528,14 @@ export class EvmProvider extends BaseProvider {
504528 }
505529
506530 return events . map ( log => ( {
507- blockNumber : log . blockNumber ,
531+ blockNumber : Number ( log . blockNumber ) ,
508532 contractAddress : log . address
509533 } ) ) ;
510534 }
511535
512536 getEventHash ( eventName : string ) {
513537 if ( ! this . sourceHashes . has ( eventName ) ) {
514- this . sourceHashes . set ( eventName , keccak256 ( toUtf8Bytes ( eventName ) ) ) ;
538+ this . sourceHashes . set ( eventName , keccak256 ( stringToBytes ( eventName ) ) ) ;
515539 }
516540
517541 return this . sourceHashes . get ( eventName ) as string ;
0 commit comments