Skip to content

chore: wrapper simulation requires manual per-protocol hardcoding - no general mechanism exists #4458

Description

@dymchenkko

Background

CoW Protocol's wrapper feature lets users trade tokens locked in external DeFi protocols (like Euler) without moving them first. Before accepting such an order, CoW runs a quote simulation on a forked copy of the chain to verify the price is accurate. Wrapper contracts depend on state inside the external protocol's contracts - on the real chain that state exists, but on the simulation fork it doesn't, so the simulation fails unless CoW manually injects the right storage slot values first.

PR #4409 solved this for Euler by reverse-engineering its internal storage layout and hardcoding the slot computation into compute_euler_override in simulation_builder.rs. The call site at line 221 carries an explicit TODO: REMOVE THIS HACK! comment. The PR itself said: "Obviously this hack should be reverted ASAP. The long term solution likely requires us to allow users to pass state overrides with their request to make a quote verifiable." That long-term solution was never implemented.

Without a general mechanism, adding any new wrapper integration or even adding a new contract address to an existing one requires a CoW developer to study the protocol's internal storage layout, identify which slots need to be set, and hardcode the computation into simulation_builder.rs. If Euler deploys a new wrapper contract, compute_euler_override silently returns empty overrides for unknown addresses and simulation fails.

Details

Add an optional simulation_overrides field to WrapperCall in crates/app-data/src/app_data.rs. The field would be a simple map of address -> storage slots, a plain data type defined in app-data itself (it cannot reuse simulator types due to the simulator -> app-data crate dependency direction).

Each wrapper integration pre-computes the overrides it needs and embeds them in the app data alongside the wrapper address and calldata, either via the external protocol's SDK or a CoW-owned integration layer. The simulator reads the field in parameters_from_app_data and applies the overrides with no knowledge of any specific protocol.

One tradeoff: this moves trust from CoW (which currently controls what gets injected) to the order submitter. A malicious override could make simulation pass while the on-chain transaction fails - a DoS concern (wasted solver compute) rather than a funds risk. It is worth bounding or validating the field size at order submission time.

Acceptance criteria

  • WrapperCall in crates/app-data/src/app_data.rs has an optional simulation_overrides field (address -> storage slots map, defaults to empty)
  • parameters_from_app_data reads and applies simulation_overrides when present
  • compute_euler_override is kept as a fallback for existing orders that do not have simulation_overrides set (backwards compatibility)
  • compute_euler_override is deleted once the Euler integration is updated to include pre-computed overrides in WrapperCall
  • No changes required to simulation_builder.rs to support a new wrapper protocol or a new Euler contract address

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions