Skip to content

Conversation

@antoinedc
Copy link
Member

@antoinedc antoinedc commented Dec 27, 2025

User description

Summary

  • Batch RPC calls: Fetch multiple transaction receipts in a single JSON-RPC batch request instead of individual calls
  • Inline receipt fetching: For blocks with ≤10 transactions, fetch and store receipts inline in blockSync job (eliminates job queue overhead)
  • Parallel storage: Store multiple receipts concurrently with configurable concurrency limit (default: 5)
  • Workspace caching: Pass cached workspace data (rpcServer, rateLimitInterval, etc.) in receiptSync job payload to skip expensive DB lookups

Expected Impact

  • Real-time syncing: 2-5x faster for typical blocks (< 10 txs)
  • Historical syncing: 3-10x faster with batch RPC calls
  • Database load: Reduced by ~50% from cached workspace queries

Files Changed

  • run/lib/rpc.js: Added fetchTransactionReceiptsBatch() method with fallback to sequential
  • run/jobs/blockSync.js: Inline receipt fetching for small blocks, workspace caching for larger blocks
  • run/jobs/receiptSync.js: Use cached workspace data when available, lighter DB queries

Test plan

  • Verify blocks with ≤10 txs are synced inline (no receiptSync jobs created)
  • Verify blocks with >10 txs queue receiptSync jobs with cached workspace data
  • Verify batch RPC fallback works when batch request fails
  • Verify existing receiptSync jobs (without cached data) still work

🤖 Generated with Claude Code


CodeAnt-AI Description

Fetch and store transaction receipts in batches for faster block sync

What Changed

  • Blocks with ≤10 transactions now fetch receipts in a single batch RPC call and store them in parallel, so receipts appear faster without queuing extra jobs
  • Larger blocks queue receipt processing jobs that include cached workspace settings, so those jobs skip expensive workspace DB lookups
  • Receipt processing now accepts cached workspace data (used for rate-limiting, RPC server selection, and retry timing) and preserves that cache when requeueing; batch receipt requests fall back to individual requests if the batch fails

Impact

✅ Faster block sync for small blocks (receipts available without extra jobs)
✅ Fewer workspace DB queries during receipt processing
✅ Shorter time-to-receipt availability for typical blocks

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

- Add batch RPC method to fetch multiple receipts in single request
- Inline receipt fetching for small blocks (≤10 txs) to reduce latency
- Parallel receipt storage with configurable concurrency limit
- Cache workspace data in receiptSync job payload to skip DB lookups
- Fallback to sequential fetching if batch request fails

