Skip to content

[evm]: Select UserOpHash instead of solver address in solver selection #816

Closed

Description

@seunlanlege

Background

Currently, solver selection authorizes a solver address — the session key signs SelectSolver(bytes32 commitment, address solver), and fillOrder checks msg.sender == stored solver. This means the session key's authorization is bound to a solver identity, but not to the specific UserOperation being executed.

Proposed Change

Replace address solver with bytes32 userOpHash in the SelectSolver EIP-712 struct. The session key would sign over the specific UserOperation hash, creating a tighter binding — a solver couldn't reuse a session signature with different calldata or gas parameters.

Blockers

  1. fillOrder needs to verify the currently executing userOpHash against the stored value. IEntryPoint.getCurrentUserOpHash() is only available in EntryPoint v0.9. The move to v0.9 is tracked in the EntryPoint v0.9 milestone: [evm]: migrate SolverAccount to EntryPoint v0.9 #1366 for SolverAccount, [evm]: migrate SimplexPaymaster to EntryPoint v0.9 #1365 for SimplexPaymaster, [sdk, simplex]: sign and submit bids against EntryPoint v0.9 #1367 for the SDK and simplex, and [indexer]: index fills executed through EntryPoint v0.9 #1368 for the indexer.

The Circle paymaster's limit to v0.7 and v0.8 no longer blocks this. Bids are sponsored by our own SimplexPaymaster, which moves to v0.9 in #1365.

Action

Ship in the EntryPoint v0.9 milestone, with or after the migrations above. #1366 asks whether this goes out in the same SolverAccount deployment, since every new deployment means each solver re-delegates.

Affected Files

  • evm/src/apps/intentsv2/IntentsBase.sol — SELECT_SOLVER_TYPEHASH, _select
  • evm/src/apps/IntentGatewayV2.sol — fillOrder solver check
  • evm/src/apps/intentsv2/SolverAccount.sol — validateUserOp
  • sdk/packages/core/contracts/apps/IntentGatewayV2.sol — SelectOptions struct
  • sdk/packages/sdk/src/protocols/intents/CryptoUtils.ts — signSolverSelection
  • sdk/packages/sdk/src/protocols/intents/Bid.ts — signs the selection for a bid
  • sdk/packages/sdk/src/protocols/intents/BidManager.ts — selectBid

Activity

  1. changed the title [-]Select userOpHash instead of solver address in solver selection[/-] [+][evm]: Select UserOpHash instead of solver address in solver selection[/+] on May 9, 2026
  2. Psalmuel01 commented on May 23, 2026

    @Psalmuel01

    I'd like to work on this issue.

    The current SelectSolver(bytes32 commitment, address solver) flow binds authorization to a solver identity, but not to the specific UserOperation being executed. Replacing address solver with bytes32 userOpHash would tighten the authorization scope and prevent reuse of solver selection signatures across modified calldata or gas configurations.

    Proposed Approach

    Solidity Changes

    IntentsBase.sol

    • Update SELECT_SOLVER_TYPEHASH:

      keccak256("SelectSolver(bytes32 commitment,bytes32 userOpHash)")
    • Refactor _select() to accept/store userOpHash instead of solver address

    IntentGatewayV2.sol

    • Replace solver identity checks in fillOrder() with IEntryPoint.getCurrentUserOpHash()
    • Verify the currently executing userOpHash matches the stored authorization

    SolverAccount.sol

    • Update validateUserOp() flow to compute/pass the relevant userOpHash

    SDK Contract Types

    • Update SelectOptions:

      address solver -> bytes32 userOpHash

    SDK Updates

    CryptoUtils.ts

    • Update SELECT_SOLVER_TYPEHASH
    • Refactor signSolverSelection() to sign userOpHash
    • Update ABI encoding from address → bytes32

    BidManager.ts

    • Update selectBid() to pass userOpHash through the signing flow

    Testing

    I also plan to add regression tests covering:

    • EIP-712 encoding parity across Solidity/SDK implementations
    • Signature reuse attempts with modified calldata/gas parameters
    • Verification that mismatched UserOperations fail authorization checks

    Current Blocker

    The main dependency here is EntryPoint v0.9, since getCurrentUserOpHash() is only available there. Current Circle paymaster support appears limited to v0.7/v0.8, so this likely needs to land alongside (or after) the v0.9 migration.

    A few questions before I begin:

    • Is there already a planned timeline for the EntryPoint v0.9 upgrade?
    • Would you prefer a preparatory refactor PR now, with the execution binding logic gated until v0.9?
    • Are there existing v0.9 mocks/test branches I should build against?

    Happy to take this on once direction around the v0.9 migration is clarified.

  3. added this to the EntryPoint v0.9 milestone on Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions