@@ -25,11 +25,50 @@ export type InstrumentedEVMProviderProps = {
25
25
export class InstrumentedEVMProvider extends ethers . providers . StaticJsonRpcProvider {
26
26
private readonly name : ProviderName
27
27
private readonly metricPrefix : string
28
+ private readonly blockCache : Map < string , Promise < any > >
28
29
29
30
constructor ( { url, network, name } : InstrumentedEVMProviderProps ) {
30
31
super ( url , network )
31
32
this . name = name
32
33
this . metricPrefix = `RPC_${ this . name } _${ this . network . chainId } `
34
+ this . blockCache = new Map ( )
35
+ // Set an event listener to clear the cache on every new block.
36
+ this . on ( 'block' , ( ) => {
37
+ metric . putMetric ( 'RPCProviderCacheClear' , 1 , MetricLoggerUnit . Count )
38
+ this . blockCache . clear ( )
39
+ } )
40
+ }
41
+
42
+ // Adds caching functionality to the RPC provider
43
+ override send ( method : string , params : Array < any > ) : Promise < any > {
44
+ // Only cache eth_call's.
45
+ if ( method !== 'eth_call' ) return super . send ( method , params )
46
+ let key : string | undefined = undefined
47
+
48
+ try {
49
+ key = `call:${ JSON . stringify ( params ) } `
50
+ } catch ( e ) {
51
+ metric . putMetric ( 'RPCProviderCacheKeyError' , 1 , MetricLoggerUnit . Count )
52
+ }
53
+
54
+ if ( key ) {
55
+ const cached = this . blockCache . get ( key )
56
+ if ( cached ) {
57
+ metric . putMetric ( 'RPCProviderCacheHit' , 1 , MetricLoggerUnit . Count )
58
+ return cached
59
+ } else {
60
+ metric . putMetric ( 'RPCProviderCacheMiss' , 1 , MetricLoggerUnit . Count )
61
+ }
62
+ }
63
+
64
+ const result = super . send ( method , params )
65
+
66
+ if ( key ) {
67
+ metric . putMetric ( 'RPCProviderCacheInsert' , 1 , MetricLoggerUnit . Count )
68
+ this . blockCache . set ( key , result )
69
+ }
70
+
71
+ return result
33
72
}
34
73
35
74
override call ( transaction : Deferrable < TransactionRequest > , blockTag ?: BlockTag | Promise < BlockTag > ) : Promise < string > {
0 commit comments