Skip to content
Open
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
160 changes: 159 additions & 1 deletion crates/e2e/tests/e2e/place_order_with_quote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ use {
configs::{
autopilot::Configuration,
order_quoting::{ExternalSolver, OrderQuoting},
orderbook::order_validation::{OrderValidationConfig, SameTokensPolicy},
shared::SharedConfig,
test_util::TestDefault,
},
e2e::setup::{colocation, wait_for_condition, *},
ethrpc::alloy::{CallBuilderExt, EvmProviderExt},
model::{
order::{OrderCreation, OrderKind},
order::{BUY_ETH_ADDRESS, OrderCreation, OrderKind},
quote::{OrderQuoteRequest, OrderQuoteSide, SellAmount},
signature::EcdsaSigningScheme,
},
Expand All @@ -37,6 +38,18 @@ async fn local_node_fallback_native_price_estimator() {
run_test(fallback_native_price_estimator).await;
}

#[tokio::test]
#[ignore]
async fn local_node_native_same_token_sell_with_eth_buy_marker() {
run_test(native_same_token_sell_with_eth_buy_marker).await;
}

#[tokio::test]
#[ignore]
async fn local_node_native_same_token_buy_with_eth_buy_marker_rejected() {
run_test(native_same_token_buy_with_eth_buy_marker_rejected).await;
}

async fn place_order_with_quote(web3: Web3) {
let mut onchain = OnchainComponents::deploy(web3.clone()).await;

Expand Down Expand Up @@ -175,6 +188,151 @@ async fn disabled_same_sell_and_buy_token_order_feature(web3: Web3) {
);
}

async fn native_same_token_sell_with_eth_buy_marker(web3: Web3) {
let mut onchain = OnchainComponents::deploy(web3.clone()).await;

let [solver] = onchain.make_solvers(10u64.eth()).await;
let [trader] = onchain.make_accounts(10u64.eth()).await;

onchain
.contracts()
.weth
.approve(onchain.contracts().allowance, 3u64.eth())
.from(trader.address())
.send_and_watch()
.await
.unwrap();
onchain
.contracts()
.weth
.deposit()
.from(trader.address())
.value(3u64.eth())
.send_and_watch()
.await
.unwrap();

tracing::info!("Starting services.");
let services = Services::new(&onchain).await;
let orderbook_config = configs::orderbook::Configuration {
order_validation: OrderValidationConfig {
same_tokens_policy: SameTokensPolicy::AllowSell,
..Default::default()
},
..configs::orderbook::Configuration::test_default()
};
services
.start_protocol_with_args(
Default::default(),
Configuration::test("test_solver", solver.address()),
orderbook_config,
solver.clone(),
)
.await;

web3.provider
.evm_set_automine(false)
.await
.expect("Must be able to disable automine");

let quote_sell_amount = 1u64.eth();
let quote_request = OrderQuoteRequest {
from: trader.address(),
sell_token: *onchain.contracts().weth.address(),
buy_token: BUY_ETH_ADDRESS,
side: OrderQuoteSide::Sell {
sell_amount: SellAmount::BeforeFee {
value: NonZeroU256::try_from(quote_sell_amount).unwrap(),
},
},
..Default::default()
};

let quote_response = services.submit_quote(&quote_request).await.unwrap();
assert!(quote_response.verified);
assert!(quote_response.id.is_some());

let order = OrderCreation {
quote_id: quote_response.id,
sell_token: *onchain.contracts().weth.address(),
sell_amount: quote_sell_amount,
buy_token: BUY_ETH_ADDRESS,
buy_amount: quote_response.quote.buy_amount,
valid_to: model::time::now_in_epoch_seconds() + 300,
kind: OrderKind::Sell,
..Default::default()
}
.sign(
EcdsaSigningScheme::Eip712,
&onchain.contracts().domain_separator,
&trader.signer,
);

services.create_order(&order).await.unwrap();
}

async fn native_same_token_buy_with_eth_buy_marker_rejected(web3: Web3) {
let mut onchain = OnchainComponents::deploy(web3.clone()).await;

let [solver] = onchain.make_solvers(10u64.eth()).await;
let [trader] = onchain.make_accounts(10u64.eth()).await;

onchain
.contracts()
.weth
.approve(onchain.contracts().allowance, 3u64.eth())
.from(trader.address())
.send_and_watch()
.await
.unwrap();
onchain
.contracts()
.weth
.deposit()
.from(trader.address())
.value(3u64.eth())
.send_and_watch()
.await
.unwrap();

tracing::info!("Starting services.");
let services = Services::new(&onchain).await;
let orderbook_config = configs::orderbook::Configuration {
order_validation: OrderValidationConfig {
same_tokens_policy: SameTokensPolicy::AllowSell,
..Default::default()
},
..configs::orderbook::Configuration::test_default()
};
services
.start_protocol_with_args(
Default::default(),
Configuration::test("test_solver", solver.address()),
orderbook_config,
solver.clone(),
)
.await;

web3.provider
.evm_set_automine(false)
.await
.expect("Must be able to disable automine");

let quote_request = OrderQuoteRequest {
from: trader.address(),
sell_token: *onchain.contracts().weth.address(),
buy_token: BUY_ETH_ADDRESS,
side: OrderQuoteSide::Buy {
buy_amount_after_fee: NonZeroU256::try_from(1u64.eth()).unwrap(),
},
..Default::default()
};

assert!(
matches!(services.submit_quote(&quote_request).await, Err((reqwest::StatusCode::BAD_REQUEST, response)) if response.contains("SameBuyAndSellToken"))
);
}

async fn fallback_native_price_estimator(web3: Web3) {
let mut onchain = OnchainComponents::deploy(web3.clone()).await;

Expand Down
49 changes: 44 additions & 5 deletions crates/shared/src/order_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,10 @@ fn validate_same_sell_and_buy_token(
order: &PreOrderData,
native_token: &Address,
) -> Result<(), PartialValidationError> {
// Check for orders selling wrapped native token for native token.
if &order.sell_token == native_token && order.buy_token == BUY_ETH_ADDRESS {
return Err(PartialValidationError::SameBuyAndSellToken);
}
let same_token = order.sell_token == order.buy_token
|| (&order.sell_token == native_token && order.buy_token == BUY_ETH_ADDRESS);

if order.sell_token != order.buy_token {
if !same_token {
return Ok(());
}

Expand Down Expand Up @@ -1075,6 +1073,7 @@ mod tests {
#[tokio::test]
async fn pre_validate_err() {
let native_token = WETH9::Instance::new([0xef; 20].into(), ethrpc::mock::web3().provider);
let native_token_address = *native_token.address();
let validity_configuration = OrderValidPeriodConfiguration {
min: Duration::from_secs(1),
max_market: Duration::from_secs(100),
Expand Down Expand Up @@ -1206,6 +1205,17 @@ mod tests {
.await,
Err(PartialValidationError::SameBuyAndSellToken)
));
assert!(matches!(
validator
.partial_validate(PreOrderData {
valid_to: legit_valid_to,
buy_token: BUY_ETH_ADDRESS,
sell_token: native_token_address,
..Default::default()
})
.await,
Err(PartialValidationError::SameBuyAndSellToken)
));
assert!(matches!(
validator
.partial_validate(PreOrderData {
Expand Down Expand Up @@ -1366,6 +1376,35 @@ mod tests {
)
.is_ok()
);

let native_order = || PreOrderData {
buy_token: BUY_ETH_ADDRESS,
sell_token: *native_token.address(),
valid_to: time::now_in_epoch_seconds()
+ validity_configuration.min.as_secs() as u32
+ 2,
..Default::default()
};

assert!(matches!(
validator
.partial_validate(PreOrderData {
kind: OrderKind::Buy,
..native_order()
})
.await,
Err(PartialValidationError::SameBuyAndSellToken)
));

assert!(
validator
.partial_validate(PreOrderData {
kind: OrderKind::Sell,
..native_order()
})
.await
.is_ok()
);
}

#[tokio::test]
Expand Down
Loading