Skip to content

Commit 7bf924b

Browse files
authored
Merge pull request #213 from r4topunk/fix/etherscan-rate-throttle
fix(stake): throttle Etherscan calls under the 5 req/s free limit
2 parents 35cd930 + c59b9cf commit 7bf924b

1 file changed

Lines changed: 25 additions & 17 deletions

File tree

src/services/stake-graph.ts

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -181,23 +181,31 @@ async function morBackersByRider(ethUsd: number): Promise<Record<string, OrbitBa
181181
{ asset: "usdc", pool: MORPHEUS_POOLS.usdc.pool, decimals: 6 },
182182
];
183183
const pairs = escPools.flatMap((p) => idWallets.map(([id, ref]) => ({ ...p, id, ref })));
184-
const rows = await Promise.all(
185-
pairs.map(async ({ asset, pool, decimals, id, ref }) => {
186-
const logs = await etherscanReferred(pool, ref, ETHERSCAN_KEY as string);
187-
// Sum the staked amount straight from the events (datacenter-safe, no
188-
// extra eth_call that would blow the free rate limit). Withdrawals aren't
189-
// subtracted, but the 7-day lock makes that rare.
190-
const byUser = new Map<string, bigint>();
191-
for (const l of logs) byUser.set(l.user.toLowerCase(), (byUser.get(l.user.toLowerCase()) ?? BigInt(0)) + l.amount);
192-
const out: Array<{ id: RiderId; backer: OrbitBacker }> = [];
193-
for (const [userLc, amt] of byUser) {
194-
const tokens = Number(formatUnits(amt, decimals));
195-
const usd = asset === "steth" ? tokens * ethUsd : tokens;
196-
if (usd > 0) out.push({ id, backer: { address: getAddress(userLc), amount: usd, kind: "mor", asset } });
197-
}
198-
return out;
199-
}),
200-
);
184+
const rows: Array<{ id: RiderId; backer: OrbitBacker }>[] = [];
185+
// Etherscan's free tier caps at ~5 req/s — run the calls in small waves with
186+
// a gap so they don't get rate-limited into empty results. The route caches
187+
// the whole graph for 60s, so this runs at most once a minute.
188+
const BATCH = 4;
189+
for (let i = 0; i < pairs.length; i += BATCH) {
190+
const wave = await Promise.all(
191+
pairs.slice(i, i + BATCH).map(async ({ asset, pool, decimals, id, ref }) => {
192+
const logs = await etherscanReferred(pool, ref, ETHERSCAN_KEY as string);
193+
// Staked amount straight from the events (no eth_call). Withdrawals
194+
// aren't subtracted, but the 7-day lock makes that rare.
195+
const byUser = new Map<string, bigint>();
196+
for (const l of logs) byUser.set(l.user.toLowerCase(), (byUser.get(l.user.toLowerCase()) ?? BigInt(0)) + l.amount);
197+
const out: Array<{ id: RiderId; backer: OrbitBacker }> = [];
198+
for (const [userLc, amt] of byUser) {
199+
const tokens = Number(formatUnits(amt, decimals));
200+
const usd = asset === "steth" ? tokens * ethUsd : tokens;
201+
if (usd > 0) out.push({ id, backer: { address: getAddress(userLc), amount: usd, kind: "mor", asset } });
202+
}
203+
return out;
204+
}),
205+
);
206+
rows.push(...wave);
207+
if (i + BATCH < pairs.length) await sleep(320);
208+
}
201209
for (const { id, backer } of rows.flat()) (byRider[id] ||= []).push(backer);
202210
return byRider;
203211
}

0 commit comments

Comments
 (0)