From c5928c0021a577f10f6eb857a37bcdd5a2ee100e Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Tue, 13 Jan 2026 17:49:09 +0100 Subject: [PATCH 01/11] Add v1/orders POST endpoint to get orders in batches Adds POST handler for `v1/orders` endpoint that requires a list of order uids and responds with a vector of their data. Has a hardcoded limit of 5000 orders per request. --- crates/orderbook/src/api.rs | 5 ++ crates/orderbook/src/api/get_orders_by_uid.rs | 84 +++++++++++++++++++ crates/orderbook/src/database/orders.rs | 27 ++++++ crates/orderbook/src/orderbook.rs | 4 + 4 files changed, 120 insertions(+) create mode 100644 crates/orderbook/src/api/get_orders_by_uid.rs diff --git a/crates/orderbook/src/api.rs b/crates/orderbook/src/api.rs index 4450499d5b..f7cdcd86e9 100644 --- a/crates/orderbook/src/api.rs +++ b/crates/orderbook/src/api.rs @@ -26,6 +26,7 @@ mod get_app_data; mod get_auction; mod get_native_price; mod get_order_by_uid; +mod get_orders_by_uid; mod get_order_status; mod get_orders_by_tx; mod get_solver_competition; @@ -62,6 +63,10 @@ pub fn handle_all_routes( "v1/get_order", box_filter(get_order_by_uid::get_order_by_uid(orderbook.clone())), ), + ( + "v1/get_orders", + box_filter(get_orders_by_uid::get_orders_by_uid(orderbook.clone())), + ), ( "v1/get_order_status", box_filter(get_order_status::get_status(orderbook.clone())), diff --git a/crates/orderbook/src/api/get_orders_by_uid.rs b/crates/orderbook/src/api/get_orders_by_uid.rs new file mode 100644 index 0000000000..9e55f938e1 --- /dev/null +++ b/crates/orderbook/src/api/get_orders_by_uid.rs @@ -0,0 +1,84 @@ +use { + crate::{api::{error, extract_payload}, orderbook::Orderbook}, + anyhow::Result, + model::order::{Order, OrderUid}, + std::{convert::Infallible, sync::Arc}, + warp::{Filter, Rejection, hyper::StatusCode, reply}, +}; + +const MAX_ORDERS_LIMIT: usize = 5000; + +#[derive(Debug, Eq, PartialEq)] +enum ValidationError { + TooManyOrders(usize), +} + +fn validate(uids: Vec) -> Result, ValidationError> { + if uids.len() > MAX_ORDERS_LIMIT { + return Err(ValidationError::TooManyOrders(uids.len())); + } + Ok(uids) +} + +fn get_orders_by_uid_request() +-> impl Filter, ValidationError>,), Error = Rejection> + Clone +{ + warp::path!("v1" / "orders") + .and(warp::post()) + .and(extract_payload()) + .map(|uids: Vec| validate(uids)) +} + +pub fn get_orders_by_uid_response(result: Result>>) -> super::ApiReply { + let orders = match result { + Ok(orders) => orders, + Err(err) => { + tracing::error!(?err, "get_orders_by_uids_response"); + return crate::api::internal_error_reply(); + } + }; + reply::with_status(reply::json(&orders), StatusCode::OK) +} + +pub fn get_orders_by_uid(orderbook: Arc,) -> impl Filter + Clone { + get_orders_by_uid_request().and_then(move |request_result: Result, ValidationError>| { + let orderbook = orderbook.clone(); + async move { + Result::<_, Infallible>::Ok(match request_result { + Ok(uids) => { + let result = orderbook.get_orders(&uids).await; + get_orders_by_uid_response(result) + } + Err(ValidationError::TooManyOrders(requested)) => { + let err = error( + "TooManyOrders", + format!("Too many order UIDs requested: {requested}. Maximum allowed: {MAX_ORDERS_LIMIT}"), + ); + reply::with_status(err, StatusCode::BAD_REQUEST) + } + }) + } + }) +} + +#[cfg(test)] +mod tests { + use { + super::*, + crate::api::response_body, + warp::{Reply, test::request}, + }; + + #[tokio::test] + async fn get_orders_by_uid_request_ok() { + let uid = OrderUid::default(); + let request = request().path(&format!("/v1/orders")) + .method("POST") + .header("content-type", "application-json") + .json(&[uid]); + + let filter = get_orders_by_uid_request(); + let result = request.filter(&filter).await.unwrap().unwrap(); + assert_eq!(result, [uid]); + } +} \ No newline at end of file diff --git a/crates/orderbook/src/database/orders.rs b/crates/orderbook/src/database/orders.rs index d6c44e1e62..8a3fc153cd 100644 --- a/crates/orderbook/src/database/orders.rs +++ b/crates/orderbook/src/database/orders.rs @@ -75,6 +75,7 @@ pub trait OrderStoring: Send + Sync { ) -> Result>; async fn latest_order_event(&self, order_uid: &OrderUid) -> Result>; async fn single_order(&self, uid: &OrderUid) -> Result>; + async fn many_orders(&self, uids: &[OrderUid]) -> Result>>; } #[derive(Debug)] @@ -313,6 +314,32 @@ impl OrderStoring for Postgres { .transpose() } + async fn many_orders(&self, uids: &[OrderUid]) -> Result>> { + let _timer = super::Metrics::get() + .database_queries + .with_label_values(&["many_orders"]) + .start_timer(); + let mut ex = self.pool.acquire().await?; + let mut results = Vec::new(); + + for uid in uids { + results.push(match orders::single_full_order_with_quote(&mut ex, &ByteArray(uid.0)).await? { + Some(order_with_quote) => { + let (order, quote) = order_with_quote.into_order_and_quote(); + Some(full_order_with_quote_into_model_order(order, quote.as_ref())) + }, + None => { + // try to find the order in the JIT orders table + database::jit_orders::get_by_id(&mut ex, &ByteArray(uid.0)) + .await? + .map(full_order_into_model_order) + } + }.transpose()); + } + + results.into_iter().collect() + } + async fn orders_for_tx(&self, tx_hash: &B256) -> Result> { tokio::try_join!( self.user_order_for_tx(tx_hash), diff --git a/crates/orderbook/src/orderbook.rs b/crates/orderbook/src/orderbook.rs index 3c77c6515a..73d3f9fe0c 100644 --- a/crates/orderbook/src/orderbook.rs +++ b/crates/orderbook/src/orderbook.rs @@ -494,6 +494,10 @@ impl Orderbook { self.database_replica.single_order(uid).await } + pub async fn get_orders(&self, uids: &[OrderUid]) -> Result>> { + self.database_replica.many_orders(uids).await + } + pub async fn get_orders_for_tx(&self, hash: &B256) -> Result> { self.database_replica.orders_for_tx(hash).await } From b768ac86a460cd3fe9f504716812b24b95722924 Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Fri, 16 Jan 2026 14:49:32 +0100 Subject: [PATCH 02/11] fmt --- crates/orderbook/src/api.rs | 2 +- crates/orderbook/src/api/get_orders_by_uid.rs | 58 +++++++++++-------- crates/orderbook/src/database/orders.rs | 28 +++++---- 3 files changed, 52 insertions(+), 36 deletions(-) diff --git a/crates/orderbook/src/api.rs b/crates/orderbook/src/api.rs index f7cdcd86e9..b7b3281b7a 100644 --- a/crates/orderbook/src/api.rs +++ b/crates/orderbook/src/api.rs @@ -26,9 +26,9 @@ mod get_app_data; mod get_auction; mod get_native_price; mod get_order_by_uid; -mod get_orders_by_uid; mod get_order_status; mod get_orders_by_tx; +mod get_orders_by_uid; mod get_solver_competition; mod get_solver_competition_v2; mod get_token_metadata; diff --git a/crates/orderbook/src/api/get_orders_by_uid.rs b/crates/orderbook/src/api/get_orders_by_uid.rs index 9e55f938e1..1f5f567142 100644 --- a/crates/orderbook/src/api/get_orders_by_uid.rs +++ b/crates/orderbook/src/api/get_orders_by_uid.rs @@ -1,5 +1,8 @@ use { - crate::{api::{error, extract_payload}, orderbook::Orderbook}, + crate::{ + api::{error, extract_payload}, + orderbook::Orderbook, + }, anyhow::Result, model::order::{Order, OrderUid}, std::{convert::Infallible, sync::Arc}, @@ -21,8 +24,7 @@ fn validate(uids: Vec) -> Result, ValidationError> { } fn get_orders_by_uid_request() --> impl Filter, ValidationError>,), Error = Rejection> + Clone -{ +-> impl Filter, ValidationError>,), Error = Rejection> + Clone { warp::path!("v1" / "orders") .and(warp::post()) .and(extract_payload()) @@ -40,25 +42,32 @@ pub fn get_orders_by_uid_response(result: Result>>) -> super:: reply::with_status(reply::json(&orders), StatusCode::OK) } -pub fn get_orders_by_uid(orderbook: Arc,) -> impl Filter + Clone { - get_orders_by_uid_request().and_then(move |request_result: Result, ValidationError>| { - let orderbook = orderbook.clone(); - async move { - Result::<_, Infallible>::Ok(match request_result { - Ok(uids) => { - let result = orderbook.get_orders(&uids).await; - get_orders_by_uid_response(result) - } - Err(ValidationError::TooManyOrders(requested)) => { - let err = error( - "TooManyOrders", - format!("Too many order UIDs requested: {requested}. Maximum allowed: {MAX_ORDERS_LIMIT}"), - ); - reply::with_status(err, StatusCode::BAD_REQUEST) - } - }) - } - }) +pub fn get_orders_by_uid( + orderbook: Arc, +) -> impl Filter + Clone { + get_orders_by_uid_request().and_then( + move |request_result: Result, ValidationError>| { + let orderbook = orderbook.clone(); + async move { + Result::<_, Infallible>::Ok(match request_result { + Ok(uids) => { + let result = orderbook.get_orders(&uids).await; + get_orders_by_uid_response(result) + } + Err(ValidationError::TooManyOrders(requested)) => { + let err = error( + "TooManyOrders", + format!( + "Too many order UIDs requested: {requested}. Maximum allowed: \ + {MAX_ORDERS_LIMIT}" + ), + ); + reply::with_status(err, StatusCode::BAD_REQUEST) + } + }) + } + }, + ) } #[cfg(test)] @@ -72,7 +81,8 @@ mod tests { #[tokio::test] async fn get_orders_by_uid_request_ok() { let uid = OrderUid::default(); - let request = request().path(&format!("/v1/orders")) + let request = request() + .path(&format!("/v1/orders")) .method("POST") .header("content-type", "application-json") .json(&[uid]); @@ -81,4 +91,4 @@ mod tests { let result = request.filter(&filter).await.unwrap().unwrap(); assert_eq!(result, [uid]); } -} \ No newline at end of file +} diff --git a/crates/orderbook/src/database/orders.rs b/crates/orderbook/src/database/orders.rs index 8a3fc153cd..6a281dbb33 100644 --- a/crates/orderbook/src/database/orders.rs +++ b/crates/orderbook/src/database/orders.rs @@ -323,18 +323,24 @@ impl OrderStoring for Postgres { let mut results = Vec::new(); for uid in uids { - results.push(match orders::single_full_order_with_quote(&mut ex, &ByteArray(uid.0)).await? { - Some(order_with_quote) => { - let (order, quote) = order_with_quote.into_order_and_quote(); - Some(full_order_with_quote_into_model_order(order, quote.as_ref())) - }, - None => { - // try to find the order in the JIT orders table - database::jit_orders::get_by_id(&mut ex, &ByteArray(uid.0)) - .await? - .map(full_order_into_model_order) + results.push( + match orders::single_full_order_with_quote(&mut ex, &ByteArray(uid.0)).await? { + Some(order_with_quote) => { + let (order, quote) = order_with_quote.into_order_and_quote(); + Some(full_order_with_quote_into_model_order( + order, + quote.as_ref(), + )) + } + None => { + // try to find the order in the JIT orders table + database::jit_orders::get_by_id(&mut ex, &ByteArray(uid.0)) + .await? + .map(full_order_into_model_order) + } } - }.transpose()); + .transpose(), + ); } results.into_iter().collect() From f897dcdefa71ab2e56397010c448496765189afb Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Fri, 16 Jan 2026 14:55:31 +0100 Subject: [PATCH 03/11] clippy --- crates/orderbook/src/api/get_orders_by_uid.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/orderbook/src/api/get_orders_by_uid.rs b/crates/orderbook/src/api/get_orders_by_uid.rs index 1f5f567142..50d454f2b0 100644 --- a/crates/orderbook/src/api/get_orders_by_uid.rs +++ b/crates/orderbook/src/api/get_orders_by_uid.rs @@ -74,15 +74,14 @@ pub fn get_orders_by_uid( mod tests { use { super::*, - crate::api::response_body, - warp::{Reply, test::request}, + warp::test::request, }; #[tokio::test] async fn get_orders_by_uid_request_ok() { let uid = OrderUid::default(); let request = request() - .path(&format!("/v1/orders")) + .path("/v1/orders") .method("POST") .header("content-type", "application-json") .json(&[uid]); From 0c7b05204cd9b3c305319fbac7925b81d43b097c Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Tue, 20 Jan 2026 18:31:01 +0100 Subject: [PATCH 04/11] fmt --- crates/orderbook/src/api/get_orders_by_uid.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/orderbook/src/api/get_orders_by_uid.rs b/crates/orderbook/src/api/get_orders_by_uid.rs index 50d454f2b0..ecf08d607e 100644 --- a/crates/orderbook/src/api/get_orders_by_uid.rs +++ b/crates/orderbook/src/api/get_orders_by_uid.rs @@ -72,10 +72,7 @@ pub fn get_orders_by_uid( #[cfg(test)] mod tests { - use { - super::*, - warp::test::request, - }; + use {super::*, warp::test::request}; #[tokio::test] async fn get_orders_by_uid_request_ok() { From 01eccfe5eaba3bf050050d4603871a3705be63b2 Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Fri, 23 Jan 2026 12:20:01 +0100 Subject: [PATCH 05/11] Change the bulk endpoint to /v1/orders/lookup to avoid collision --- crates/orderbook/src/api.rs | 8 ++++---- crates/orderbook/src/api/get_orders_by_uid.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/orderbook/src/api.rs b/crates/orderbook/src/api.rs index 9d072d6417..5f0bb5dabf 100644 --- a/crates/orderbook/src/api.rs +++ b/crates/orderbook/src/api.rs @@ -59,14 +59,14 @@ pub fn handle_all_routes( "v1/create_order", box_filter(post_order::post_order(orderbook.clone())), ), - ( - "v1/get_order", - box_filter(get_order_by_uid::get_order_by_uid(orderbook.clone())), - ), ( "v1/get_orders", box_filter(get_orders_by_uid::get_orders_by_uid(orderbook.clone())), ), + ( + "v1/get_order", + box_filter(get_order_by_uid::get_order_by_uid(orderbook.clone())), + ), ( "v1/get_order_status", box_filter(get_order_status::get_status(orderbook.clone())), diff --git a/crates/orderbook/src/api/get_orders_by_uid.rs b/crates/orderbook/src/api/get_orders_by_uid.rs index ecf08d607e..068dd9c7b3 100644 --- a/crates/orderbook/src/api/get_orders_by_uid.rs +++ b/crates/orderbook/src/api/get_orders_by_uid.rs @@ -25,7 +25,7 @@ fn validate(uids: Vec) -> Result, ValidationError> { fn get_orders_by_uid_request() -> impl Filter, ValidationError>,), Error = Rejection> + Clone { - warp::path!("v1" / "orders") + warp::path!("v1" / "orders" / "lookup") .and(warp::post()) .and(extract_payload()) .map(|uids: Vec| validate(uids)) From 9b5d6183801eeb48343a7169c5039aa340ac9fa9 Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Fri, 23 Jan 2026 12:27:22 +0100 Subject: [PATCH 06/11] Fix unit tests --- crates/orderbook/src/api/get_orders_by_uid.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/orderbook/src/api/get_orders_by_uid.rs b/crates/orderbook/src/api/get_orders_by_uid.rs index 068dd9c7b3..a7e5bb811a 100644 --- a/crates/orderbook/src/api/get_orders_by_uid.rs +++ b/crates/orderbook/src/api/get_orders_by_uid.rs @@ -78,7 +78,7 @@ mod tests { async fn get_orders_by_uid_request_ok() { let uid = OrderUid::default(); let request = request() - .path("/v1/orders") + .path("/v1/orders/lookup") .method("POST") .header("content-type", "application-json") .json(&[uid]); From 35c2ce78e85d0a9fed6c818064780d85e4f5d183 Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Fri, 23 Jan 2026 15:58:09 +0100 Subject: [PATCH 07/11] Add negative test case --- crates/orderbook/src/api/get_orders_by_uid.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/crates/orderbook/src/api/get_orders_by_uid.rs b/crates/orderbook/src/api/get_orders_by_uid.rs index a7e5bb811a..f661a26be2 100644 --- a/crates/orderbook/src/api/get_orders_by_uid.rs +++ b/crates/orderbook/src/api/get_orders_by_uid.rs @@ -87,4 +87,22 @@ mod tests { let result = request.filter(&filter).await.unwrap().unwrap(); assert_eq!(result, [uid]); } + + #[tokio::test] + async fn get_orders_by_uid_request_too_many_orders() { + let mut uids = Vec::new(); + for _ in 0..5001 { + uids.push(OrderUid::default()); + } + let request = request() + .path("/v1/orders/lookup") + .method("POST") + .header("content-type", "application-json") + .json(&uids); + + let filter = get_orders_by_uid_request(); + let result = request.filter(&filter).await; + // Assert that the error is a rejection. + assert!(result.is_err()); + } } From 508aa5b9790840f6ae91c5556c3b88004ab0d0ac Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Fri, 30 Jan 2026 17:51:52 +0100 Subject: [PATCH 08/11] Use bulk query for many orders --- crates/database/src/jit_orders.rs | 11 ++++ crates/database/src/orders.rs | 40 ++++++++++----- crates/orderbook/src/api/get_orders_by_uid.rs | 2 +- crates/orderbook/src/database/orders.rs | 50 +++++++++---------- crates/orderbook/src/orderbook.rs | 2 +- 5 files changed, 65 insertions(+), 40 deletions(-) diff --git a/crates/database/src/jit_orders.rs b/crates/database/src/jit_orders.rs index 69a48a8a76..a963fca8ce 100644 --- a/crates/database/src/jit_orders.rs +++ b/crates/database/src/jit_orders.rs @@ -6,6 +6,7 @@ use { TransactionHash, orders::{self, BuyTokenDestination, OrderKind, SellTokenSource, SigningScheme}, }, + futures::stream::BoxStream, sqlx::{ PgConnection, QueryBuilder, @@ -54,6 +55,16 @@ SELECT, sqlx::query_as(QUERY).bind(uid).fetch_optional(ex).await } +#[instrument(skip_all)] +pub async fn get_many_by_id<'a>( + ex: &'a mut PgConnection, + order_ids: &'a [OrderUid], +) -> BoxStream<'a, Result> { + const QUERY: &str = + const_format::concatcp!("SELECT ", SELECT, " FROM ", FROM, " WHERE o.uid = ANY($1)"); + sqlx::query_as(QUERY).bind(order_ids).fetch(ex) +} + #[instrument(skip_all)] pub async fn get_by_tx( ex: &mut PgConnection, diff --git a/crates/database/src/orders.rs b/crates/database/src/orders.rs index 8da392897a..b7933c2e1e 100644 --- a/crates/database/src/orders.rs +++ b/crates/database/src/orders.rs @@ -633,6 +633,21 @@ COALESCE((SELECT executed_fee_token FROM order_execution oe WHERE oe.order_uid = "#; pub const FROM: &str = "orders o"; +const FULL_ORDER_WITH_QUOTE: &str = const_format::concatcp!( + "SELECT ", + SELECT, + ", o_quotes.sell_amount as quote_sell_amount", + ", o_quotes.buy_amount as quote_buy_amount", + ", o_quotes.gas_amount as quote_gas_amount", + ", o_quotes.gas_price as quote_gas_price", + ", o_quotes.sell_token_price as quote_sell_token_price", + ", o_quotes.verified as quote_verified", + ", o_quotes.metadata as quote_metadata", + ", o_quotes.solver as solver", + " FROM ", + FROM, + " LEFT JOIN order_quotes o_quotes ON o.uid = o_quotes.order_uid", +); #[instrument(skip_all)] pub async fn single_full_order_with_quote( @@ -641,22 +656,21 @@ pub async fn single_full_order_with_quote( ) -> Result, sqlx::Error> { #[rustfmt::skip] const QUERY: &str = const_format::concatcp!( - "SELECT ", SELECT, - ", o_quotes.sell_amount as quote_sell_amount", - ", o_quotes.buy_amount as quote_buy_amount", - ", o_quotes.gas_amount as quote_gas_amount", - ", o_quotes.gas_price as quote_gas_price", - ", o_quotes.sell_token_price as quote_sell_token_price", - ", o_quotes.verified as quote_verified", - ", o_quotes.metadata as quote_metadata", - ", o_quotes.solver as solver", - " FROM ", FROM, - " LEFT JOIN order_quotes o_quotes ON o.uid = o_quotes.order_uid", - " WHERE o.uid = $1", - ); + FULL_ORDER_WITH_QUOTE, + " WHERE o.uid = $1" + ); sqlx::query_as(QUERY).bind(uid).fetch_optional(ex).await } +#[instrument(skip_all)] +pub async fn many_full_orders_with_quotes<'a>( + ex: &'a mut PgConnection, + order_ids: &'a [OrderUid], +) -> BoxStream<'a, Result> { + const QUERY: &str = const_format::concatcp!(FULL_ORDER_WITH_QUOTE, " WHERE o.uid = ANY($1)"); + sqlx::query_as(QUERY).bind(order_ids).fetch(ex) +} + // Partial query for getting the log indices of events of a single settlement. // // This will fail if we ever have multiple settlements in the same transaction diff --git a/crates/orderbook/src/api/get_orders_by_uid.rs b/crates/orderbook/src/api/get_orders_by_uid.rs index f661a26be2..e34a8fe7d0 100644 --- a/crates/orderbook/src/api/get_orders_by_uid.rs +++ b/crates/orderbook/src/api/get_orders_by_uid.rs @@ -31,7 +31,7 @@ fn get_orders_by_uid_request() .map(|uids: Vec| validate(uids)) } -pub fn get_orders_by_uid_response(result: Result>>) -> super::ApiReply { +pub fn get_orders_by_uid_response(result: Result>) -> super::ApiReply { let orders = match result { Ok(orders) => orders, Err(err) => { diff --git a/crates/orderbook/src/database/orders.rs b/crates/orderbook/src/database/orders.rs index db067b99a8..12196540a3 100644 --- a/crates/orderbook/src/database/orders.rs +++ b/crates/orderbook/src/database/orders.rs @@ -75,7 +75,7 @@ pub trait OrderStoring: Send + Sync { ) -> Result>; async fn latest_order_event(&self, order_uid: &OrderUid) -> Result>; async fn single_order(&self, uid: &OrderUid) -> Result>; - async fn many_orders(&self, uids: &[OrderUid]) -> Result>>; + async fn many_orders(&self, uids: &[OrderUid]) -> Result>; } #[derive(Debug)] @@ -314,36 +314,36 @@ impl OrderStoring for Postgres { .transpose() } - async fn many_orders(&self, uids: &[OrderUid]) -> Result>> { + async fn many_orders(&self, uids: &[OrderUid]) -> Result> { let _timer = super::Metrics::get() .database_queries .with_label_values(&["many_orders"]) .start_timer(); let mut ex = self.pool.acquire().await?; - let mut results = Vec::new(); - - for uid in uids { - results.push( - match orders::single_full_order_with_quote(&mut ex, &ByteArray(uid.0)).await? { - Some(order_with_quote) => { - let (order, quote) = order_with_quote.into_order_and_quote(); - Some(full_order_with_quote_into_model_order( - order, - quote.as_ref(), - )) - } - None => { - // try to find the order in the JIT orders table - database::jit_orders::get_by_id(&mut ex, &ByteArray(uid.0)) - .await? - .map(full_order_into_model_order) - } - } - .transpose(), - ); - } + let uids = uids + .into_iter() + .map(|uid| ByteArray(uid.0)) + .collect::>(); + + let orders: Vec> = + orders::many_full_orders_with_quotes(&mut ex, uids.as_slice()) + .await + .filter_map(async |order| order.ok()) + .map(|order| { + let (order, quote) = order.into_order_and_quote(); + full_order_with_quote_into_model_order(order, quote.as_ref()) + }) + .collect() + .await; + let jit_orders: Vec> = + database::jit_orders::get_many_by_id(&mut ex, uids.as_slice()) + .await + .filter_map(async |order| order.ok()) + .map(|order| full_order_into_model_order(order)) + .collect() + .await; - results.into_iter().collect() + orders.into_iter().chain(jit_orders).collect() } async fn orders_for_tx(&self, tx_hash: &B256) -> Result> { diff --git a/crates/orderbook/src/orderbook.rs b/crates/orderbook/src/orderbook.rs index 4764606592..c69ab72fa7 100644 --- a/crates/orderbook/src/orderbook.rs +++ b/crates/orderbook/src/orderbook.rs @@ -494,7 +494,7 @@ impl Orderbook { self.database_replica.single_order(uid).await } - pub async fn get_orders(&self, uids: &[OrderUid]) -> Result>> { + pub async fn get_orders(&self, uids: &[OrderUid]) -> Result> { self.database_replica.many_orders(uids).await } From 17a26c3a614b67221bd47043a319e8a6e426a785 Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Thu, 5 Feb 2026 14:58:28 +0100 Subject: [PATCH 09/11] Clippy fix --- crates/database/src/trades.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/database/src/trades.rs b/crates/database/src/trades.rs index 7c7430b128..0d3080cf7d 100644 --- a/crates/database/src/trades.rs +++ b/crates/database/src/trades.rs @@ -705,7 +705,8 @@ mod tests { } // Sort expected trades by block_number DESC (matching query ORDER BY) - expected_trades.sort_by(|a, b| b.block_number.cmp(&a.block_number)); + expected_trades.sort_by_key(|b| std::cmp::Reverse(b.block_number)); + // Test limit: get first 2 trades (blocks 4 and 3 in DESC order) let result = trades(&mut db, Some(&owner), None, 0, 2) From 3dbd193c2a4703f7672c713a7ba94245e2c5d6ee Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Thu, 5 Feb 2026 15:17:54 +0100 Subject: [PATCH 10/11] fmt and clippy --- crates/database/src/trades.rs | 1 - crates/orderbook/src/api/get_orders_by_uid.rs | 2 +- crates/orderbook/src/database/orders.rs | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/database/src/trades.rs b/crates/database/src/trades.rs index 0d3080cf7d..4b5c66ddf8 100644 --- a/crates/database/src/trades.rs +++ b/crates/database/src/trades.rs @@ -707,7 +707,6 @@ mod tests { // Sort expected trades by block_number DESC (matching query ORDER BY) expected_trades.sort_by_key(|b| std::cmp::Reverse(b.block_number)); - // Test limit: get first 2 trades (blocks 4 and 3 in DESC order) let result = trades(&mut db, Some(&owner), None, 0, 2) .into_inner() diff --git a/crates/orderbook/src/api/get_orders_by_uid.rs b/crates/orderbook/src/api/get_orders_by_uid.rs index e34a8fe7d0..e4d5cfc755 100644 --- a/crates/orderbook/src/api/get_orders_by_uid.rs +++ b/crates/orderbook/src/api/get_orders_by_uid.rs @@ -91,7 +91,7 @@ mod tests { #[tokio::test] async fn get_orders_by_uid_request_too_many_orders() { let mut uids = Vec::new(); - for _ in 0..5001 { + for _ in 0..(MAX_ORDERS_LIMIT + 1) { uids.push(OrderUid::default()); } let request = request() diff --git a/crates/orderbook/src/database/orders.rs b/crates/orderbook/src/database/orders.rs index 12196540a3..bdd60b1601 100644 --- a/crates/orderbook/src/database/orders.rs +++ b/crates/orderbook/src/database/orders.rs @@ -321,7 +321,7 @@ impl OrderStoring for Postgres { .start_timer(); let mut ex = self.pool.acquire().await?; let uids = uids - .into_iter() + .iter() .map(|uid| ByteArray(uid.0)) .collect::>(); @@ -339,7 +339,7 @@ impl OrderStoring for Postgres { database::jit_orders::get_many_by_id(&mut ex, uids.as_slice()) .await .filter_map(async |order| order.ok()) - .map(|order| full_order_into_model_order(order)) + .map(full_order_into_model_order) .collect() .await; From 5de3e9c245f41cd00386aa43064cebe3ea782c11 Mon Sep 17 00:00:00 2001 From: Marcin Szymczak Date: Thu, 5 Feb 2026 16:37:48 +0100 Subject: [PATCH 11/11] fmt --- crates/orderbook/src/database/orders.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/orderbook/src/database/orders.rs b/crates/orderbook/src/database/orders.rs index bdd60b1601..6b1e3eb662 100644 --- a/crates/orderbook/src/database/orders.rs +++ b/crates/orderbook/src/database/orders.rs @@ -320,10 +320,7 @@ impl OrderStoring for Postgres { .with_label_values(&["many_orders"]) .start_timer(); let mut ex = self.pool.acquire().await?; - let uids = uids - .iter() - .map(|uid| ByteArray(uid.0)) - .collect::>(); + let uids = uids.iter().map(|uid| ByteArray(uid.0)).collect::>(); let orders: Vec> = orders::many_full_orders_with_quotes(&mut ex, uids.as_slice())