diff --git a/codegenerator/cli/src/config_parsing/chain_helpers.rs b/codegenerator/cli/src/config_parsing/chain_helpers.rs index dc860a741..ed735e40f 100644 --- a/codegenerator/cli/src/config_parsing/chain_helpers.rs +++ b/codegenerator/cli/src/config_parsing/chain_helpers.rs @@ -313,7 +313,7 @@ pub enum Network { #[subenum(NetworkWithExplorer)] Taiko = 167000, - #[subenum(HypersyncNetwork, NetworkWithExplorer)] + #[subenum(NetworkWithExplorer)] Tangle = 5845, #[subenum(HypersyncNetwork, NetworkWithExplorer)] @@ -518,7 +518,7 @@ impl HypersyncNetwork { | ArbitrumOne | Merlin => Silver, Zora | MoonbaseAlpha | Morph | Kroma | Lukso | C1Milkomeda | Crab | Sophon | Flare - | PolygonZkevm | MevCommit | Tangle => Bronze, + | PolygonZkevm | MevCommit => Bronze, SophonTestnet | MorphTestnet | GaladrielDevnet | CitreaTestnet | Goerli | BscTestnet | UnichainSepolia | Zircuit | Celo | Opbnb | GnosisChiado diff --git a/codegenerator/cli/src/config_parsing/human_config.rs b/codegenerator/cli/src/config_parsing/human_config.rs index 55c508f31..65227a112 100644 --- a/codegenerator/cli/src/config_parsing/human_config.rs +++ b/codegenerator/cli/src/config_parsing/human_config.rs @@ -240,8 +240,10 @@ pub mod evm { CumulativeGasUsed, EffectiveGasPrice, GasUsed, + #[subenum(RpcTransactionField)] Input, Nonce, + #[subenum(RpcTransactionField)] Value, V, R, diff --git a/codegenerator/cli/templates/static/codegen/src/EventFetching.res b/codegenerator/cli/templates/static/codegen/src/EventFetching.res index 9e7cd26f4..05e22dbc8 100644 --- a/codegenerator/cli/templates/static/codegen/src/EventFetching.res +++ b/codegenerator/cli/templates/static/codegen/src/EventFetching.res @@ -1,3 +1,5 @@ +open Belt + exception QueryTimout(string) exception EventRoutingFailed @@ -31,6 +33,51 @@ let rec getKnownBlockWithBackoff = async (~provider, ~blockNumber, ~backoffMsOnF | result => result } +let getSuggestedBlockIntervalFromExn = { + let suggestedRangeRegExp = %re(`/retry with the range (\d+)-(\d+)/`) + + let blockRangeLimitRegExp = %re(`/limited to a (\d+) blocks range/`) + + exn => + switch exn { + | Js.Exn.Error(error) => + try { + // Didn't use parse here since it didn't work + // because the error is some sort of weird Ethers object + let message: string = (error->Obj.magic)["error"]["message"] + message->S.assertOrRaiseWith(S.string) + switch suggestedRangeRegExp->Js.Re.exec_(message) { + | Some(execResult) => + switch execResult->Js.Re.captures { + | [_, Js.Nullable.Value(fromBlock), Js.Nullable.Value(toBlock)] => + switch (fromBlock->Int.fromString, toBlock->Int.fromString) { + | (Some(fromBlock), Some(toBlock)) if toBlock >= fromBlock => + Some(toBlock - fromBlock + 1) + | _ => None + } + | _ => None + } + | None => + switch blockRangeLimitRegExp->Js.Re.exec_(message) { + | Some(execResult) => + switch execResult->Js.Re.captures { + | [_, Js.Nullable.Value(blockRangeLimit)] => + switch blockRangeLimit->Int.fromString { + | Some(blockRangeLimit) if blockRangeLimit > 0 => Some(blockRangeLimit) + | _ => None + } + | _ => None + } + | None => None + } + } + } catch { + | _ => None + } + | _ => None + } +} + type eventBatchQuery = { logs: array, latestFetchedBlock: Ethers.JsonRpcProvider.block, @@ -93,26 +140,37 @@ let getNextPage = ( [queryTimoutPromise, logsPromise] ->Promise.race ->Promise.catch(err => { - // Might fail with message "query exceeds max results 20000, retry with the range 6000438-6000934" - // Ideally to handle it and respect - - logger->Logging.childWarn({ - "msg": "Error getting events. Will retry after backoff time", - "backOffMilliseconds": sc.backoffMillis, - "suggestedBlockInterval": suggestedBlockInterval, - "err": err, - }) + switch getSuggestedBlockIntervalFromExn(err) { + | Some(nextBlockIntervalTry) => { + logger->Logging.childTrace({ + "msg": "Failed getting events for the block interval. Retrying with the block interval suggested by the RPC provider.", + "fromBlock": fromBlock, + "toBlock": fromBlock + nextBlockIntervalTry - 1, + "prevBlockInterval": suggestedBlockInterval, + }) + executeQuery(~suggestedBlockInterval=nextBlockIntervalTry) + } + | None => { + logger->Logging.childWarn({ + "msg": "Failed getting events for the block interval. Will retry after backoff time", + "backOffMilliseconds": sc.backoffMillis, + "prevBlockInterval": suggestedBlockInterval, + "err": err, + }) - Time.resolvePromiseAfterDelay(~delayMilliseconds=sc.backoffMillis)->Promise.then(_ => { - let nextBlockIntervalTry = - (suggestedBlockInterval->Belt.Int.toFloat *. sc.backoffMultiplicative)->Belt.Int.fromFloat - logger->Logging.childTrace({ - "msg": "Retrying query with a smaller block interval", - "fromBlock": fromBlock, - "toBlock": fromBlock + nextBlockIntervalTry - 1, - }) - executeQuery(~suggestedBlockInterval={nextBlockIntervalTry}) - }) + Time.resolvePromiseAfterDelay(~delayMilliseconds=sc.backoffMillis)->Promise.then(_ => { + let nextBlockIntervalTry = + (suggestedBlockInterval->Belt.Int.toFloat *. sc.backoffMultiplicative) + ->Belt.Int.fromFloat + logger->Logging.childTrace({ + "msg": "Retrying query with a smaller block interval", + "fromBlock": fromBlock, + "toBlock": fromBlock + nextBlockIntervalTry - 1, + }) + executeQuery(~suggestedBlockInterval={nextBlockIntervalTry}) + }) + } + } }) } diff --git a/scenarios/test_codegen/test/RpcWorker_test.res b/scenarios/test_codegen/test/RpcWorker_test.res index e2f625ce5..7a379f016 100644 --- a/scenarios/test_codegen/test/RpcWorker_test.res +++ b/scenarios/test_codegen/test/RpcWorker_test.res @@ -431,3 +431,105 @@ describe("RpcWorker - getSelectionConfig", () => { }, ) }) + +describe("RpcWorker - getSuggestedBlockIntervalFromExn", () => { + let getSuggestedBlockIntervalFromExn = EventFetching.getSuggestedBlockIntervalFromExn + + it("Should handle retry with the range", () => { + let error = JsError( + %raw(`{ + "code": "UNKNOWN_ERROR", + "error": { + "code": -32602, + "message": "query exceeds max results 20000, retry with the range 6000000-6000509" + }, + "payload": { + "method": "eth_getLogs", + "params": [ + { + "topics": [ + [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + ] + ], + "fromBlock": "0x5b8d80", + "toBlock": "0x5b9168" + } + ], + "id": 4, + "jsonrpc": "2.0" + }, + "shortMessage": "could not coalesce error" + + }`), + ) + + Assert.deepEqual(getSuggestedBlockIntervalFromExn(error), Some(510)) + }) + + it("Should ignore invalid range errors where toBlock is less than fromBlock", () => { + let error = JsError( + %raw(`{ + "code": "UNKNOWN_ERROR", + "error": { + "code": -32602, + "message": "query exceeds max results 20000, retry with the range 6000509-6000000" + }, + "payload": { + "method": "eth_getLogs", + "params": [ + { + "topics": [ + [ + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + ] + ], + "fromBlock": "0x5b8d80", + "toBlock": "0x5b9168" + } + ], + "id": 4, + "jsonrpc": "2.0" + }, + "shortMessage": "could not coalesce error" + + }`), + ) + + Assert.deepEqual(getSuggestedBlockIntervalFromExn(error), None) + }) + + it("Should handle block range limit from https://1rpc.io/eth", () => { + let error = JsError( + %raw(`{ + "code": "UNKNOWN_ERROR", + "error": { + "code": -32000, + "message": "eth_getLogs is limited to a 1000 blocks range" + }, + "payload": { + "method": "eth_getLogs", + "params": [ + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "topics": [ + [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" + ] + ], + "fromBlock": "0x5b8d80", + "toBlock": "0x5ba17f" + } + ], + "id": 18, + "jsonrpc": "2.0" + }, + "shortMessage": "could not coalesce error" + }`), + ) + + Assert.deepEqual(getSuggestedBlockIntervalFromExn(error), Some(1000)) + }) +})