@@ -11,9 +11,12 @@ import yargs from "yargs";
1111import { hideBin } from "yargs/helpers" ;
1212import { z } from "zod" ;
1313
14+ const DEFAULT_RETRIES = 3 ;
15+
1416type LoadedConfig = {
1517 contract : EvmEntropyContract ;
1618 interval : number ;
19+ retries : number ;
1720} ;
1821
1922function timeToSeconds ( timeStr : string ) : number {
@@ -46,6 +49,7 @@ async function loadConfig(configPath: string): Promise<LoadedConfig[]> {
4649 "chain-id" : z . string ( ) ,
4750 interval : z . string ( ) ,
4851 "rpc-endpoint" : z . string ( ) . optional ( ) ,
52+ retries : z . number ( ) . default ( DEFAULT_RETRIES ) ,
4953 } ) ,
5054 ) ;
5155 const configContent = ( await import ( configPath , {
@@ -78,7 +82,7 @@ async function loadConfig(configPath: string): Promise<LoadedConfig[]> {
7882 evmChain . networkId ,
7983 ) ;
8084 }
81- return { contract : firstContract , interval } ;
85+ return { contract : firstContract , interval, retries : config . retries } ;
8286 } ) ;
8387 return loadedConfigs ;
8488}
@@ -188,31 +192,63 @@ export const main = function () {
188192 privateKeyFileContent . replace ( "0x" , "" ) . trimEnd ( ) ,
189193 ) ;
190194 logger . info ( "Running" ) ;
191- const promises = configs . map ( async ( { contract, interval } ) => {
192- const child = logger . child ( { chain : contract . chain . getId ( ) } ) ;
193- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
194- while ( true ) {
195- try {
196- await Promise . race ( [
197- testLatency ( contract , privateKey , child ) ,
198- new Promise ( ( _ , reject ) =>
199- setTimeout ( ( ) => {
200- reject (
201- new Error (
202- "Timeout: 120s passed but testLatency function was not resolved" ,
203- ) ,
195+ const promises = configs . map (
196+ async ( { contract, interval, retries } ) => {
197+ const child = logger . child ( { chain : contract . chain . getId ( ) } ) ;
198+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
199+ while ( true ) {
200+ let lastError : Error | undefined ;
201+ let success = false ;
202+
203+ for ( let attempt = 1 ; attempt <= retries ; attempt ++ ) {
204+ try {
205+ await Promise . race ( [
206+ testLatency ( contract , privateKey , child ) ,
207+ new Promise ( ( _ , reject ) =>
208+ setTimeout ( ( ) => {
209+ reject (
210+ new Error (
211+ "Timeout: 120s passed but testLatency function was not resolved" ,
212+ ) ,
213+ ) ;
214+ } , 120_000 ) ,
215+ ) ,
216+ ] ) ;
217+ success = true ;
218+ break ;
219+ } catch ( error ) {
220+ lastError = error as Error ;
221+ child . warn (
222+ { attempt, maxRetries : retries , error : error } ,
223+ `Attempt ${ attempt . toString ( ) } /${ retries . toString ( ) } failed, ${ attempt < retries ? "retrying..." : "all retries exhausted" } ` ,
224+ ) ;
225+
226+ if ( attempt < retries ) {
227+ // Wait a bit before retrying (exponential backoff, max 10s)
228+ const backoffDelay = Math . min (
229+ 2000 * Math . pow ( 2 , attempt - 1 ) ,
230+ 10_000 ,
231+ ) ;
232+ await new Promise ( ( resolve ) =>
233+ setTimeout ( resolve , backoffDelay ) ,
204234 ) ;
205- } , 120_000 ) ,
206- ) ,
207- ] ) ;
208- } catch ( error ) {
209- child . error ( error , "Error testing latency" ) ;
235+ }
236+ }
237+ }
238+
239+ if ( ! success && lastError ) {
240+ child . error (
241+ { error : lastError , retriesExhausted : retries } ,
242+ "All retries exhausted, callback was not called." ,
243+ ) ;
244+ }
245+
246+ await new Promise ( ( resolve ) =>
247+ setTimeout ( resolve , interval * 1000 ) ,
248+ ) ;
210249 }
211- await new Promise ( ( resolve ) =>
212- setTimeout ( resolve , interval * 1000 ) ,
213- ) ;
214- }
215- } ) ;
250+ } ,
251+ ) ;
216252 await Promise . all ( promises ) ;
217253 } ,
218254 )
0 commit comments