Skip to content

Conversation

@zeluisping
Copy link

@zeluisping zeluisping commented Oct 21, 2025

Description

This PR integrates private event support into the EntryPointService's getUserOperationEvent method, enabling it to retrieve user operation events from private event logs.

Key Changes:

  • Private Event Querying: Updated getUserOperationEvent() to query for PrivateEvent
  • Event Filtering: Filters private events by eventType to only retrieve UserOperationEvent emissions
  • Event Unwrapping: Unwraps private event payloads and validates the userOpHash matches the requested operation
  • Search Behaviour: Checks for private events, filtering by eventType, to locate the requested user operation event
  • Error Handling: Added error logging for private event unwrapping failures without breaking the flow

Implementation Details:

  • Uses the centralised event ABI definitions and unwrapping utilities from utils/abi-events
  • Each private log is validated, unwrapped, and checked against the target userOpHash
  • Returns the first matching unwrapped event, maintaining the same return type as the public event path
  • Failed unwrapping attempts are logged, but don't prevent checking remaining logs

Types of changes

What types of changes does your code introduce?
Put an x in the boxes that apply

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
    • Removes support for public events of UserOperationEvent
  • Documentation Update
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • Other (please describe):

Further comments (optional)

  • Removed support for the public events that were privatised; these are now handled solely as PrivateEvents

@zeluisping zeluisping requested a review from seromenho October 21, 2025 11:44
@zeluisping zeluisping self-assigned this Oct 21, 2025
@zeluisping zeluisping added the enhancement New feature or request label Oct 21, 2025
@seromenho seromenho requested a review from Copilot November 12, 2025 18:13
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR integrates private event support into the EntryPointService, enabling retrieval of user operation events from private event logs. The implementation replaces public UserOperationEvent querying with PrivateEvent handling throughout the bundler's RPC methods, while introducing Silent Data authentication header support.

Key Changes:

  • Added SDAuthHeaders type and extraction utility to handle Silent Data authentication headers
  • Modified getUserOperationEvent() to query for PrivateEvent logs filtered by event type, unwrap private payloads, and validate against the requested userOpHash
  • Updated method signatures across the service layer to propagate SDAuthHeaders from API endpoints through to the entry point service
  • Added validation to require Silent Data auth headers for eth_getUserOperationReceipt and eth_getUserOperationByHash RPC methods

Reviewed Changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/types/src/api/interfaces.ts Introduces SDAuthHeaders type definition and pickSDAuthHeaders extraction utility
packages/executor/src/services/EntryPointService/versions/base.ts Updates interface signatures to include sdAuthHeaders parameter
packages/executor/src/services/EntryPointService/versions/0.0.7.ts Implements private event querying with header forwarding, event unwrapping, and validation logic
packages/executor/src/services/EntryPointService/service.ts Propagates sdAuthHeaders through service method calls
packages/executor/src/modules/eth.ts Updates method signatures and comments out mempool checks due to auth requirements
packages/api/src/modules/eth.ts Adds sdAuthHeaders parameter to API module methods
packages/api/src/app.ts Extracts Silent Data headers from requests and enforces their presence for specific RPC methods

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +256 to +259
const {
'x-from-block': headerFromBlock,
...headersToForward
} = sdAuthHeaders
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The x-from-block header is extracted and excluded from forwarding, but if it's undefined, headerFromBlock will be undefined. This could lead to unexpected behavior in the subsequent logic. Consider adding explicit undefined handling or documentation explaining the expected behavior when this header is absent.

Copilot uses AI. Check for mistakes.
Comment on lines +288 to +295
headers: Object
.entries(headersToForward)
.reduce((headersToSend, [key, value]) => {
if (value !== undefined) {
headersToSend[key] = value
}
return headersToSend
}, {} as Record<string, string>),
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] This reduce operation to filter undefined values could be simplified using Object.fromEntries with filter: Object.fromEntries(Object.entries(headersToForward).filter(([_, value]) => value !== undefined)). This would improve readability and reduce code complexity.

