Skip to content

Custom order simulation#4304

Open
m-sz wants to merge 106 commits intomainfrom
custom-order-simulation
Open

Custom order simulation#4304
m-sz wants to merge 106 commits intomainfrom
custom-order-simulation

Conversation

@m-sz
Copy link
Copy Markdown
Contributor

@m-sz m-sz commented Apr 1, 2026

Description

The order simulation endpoint allows one to simulate a specific, existing order. It is useful to be able to simulate any, custom order.

Changes

Implements a POST endpoint /api/v1/debug/simulation that allows a request specifying custom order details to be simulated:

pub struct SimulationRequest {
    pub sell_token: Address,
    pub buy_token: Address,
    #[serde_as(as = "HexOrDecimalU256")]
    pub sell_amount: alloy::primitives::U256,
    #[serde_as(as = "HexOrDecimalU256")]
    pub buy_amount: alloy::primitives::U256,
    pub kind: OrderKind,
    pub owner: Address,
    #[serde(default)]
    pub receiver: Option<Address>,
    #[serde(default)]
    pub sell_token_balance: SellTokenSource,
    #[serde(default)]
    pub buy_token_balance: BuyTokenDestination,
    /// Full app data JSON. Defaults to `"{}"` if omitted.
    #[serde(default)]
    pub app_data: Option<String>,
    #[serde(default)]
    pub interactions: Interactions,
}
  • Add exhaustive order details in a follow-up PR.

How to test

E2E test that runs a custom order simulation on a trader with no funds (it reverts), then account is funded and the same request passes.

m-sz and others added 30 commits March 12, 2026 18:02
Contact and Token addresses now are private structs containing Address.
Implemented Deref for them and updated usages.
Contact and Token addresses now are private structs containing Address.
Implemented Deref for them and updated usages.
Adds dependency on simulator for solver
These can be added as post-processing step at the callsite.

Reverted invalid contracts changes
Addressed comments
Co-authored-by: José Duarte <duarte.gmj@gmail.com>
@m-sz m-sz marked this pull request as ready for review April 1, 2026 14:50
@m-sz m-sz requested a review from a team as a code owner April 1, 2026 14:50
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new debug endpoint, POST /api/v1/debug/simulation, which allows for the simulation of arbitrary orders without requiring them to be stored in the database. The changes include updates to the OpenAPI specification, the addition of a new request handler in the orderbook API, and a corresponding simulation method in the core orderbook logic. Feedback focuses on the SimulationRequest schema, which is currently missing critical fields like validTo, partiallyFillable, and feeAmount that are necessary for realistic simulations. Additionally, the order simulation logic needs to ensure internal consistency by correctly hashing appData and calculating the OrderUid before processing the swap.

@m-sz m-sz mentioned this pull request Apr 1, 2026
3 tasks
@m-sz m-sz force-pushed the block-number-order-simulation branch from ccad001 to 790253a Compare April 1, 2026 16:19
@m-sz m-sz force-pushed the custom-order-simulation branch from a9b4cf4 to 098f938 Compare April 1, 2026 16:24
@m-sz m-sz force-pushed the custom-order-simulation branch from 098f938 to 031704f Compare April 1, 2026 16:53
github-merge-queue bot pushed a commit that referenced this pull request Apr 1, 2026
# Description
Based on #4265

Adds a /api/v1/debug/simulation/{uid} endpoint to the orderbook that
lets you simulate an order's execution via Tenderly.

# Changes
- debug_simulation.rs — new API handler that accepts an order UID and
returns the Tenderly simulation request + any error
- Orderbook::simulate_order() — fetches the order, encodes it as a swap,
simulates it, and returns the Tenderly request payload
- OrderSimulator struct — wraps SwapSimulator with chain ID context;
handles encoding orders into EncodedSwap and running simulations

### Notice
**Simulation runs against the current block (not a historical replay at
the block the order was filled), and that it uses the original order
amounts — so for partially filled orders, the full amount is simulated
against the trader's current balance.**

### Follow-up PR
- [ ] Add support to simulate on specific block number
#4305
- [ ] Add support for partially filled orders.
- [ ] Custom order simulation (not an existing one)
#4304

Encoding improvements in simulator:
- InteractionEncoding trait extracted so both Interaction and
InteractionData (from model crate) can be encoded — this allows order
pre/post interactions to be encoded directly without manual conversion
- WrapperCall struct + encode_wrapper_settlement() function added to
handle settlements that go through wrapper contracts
- SwapSimulator::fake_swap() extended to support wrapper contracts in
the query

Supporting changes:

- app-data: added wrappers() accessor to expose wrapper calls from app
data
- configs/orderbook: added config for the new simulator (optional —
order_simulation_gas_limit: None keeps existing behavior)
- price-estimation: refactored to use the new InteractionEncoding trait
- driver/encoding: cleanup — moved some encoding logic to the simulator
crate

The OpenAPI spec has been updated with:

Path `/api/v1/debug/simulation/{uid}` — GET endpoint that takes an order
UID and returns 200 (OrderSimulation), 404 (not found), 501 (not
enabled), or 500 (internal error).

New schemas:

- OrderSimulation — top-level response with tenderly_request and
optional error
- TenderlyRequest — the full Tenderly simulation request structure
- SimulationType — enum full | quick | abi: 
- full (default): Detailed decoded output — call trace, function,
inputs/outputs, state diffs, and logs with Solidity types.
  - quick: Raw, minimal output only. Fastest option; no decoding.
- abi: Decoded function inputs/outputs and logs, but no state diffs.
Middle ground between quick and full.
AccessListItem — address + storage_keys
StateObject — balance/code/storage overrides

## How to test
Create order in local playground, query for its simulation. Verify on
tenderly.

