Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
0addb36
Reimplement sell=buy PoC PR
m-sz Nov 10, 2025
19941ce
Fix failing sell=buy test
m-sz Nov 12, 2025
9b44cfd
cleanup
m-sz Nov 12, 2025
ae7d14b
cleanup comments
m-sz Nov 12, 2025
ff4e678
Feature flag for sell=buy
m-sz Nov 14, 2025
8893b86
Rename liquidity_pairs to token_liquidity
m-sz Dec 2, 2025
bb1fb95
Refactor Route and sell=buy validation control
m-sz Dec 3, 2025
d7304b0
Fix quote verification for sell=buy
m-sz Dec 5, 2025
44ec35e
fmt
m-sz Dec 5, 2025
61725f9
Addressed comments
m-sz Dec 5, 2025
47ffd26
Remove sell=buy test case already covered by onchain verification
m-sz Dec 5, 2025
6994ce4
Remove the builder method for allowing same sell and buy token
m-sz Dec 5, 2025
7d3bf9b
Update crates/orderbook/src/arguments.rs
m-sz Dec 9, 2025
59fcc64
Update crates/shared/src/price_estimation/sanitized.rs
m-sz Dec 9, 2025
e59f421
Update crates/e2e/tests/e2e/place_order_with_quote.rs
m-sz Dec 9, 2025
6ebc5aa
Apply suggestion from @MartinquaXD
m-sz Dec 9, 2025
a1d568f
Rename allow_same_sell_and_buy_token to disallow_...
m-sz Dec 9, 2025
0dd6754
Address comments
m-sz Dec 9, 2025
eaec342
Update crates/shared/src/order_validation.rs
m-sz Dec 10, 2025
265d17e
Remove buy orders support for sell=buy
m-sz Dec 10, 2025
73682d5
fmt
m-sz Dec 10, 2025
2bcb29e
Remove unused QuoteComputationError
m-sz Dec 10, 2025
b7837cb
Change same token validation to return Result
m-sz Dec 11, 2025
c97d196
Update crates/driver/src/domain/quote.rs
m-sz Dec 11, 2025
a955c22
Update crates/orderbook/src/arguments.rs
m-sz Dec 11, 2025
307e7fb
Update crates/shared/src/price_estimation/factory.rs
m-sz Dec 11, 2025
d2cbf6e
Update crates/shared/src/price_estimation/sanitized.rs
m-sz Dec 11, 2025
f690b29
Update crates/shared/src/order_validation.rs
m-sz Dec 11, 2025
75273a2
Update crates/shared/src/order_validation.rs
m-sz Dec 11, 2025
ec5a055
Update crates/solvers/src/domain/solver.rs
m-sz Dec 11, 2025
348f584
fmt
m-sz Dec 11, 2025
5ab57d6
Merge branch 'main' into sell=buy
m-sz Dec 11, 2025
2656b98
NonZeroU256 usage fix in tests after merging
m-sz Dec 11, 2025
295e56b
Update crates/orderbook/src/arguments.rs
m-sz Dec 12, 2025
1476ff9
Update crates/e2e/tests/e2e/submission.rs
m-sz Dec 12, 2025
0ec4291
Address comments
m-sz Dec 12, 2025
0644027
Merge branch 'main' into sell=buy
m-sz Dec 12, 2025
8efe448
Merge branch 'main' into sell=buy
m-sz Dec 15, 2025
f42b7ae
Revert before/after balance calculation
m-sz Dec 16, 2025
b5c612c
Merge branch 'main' into sell=buy
m-sz Dec 16, 2025
f46c77d
clippy
m-sz Dec 16, 2025
6a55fd9
Fix out_amount calculation for sell=buy
m-sz Dec 17, 2025
c99d351
Minor changes: comments and log level
m-sz Dec 17, 2025
56ad09a
Merge branch 'main' into sell=buy
m-sz Dec 17, 2025
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
4 changes: 2 additions & 2 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ test-e2e-local: (test-e2e "local_node")
test-e2e-forked: (test-e2e "forked_node")

# Run End-to-end tests with custom filters
test-e2e *filters:
cargo nextest run -p e2e {{filters}} --test-threads 1 --failure-output final --run-ignored ignored-only
test-e2e filters="" *extra="":
cargo nextest run -p e2e '{{filters}}' --test-threads 1 --failure-output final --run-ignored ignored-only {{extra}}