Suggested change
headers: Object
.entries(headersToForward)
.reduce((headersToSend, [key, value]) => {
if (value !== undefined) {
headersToSend[key] = value
}
return headersToSend
}, {} as Record<string, string>),
headers: Object.fromEntries(
Object.entries(headersToForward)
.filter(([_, value]) => value !== undefined)
),

Copilot uses AI. Check for mistakes.
}
} catch (error) {
this.logger.error(
`Failed to unwrap PrivateEvent for ${UserOperationEventAbi.name}: ${error instanceof Error ? error.message : JSON.stringify(error)}`
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message includes UserOperationEventAbi.name, but if the ABI object doesn't have a name property or it's undefined, this will log 'undefined'. Consider adding a fallback value like UserOperationEventAbi.name || 'UserOperationEvent' to ensure a meaningful error message.

Suggested change
`Failed to unwrap PrivateEvent for ${UserOperationEventAbi.name}: ${error instanceof Error ? error.message : JSON.stringify(error)}`
`Failed to unwrap PrivateEvent for ${UserOperationEventAbi.name || 'UserOperationEvent'}: ${error instanceof Error ? error.message : JSON.stringify(error)}`

Copilot uses AI. Check for mistakes.
}
}
const rpcUserOp = await this.entryPointService.getUserOperationByHash(hash);
// Silent Data auth is checked in the chain RPC, mempool check not supported
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Corrected capitalization: 'auth' should be capitalized as 'Auth' to match the naming convention used elsewhere in the codebase (e.g., 'SDAuthHeaders').

Suggested change
// Silent Data auth is checked in the chain RPC, mempool check not supported
// Silent Data Auth is checked in the chain RPC, mempool check not supported

Copilot uses AI. Check for mistakes.
Comment on lines +300 to +305
if (!sdAuthHeaders) {
throw new RpcError(
"Missing Silent Data auth headers",
RpcErrorCodes.METHOD_NOT_FOUND
);
}
Copy link

Copilot AI Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using METHOD_NOT_FOUND error code for missing authentication headers is semantically incorrect. This error code typically indicates the RPC method doesn't exist. Consider using INVALID_REQUEST or creating a specific authentication error code like UNAUTHORIZED to accurately represent the authentication failure.

Copilot uses AI. Check for mistakes.
request,
req.ip,
req.headers.authorization
// Silent Data auth headers not supported for websocket requests

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block code is not related to websockets but to batch requests.
I'm not sure if the bundler supports batch requests well in the custom RPC, but we should still extract sdAuthHeaders before the loop and pass them to each handleRpcRequest call. This ensures batch requests work with private event methods if they're supported.

case BundlerRPCMethods.eth_getUserOperationReceipt:
result = await this.ethApi.getUserOperationReceipt(params[0]);
if (!sdAuthHeaders) {
throw new RpcError(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a new error code (e.g., MISSING_AUTH_HEADERS = -32604) instead of METHOD_NOT_FOUND. The error message should be: "Silent Data authentication headers are required for this method. Please include x-timestamp and x-signature (or x-eip712-signature) headers in your request."

hash: string,
sdAuthHeaders: SDAuthHeaders,
): Promise<UserOperationByHashResponse | null> {
const entry = await this.mempoolService.getEntryByHash(hash);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one! We are skipping the mempool because it's up to the Custom RPC to decide on the Auth 👍

fetchOptions: {
headers: Object
.entries(headersToForward)
.reduce((headersToSend, [key, value]) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very minor: consider using Object.fromEntries with filter for better readability.

}
case BundlerRPCMethods.eth_getUserOperationReceipt:
result = await this.ethApi.getUserOperationReceipt(params[0]);
if (!sdAuthHeaders) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of just checking if (!sdAuthHeaders), create a validation method that checks for required headers. At minimum, either x-signature OR x-eip712-signature must be present, and x-timestamp should be present.

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants