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
16 changes: 3 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/rain.orderbook
74 changes: 71 additions & 3 deletions src/routes/orders/get_by_owner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use alloy::primitives::Address;
use rain_orderbook_common::raindex_client::orders::GetOrdersFilters;
use rocket::serde::json::Json;
use rocket::State;
use std::time::Instant;
use tracing::Instrument;

pub(crate) async fn process_get_orders_by_owner(
Expand All @@ -22,30 +23,48 @@ pub(crate) async fn process_get_orders_by_owner(
let filters = GetOrdersFilters {
owners: vec![address],
active: Some(true),
has_positive_output_vault_balance: Some(true),
..Default::default()
};

let total_start = Instant::now();
let page_num = page.unwrap_or(1);
let effective_page_size = page_size
.unwrap_or(DEFAULT_PAGE_SIZE as u16)
.min(MAX_PAGE_SIZE);

let orders_stage_start = Instant::now();
let (orders, total_count) = ds
.get_orders_list(filters, Some(page_num), Some(effective_page_size))
.await?;
let orders_stage_duration_ms = orders_stage_start.elapsed().as_millis();

let quotes_stage_start = Instant::now();
tracing::info!(
quoted_orders = orders.len(),
total_orders = orders.len(),
"fetching batched quotes for orders by owner"
);
let quote_results = ds.get_order_quotes_batch(&orders).await;
let quotes_stage_duration_ms = quotes_stage_start.elapsed().as_millis();

build_orders_list_response(
let response = build_orders_list_response(
&orders,
total_count,
page_num.into(),
effective_page_size.into(),
quote_results,
)
)?;
tracing::info!(
page = page_num,
page_size = effective_page_size,
returned_orders = response.orders.len(),
total_orders = total_count,
orders_stage_duration_ms,
quotes_stage_duration_ms,
total_duration_ms = total_start.elapsed().as_millis(),
"orders by owner request processed"
);
Ok(response)
}

#[utoipa::path(
Expand Down Expand Up @@ -99,7 +118,35 @@ mod tests {
};
use crate::routes::orders::test_fixtures::MockOrdersListDataSource;
use crate::test_helpers::{basic_auth_header, seed_api_key, TestClientBuilder};
use async_trait::async_trait;
use rain_orderbook_common::raindex_client::order_quotes::RaindexOrderQuote;
use rain_orderbook_common::raindex_client::orders::RaindexOrder;
use rocket::http::{Header, Status};
use std::sync::{Arc, Mutex};

struct CapturingOrdersListDataSource {
filters: Arc<Mutex<Vec<GetOrdersFilters>>>,
}

#[async_trait]
impl OrdersListDataSource for CapturingOrdersListDataSource {
async fn get_orders_list(
&self,
filters: GetOrdersFilters,
_page: Option<u16>,
_page_size: Option<u16>,
) -> Result<(Vec<RaindexOrder>, u32), ApiError> {
self.filters.lock().expect("lock filters").push(filters);
Ok((vec![], 0))
}

async fn get_order_quotes(
&self,
_order: &RaindexOrder,
) -> Result<Vec<RaindexOrderQuote>, ApiError> {
Ok(vec![])
}
}

#[rocket::async_test]
async fn test_process_get_orders_by_owner_success() {
Expand Down Expand Up @@ -144,6 +191,27 @@ mod tests {
assert_eq!(result.pagination.total_pages, 0);
}

#[rocket::async_test]
async fn test_process_get_orders_by_owner_filters_positive_output_vault_balance() {
let filters = Arc::new(Mutex::new(Vec::new()));
let ds = CapturingOrdersListDataSource {
filters: Arc::clone(&filters),
};
let addr: Address = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
.parse()
.unwrap();

process_get_orders_by_owner(&ds, addr, None, None)
.await
.unwrap();

let filters = filters.lock().expect("lock filters");
assert_eq!(filters.len(), 1);
assert_eq!(filters[0].owners, vec![addr]);
assert_eq!(filters[0].active, Some(true));
assert_eq!(filters[0].has_positive_output_vault_balance, Some(true));
}

#[rocket::async_test]
async fn test_process_get_orders_by_owner_quote_failure_shows_dash() {
let ds = MockOrdersListDataSource {
Expand Down
76 changes: 73 additions & 3 deletions src/routes/orders/get_by_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use rain_orderbook_common::raindex_client::orders::GetOrdersFilters;
use rain_orderbook_common::raindex_client::orders::GetOrdersTokenFilter;
use rocket::serde::json::Json;
use rocket::State;
use std::time::Instant;
use tracing::Instrument;

pub(crate) async fn process_get_orders_by_token(
Expand Down Expand Up @@ -39,30 +40,48 @@ pub(crate) async fn process_get_orders_by_token(
let filters = GetOrdersFilters {
active: Some(true),
tokens: Some(token_filter),
has_positive_output_vault_balance: Some(true),
..Default::default()
};

let total_start = Instant::now();
let page_num = page.unwrap_or(1);
let effective_page_size = page_size
.unwrap_or(DEFAULT_PAGE_SIZE as u16)
.min(MAX_PAGE_SIZE);

let orders_stage_start = Instant::now();
let (orders, total_count) = ds
.get_orders_list(filters, Some(page_num), Some(effective_page_size))
.await?;
let orders_stage_duration_ms = orders_stage_start.elapsed().as_millis();

let quotes_stage_start = Instant::now();
tracing::info!(
quoted_orders = orders.len(),
total_orders = orders.len(),
"fetching batched quotes for orders by token"
);
let quote_results = ds.get_order_quotes_batch(&orders).await;
let quotes_stage_duration_ms = quotes_stage_start.elapsed().as_millis();

build_orders_list_response(
let response = build_orders_list_response(
&orders,
total_count,
page_num.into(),
effective_page_size.into(),
quote_results,
)
)?;
tracing::info!(
page = page_num,
page_size = effective_page_size,
returned_orders = response.orders.len(),
total_orders = total_count,
orders_stage_duration_ms,
quotes_stage_duration_ms,
total_duration_ms = total_start.elapsed().as_millis(),
"orders by token request processed"
);
Ok(response)
}

#[utoipa::path(
Expand Down Expand Up @@ -117,7 +136,35 @@ mod tests {
};
use crate::routes::orders::test_fixtures::MockOrdersListDataSource;
use crate::test_helpers::{basic_auth_header, seed_api_key, TestClientBuilder};
use async_trait::async_trait;
use rain_orderbook_common::raindex_client::order_quotes::RaindexOrderQuote;
use rain_orderbook_common::raindex_client::orders::RaindexOrder;
use rocket::http::{Header, Status};
use std::sync::{Arc, Mutex};

struct CapturingOrdersListDataSource {
filters: Arc<Mutex<Vec<GetOrdersFilters>>>,
}

#[async_trait]
impl OrdersListDataSource for CapturingOrdersListDataSource {
async fn get_orders_list(
&self,
filters: GetOrdersFilters,
_page: Option<u16>,
_page_size: Option<u16>,
) -> Result<(Vec<RaindexOrder>, u32), ApiError> {
self.filters.lock().expect("lock filters").push(filters);
Ok((vec![], 0))
}

async fn get_order_quotes(
&self,
_order: &RaindexOrder,
) -> Result<Vec<RaindexOrderQuote>, ApiError> {
Ok(vec![])
}
}

#[rocket::async_test]
async fn test_process_get_orders_by_token_success() {
Expand Down Expand Up @@ -162,6 +209,29 @@ mod tests {
assert_eq!(result.pagination.total_pages, 0);
}

#[rocket::async_test]
async fn test_process_get_orders_by_token_filters_positive_output_vault_balance() {
let filters = Arc::new(Mutex::new(Vec::new()));
let ds = CapturingOrdersListDataSource {
filters: Arc::clone(&filters),
};
let addr: Address = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
.parse()
.unwrap();

process_get_orders_by_token(&ds, addr, Some(OrderSide::Output), None, None)
.await
.unwrap();

let filters = filters.lock().expect("lock filters");
assert_eq!(filters.len(), 1);
assert_eq!(filters[0].active, Some(true));
assert_eq!(filters[0].has_positive_output_vault_balance, Some(true));
let token_filter = filters[0].tokens.as_ref().expect("token filter");
assert_eq!(token_filter.inputs, None);
assert_eq!(token_filter.outputs, Some(vec![addr]));
}

#[rocket::async_test]
async fn test_process_get_orders_by_token_quote_failure_shows_dash() {
let ds = MockOrdersListDataSource {
Expand Down
5 changes: 4 additions & 1 deletion src/routes/orders/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,10 @@ impl<'a> OrdersListDataSource for RaindexOrdersListDataSource<'a> {
.first()
.map(RaindexOrder::chain_id)
.unwrap_or_default();
fetch_order_quotes_batch(orders, None, None)
// Use small chunk size (4) to avoid exceeding public RPC eth_call gas
// limits, which would trigger expensive probe-and-split retries in the
// quote library.
fetch_order_quotes_batch(orders, None, Some(4))
.await
.map_err(|error| {
tracing::error!(
Expand Down
24 changes: 17 additions & 7 deletions src/routes/swap/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ use rain_orderbook_common::raindex_client::take_orders::TakeOrdersRequest;
use rain_orderbook_common::raindex_client::RaindexClient;
use rain_orderbook_common::raindex_client::RaindexError;
use rain_orderbook_common::take_orders::{
build_take_order_candidates_for_pair, TakeOrderCandidate,
build_take_order_candidates_for_pair, NoopInjector, TakeOrderCandidate,
};
use rocket::Route;

const SWAP_QUOTE_CHUNK_SIZE: u32 = 4;

#[async_trait]
pub(crate) trait SwapDataSource: Send + Sync {
async fn validate_supported_tokens(
Expand Down Expand Up @@ -109,12 +111,20 @@ impl<'a> SwapDataSource for RaindexSwapDataSource<'a> {
input_token: Address,
output_token: Address,
) -> Result<Vec<TakeOrderCandidate>, ApiError> {
build_take_order_candidates_for_pair(orders, input_token, output_token, None, None)
.await
.map_err(|e| {
tracing::error!(error = %e, "failed to build order candidates");
ApiError::Internal("failed to build order candidates".into())
})
build_take_order_candidates_for_pair(
orders,
input_token,
output_token,
None,
Some(SWAP_QUOTE_CHUNK_SIZE),
Address::ZERO,
&NoopInjector,
)
.await
.map_err(|e| {
tracing::error!(error = %e, "failed to build order candidates");
ApiError::Internal("failed to build order candidates".into())
})
}

async fn get_calldata(
Expand Down
4 changes: 2 additions & 2 deletions src/routes/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ mod tests {

#[rocket::async_test]
async fn test_get_tokens_returns_multiple_tokens() {
let settings = r#"version: 4
let settings = r#"version: 5
networks:
base:
rpcs:
Expand Down Expand Up @@ -180,7 +180,7 @@ tokens:

#[rocket::async_test]
async fn test_get_tokens_adds_name_and_isin_from_remote_tokens() {
let settings = r#"version: 4
let settings = r#"version: 5
networks:
base:
rpcs:
Expand Down
Loading
Loading