test-driver:
RUST_MIN_STACK=3145728 cargo nextest run -p driver --test-threads 1 --run-ignored ignored-only
Expand Down
4 changes: 4 additions & 0 deletions crates/driver/src/boundary/liquidity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ impl Fetcher {
pairs: &HashSet<liquidity::TokenPair>,
block: infra::liquidity::AtBlock,
) -> Result<Vec<liquidity::Liquidity>> {
if pairs.is_empty() {
return Ok(vec![]);
}

let pairs = pairs
.iter()
.map(|pair| {
Expand Down
27 changes: 9 additions & 18 deletions crates/driver/src/domain/quote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ use {
util,
},
chrono::Utc,
std::{
collections::{HashMap, HashSet},
iter,
},
std::collections::{HashMap, HashSet},
};

/// A quote describing the expected outcome of an order.
Expand Down Expand Up @@ -91,7 +88,7 @@ impl Order {
let liquidity = match solver.liquidity() {
solver::Liquidity::Fetch => {
liquidity
.fetch(&self.liquidity_pairs(), infra::liquidity::AtBlock::Recent)
.fetch(&self.token_liquidity(), infra::liquidity::AtBlock::Recent)
.await
}
solver::Liquidity::Skip => Default::default(),
Expand Down Expand Up @@ -225,29 +222,23 @@ impl Order {
}

/// Returns the token pairs to fetch liquidity for.
fn liquidity_pairs(&self) -> HashSet<liquidity::TokenPair> {
let pair = liquidity::TokenPair::try_new(self.tokens.sell(), self.tokens.buy())
.expect("sell != buy by construction");
iter::once(pair).collect()
fn token_liquidity(&self) -> HashSet<liquidity::TokenPair> {
liquidity::TokenPair::try_new(self.tokens.sell(), self.tokens.buy())
.ok()
.into_iter()
.collect()
}
}

/// The sell and buy tokens to quote for. This type maintains the invariant that
/// the sell and buy tokens are distinct.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Tokens {
sell: eth::TokenAddress,
buy: eth::TokenAddress,
}

impl Tokens {
/// Creates a new instance of [`Tokens`], verifying that the input buy and
/// sell tokens are distinct.
Comment on lines -244 to -245
Copy link
Contributor

Choose a reason for hiding this comment

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

I would like to get some feedback from solvers how they would expect this to work. Specifically to ask them about using the regular quoting logic vs. building something into the orderbook which only needs the native price of the tokens and a simulation to get the gas amount.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a good idea, I will ask them.

pub fn try_new(sell: eth::TokenAddress, buy: eth::TokenAddress) -> Result<Self, SameTokens> {
if sell == buy {
return Err(SameTokens);
}
Ok(Self { sell, buy })
pub fn new(sell: eth::TokenAddress, buy: eth::TokenAddress) -> Self {
Self { sell, buy }
}

pub fn sell(&self) -> eth::TokenAddress {
Expand Down
9 changes: 4 additions & 5 deletions crates/driver/src/infra/api/routes/quote/dto/order.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,16 @@ use {
};

impl Order {
pub fn into_domain(self) -> Result<quote::Order, Error> {
Ok(quote::Order {
tokens: quote::Tokens::try_new(self.sell_token.into(), self.buy_token.into())
.map_err(|quote::SameTokens| Error::SameTokens)?,
pub fn into_domain(self) -> quote::Order {
quote::Order {
tokens: quote::Tokens::new(self.sell_token.into(), self.buy_token.into()),
amount: self.amount.into(),
side: match self.kind {
Kind::Sell => competition::order::Side::Sell,
Kind::Buy => competition::order::Side::Buy,
},
deadline: self.deadline,
})
}
}
}

Expand Down
4 changes: 1 addition & 3 deletions crates/driver/src/infra/api/routes/quote/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ async fn route(
order: axum::extract::Query<dto::Order>,
) -> Result<axum::Json<dto::Quote>, (hyper::StatusCode, axum::Json<Error>)> {
let handle_request = async {
let order = order.0.into_domain().inspect_err(|err| {
observe::invalid_dto(err, "order");
})?;
let order = order.0.into_domain();
observe::quoting(&order);
let quote = order
.quote(
Expand Down
54 changes: 53 additions & 1 deletion crates/e2e/tests/e2e/place_order_with_quote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,16 @@ use {

#[tokio::test]
#[ignore]
async fn local_node_test() {
async fn local_node_place_order_with_quote_basic() {
run_test(place_order_with_quote).await;
}

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

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

Expand Down Expand Up @@ -112,3 +118,49 @@ async fn place_order_with_quote(web3: Web3) {
assert_eq!(quote_response.verified, order_quote.verified);
assert_eq!(quote_metadata.unwrap().0, order_quote.metadata);
}

async fn disabled_same_sell_and_buy_token_order_feature(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;
let [token] = onchain
.deploy_tokens_with_weth_uni_v2_pools(1_000u64.eth(), 1_000u64.eth())
.await;

token.mint(trader.address(), 10u64.eth()).await;

token
.approve(onchain.contracts().allowance.into_alloy(), 10u64.eth())
.from(trader.address())
.send_and_watch()
.await
.unwrap();

tracing::info!("Starting services.");
let services = Services::new(&onchain).await;
services.start_protocol(solver.clone()).await;

// Disable auto-mine so we don't accidentally mine a settlement
web3.api::<TestNodeApi<_>>()
.set_automine_enabled(false)
.await
.expect("Must be able to disable automine");

tracing::info!("Quoting");
let quote_sell_amount = 1u64.eth();
let quote_request = OrderQuoteRequest {
from: trader.address(),
sell_token: *token.address(),
buy_token: *token.address(),
side: OrderQuoteSide::Sell {
sell_amount: SellAmount::BeforeFee {
value: NonZeroU256::try_from(quote_sell_amount).unwrap(),
},
},
..Default::default()
};
assert!(
matches!(services.submit_quote(&quote_request).await, Err((reqwest::StatusCode::BAD_REQUEST, response)) if response.contains("SameBuyAndSellToken"))
);
}
Loading
Loading