diff --git a/.changeset/light-weeks-scream.md b/.changeset/light-weeks-scream.md new file mode 100644 index 000000000..2dca1166a --- /dev/null +++ b/.changeset/light-weeks-scream.md @@ -0,0 +1,5 @@ +--- +"@ponder/utils": patch +--- + +Added retry range detection for coinbase rpcs. diff --git a/packages/utils/.env.example b/packages/utils/.env.example index 56fb54b4b..b04cc012b 100644 --- a/packages/utils/.env.example +++ b/packages/utils/.env.example @@ -8,4 +8,7 @@ RPC_URL_INFURA_1=... RPC_URL_QUICKNODE_1=... # Mainnet chainstack url -RPC_URL_CHAINSTACK_1=... \ No newline at end of file +RPC_URL_CHAINSTACK_1=... + +# Coinbase url +RPC_URL_COINBASE_8453=... \ No newline at end of file diff --git a/packages/utils/src/_test/coinbase.test.ts b/packages/utils/src/_test/coinbase.test.ts new file mode 100644 index 000000000..78ffd5e89 --- /dev/null +++ b/packages/utils/src/_test/coinbase.test.ts @@ -0,0 +1,66 @@ +import { LimitExceededRpcError, numberToHex } from "viem"; +import { expect, test } from "vitest"; +import { getLogsRetryHelper } from "../getLogsRetryHelper.js"; +import { type Params, getRequest } from "./utils.js"; + +const request = getRequest(process.env.RPC_URL_COINBASE_8453!); +const fromBlock = 10_000_000n; +const maxBlockRange = 999n; + +test("coinbase success", async () => { + const logs = await request({ + method: "eth_getLogs", + params: [ + { + address: "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22", + fromBlock: numberToHex(fromBlock), + toBlock: numberToHex(fromBlock + maxBlockRange), + }, + ], + }); + + expect(logs).toHaveLength(77); +}); + +test( + "coinbase block range", + async () => { + const params: Params = [ + { + address: "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22", + fromBlock: numberToHex(fromBlock), + toBlock: numberToHex(fromBlock + maxBlockRange + 1n), + }, + ]; + + const error = await request({ + method: "eth_getLogs", + params, + }).catch((error) => error); + + expect(error).toBeInstanceOf(LimitExceededRpcError); + expect(JSON.stringify(error)).includes( + "please limit the query to at most 1000 blocks", + ); + + const retry = getLogsRetryHelper({ + params, + error, + }); + + expect(retry).toStrictEqual({ + shouldRetry: true, + ranges: [ + { + fromBlock: numberToHex(fromBlock), + toBlock: numberToHex(fromBlock + maxBlockRange), + }, + { + fromBlock: numberToHex(fromBlock + maxBlockRange + 1n), + toBlock: numberToHex(fromBlock + maxBlockRange + 1n), + }, + ], + }); + }, + { timeout: 15_000 }, +); diff --git a/packages/utils/src/getLogsRetryHelper.ts b/packages/utils/src/getLogsRetryHelper.ts index cab85aa1e..1fdd32553 100644 --- a/packages/utils/src/getLogsRetryHelper.ts +++ b/packages/utils/src/getLogsRetryHelper.ts @@ -303,6 +303,24 @@ export const getLogsRetryHelper = ({ } as const; } + // coinbase + match = sError.match(/please limit the query to at most ([\d,.]+) blocks/); + if (match !== null) { + const ranges = chunk({ + params, + range: BigInt(match[1]!.replace(/[,.]/g, "")) - 1n, + }); + + if (isRangeUnchanged(params, ranges)) { + return { shouldRetry: false } as const; + } + + return { + shouldRetry: true, + ranges, + } as const; + } + // No match found return { shouldRetry: false,