Partially filled orders support in simulation#4306
Conversation
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>
crates/orderbook/openapi.yml
Outdated
| description: > | ||
| Block number to simulate the order at. If not specified, the | ||
| simulation uses the latest block. | ||
| - in: query |
There was a problem hiding this comment.
(Spent some time with claude, brushing up on openAPI spec)
The CI is failing because required: false (L819 below) on the blockNumber property inside the SimulationRequest schema isn't valid OpenAPI 3.0.
In Schema Objects, required is only valid as an array on the parent object.
That one should probably fix it 👀
There was a problem hiding this comment.
Since you went through all that, you could use the suggest feature in the GitHub to provide a more concrete change
crates/orderbook/openapi.yml
Outdated
| description: > | ||
| Block number to simulate the order at. If not specified, the | ||
| simulation uses the latest block. | ||
| - in: query |
There was a problem hiding this comment.
Since you went through all that, you could use the suggest feature in the GitHub to provide a more concrete change
| /// Override for how much of the order has already been filled, expressed | ||
| /// in the order's fill token (sell token for sell orders, buy token for | ||
| /// buy orders). When absent, the current on-chain fill state from the | ||
| /// order metadata is used. | ||
| #[serde_as(as = "Option<HexOrDecimalU256>")] | ||
| pub executed_amount: Option<U256>, |
There was a problem hiding this comment.
I don't like this, are we adding all parameters in the query from here on? Feels like a slippery slope to me
There was a problem hiding this comment.
This is a GET endpoint, the POST is used to simulate a custom order and has the respective fields. I do not foresee more query parameters than the simulation block number and executed amounts so I would be for keeping it this way.
There was a problem hiding this comment.
What's the reason for making the user provide this value in the first place? We could use an RPC call to automatically figure out the remaining fill amount on the given block. (order.amount - settlementContract.filledAmount(orderUid)).
There was a problem hiding this comment.
I will change the implementation to not allow specifying filled amount by the user and issue an RPC call to figure out the executed amount.
| &self, | ||
| order: &Order, | ||
| wrappers: Vec<WrapperCall>, | ||
| executed_amount: Option<U256>, |
There was a problem hiding this comment.
I'm not sure if we should move this up and split the function in two: with and without executed amount — which would eventually be "with and without overrides"
I'd say that for now it can stay but I'm afraid it will gather more parameters if we're not careful
There was a problem hiding this comment.
Let's keep it as-is, and reiterate if the number of overrides grows.
| // Clearing prices represent the limit price of the order; both order kinds | ||
| // produce the same ratio: [buy_amount, sell_amount] for [sell_token, | ||
| // buy_token]. | ||
| let clearing_prices = vec![order.data.buy_amount, order.data.sell_amount]; |
There was a problem hiding this comment.
if they represent the limit prices, why isn't this called "limit_prices" instead, it's much cleaner and there's no confusion with the whole concept of (uniform) clearing prices
There was a problem hiding this comment.
It is called clearing prices everywhere where quoting and orders are involved. I would keep it as-is.
| let (_, wrappers) = self | ||
| .parse_interactions_and_wrappers( | ||
| order.metadata.full_app_data.as_deref().unwrap_or_default(), | ||
| ) | ||
| .map_err(OrderSimulationError::Other)?; |
There was a problem hiding this comment.
Seems a bit strange to have some of the appdata handled outside of encode_order and some of it inside that function. Is there a reason why it was done this way?
There was a problem hiding this comment.
Some of the parts of app data go directly into the "Order" struct, and the wrappers do not. I can change the method to just take the appdata extracted wrappers to keep it in one place.
There was a problem hiding this comment.
There is no app data handling inside encode order, the method takes what is provided by the calller (Order struct with filled in interactions) and wrappers that it then encodes.
| "/api/v1/debug/simulation": | ||
| post: | ||
| operationId: debugSimulationPost | ||
| summary: Simulate an arbitrary order. |
There was a problem hiding this comment.
Is the PR correctly rebased? IIRC there was another PR that introduced simulations for arbitrary orders, right?
There was a problem hiding this comment.
It must have been lost during some of the rebases that I have done on the PRs. Let's keep it here then so it is in the openapi spec. Sorry for that.
Description
The order simulation endpoint so far always tried to fill the order with the original 100% fill amount. This leads to reverts if an order was already partially filled.
Changes
Fetches the order's fill amount using an RPC call at the given block and deducts that from the full order amount. This will cause the simulation to always fill the remaining executable amount.
How to test
E2E test simulating a partially filled order.