Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

context.client.readContract occasionally returns 0x #1477

Open
donguks opened this issue Jan 31, 2025 · 2 comments
Open

context.client.readContract occasionally returns 0x #1477

donguks opened this issue Jan 31, 2025 · 2 comments

Comments

@donguks
Copy link

donguks commented Jan 31, 2025

Version

latest

Current behavior

  • When processing events in Ponder, a contract read function (readValue) is called using the event’s blockNumber to retrieve state at the time of the event.
  • Occasionally, the readValue function unexpectedly returns 0x instead of the expected stored value.
  • The issue seems to be more prevalent when using L2 networks (e.g., Op Stack).

Expected behavior

  • The readValue function should return the correct stored value at the exact block number where the ValueUpdated event was emitted.
  • The RPC provider should not return 0x unless the queried data is truly absent.
  • If the event has been emitted and indexed, the corresponding state update should be immediately available for read operations without requiring retries.
  • Ponder should ensure event processing does not occur before the L2 state update is fully committed.

Steps to reproduce

  1. Set Up Ponder Configuration: Define the network and contract in ponder.config.ts:
import { createConfig } from "ponder";
import { http } from "viem";

import { ContractAbi } from "./abis/ContractAbi";

export default createConfig({
  networks: {
    l2Network: {
      chainId: 1234,
      transport: http(process.env.PONDER_RPC_URL_1234),
    },
  },
  contracts: {
    MyContract: {
      network: "l2Network",
      abi: ContractAbi,
      address: "0xYourContractAddress",
      startBlock: 1000000,
    },
  },
});
  1. Implement Event Listener and Read Function: In src/index.ts, set up the event listener and call the read function
import { ponder } from "ponder:registry";

ponder.on("MyContract:ValueUpdated", async ({ event, context }) => {
  const { client } = context;
  const { MyContract } = context.contracts;

  try {
    // Call the read function at the block number where the event was emitted
    const value = await client.readContract({
      abi: MyContract.abi,
      address: MyContract.address,
      functionName: "readValue",
      args: [event.args.user],
    });

    console.log("Read value:", value);
  } catch (error) {
    console.error("Error reading contract:", error);
  }
});
  1. Deploy and Test:
  • Deploy the contract on the specified L2 network.
  • Trigger the ValueUpdated event.
  • Observe the output of the read function call.

Link to repository

No response

Anything else?

Possible Causes

  1. L2 Network State Finality : The L2 network’s state may not be fully finalized at the time of the read operation, leading to temporary unavailability of the latest state.
  2. RPC Provider Latency : Some RPC providers might lag behind in syncing L2 state, causing a delay in reflecting the most recent contract storage updates.
  3. Event Processing Timing Issue in Ponder : Ponder might process events before the associated state updates are fully committed, leading to inconsistent read results.

Questions

  • Is this a known issue with Ponder when interacting with L2 networks?
  • Are there any recommended strategies to ensure that read operations retrieve the correct state immediately after event processing?
  • Could this be related to how Ponder handles event processing and state synchronization on L2 networks?
@typedarray
Copy link
Collaborator

Hi, thanks for opening!

Questions:

  1. What chain and RPC provider are you experiencing this with?
  2. Does this only happen for requests in "realtime" / that are near the chain tip?

My gut says this has nothing to do with the chain being an L2, I think this problem can happen on any chain depending on the node software being used to serve the RPC request.

It's very difficult for the framework itself to know that a 0x response should be retried, because that's a totally valid response for an eth_call request. One possible fix here is to implement user-land retries - in your indexing function, if the response is 0x, wait ~1 second then try again, etc until you get the response you're looking for.

@donguks
Copy link
Author

donguks commented Feb 3, 2025

Hi, thanks for your reply!

  • Network & RPC Providers: I’m experiencing this issue on Optimism Sepolia using Ankr and Alchemy as RPC providers.
  • Occurrence: This mostly happens for requests in real-time, near the chain tip.

I understand that this might not be specifically related to the chain being an L2, but rather to the node software serving the RPC request. However, I’ve already implemented a retry mechanism where the request is retried 5 times with a 1-second delay between attempts, yet I still frequently get 0x responses.

Would you recommend any additional strategies to ensure reliable state reads in this scenario?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants