Repository: ponder-sh/ponder
Pull request #1675 (2025-04-15 20:53:47)

skip rpc retries for reverted requests


Patch:
diff --git a/.changeset/old-gifts-exist.md b/.changeset/old-gifts-exist.md
new file mode 100644
index 00000000..8da67c82
--- /dev/null
+++ b/.changeset/old-gifts-exist.md
@@ -0,0 +1,5 @@
+---
+"ponder": patch
+---
+
+Improve rpc request performance by skipping retries for reverted requests.
diff --git a/packages/core/src/utils/requestQueue.ts b/packages/core/src/utils/requestQueue.ts
index 6ae69e32..1180ce70 100644
--- a/packages/core/src/utils/requestQueue.ts
+++ b/packages/core/src/utils/requestQueue.ts
@@ -177,6 +177,8 @@ function shouldRetry(error: Error) {
     if (error.code === MethodNotSupportedRpcError.code) return false;
     // Version of JSON-RPC protocol is not supported
     if (error.code === JsonRpcVersionUnsupportedError.code) return false;
+    // eth_call reverted
+    if (error.message.includes("revert")) return false;
   }
   if (error instanceof HttpRequestError && error.status) {
     // Method Not Allowed


Test patch:
diff --git a/packages/core/src/_test/contracts/src/Revert.sol b/packages/core/src/_test/contracts/src/Revert.sol
new file mode 100644
index 00000000..a092376b
--- /dev/null
+++ b/packages/core/src/_test/contracts/src/Revert.sol
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: UNLICENSED
+pragma solidity >=0.8.0;
+
+
+contract Revert {
+    error e();
+    function revert(bool revert) external {
+        if (revert) revert e();
+    }
+}
\ No newline at end of file
diff --git a/packages/core/src/_test/simulate.ts b/packages/core/src/_test/simulate.ts
index 31ae527b..66769547 100644
--- a/packages/core/src/_test/simulate.ts
+++ b/packages/core/src/_test/simulate.ts
@@ -8,7 +8,8 @@ import {
 } from "viem";
 import Erc20Bytecode from "./contracts/out/ERC20.sol/ERC20.json";
 import FactoryBytecode from "./contracts/out/Factory.sol/Factory.json";
-import { erc20ABI, factoryABI, pairABI } from "./generated.js";
+import RevertBytecode from "./contracts/out/Revert.sol/Revert.json";
+import { erc20ABI, factoryABI, pairABI, revertABI } from "./generated.js";
 import { anvil, publicClient, testClient } from "./utils.js";
 
 /** Deploy Erc20 contract and mine block. */
@@ -54,6 +55,27 @@ export const deployFactory = async (params: { sender: Address }) => {
   return { address: contractAddress!, hash };
 };
 
+/** Deploy Revert contract and mine block. */
+export const deployRevert = async (params: { sender: Address }) => {
+  const walletClient = createWalletClient({
+    chain: anvil,
+    transport: http(),
+    account: params.sender,
+  });
+
+  const hash = await walletClient.deployContract({
+    abi: revertABI,
+    bytecode: RevertBytecode.bytecode.object as Hex,
+  });
+
+  await testClient.mine({ blocks: 1 });
+  const { contractAddress } = await publicClient.waitForTransactionReceipt({
+    hash,
+  });
+
+  return { address: contractAddress!, hash };
+};
+
 export const deployMulticall = async (params: { sender: Address }) => {
   const walletClient = createWalletClient({
     chain: anvil,
diff --git a/packages/core/src/sync/transport.test.ts b/packages/core/src/sync/transport.test.ts
index bc461581..697ada16 100644
--- a/packages/core/src/sync/transport.test.ts
+++ b/packages/core/src/sync/transport.test.ts
@@ -1,5 +1,5 @@
 import { ALICE } from "@/_test/constants.js";
-import { erc20ABI } from "@/_test/generated.js";
+import { erc20ABI, revertABI } from "@/_test/generated.js";
 import {
   setupAnvil,
   setupCleanup,
@@ -7,7 +7,12 @@ import {
   setupDatabaseServices,
   setupIsolatedDatabase,
 } from "@/_test/setup.js";
-import { deployErc20, deployMulticall, mintErc20 } from "@/_test/simulate.js";
+import {
+  deployErc20,
+  deployMulticall,
+  deployRevert,
+  mintErc20,
+} from "@/_test/simulate.js";
 import { anvil, getNetwork, publicClient } from "@/_test/utils.js";
 import { createRequestQueue } from "@/utils/requestQueue.js";
 import {
@@ -342,3 +347,41 @@ test("request() multicall empty", async (context) => {
 
   expect(result).toMatchInlineSnapshot("[]");
 });
+
+test("request() revert", async (context) => {
+  const network = getNetwork();
+  const requestQueue = createRequestQueue({
+    network,
+    common: context.common,
+  });
+
+  const { address } = await deployRevert({ sender: ALICE });
+
+  const { syncStore } = await setupDatabaseServices(context);
+
+  const transport = cachedTransport({
+    requestQueue,
+    syncStore,
+  })({
+    chain: anvil,
+  });
+
+  const response1 = await transport
+    .request({
+      method: "eth_call",
+      params: [
+        {
+          to: address,
+          data: encodeFunctionData({
+            abi: revertABI,
+            functionName: "revert",
+            args: [true],
+          }),
+        },
+        "0x1",
+      ],
+    })
+    .catch((error) => error);
+
+  expect(response1).toBeInstanceOf(Error);
+});