-
Notifications
You must be signed in to change notification settings - Fork 0
feat: entry point service private event support #6
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
base: feat/private-event-utilities
Are you sure you want to change the base?
feat: entry point service private event support #6
Conversation
There was a problem hiding this 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
SDAuthHeaderstype and extraction utility to handle Silent Data authentication headers - Modified
getUserOperationEvent()to query forPrivateEventlogs filtered by event type, unwrap private payloads, and validate against the requesteduserOpHash - Updated method signatures across the service layer to propagate
SDAuthHeadersfrom API endpoints through to the entry point service - Added validation to require Silent Data auth headers for
eth_getUserOperationReceiptandeth_getUserOperationByHashRPC 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.
| const { | ||
| 'x-from-block': headerFromBlock, | ||
| ...headersToForward | ||
| } = sdAuthHeaders |
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
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.
| headers: Object | ||
| .entries(headersToForward) | ||
| .reduce((headersToSend, [key, value]) => { | ||
| if (value !== undefined) { | ||
| headersToSend[key] = value | ||
| } | ||
| return headersToSend | ||
| }, {} as Record<string, string>), |
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
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.
| 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) | |
| ), |
| } | ||
| } catch (error) { | ||
| this.logger.error( | ||
| `Failed to unwrap PrivateEvent for ${UserOperationEventAbi.name}: ${error instanceof Error ? error.message : JSON.stringify(error)}` |
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
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.
| `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)}` |
| } | ||
| } | ||
| const rpcUserOp = await this.entryPointService.getUserOperationByHash(hash); | ||
| // Silent Data auth is checked in the chain RPC, mempool check not supported |
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
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').
| // 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 |
| if (!sdAuthHeaders) { | ||
| throw new RpcError( | ||
| "Missing Silent Data auth headers", | ||
| RpcErrorCodes.METHOD_NOT_FOUND | ||
| ); | ||
| } |
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
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.
| request, | ||
| req.ip, | ||
| req.headers.authorization | ||
| // Silent Data auth headers not supported for websocket requests |
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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]) => { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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.
Description
This PR integrates private event support into the EntryPointService's
getUserOperationEventmethod, enabling it to retrieve user operation events from private event logs.Key Changes:
getUserOperationEvent()to query forPrivateEventeventTypeto only retrieveUserOperationEventemissionsuserOpHashmatches the requested operationeventType, to locate the requested user operation eventImplementation Details:
utils/abi-eventsuserOpHashTypes of changes
What types of changes does your code introduce?
Put an
xin the boxes that applyUserOperationEventFurther comments (optional)
PrivateEvents