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
19 changes: 16 additions & 3 deletions crates/cli/src/subgraph.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use clap::Args;
use rain_orderbook_common::subgraph::SubgraphArgs;
use rain_orderbook_subgraph_client::{
types::common::{SgBytes, SgOrdersListFilterArgs, SgVaultsListFilterArgs},
types::common::{
SgBytes, SgOrdersListFilterArgs, SgOrdersTokensFilterArgs, SgVaultsListFilterArgs,
},
SgPaginationArgs,
};

Expand Down Expand Up @@ -93,11 +95,19 @@ pub struct CliFilterArgs {

impl From<CliFilterArgs> for SgOrdersListFilterArgs {
fn from(val: CliFilterArgs) -> Self {
let tokens = if val.tokens.is_empty() {
None
} else {
Some(SgOrdersTokensFilterArgs {
inputs: val.tokens.clone(),
outputs: val.tokens.clone(),
})
};
Self {
owners: val.owners.into_iter().map(SgBytes).collect(),
active: val.active,
order_hash: val.order_hash.map(SgBytes),
tokens: val.tokens,
tokens,
}
}
}
Expand Down Expand Up @@ -155,7 +165,10 @@ mod tests {
);
assert_eq!(filter_args.active, Some(true));
assert_eq!(filter_args.order_hash, Some(SgBytes("0x789".to_string())));
assert_eq!(filter_args.tokens, tokens);
assert!(filter_args.tokens.is_some());
let tokens_filter = filter_args.tokens.unwrap();
assert_eq!(tokens_filter.inputs, tokens);
assert_eq!(tokens_filter.outputs, tokens_filter.inputs);
}

#[test]
Expand Down
107 changes: 90 additions & 17 deletions crates/common/src/local_db/query/fetch_orders/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,20 @@ pub enum FetchOrdersActiveFilter {
Inactive,
}

#[derive(Debug, Clone, Default)]
pub struct FetchOrdersTokensFilter {
pub inputs: Vec<Address>,
pub outputs: Vec<Address>,
}

