@@ -146,12 +146,6 @@ const ETHERSCAN_KEY = process.env.ETHERSCAN_API_KEY;
146146const USER_REFERRED_SIG = keccak256 ( toHex ( "UserReferred(uint256,address,address,uint256)" ) ) ;
147147const pad32 = ( a : Address ) => `0x000000000000000000000000${ a . slice ( 2 ) . toLowerCase ( ) } ` ;
148148
149- // Temporary diag: which key env is set + the last Etherscan response.
150- let _morNote = "init" ;
151- export function lastMorNote ( ) {
152- return `esk=${ process . env . ETHERSCAN_API_KEY ? "Y" : "n" } bsk=${ process . env . BASESCAN_API_KEY ? "Y" : "n" } | ${ _morNote } ` ;
153- }
154-
155149/** A rider's referred stakes on a pool, straight from the UserReferred events.
156150 * Needs a mainnet-capable Etherscan key (a Basescan-only key returns NOTOK for
157151 * chainid=1) — set ETHERSCAN_API_KEY in the env. */
@@ -160,15 +154,16 @@ async function etherscanReferred(pool: Address, referrer: Address, key: string):
160154 `https://api.etherscan.io/v2/api?chainid=1&module=logs&action=getLogs&address=${ pool } ` +
161155 `&topic0=${ USER_REFERRED_SIG } &topic0_3_opr=and&topic3=${ pad32 ( referrer ) } ` +
162156 `&fromBlock=0&toBlock=latest&page=1&offset=1000&apikey=${ key } ` ;
163- for ( let i = 0 ; i < 3 ; i ++ ) {
157+ for ( let i = 0 ; i < 4 ; i ++ ) {
164158 try {
165159 const res = await fetch ( url , { cache : "no-store" } ) ;
166160 const j = ( await res . json ( ) ) as { status ?: string ; message ?: string ; result ?: unknown } ;
167- _morNote = `${ j . status ?? "?" } |${ String ( j . message ?? "?" ) . slice ( 0 , 24 ) } |${ Array . isArray ( j . result ) ? `n=${ j . result . length } ` : String ( j . result ) . slice ( 0 , 50 ) } ` ;
168161 if ( j . status === "1" && Array . isArray ( j . result ) ) {
169162 return ( j . result as Array < { topics : string [ ] ; data : string } > ) . map ( ( l ) => ( { user : getAddress ( `0x${ l . topics [ 2 ] . slice ( 26 ) } ` ) , amount : BigInt ( l . data ) } ) ) ;
170163 }
171- if ( typeof j . message === "string" && / r a t e l i m i t / i. test ( j . message ) ) { await sleep ( 500 ) ; continue ; }
164+ // The rate-limit note lives in `result` ("Max calls per sec rate limit
165+ // reached (3/sec)"), not `message` — retry with backoff before giving up.
166+ if ( / r a t e l i m i t / i. test ( String ( j . message ) ) || / r a t e l i m i t / i. test ( String ( j . result ) ) ) { await sleep ( 600 ) ; continue ; }
172167 return [ ] ; // "No records found" / bad key
173168 } catch { await sleep ( 300 ) ; }
174169 }
@@ -192,10 +187,10 @@ async function morBackersByRider(ethUsd: number): Promise<Record<string, OrbitBa
192187 ] ;
193188 const pairs = escPools . flatMap ( ( p ) => idWallets . map ( ( [ id , ref ] ) => ( { ...p , id, ref } ) ) ) ;
194189 const rows : Array < { id : RiderId ; backer : OrbitBacker } > [ ] = [ ] ;
195- // Etherscan 's free tier caps at ~5 req/s — run the calls in small waves with
196- // a gap so they don't get rate-limited into empty results. The route caches
197- // the whole graph for 60s, so this runs at most once a minute .
198- const BATCH = 4 ;
190+ // The key 's tier caps at ~3 req/s — run the calls in waves of 3 with a gap
191+ // so they don't get rate-limited into empty results (the retry backoff
192+ // covers the rest). The route caches the whole graph for 60s .
193+ const BATCH = 3 ;
199194 for ( let i = 0 ; i < pairs . length ; i += BATCH ) {
200195 const wave = await Promise . all (
201196 pairs . slice ( i , i + BATCH ) . map ( async ( { asset, pool, decimals, id, ref } ) => {
0 commit comments