🤖 Generated with [Claude Code](https://claude.ai/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@codeant-ai
Copy link

codeant-ai bot commented Dec 27, 2025

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai bot added the size:L This PR changes 100-499 lines, ignoring generated files label Dec 27, 2025
@codeant-ai
Copy link

codeant-ai bot commented Dec 27, 2025

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Batch receipts ordering / fallback
    The code assumes the array returned by fetchTransactionReceiptsBatch matches the same index order as transactions.map(tx => tx.hash). If the RPC returns receipts out-of-order or omits some receipts, the index-based lookup will mis-associate receipts. Also there is no robust fallback if the batch call fails — a thrown error would fail the job. Consider mapping receipts by tx hash and adding a safe fallback path (sequential fetch or enqueue receipt jobs).

  • Batch response shape
    The code assumes the batch JSON-RPC response is an array and calls .sort() on it. If the provider returns a non-array (error object, single response, or null), .sort() will throw. Validate the response shape and handle errors per-item rather than assuming an array.

  • fetch availability & response handling
    The code uses global fetch and .json() without checking response.ok. In Node environments older than 18, fetch may be unavailable. Also non-2xx responses should be handled (inspect response.ok / status) before parsing body.

  • Provider URL access
    The code accesses provider internals to obtain the request URL (this.provider.connection and this.provider._getConnection()). Those properties/methods are implementation-specific and may be missing on some ethers Provider implementations, causing runtime exceptions. Confirm compatibility across supported provider types and add safe guards/fallbacks.

  • Missing error resilience for batch fetch
    fetchTransactionReceiptsBatch is called directly without try/catch. If it throws (even if rpc.js implements an internal fallback), the job can fail. Add error handling to gracefully retry, fall back to individual fetches, or enqueue receiptSync jobs when batch retrieval fails.

@codeant-ai
Copy link

codeant-ai bot commented Dec 27, 2025

PR Code Suggestions ✨

CategorySuggestion                                                                                                                                    Score
Logic error
Inline receipt syncing bypasses the public workspace check, allowing receipts to be synced for private workspaces contrary to existing behavior

The new inline receipt syncing path ignores the existing public workspace check
enforced in receiptSync, so private workspaces will now have receipts synced inline
even though the dedicated receipt job explicitly prevents syncing on private
workspaces, leading to inconsistent behavior and violating the intended access
control.

run/jobs/blockSync.js [182-187]

 // For blocks with few transactions, fetch receipts inline for lower latency
-if (transactions.length > 0 && transactions.length <= INLINE_RECEIPT_THRESHOLD) {
+// Only perform receipt syncing for public workspaces to match receiptSync behavior
+if (transactions.length > 0 && transactions.length <= INLINE_RECEIPT_THRESHOLD && workspace.public) {
     // Fetch all receipts in a single batch RPC request
     const receipts = await providerConnector.fetchTransactionReceiptsBatch(
         transactions.map(tx => tx.hash)
     );
 
Suggestion importance[1-10]: 10

Why it matters? 🤔: The queued receiptSync path caches workspace.public and presumably enforces the public check before syncing receipts. The inline path in this PR does not check workspace.public, so it changes behavior for private workspaces (they'd get receipts fetched/stored inline). That's an access/control inconsistency that can be a real bug. Adding the workspace.public guard aligns behavior and prevents unexpected privacy changes.

10
Batch JSON-RPC requests drop RPC basic auth credentials, causing unnecessary authentication failures and fallback to the slower path

For RPC URLs that use basic authentication, the batch receipt fetch currently uses
only connection.url without re-attaching the user/password from the provider
connection, causing unauthenticated HTTP requests that will typically be rejected
and force every batch call to fall back to slower sequential fetching instead of
benefiting from the optimization.

run/lib/rpc.js [259]

-const url = this.provider.connection ? this.provider.connection.url : this.provider._getConnection().url;
+const connection = this.provider.connection || this.provider._getConnection();
+let url = connection.url;
+if ((connection.user || connection.password) && url && !url.includes('@')) {
+    const parsedUrl = new URL(url);
+    if (connection.user) parsedUrl.username = connection.user;
+    if (connection.password) parsedUrl.password = connection.password;
+    url = parsedUrl.toString();
+}
 
Suggestion importance[1-10]: 10

Why it matters? 🤔: The code tries to use the provider's connection URL for a direct HTTP POST, but earlier in getProvider the authenticated connection may have been represented as an object { url, user, password } when credentials are present. Using only connection.url can cause unauthenticated requests if the provider uses basic auth and the credentials are not embedded in the url string. This will cause many batch attempts to fail and fall back to the slower sequential path — a real logic/regression risk. The suggested approach to include user/password when present (or otherwise attach proper auth headers) fixes this behavior.

10
Missing error propagation
Errors during inline receipt creation are swallowed after logging, so failed writes never trigger job failure or retries

In the inline receipt path, errors from safeCreateReceipt are caught and only logged
without being rethrown, so failed receipt writes will be silently ignored instead of
failing the job as they do in receiptSync, leading to silent data inconsistencies
and preventing retries.

run/jobs/blockSync.js [199-206]

 const processedReceipt = processRawRpcObject(
     receipt,
     Object.keys(TransactionReceipt.rawAttributes).concat(['logs'])
 );
 processedReceipt.workspace = workspace;
 return tx.safeCreateReceipt(processedReceipt).catch(err => {
     logger.error(`Failed to store receipt for ${tx.hash}`, { error: err.message });
+    // Propagate the error so the job can fail and be retried instead of silently skipping this receipt
+    throw err;
 });
 
Suggestion importance[1-10]: 10

Why it matters? 🤔: Currently the inline path logs safeCreateReceipt failures but swallows them, which means transient storage errors won't fail the job and won't be retried. That can lead to silent data loss/inconsistency. Propagating the error (or otherwise surfacing it) so the blockSync job can retry is a valid behavioral fix to make failure handling consistent with the queued receipt path.

10
Type error
Using a minimal workspace stub without orbit data causes orbit-related receipt processing to crash when it iterates over missing properties

When using cached workspace data, processedReceipt.workspace is set to a minimal
object { id: workspaceId }, but Transaction.safeCreateReceipt expects
receipt.workspace.orbitConfig and iterates over receipt.workspace.orbitChildConfigs,
so for cached jobs this will make orbitChildConfigs undefined and cause a runtime
TypeError when the orbit-processing loop runs. The fix is to ensure that, even in
the cached-workspace path, processedReceipt.workspace includes proper orbit-related
fields (or at least safe defaults) by fetching the workspace with its orbit
associations or providing a fallback object with an empty orbitChildConfigs array.

run/jobs/receiptSync.js [161-167]

 // For safeCreateReceipt, we need to pass workspace context for orbit processing
 // When using cached data, construct a minimal workspace-like object
 if (hasCachedWorkspace) {
-    processedReceipt.workspace = { id: data.workspaceId };
+    const workspace = await Workspace.findByPk(data.workspaceId, { include: ['orbitConfig', 'orbitChildConfigs'] });
+    processedReceipt.workspace = workspace || { id: data.workspaceId, orbitConfig: null, orbitChildConfigs: [] };
 } else {
     processedReceipt.workspace = transaction.workspace;
 }
 
Suggestion importance[1-10]: 10

Why it matters? 🤔: The suggestion flags a real runtime issue. In Transaction.safeCreateReceipt the code assumes receipt.workspace.orbitChildConfigs exists and immediately iterates over it:
for (const orbitChildConfig of receipt.workspace.orbitChildConfigs) { ... }
If processedReceipt.workspace is a minimal object with only { id } then receipt.workspace.orbitChildConfigs is undefined and that loop will throw a TypeError. I verified the iteration exists in run/models/transaction.js (around the orbit processing section), and receiptSync.js currently sets the minimal workspace stub only when cached workspace data is used. Adding orbit-related fields (or an explicit empty array) for the cached path prevents the TypeError and fixes a real bug.

10

@codeant-ai
Copy link

codeant-ai bot commented Dec 27, 2025

CodeAnt AI finished reviewing your PR.

Antoine de Chevigné and others added 2 commits December 27, 2025 17:24
- Add fetch availability check with graceful fallback to sequential
- Handle authenticated RPC URLs in batch fetch with proper auth headers
- Skip workspace caching for orbit workspaces to preserve full context
- Add unit tests for inline receipt fetching, cached workspace, and orbit handling
- Update mock to include fetchTransactionReceiptsBatch

🤖 Generated with [Claude Code](https://claude.ai/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Only perform inline receipt fetching for public workspaces to match
the access control behavior in receiptSync. Private workspaces will
queue jobs that get properly rejected by receiptSync.

🤖 Generated with [Claude Code](https://claude.ai/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@codeant-ai
Copy link

codeant-ai bot commented Dec 28, 2025

CodeAnt AI is running Incremental review


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants