-
Notifications
You must be signed in to change notification settings - Fork 97
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
Implement API route for replacing orders #239
Conversation
If I understand the code correctly (it might be nice to add this to the PR description), as long as I have a signed order from a user I can use it to cancel any other existing order of that user without their explicit consent? I don't think this is necessary a huge blocker (assuming replaying an existing order for that would lead to an error) since users will likely place their orders before someone can intercept them, but I was wondering if you thought about including a "replace_order_uid" field in the app_data json. This way the user would explicitly sign their intent to replace an existing order. More generally, given that we have seen this could enable a bunch of different use cases, I think it may make sense to invest into a component that can decode, fetch and cache the content behind app_data from IPFS. |
I don't think so, unless I made a mistake somewhere: // Verify that the new order is a valid replacement order by checking
// that the `app_data` encodes an order cancellation and that both the
// old and new orders have the same signer.
let cancellation = OrderCancellation {
order_uid: old_order.metadata.uid,
..Default::default()
};
if new_order.creation.app_data != cancellation.hash_struct()
|| new_order.metadata.owner != old_order.metadata.owner
{
return Err(ReplaceOrderError::InvalidReplacement);
} Specifically, the |
This is definitely the plan - we want to use this for liquidity orders for example. |
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.
Do we still allow anyone to create presign orders before any transaction occurred onchain? In case, it could be used by anyone to cancel an order for any user.
Otherwise, assuming that order.metadata.owner
is always validated by a signature or transaction, this looks like a good approach to me. (I'd also prefer to see the metadata from appData
decoded somewhere else independently, so we can use them to also store other data instead of reusing the same field for multiple purposes.)
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.
Looks good overall. I also would like to see proper support for app_data
but this "shortcut" seems reasonable enough for now.
old_order: &model::order::OrderUid, | ||
new_order: &model::order::Order, |
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.
Why not take the orders by value instead of taking a reference and copying immediately afterwards?
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.
It was mostly for consistency with the insert_order
and cancel_order
methods.
This can be changed.
Codecov Report
@@ Coverage Diff @@
## main #239 +/- ##
==========================================
- Coverage 64.48% 64.43% -0.05%
==========================================
Files 190 191 +1
Lines 39061 39479 +418
==========================================
+ Hits 25188 25440 +252
- Misses 13873 14039 +166 |
You could argue that replacement orders should have the exact same app data as the transaction it is replacing. That being said, I agree that we should enhance this "app data" thing. |
How do you think of these things @fedgiac! Definitely an issue. We can scope order replacements to ECDSA-signed orders. |
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.
Looks good to me!
Co-authored-by: Federico Giacon <[email protected]>
Co-authored-by: Federico Giacon <[email protected]>
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.
Code looks good. Haven't though too deeply on the mechanism.
Fixes #120
This PR introduces a new
PATCH api/v1/orders/${order_uid}
route for cancelling an existing order and creating a new replacement order in an atomic operation. This way, if an existing user order goes "out-of-price", the CowSwap UI could replace the old order with a new "in-price" order with a single user signature and request. Also, since the operation is atomic, we guarantee that:cancelling > creating
could, in some cases, cause a user to miss an auction if a new auction was cut right after the user order was cancelled but before the new one was created)creating > cancelling
could cause this if an auction was cut right after the new order was created but before the old one was cancelled).This works by verifying:
appData
field of the replacement order is equal to the EIP-712 struct hash of a cancellation request for the original order; in Ethers.js this would be:PreSign
orders cannot be accepted as replacements as their signature is not verified on order creation (instead, the orderbook waits for an on-chainPreSignature
event for the specific order) - meaning that fakePreSign
order with no intent on being filled (because the owner would not callsetPreSignature(true)
for the order) could be used to cancel arbitrary user orders without signatures.Test Plan
Added new unit tests around replacement PostgreSQL transaction. We were able to reuse almost all of the validation logic for cancelling and creating orders with some light refactoring, so no new tests were needed for those code paths.
Sample script that tests new feature
Make sure to install
hdwallet
for command-line signing.Result:
Release notes
In order to add more metrics around order replacements, I refactored the recently added orderbook metrics (
user_order_created
andliquidity_order_created
) to be aCounterVec
and use labels to distinguish between the order "kind" (so user orders or liquidity orders) as well as the operation (created and cancelled). This requires changes to the Grafana dashboards.