@@ -24,25 +24,31 @@ const (
2424 // Default retry configuration for RPC calls
2525 RPCDefaultRetryAttempts = 1
2626 RPCDefaultRetryDelay = 1000 * time .Millisecond
27+ RPCDefaultRetryTimeout = 10 * time .Second
2728
2829 // Default retry configuration for dialing RPC endpoints
2930 RPCDefaultDialRetryAttempts = 1
3031 RPCDefaultDialRetryDelay = 1000 * time .Millisecond
32+ RPCDefaultDialTimeout = 10 * time .Second
3133)
3234
3335type RetryConfig struct {
3436 Attempts uint
3537 Delay time.Duration
38+ Timeout time.Duration
3639 DialAttempts uint
3740 DialDelay time.Duration
41+ DialTimeout time.Duration
3842}
3943
4044func defaultRetryConfig () RetryConfig {
4145 return RetryConfig {
4246 Attempts : RPCDefaultRetryAttempts ,
4347 Delay : RPCDefaultRetryDelay ,
48+ Timeout : RPCDefaultRetryTimeout ,
4449 DialAttempts : RPCDefaultDialRetryAttempts ,
4550 DialDelay : RPCDefaultDialRetryDelay ,
51+ DialTimeout : RPCDefaultDialTimeout ,
4652 }
4753}
4854
@@ -95,16 +101,16 @@ func NewMultiClient(lggr logger.Logger, rpcsCfg RPCConfig, opts ...func(client *
95101}
96102
97103func (mc * MultiClient ) SendTransaction (ctx context.Context , tx * types.Transaction ) error {
98- return mc .retryWithBackups ("SendTransaction" , func (client * ethclient.Client ) error {
99- return client .SendTransaction (ctx , tx )
104+ return mc .retryWithBackups (ctx , "SendTransaction" , func (ct context. Context , client * ethclient.Client ) error {
105+ return client .SendTransaction (ct , tx )
100106 })
101107}
102108
103109func (mc * MultiClient ) CallContract (ctx context.Context , msg ethereum.CallMsg , blockNumber * big.Int ) ([]byte , error ) {
104110 var result []byte
105- err := mc .retryWithBackups ("CallContract" , func (client * ethclient.Client ) error {
111+ err := mc .retryWithBackups (ctx , "CallContract" , func (ct context. Context , client * ethclient.Client ) error {
106112 var err error
107- result , err = client .CallContract (ctx , msg , blockNumber )
113+ result , err = client .CallContract (ct , msg , blockNumber )
108114
109115 return err
110116 })
@@ -114,9 +120,9 @@ func (mc *MultiClient) CallContract(ctx context.Context, msg ethereum.CallMsg, b
114120
115121func (mc * MultiClient ) CallContractAtHash (ctx context.Context , msg ethereum.CallMsg , blockHash common.Hash ) ([]byte , error ) {
116122 var result []byte
117- err := mc .retryWithBackups ("CallContractAtHash" , func (client * ethclient.Client ) error {
123+ err := mc .retryWithBackups (ctx , "CallContractAtHash" , func (ct context. Context , client * ethclient.Client ) error {
118124 var err error
119- result , err = client .CallContractAtHash (ctx , msg , blockHash )
125+ result , err = client .CallContractAtHash (ct , msg , blockHash )
120126
121127 return err
122128 })
@@ -126,9 +132,9 @@ func (mc *MultiClient) CallContractAtHash(ctx context.Context, msg ethereum.Call
126132
127133func (mc * MultiClient ) CodeAt (ctx context.Context , account common.Address , blockNumber * big.Int ) ([]byte , error ) {
128134 var code []byte
129- err := mc .retryWithBackups ("CodeAt" , func (client * ethclient.Client ) error {
135+ err := mc .retryWithBackups (ctx , "CodeAt" , func (ct context. Context , client * ethclient.Client ) error {
130136 var err error
131- code , err = client .CodeAt (ctx , account , blockNumber )
137+ code , err = client .CodeAt (ct , account , blockNumber )
132138
133139 return err
134140 })
@@ -138,9 +144,9 @@ func (mc *MultiClient) CodeAt(ctx context.Context, account common.Address, block
138144
139145func (mc * MultiClient ) CodeAtHash (ctx context.Context , account common.Address , blockHash common.Hash ) ([]byte , error ) {
140146 var code []byte
141- err := mc .retryWithBackups ("CodeAtHash" , func (client * ethclient.Client ) error {
147+ err := mc .retryWithBackups (ctx , "CodeAtHash" , func (ct context. Context , client * ethclient.Client ) error {
142148 var err error
143- code , err = client .CodeAtHash (ctx , account , blockHash )
149+ code , err = client .CodeAtHash (ct , account , blockHash )
144150
145151 return err
146152 })
@@ -150,9 +156,9 @@ func (mc *MultiClient) CodeAtHash(ctx context.Context, account common.Address, b
150156
151157func (mc * MultiClient ) NonceAt (ctx context.Context , account common.Address , block * big.Int ) (uint64 , error ) {
152158 var count uint64
153- err := mc .retryWithBackups ("NonceAt" , func (client * ethclient.Client ) error {
159+ err := mc .retryWithBackups (ctx , "NonceAt" , func (ct context. Context , client * ethclient.Client ) error {
154160 var err error
155- count , err = client .NonceAt (ctx , account , block )
161+ count , err = client .NonceAt (ct , account , block )
156162
157163 return err
158164 })
@@ -162,9 +168,9 @@ func (mc *MultiClient) NonceAt(ctx context.Context, account common.Address, bloc
162168
163169func (mc * MultiClient ) NonceAtHash (ctx context.Context , account common.Address , blockHash common.Hash ) (uint64 , error ) {
164170 var count uint64
165- err := mc .retryWithBackups ("NonceAtHash" , func (client * ethclient.Client ) error {
171+ err := mc .retryWithBackups (ctx , "NonceAtHash" , func (ct context. Context , client * ethclient.Client ) error {
166172 var err error
167- count , err = client .NonceAtHash (ctx , account , blockHash )
173+ count , err = client .NonceAtHash (ct , account , blockHash )
168174
169175 return err
170176 })
@@ -174,9 +180,9 @@ func (mc *MultiClient) NonceAtHash(ctx context.Context, account common.Address,
174180
175181func (mc * MultiClient ) HeaderByNumber (ctx context.Context , number * big.Int ) (* types.Header , error ) {
176182 var header * types.Header
177- err := mc .retryWithBackups ("HeaderByNumber" , func (client * ethclient.Client ) error {
183+ err := mc .retryWithBackups (ctx , "HeaderByNumber" , func (ct context. Context , client * ethclient.Client ) error {
178184 var err error
179- header , err = client .HeaderByNumber (ctx , number )
185+ header , err = client .HeaderByNumber (ct , number )
180186
181187 return err
182188 })
@@ -186,9 +192,9 @@ func (mc *MultiClient) HeaderByNumber(ctx context.Context, number *big.Int) (*ty
186192
187193func (mc * MultiClient ) SuggestGasPrice (ctx context.Context ) (* big.Int , error ) {
188194 var gasPrice * big.Int
189- err := mc .retryWithBackups ("SuggestGasPrice" , func (client * ethclient.Client ) error {
195+ err := mc .retryWithBackups (ctx , "SuggestGasPrice" , func (ct context. Context , client * ethclient.Client ) error {
190196 var err error
191- gasPrice , err = client .SuggestGasPrice (ctx )
197+ gasPrice , err = client .SuggestGasPrice (ct )
192198
193199 return err
194200 })
@@ -198,9 +204,9 @@ func (mc *MultiClient) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
198204
199205func (mc * MultiClient ) SuggestGasTipCap (ctx context.Context ) (* big.Int , error ) {
200206 var gasTipCap * big.Int
201- err := mc .retryWithBackups ("SuggestGasTipCap" , func (client * ethclient.Client ) error {
207+ err := mc .retryWithBackups (ctx , "SuggestGasTipCap" , func (ct context. Context , client * ethclient.Client ) error {
202208 var err error
203- gasTipCap , err = client .SuggestGasTipCap (ctx )
209+ gasTipCap , err = client .SuggestGasTipCap (ct )
204210
205211 return err
206212 })
@@ -210,9 +216,9 @@ func (mc *MultiClient) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
210216
211217func (mc * MultiClient ) PendingCodeAt (ctx context.Context , account common.Address ) ([]byte , error ) {
212218 var code []byte
213- err := mc .retryWithBackups ("PendingCodeAt" , func (client * ethclient.Client ) error {
219+ err := mc .retryWithBackups (ctx , "PendingCodeAt" , func (ct context. Context , client * ethclient.Client ) error {
214220 var err error
215- code , err = client .PendingCodeAt (ctx , account )
221+ code , err = client .PendingCodeAt (ct , account )
216222
217223 return err
218224 })
@@ -222,9 +228,9 @@ func (mc *MultiClient) PendingCodeAt(ctx context.Context, account common.Address
222228
223229func (mc * MultiClient ) PendingNonceAt (ctx context.Context , account common.Address ) (uint64 , error ) {
224230 var count uint64
225- err := mc .retryWithBackups ("PendingNonceAt" , func (client * ethclient.Client ) error {
231+ err := mc .retryWithBackups (ctx , "PendingNonceAt" , func (ct context. Context , client * ethclient.Client ) error {
226232 var err error
227- count , err = client .PendingNonceAt (ctx , account )
233+ count , err = client .PendingNonceAt (ct , account )
228234
229235 return err
230236 })
@@ -234,9 +240,9 @@ func (mc *MultiClient) PendingNonceAt(ctx context.Context, account common.Addres
234240
235241func (mc * MultiClient ) EstimateGas (ctx context.Context , call ethereum.CallMsg ) (uint64 , error ) {
236242 var gas uint64
237- err := mc .retryWithBackups ("EstimateGas" , func (client * ethclient.Client ) error {
243+ err := mc .retryWithBackups (ctx , "EstimateGas" , func (ct context. Context , client * ethclient.Client ) error {
238244 var err error
239- gas , err = client .EstimateGas (ctx , call )
245+ gas , err = client .EstimateGas (ct , call )
240246
241247 return err
242248 })
@@ -246,9 +252,9 @@ func (mc *MultiClient) EstimateGas(ctx context.Context, call ethereum.CallMsg) (
246252
247253func (mc * MultiClient ) BalanceAt (ctx context.Context , account common.Address , blockNumber * big.Int ) (* big.Int , error ) {
248254 var balance * big.Int
249- err := mc .retryWithBackups ("BalanceAt" , func (client * ethclient.Client ) error {
255+ err := mc .retryWithBackups (ctx , "BalanceAt" , func (ct context. Context , client * ethclient.Client ) error {
250256 var err error
251- balance , err = client .BalanceAt (ctx , account , blockNumber )
257+ balance , err = client .BalanceAt (ct , account , blockNumber )
252258
253259 return err
254260 })
@@ -258,9 +264,9 @@ func (mc *MultiClient) BalanceAt(ctx context.Context, account common.Address, bl
258264
259265func (mc * MultiClient ) FilterLogs (ctx context.Context , q ethereum.FilterQuery ) ([]types.Log , error ) {
260266 var logs []types.Log
261- err := mc .retryWithBackups ("FilterLogs" , func (client * ethclient.Client ) error {
267+ err := mc .retryWithBackups (ctx , "FilterLogs" , func (ct context. Context , client * ethclient.Client ) error {
262268 var err error
263- logs , err = client .FilterLogs (ctx , q )
269+ logs , err = client .FilterLogs (ct , q )
264270
265271 return err
266272 })
@@ -270,16 +276,18 @@ func (mc *MultiClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) (
270276
271277func (mc * MultiClient ) SubscribeFilterLogs (ctx context.Context , q ethereum.FilterQuery , ch chan <- types.Log ) (ethereum.Subscription , error ) {
272278 var sub ethereum.Subscription
273- err := mc .retryWithBackups ("SubscribeFilterLogs" , func (client * ethclient.Client ) error {
279+ err := mc .retryWithBackups (ctx , "SubscribeFilterLogs" , func (ct context. Context , client * ethclient.Client ) error {
274280 var err error
275- sub , err = client .SubscribeFilterLogs (ctx , q , ch )
281+ sub , err = client .SubscribeFilterLogs (ct , q , ch )
276282
277283 return err
278284 })
279285
280286 return sub , err
281287}
282288
289+ // WaitMined waits for a transaction to be mined and returns the receipt.
290+ // Note: retryConfig timeout settings are not used for this operation, a timeout can be set in the context.
283291func (mc * MultiClient ) WaitMined (ctx context.Context , tx * types.Transaction ) (* types.Receipt , error ) {
284292 mc .lggr .Debugf ("Waiting for tx %s to be mined for chain %s" , tx .Hash ().Hex (), mc .chainName )
285293 // no retries here because we want to wait for the tx to be mined
@@ -318,13 +326,16 @@ func (mc *MultiClient) WaitMined(ctx context.Context, tx *types.Transaction) (*t
318326 }
319327}
320328
321- func (mc * MultiClient ) retryWithBackups (opName string , op func (* ethclient.Client ) error ) error {
329+ func (mc * MultiClient ) retryWithBackups (ctx context. Context , opName string , op func (context. Context , * ethclient.Client ) error ) error {
322330 var err error
323331 traceID := uuid .New ()
324332 for i , client := range append ([]* ethclient.Client {mc .Client }, mc .Backups ... ) {
325333 retryCount := 0
326334 err2 := retry .Do (func () error {
327- err = op (client )
335+ timeoutCtx , cancel := ensureTimeout (ctx , mc .RetryConfig .Timeout )
336+ defer cancel ()
337+
338+ err = op (timeoutCtx , client )
328339 if err != nil {
329340 mc .lggr .Warnf ("traceID %q: chain %q: op: %q: client index %d: failed execution - retryable error '%s'" , traceID .String (), mc .chainName , opName , i , MaybeDataErr (err ))
330341 return err
@@ -356,9 +367,12 @@ func (mc *MultiClient) dialWithRetry(rpc RPC, lggr logger.Logger) (*ethclient.Cl
356367 var client * ethclient.Client
357368 retryCount := 0
358369 err = retry .Do (func () error {
370+ ctx , cancel := context .WithTimeout (context .Background (), mc .RetryConfig .DialTimeout )
371+ defer cancel ()
372+
359373 var err2 error
360374 mc .lggr .Debugf ("traceID %q: chain %q: rpc: %q: dialing endpoint '%s'" , traceID .String (), mc .chainName , rpc .Name , endpoint )
361- client , err2 = ethclient .Dial ( endpoint )
375+ client , err2 = ethclient .DialContext ( ctx , endpoint )
362376 if err2 != nil {
363377 lggr .Warnf ("traceID %q: chain %q: rpc: %q: dialing failed - retryable error: %s: %v" , traceID .String (), mc .chainName , rpc .Name , endpoint , err2 )
364378 return err2
@@ -377,3 +391,17 @@ func (mc *MultiClient) dialWithRetry(rpc RPC, lggr logger.Logger) (*ethclient.Cl
377391
378392 return client , nil
379393}
394+
395+ // ensureTimeout checks if the parent context has a deadline.
396+ // If it does, it returns a new cancelable context using the parent's deadline.
397+ // If it doesn't, it creates a new context with the specified timeout.
398+ func ensureTimeout (parent context.Context , timeout time.Duration ) (context.Context , context.CancelFunc ) {
399+ // check if the parent context already has a deadline
400+ if _ , hasDeadline := parent .Deadline (); hasDeadline {
401+ // derive a new cancelable context from the parent context with the same deadline
402+ return context .WithCancel (parent )
403+ }
404+
405+ // create a new context with the specified timeout
406+ return context .WithTimeout (parent , timeout )
407+ }
0 commit comments