Example output:
```json
{
    "tenderly_request": {
        "network_id": "1",
        "from": "0xfcc789354262dd9c2f2ff1b0a5f9067b55af1bfa",
        "to": "0xa513e6e4b8f2a923d98304ec87f64353c4d5c853",
        "input": "0x13d79a0b000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000000020000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa300000000000000000000000068b1d87f95878fe05b998f19b66f4baba5de1aed00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000009315fc8ffae493123b9b6b1c93d50c9b9eef03440000000000000000000000000000000000000000000000001bc16d674ec800000000000000000000000000000000000000000000000000000de0b6b3a764000000000000000000000000000000000000000000000000000000000000ffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000149315fc8ffae493123b9b6b1c93d50c9b9eef03440000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
        "gas": 16777215,
        "simulation_type": "full",
        "save": true,
        "save_if_fails": true,
        "state_objects": {
            "0x68b1d87f95878fe05b998f19b66f4baba5de1aed": {
                "storage": {
                    "0xd7a1f0b46140686e5b4c405dcbec93c326c8c0e52cb615e05dcf53ad4119c720": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000"
                }
            },
            "0x9315fc8ffae493123b9b6b1c93d50c9b9eef0344": {
                "code": "0x608060405260043610610037575f3560e01c80631626ba7e1461008d578063542eb77d14610104578063eb5625d9146101255761003e565b3661003e57005b5f6100835f368080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525062010000939250506101449050565b9050805160208201f35b348015610098575f5ffd5b506100cf6100a7366004610b01565b7f1626ba7e000000000000000000000000000000000000000000000000000000009392505050565b6040517fffffffff00000000000000000000000000000000000000000000000000000000909116815260200160405180910390f35b34801561010f575f5ffd5b5061012361011e366004610b9c565b6101c2565b005b348015610130575f5ffd5b5061012361013f366004610c00565b610916565b60605f8373ffffffffffffffffffffffffffffffffffffffff168360405161016c9190610c3e565b5f60405180830381855af49150503d805f81146101a4576040519150601f19603f3d011682016040523d82523d5f602084013e6101a9565b606091505b5092509050806101bb57815160208301fd5b5092915050565b7f02565dba7d68dcbed629110024b7b5e785bfc1a484602045eea513de8a2dcf99805460017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082161790915560ff16156102a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f70726570617265537761702063616e206f6e6c792062652063616c6c6564206f60448201527f6e6365000000000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16036103e4576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8616906370a0823190602401602060405180830381865afa158015610340573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103649190610c6a565b9050838110156103e2575f6103798286610c81565b90508047106103e0578373ffffffffffffffffffffffffffffffffffffffff1663d0e30db0826040518263ffffffff1660e01b81526004015f604051808303818588803b1580156103c8575f5ffd5b505af11580156103da573d5f5f3e3d5ffd5b50505050505b505b505b5f8573ffffffffffffffffffffffffffffffffffffffff16639b552cc26040518163ffffffff1660e01b8152600401602060405180830381865afa15801561042e573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104529190610cb9565b6040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff80831660248301529192505f9187169063dd62ed3e90604401602060405180830381865afa1580156104c7573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104eb9190610c6a565b905084811015610748576040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8088166004830152831660248201525f6044820152309063eb5625d9906064015f604051808303815f87803b158015610567575f5ffd5b505af1925050508015610578575060015b506040517feb5625d900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8088166004830152831660248201527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6044820152309063eb5625d9906064015f604051808303815f87803b15801561060b575f5ffd5b505af192505050801561061c575060015b506040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301525f919088169063dd62ed3e90604401602060405180830381865afa158015610690573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106b49190610c6a565b905085811015610746576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f74726164657220646964206e6f7420676976652074686520726571756972656460448201527f20617070726f76616c7300000000000000000000000000000000000000000000606482015260840161029a565b505b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201525f9073ffffffffffffffffffffffffffffffffffffffff8816906370a0823190602401602060405180830381865afa1580156107b2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107d69190610c6a565b90508581101561090c5773ffffffffffffffffffffffffffffffffffffffff841663494666b688610807848a610c81565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff909216600483015260248201526044015f604051808303815f87803b15801561086f575f5ffd5b505af1925050508015610880575060015b61090c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f74726164657220646f6573206e6f74206861766520656e6f7567682073656c6c60448201527f20746f6b656e0000000000000000000000000000000000000000000000000000606482015260840161029a565b5050505050505050565b61093773ffffffffffffffffffffffffffffffffffffffff8416838361093c565b505050565b6040805173ffffffffffffffffffffffffffffffffffffffff848116602483015260448083018590528351808403909101815260649092019092526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052905f906109ce90861683610a46565b90506109d981610a5a565b610a3f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5361666545524332303a20617070726f76616c206661696c6564000000000000604482015260640161029a565b5050505050565b6060610a53835f84610a7f565b9392505050565b5f81515f1480610a79575081806020019051810190610a799190610cd4565b92915050565b60605f8473ffffffffffffffffffffffffffffffffffffffff168484604051610aa89190610c3e565b5f6040518083038185875af1925050503d805f8114610ae2576040519150601f19603f3d011682016040523d82523d5f602084013e610ae7565b606091505b509250905080610af957815160208301fd5b509392505050565b5f5f5f60408486031215610b13575f5ffd5b83359250602084013567ffffffffffffffff811115610b30575f5ffd5b8401601f81018613610b40575f5ffd5b803567ffffffffffffffff811115610b56575f5ffd5b866020828401011115610b67575f5ffd5b939660209190910195509293505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610b99575f5ffd5b50565b5f5f5f5f5f60a08688031215610bb0575f5ffd5b8535610bbb81610b78565b94506020860135610bcb81610b78565b9350604086013592506060860135610be281610b78565b91506080860135610bf281610b78565b809150509295509295909350565b5f5f5f60608486031215610c12575f5ffd5b8335610c1d81610b78565b92506020840135610c2d81610b78565b929592945050506040919091013590565b5f82515f5b81811015610c5d5760208186018101518583015201610c43565b505f920191825250919050565b5f60208284031215610c7a575f5ffd5b5051919050565b81810381811115610a79577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60208284031215610cc9575f5ffd5b8151610a5381610b78565b5f60208284031215610ce4575f5ffd5b81518015158114610a53575f5ffdfea164736f6c634300081e000a"
            },
            "0xfcc789354262dd9c2f2ff1b0a5f9067b55af1bfa": {
                "balance": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
            },
            "0x5fc8d32690cc91d4c39d9d3abcbd16989f875707": {
                "code": "0x6080604052348015600e575f5ffd5b50600436106026575f3560e01c806302cc250d14602a575b5f5ffd5b603b6035366004604f565b50600190565b604051901515815260200160405180910390f35b5f60208284031215605e575f5ffd5b813573ffffffffffffffffffffffffffffffffffffffff811681146080575f5ffd5b939250505056fea164736f6c634300081e000a"
            }
        }
    },
    "error": null
}
```