#[derive(Debug, Clone, Default)]
pub struct FetchOrdersArgs {
pub chain_ids: Vec<u32>,
pub orderbook_addresses: Vec<Address>,
pub filter: FetchOrdersActiveFilter,
pub owners: Vec<Address>,
pub order_hash: Option<B256>,
pub tokens: Vec<Address>,
pub tokens: FetchOrdersTokensFilter,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
Expand Down Expand Up @@ -51,9 +57,11 @@ const OWNERS_CLAUSE_BODY: &str = "AND l.order_owner IN ({list})";
const ORDER_HASH_CLAUSE: &str = "/*ORDER_HASH_CLAUSE*/";
const ORDER_HASH_CLAUSE_BODY: &str = "AND COALESCE(la.order_hash, l.order_hash) = {param}";

const TOKENS_CLAUSE: &str = "/*TOKENS_CLAUSE*/";
const TOKENS_CLAUSE_BODY: &str =
"AND EXISTS (\n SELECT 1 FROM order_ios io2\n WHERE io2.chain_id = l.chain_id\n AND io2.orderbook_address = l.orderbook_address\n AND io2.transaction_hash = la.transaction_hash\n AND io2.log_index = la.log_index\n AND io2.token IN ({list})\n )";
const INPUT_TOKENS_CLAUSE: &str = "/*INPUT_TOKENS_CLAUSE*/";
const INPUT_TOKENS_CLAUSE_BODY: &str = "AND EXISTS (\n SELECT 1 FROM order_ios io2\n WHERE io2.chain_id = l.chain_id\n AND io2.orderbook_address = l.orderbook_address\n AND io2.transaction_hash = la.transaction_hash\n AND io2.log_index = la.log_index\n AND lower(io2.io_type) = 'input'\n AND io2.token IN ({list})\n )";

const OUTPUT_TOKENS_CLAUSE: &str = "/*OUTPUT_TOKENS_CLAUSE*/";
const OUTPUT_TOKENS_CLAUSE_BODY: &str = "AND EXISTS (\n SELECT 1 FROM order_ios io2\n WHERE io2.chain_id = l.chain_id\n AND io2.orderbook_address = l.orderbook_address\n AND io2.transaction_hash = la.transaction_hash\n AND io2.log_index = la.log_index\n AND lower(io2.io_type) = 'output'\n AND io2.token IN ({list})\n )";

const MAIN_CHAIN_IDS_CLAUSE: &str = "/*MAIN_CHAIN_IDS_CLAUSE*/";
const MAIN_CHAIN_IDS_CLAUSE_BODY: &str = "AND oe.chain_id IN ({list})";
Expand Down Expand Up @@ -172,14 +180,23 @@ pub fn build_fetch_orders_stmt(args: &FetchOrdersArgs) -> Result<SqlStatement, S
let order_hash_val = args.order_hash.as_ref().map(|hash| SqlValue::from(*hash));
stmt.bind_param_clause(ORDER_HASH_CLAUSE, ORDER_HASH_CLAUSE_BODY, order_hash_val)?;

// Tokens list
let mut tokens_lower = args.tokens.clone();
tokens_lower.sort();
tokens_lower.dedup();
// Directional token filters
let mut input_tokens_lower = args.tokens.inputs.clone();
input_tokens_lower.sort();
input_tokens_lower.dedup();
stmt.bind_list_clause(
INPUT_TOKENS_CLAUSE,
INPUT_TOKENS_CLAUSE_BODY,
input_tokens_lower.into_iter().map(SqlValue::from),
)?;

let mut output_tokens_lower = args.tokens.outputs.clone();
output_tokens_lower.sort();
output_tokens_lower.dedup();
stmt.bind_list_clause(
TOKENS_CLAUSE,
TOKENS_CLAUSE_BODY,
tokens_lower.into_iter().map(SqlValue::from),
OUTPUT_TOKENS_CLAUSE,
OUTPUT_TOKENS_CLAUSE_BODY,
output_tokens_lower.into_iter().map(SqlValue::from),
)?;

Ok(stmt)
Expand All @@ -201,7 +218,8 @@ mod tests {
let stmt = build_fetch_orders_stmt(&args).unwrap();
assert!(stmt.sql.contains("?1 = 'all'"));
assert!(!stmt.sql.contains(OWNERS_CLAUSE));
assert!(!stmt.sql.contains(TOKENS_CLAUSE));
assert!(!stmt.sql.contains(INPUT_TOKENS_CLAUSE));
assert!(!stmt.sql.contains(OUTPUT_TOKENS_CLAUSE));
assert!(!stmt.sql.contains(ORDER_HASH_CLAUSE));
}

Expand All @@ -219,10 +237,13 @@ mod tests {
order_hash: Some(b256!(
"0x00000000000000000000000000000000000000000000000000000000deadbeef"
)),
tokens: vec![
address!("0xF3dEe5b36E3402893e6953A8670E37D329683ABB"),
address!("0x7D3Dd01feD0C16A6c353ce3BACF26467726EF96e"),
],
tokens: FetchOrdersTokensFilter {
inputs: vec![
address!("0xF3dEe5b36E3402893e6953A8670E37D329683ABB"),
address!("0x7D3Dd01feD0C16A6c353ce3BACF26467726EF96e"),
],
outputs: vec![address!("0xF3dEe5b36E3402893e6953A8670E37D329683ABB")],
},
};

let stmt = build_fetch_orders_stmt(&args).unwrap();
Expand All @@ -232,14 +253,66 @@ mod tests {

// Owners clause present, tokens clause present, order hash clause present
assert!(!stmt.sql.contains(OWNERS_CLAUSE));
assert!(!stmt.sql.contains(TOKENS_CLAUSE));
assert!(!stmt.sql.contains(INPUT_TOKENS_CLAUSE));
assert!(!stmt.sql.contains(OUTPUT_TOKENS_CLAUSE));
assert!(!stmt.sql.contains(ORDER_HASH_CLAUSE));

// Params include active filter followed by chain/orderbook filters
assert!(stmt.params.len() >= 3);
assert_eq!(stmt.params[0], SqlValue::Text("active".to_string()));
}

#[test]
fn input_tokens_clause_only_when_inputs_present() {
let args = FetchOrdersArgs {
chain_ids: vec![1],
tokens: FetchOrdersTokensFilter {
inputs: vec![address!("0x00000000000000000000000000000000000000aa")],
outputs: vec![],
},
..FetchOrdersArgs::default()
};

let stmt = build_fetch_orders_stmt(&args).unwrap();
assert!(stmt.sql.contains("AND lower(io2.io_type) = 'input'"));
assert!(stmt.sql.contains("AND io2.token IN ("));
assert!(!stmt.sql.contains("AND lower(io2.io_type) = 'output'"));
}

#[test]
fn output_tokens_clause_only_when_outputs_present() {
let args = FetchOrdersArgs {
chain_ids: vec![1],
tokens: FetchOrdersTokensFilter {
inputs: vec![],
outputs: vec![address!("0x00000000000000000000000000000000000000bb")],
},
..FetchOrdersArgs::default()
};

let stmt = build_fetch_orders_stmt(&args).unwrap();
assert!(stmt.sql.contains("AND lower(io2.io_type) = 'output'"));
assert!(stmt.sql.contains("AND io2.token IN ("));
assert!(!stmt.sql.contains("AND lower(io2.io_type) = 'input'"));
}

#[test]
fn both_token_clauses_when_inputs_and_outputs_present() {
let args = FetchOrdersArgs {
chain_ids: vec![1],
tokens: FetchOrdersTokensFilter {
inputs: vec![address!("0x00000000000000000000000000000000000000aa")],
outputs: vec![address!("0x00000000000000000000000000000000000000bb")],
},
..FetchOrdersArgs::default()
};

let stmt = build_fetch_orders_stmt(&args).unwrap();
assert!(stmt.sql.contains("AND lower(io2.io_type) = 'input'"));
assert!(stmt.sql.contains("AND lower(io2.io_type) = 'output'"));
assert!(stmt.sql.matches("AND io2.token IN (").count() >= 2);
}

#[test]
fn filter_inactive_string() {
let args = FetchOrdersArgs {
Expand Down
3 changes: 2 additions & 1 deletion crates/common/src/local_db/query/fetch_orders/query.sql
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,8 @@ WHERE
)
/*OWNERS_CLAUSE*/
/*ORDER_HASH_CLAUSE*/
/*TOKENS_CLAUSE*/
/*INPUT_TOKENS_CLAUSE*/
/*OUTPUT_TOKENS_CLAUSE*/
GROUP BY
l.chain_id,
COALESCE(la.order_hash, l.order_hash),
Expand Down
23 changes: 18 additions & 5 deletions crates/common/src/raindex_client/local_db/query/fetch_orders.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::local_db::query::fetch_orders::{
build_fetch_orders_stmt, FetchOrdersActiveFilter, FetchOrdersArgs, LocalDbOrder,
build_fetch_orders_stmt, FetchOrdersActiveFilter, FetchOrdersArgs, FetchOrdersTokensFilter,
LocalDbOrder,
};
use crate::local_db::query::{LocalDbQueryError, LocalDbQueryExecutor};
use crate::raindex_client::orders::GetOrdersFilters;
Expand All @@ -11,12 +12,19 @@ impl From<GetOrdersFilters> for FetchOrdersArgs {
Some(false) => FetchOrdersActiveFilter::Inactive,
None => FetchOrdersActiveFilter::All,
};
let tokens = filters
.tokens
.map(|tokens| FetchOrdersTokensFilter {
inputs: tokens.inputs.unwrap_or_default(),
outputs: tokens.outputs.unwrap_or_default(),
})
.unwrap_or_default();

FetchOrdersArgs {
filter,
owners: filters.owners,
order_hash: filters.order_hash,
tokens: filters.tokens.unwrap_or_default(),
tokens,
..FetchOrdersArgs::default()
}
}
Expand All @@ -33,6 +41,7 @@ pub async fn fetch_orders<E: LocalDbQueryExecutor + ?Sized>(
#[cfg(test)]
mod tests {
use super::*;
use crate::raindex_client::orders::GetOrdersTokenFilter;
use alloy::primitives::{address, b256, Address};
use std::str::FromStr;

Expand All @@ -46,7 +55,10 @@ mod tests {
order_hash: Some(b256!(
"0x00000000000000000000000000000000000000000000000000000000deadbeef"
)),
tokens: Some(vec![token]),
tokens: Some(GetOrdersTokenFilter {
inputs: Some(vec![token]),
outputs: None,
}),
};
let args: FetchOrdersArgs = filters.into();
// Active mapping
Expand All @@ -56,9 +68,10 @@ mod tests {
vec![address!("0x0123456789abcdef0123456789abcdef01234567")]
);
assert_eq!(
args.tokens,
args.tokens.inputs,
vec![address!("0x89abcdef0123456789abcdef0123456789abcdef")]
);
assert_eq!(args.tokens.outputs, Vec::<Address>::new());
// Order hash string preserved
assert_eq!(
args.order_hash,
Expand Down Expand Up @@ -94,7 +107,7 @@ mod tests {
address!("0x0000000000000000000000000000000000000abc"),
address!("0x00000000000000000000000000000000000000ef"),
];
args.tokens = vec![address!("0x00000000000000000000000000000000000000aa")];
args.tokens.inputs = vec![address!("0x00000000000000000000000000000000000000aa")];
args.order_hash = Some(b256!(
"0x0000000000000000000000000000000000000000000000000000000000000001"
));
Expand Down
Loading