Skip to content
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

Merged
merged 8 commits into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/e2e/tests/e2e/services.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ impl OrderbookServices {
HashSet::default(),
Duration::from_secs(120),
Duration::MAX,
true,
fee_calculator.clone(),
bad_token_detector.clone(),
balance_fetcher,
Expand All @@ -186,7 +187,6 @@ impl OrderbookServices {
contracts.gp_settlement.address(),
db.clone(),
bad_token_detector,
true,
solvable_orders_cache.clone(),
Duration::from_secs(600),
order_validator.clone(),
Expand Down
6 changes: 6 additions & 0 deletions crates/model/src/app_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ impl<'de> Deserialize<'de> for AppId {
}
}

impl PartialEq<[u8; 32]> for AppId {
fn eq(&self, other: &[u8; 32]) -> bool {
self.0 == *other
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
82 changes: 82 additions & 0 deletions crates/orderbook/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,57 @@ paths:
description: Order deleted
400:
description: Malformed signature
content:
application/json:
schema:
$ref: "#/components/schemas/OrderCancellationError"
401:
description: Invalid signature
404:
description: Order was not found
patch:
summary: Cancels order and replaces it with a new one
description: |
Cancel an order by providing a replacement order where the app data field
is the EIP-712-struct-hash of a cancellation for the original order. This
allows an old order to be cancelled AND a new order to be created in an
atomic operation with a single signature. This may be useful for replacing
orders when on-chain prices move outside of the original order's limit price.
parameters:
- in: path
name: UID
schema:
$ref: "#/components/schemas/UID"
required: true
requestBody:
description: "replacement order"
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/OrderCreation"
responses:
201:
description: Previous order was cancelled and the new replacement order was created.
content:
application/json:
schema:
$ref: "#/components/schemas/UID"
400:
description: Error cancelling and replacing new order with an old one.
content:
application/json:
schema:
$ref: "#/components/schemas/ReplaceOrderError"
401:
description: |
Invalid replacement order. This can happen if the old and new orders have
different signers, the new order's app data is not an encoded cancellation of
the old order, or the new order is based on presign or EIP-1271 signatures.
403:
description: Forbidden
404:
description: Order was not found
/api/v1/transactions/{txHash}/orders:
get:
summary: Get orders by settlement transaction hash.
Expand Down Expand Up @@ -863,6 +910,7 @@ components:
UnsupportedBuyTokenDestination,
UnsupportedSellTokenSource,
UnsupportedOrderType,
UnsupportedSignature,
]
description:
type: string
Expand All @@ -889,6 +937,40 @@ components:
required:
- errorType
- description
ReplaceOrderError:
type: object
properties:
errorType:
type: string
enum:
[
AlreadyCancelled,
OrderFullyExecuted,
OrderExpired,
OnChainOrder,
DuplicateOrder,
InsufficientFee,
InsufficientAllowance,
InsufficientBalance,
InsufficientValidTo,
ExcessiveValidTo,
InvalidSignature,
TransferEthToContract,
TransferSimulationFailed,
UnsupportedToken,
WrongOwner,
SameBuyAndSellToken,
ZeroAmount,
UnsupportedBuyTokenDestination,
UnsupportedSellTokenSource,
UnsupportedOrderType,
UnsupportedSignature,
]
description:
type: string
required:
- errorType
- description
FeeAndQuoteSellResponse:
type: object
properties:
Expand Down
6 changes: 6 additions & 0 deletions crates/orderbook/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod get_user_orders;
pub mod order_validation;
pub mod post_quote;
pub mod post_solver_competition;
mod replace_order;

use crate::solver_competition::SolverCompetition;
use crate::{
Expand Down Expand Up @@ -69,6 +70,9 @@ pub fn handle_all_routes(
let cancel_order = cancel_order::cancel_order(orderbook.clone())
.map(|result| (result, "v1/cancel_order"))
.boxed();
let replace_order = replace_order::filter(orderbook.clone())
.map(|result| (result, "v1/replace_order"))
.boxed();
let get_amount_estimate = get_markets::get_amount_estimate(quoter.price_estimator.clone())
.map(|result| (result, "v1/get_amount_estimate"))
.boxed();
Expand Down Expand Up @@ -112,6 +116,8 @@ pub fn handle_all_routes(
.unify()
.or(cancel_order)
.unify()
.or(replace_order)
.unify()
.or(get_amount_estimate)
.unify()
.or(get_fee_and_quote_sell)
Expand Down
12 changes: 6 additions & 6 deletions crates/orderbook/src/api/cancel_order.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use crate::api::{convert_json_response, extract_payload, IntoWarpReply};
use crate::orderbook::{OrderCancellationError, Orderbook};
use crate::{
api::{convert_json_response, extract_payload, IntoWarpReply},
orderbook::{OrderCancellationError, Orderbook},
};
use anyhow::Result;
use model::signature::EcdsaSignature;
use model::{
order::{OrderCancellation, OrderUid},
signature::EcdsaSigningScheme,
signature::{EcdsaSignature, EcdsaSigningScheme},
};
use serde::{Deserialize, Serialize};
use std::{convert::Infallible, sync::Arc};
use warp::reply::with_status;
use warp::{hyper::StatusCode, Filter, Rejection};
use warp::{hyper::StatusCode, reply::with_status, Filter, Rejection};

#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
Expand Down
4 changes: 0 additions & 4 deletions crates/orderbook/src/api/create_order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ impl IntoWarpReply for AddOrderError {
fn into_warp_reply(self) -> super::ApiReply {
match self {
Self::OrderValidation(err) => err.into_warp_reply(),
Self::UnsupportedSignature => with_status(
super::error("UnsupportedSignature", "signing scheme is not supported"),
StatusCode::BAD_REQUEST,
),
Self::DuplicatedOrder => with_status(
super::error("DuplicatedOrder", "order already exists"),
StatusCode::BAD_REQUEST,
Expand Down
45 changes: 42 additions & 3 deletions crates/orderbook/src/api/order_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ pub enum PartialValidationError {
UnsupportedBuyTokenDestination(BuyTokenDestination),
UnsupportedSellTokenSource(SellTokenSource),
UnsupportedOrderType,
UnsupportedSignature,
Other(anyhow::Error),
}

Expand Down Expand Up @@ -123,6 +124,10 @@ impl IntoWarpReply for PartialValidationError {
),
StatusCode::BAD_REQUEST,
),
Self::UnsupportedSignature => with_status(
super::error("UnsupportedSignature", "signing scheme is not supported"),
StatusCode::BAD_REQUEST,
),
Self::Other(err) => with_status(
super::internal_error(err.context("partial_validation")),
StatusCode::INTERNAL_SERVER_ERROR,
Expand Down Expand Up @@ -222,6 +227,7 @@ pub struct OrderValidator {
liquidity_order_owners: HashSet<H160>,
min_order_validity_period: Duration,
max_order_validity_period: Duration,
enable_presign_orders: bool,
/// For Full-Validation: performed time of order placement
fee_validator: Arc<dyn MinFeeCalculating>,
bad_token_detector: Arc<dyn BadTokenDetecting>,
Expand Down Expand Up @@ -281,6 +287,7 @@ impl OrderValidator {
liquidity_order_owners: HashSet<H160>,
min_order_validity_period: Duration,
max_order_validity_period: Duration,
enable_presign_orders: bool,
fee_validator: Arc<dyn MinFeeCalculating>,
bad_token_detector: Arc<dyn BadTokenDetecting>,
balance_fetcher: Arc<dyn BalanceFetching>,
Expand All @@ -292,6 +299,7 @@ impl OrderValidator {
liquidity_order_owners,
min_order_validity_period,
max_order_validity_period,
enable_presign_orders,
fee_validator,
bad_token_detector,
balance_fetcher,
Expand All @@ -302,12 +310,14 @@ impl OrderValidator {
#[async_trait::async_trait]
impl OrderValidating for OrderValidator {
async fn partial_validate(&self, order: PreOrderData) -> Result<(), PartialValidationError> {
if order.partially_fillable && !order.is_liquidity_order {
return Err(PartialValidationError::UnsupportedOrderType);
}
if self.banned_users.contains(&order.owner) {
return Err(PartialValidationError::Forbidden);
}

if order.partially_fillable && !order.is_liquidity_order {
return Err(PartialValidationError::UnsupportedOrderType);
}

if order.buy_token_balance != BuyTokenDestination::Erc20 {
return Err(PartialValidationError::UnsupportedBuyTokenDestination(
order.buy_token_balance,
Expand All @@ -322,6 +332,14 @@ impl OrderValidating for OrderValidator {
));
}

// Eventually we will support all Signature types and can remove this.
if !matches!(
(order.signing_scheme, self.enable_presign_orders),
(SigningScheme::Eip712 | SigningScheme::EthSign, _) | (SigningScheme::PreSign, true)
) {
return Err(PartialValidationError::UnsupportedSignature);
}

let now = model::time::now_in_epoch_seconds();
if order.valid_to < now + self.min_order_validity_period.as_secs() as u32 {
return Err(PartialValidationError::InsufficientValidTo);
Expand Down Expand Up @@ -596,6 +614,7 @@ mod tests {
hashset!(),
min_order_validity_period,
max_order_validity_period,
false,
Arc::new(MockMinFeeCalculating::new()),
Arc::new(MockBadTokenDetecting::new()),
Arc::new(MockBalanceFetching::new()),
Expand Down Expand Up @@ -689,6 +708,16 @@ mod tests {
.await,
Err(PartialValidationError::InvalidNativeSellToken)
));
assert!(matches!(
validator
.partial_validate(PreOrderData {
valid_to: legit_valid_to,
signing_scheme: SigningScheme::PreSign,
..Default::default()
})
.await,
Err(PartialValidationError::UnsupportedSignature)
));

let mut code_fetcher = Box::new(MockCodeFetching::new());
let _err = anyhow!("Failed to fetch Code Size!");
Expand All @@ -703,6 +732,7 @@ mod tests {
hashset!(),
Duration::from_secs(1),
Duration::from_secs(100),
false,
Arc::new(MockMinFeeCalculating::new()),
Arc::new(MockBadTokenDetecting::new()),
Arc::new(MockBalanceFetching::new()),
Expand Down Expand Up @@ -732,6 +762,7 @@ mod tests {
hashset!(liquidity_order_owner),
min_order_validity_period,
max_order_validity_period,
true,
Arc::new(MockMinFeeCalculating::new()),
Arc::new(MockBadTokenDetecting::new()),
Arc::new(MockBalanceFetching::new()),
Expand Down Expand Up @@ -787,6 +818,7 @@ mod tests {
hashset!(),
Duration::from_secs(1),
Duration::from_secs(100),
true,
Arc::new(fee_calculator),
Arc::new(bad_token_detector),
Arc::new(balance_fetcher),
Expand Down Expand Up @@ -827,6 +859,7 @@ mod tests {
hashset!(),
Duration::from_secs(1),
Duration::from_secs(100),
true,
Arc::new(fee_calculator),
Arc::new(bad_token_detector),
Arc::new(balance_fetcher),
Expand Down Expand Up @@ -867,6 +900,7 @@ mod tests {
hashset!(),
Duration::from_secs(1),
Duration::from_secs(100),
true,
Arc::new(fee_calculator),
Arc::new(bad_token_detector),
Arc::new(balance_fetcher),
Expand Down Expand Up @@ -911,6 +945,7 @@ mod tests {
hashset!(),
Duration::from_secs(1),
Duration::from_secs(100),
true,
Arc::new(fee_calculator),
Arc::new(bad_token_detector),
Arc::new(balance_fetcher),
Expand Down Expand Up @@ -953,6 +988,7 @@ mod tests {
hashset!(),
Duration::from_secs(1),
Duration::from_secs(100),
true,
Arc::new(fee_calculator),
Arc::new(bad_token_detector),
Arc::new(balance_fetcher),
Expand Down Expand Up @@ -993,6 +1029,7 @@ mod tests {
hashset!(),
Duration::from_secs(1),
Duration::from_secs(100),
true,
Arc::new(fee_calculator),
Arc::new(bad_token_detector),
Arc::new(balance_fetcher),
Expand Down Expand Up @@ -1034,6 +1071,7 @@ mod tests {
hashset!(),
Duration::from_secs(1),
Duration::from_secs(100),
true,
Arc::new(fee_calculator),
Arc::new(bad_token_detector),
Arc::new(balance_fetcher),
Expand Down Expand Up @@ -1076,6 +1114,7 @@ mod tests {
hashset!(),
Duration::from_secs(1),
Duration::MAX,
true,
Arc::new(fee_calculator),
Arc::new(bad_token_detector),
Arc::new(balance_fetcher),
Expand Down
Loading