# Migration Guide: simulation-endpoint branch
New field: order-simulation-gas-limit
A new optional top-level field has been added to the orderbook TOML
configuration.

Type: string (decimal integer, interpreted as U256)
Default: omitted / disabled


## Enable the order simulation debug endpoint
`order-simulation-gas-limit = "16777215"`
Effect:

When absent (default): the GET `/api/v1/debug/simulation/{uid}` endpoint
is disabled. Requests return a 501 Not Implemented response.
When present: the endpoint is enabled. The value sets the gas limit used
when simulating the order's settlement call via Tenderly (through the
price estimation driver's balance-override infrastructure).
Recommended value: "16777215" (0xFFFFFF — same as the E2E test default).
Adjust downward if you want to cap simulation gas usage.

No action required if you do not want to expose the simulation endpoint
— omitting the field is the safe default.

---------

Co-authored-by: José Duarte <duarte.gmj@gmail.com>
Co-authored-by: ilya <ilya@cow.fi>
@m-sz m-sz force-pushed the custom-order-simulation branch from 031704f to b96c0ea Compare April 1, 2026 17:26
Copy link
Copy Markdown
Contributor

@fafk fafk left a comment

Choose a reason for hiding this comment

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

LGTM, but isn't a lot missing? Like simulating different order types, different signatures etc?

@m-sz
Copy link
Copy Markdown
Contributor Author

m-sz commented Apr 3, 2026

@fafk I'll add more order details to the request body by re-using OrderCreation struct

@m-sz
Copy link
Copy Markdown
Contributor Author

m-sz commented Apr 3, 2026

Actually, thinking more about it. The signatures etc. are all validated as part of order creation, while it is not strictly necessary to come up with a simulation. I think it does not make much sense for the order simulation endpoint to actually fully validate an order etc, which would limit its ability to experiment with arbitrary orders.

The same order that passes simulation (because the interactions, balances, allowances are ok) would be able to fail the validation due to signature checks etc. which I understand is fine.

The most important, especially for our partners is to simulate order execution before trying to place it.

@m-sz
Copy link
Copy Markdown
Contributor Author

m-sz commented Apr 3, 2026

As discussed with @fafk, the order simulation request object is going to stay as-is. If it proves not enough for Aave we can always extend it with additional fields per their requirements.

Copy link
Copy Markdown
Contributor

@squadgazzz squadgazzz left a comment

Choose a reason for hiding this comment

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

LGTM. Some nits.

@m-sz
Copy link
Copy Markdown
Contributor Author

m-sz commented Apr 3, 2026

  1. Changed the sellAmount to be NonZeroU256
  2. Removed full path usage
  3. blockNumber added to OpenApi spec
  4. Revisited error handling for malformed inputs, now returns properly BadRequest
  5. Reduced log level to warn for user errors.

Base automatically changed from block-number-order-simulation to main April 3, 2026 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants