Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
use super::*;
use serde::{Deserialize, Serialize};

const QUERY: &str = include_str!("query.sql");

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LocalDbOrderTrade {
Copy link
Copy Markdown
Collaborator

@0xgleb 0xgleb Oct 7, 2025

Choose a reason for hiding this comment

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

looks like

  • we don't need the alias stuff everywhere since it's already snake case
  • consider using
    • an enum for trade_kind
    • Address for all address fields (orderbook_address, order_owner, transaction_sender, input_token, output_token)
    • B256 for 32-byte values (order_hash, transaction_hash, input_vault_id, output_vault_id, trade_id)
    • Float for amounts (input_delta, input_running_balance, output_delta, output_running_balance)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

will be getting back to this with this issue #2182

#[serde(alias = "trade_kind")]
pub trade_kind: String,
#[serde(alias = "orderbook_address")]
pub orderbook_address: String,
#[serde(alias = "order_hash")]
pub order_hash: String,
#[serde(alias = "order_owner")]
pub order_owner: String,
#[serde(alias = "order_nonce")]
pub order_nonce: String,
#[serde(alias = "transaction_hash")]
pub transaction_hash: String,
#[serde(alias = "log_index")]
pub log_index: u64,
#[serde(alias = "block_number")]
pub block_number: u64,
#[serde(alias = "block_timestamp")]
pub block_timestamp: u64,
#[serde(alias = "transaction_sender")]
pub transaction_sender: String,
#[serde(alias = "input_vault_id")]
pub input_vault_id: String,
#[serde(alias = "input_token")]
pub input_token: String,
#[serde(alias = "input_token_name")]
pub input_token_name: Option<String>,
#[serde(alias = "input_token_symbol")]
pub input_token_symbol: Option<String>,
#[serde(alias = "input_token_decimals")]
pub input_token_decimals: Option<u8>,
#[serde(alias = "input_delta")]
pub input_delta: String,
#[serde(alias = "input_running_balance")]
pub input_running_balance: Option<String>,
#[serde(alias = "output_vault_id")]
pub output_vault_id: String,
#[serde(alias = "output_token")]
pub output_token: String,
#[serde(alias = "output_token_name")]
pub output_token_name: Option<String>,
#[serde(alias = "output_token_symbol")]
pub output_token_symbol: Option<String>,
#[serde(alias = "output_token_decimals")]
pub output_token_decimals: Option<u8>,
#[serde(alias = "output_delta")]
pub output_delta: String,
#[serde(alias = "output_running_balance")]
pub output_running_balance: Option<String>,
#[serde(alias = "trade_id")]
pub trade_id: String,
}

impl LocalDbQuery {
pub async fn fetch_order_trades(
db_callback: &js_sys::Function,
chain_id: u32,
order_hash: &str,
start_timestamp: Option<u64>,
end_timestamp: Option<u64>,
) -> Result<Vec<LocalDbOrderTrade>, LocalDbQueryError> {
let sanitize_literal = |value: &str| value.replace('\'', "''");

let order_hash = sanitize_literal(&order_hash.trim().to_lowercase());

let filter_start_timestamp = start_timestamp
.map(|ts| format!("\nAND block_timestamp >= {}\n", ts))
.unwrap_or_default();
let filter_end_timestamp = end_timestamp
.map(|ts| format!("\nAND block_timestamp <= {}\n", ts))
.unwrap_or_default();

let sql = QUERY
.replace("'?order_hash'", &format!("'{}'", order_hash))
.replace("'?chain_id'", &chain_id.to_string())
.replace("?filter_start_timestamp", &filter_start_timestamp)
.replace("?filter_end_timestamp", &filter_end_timestamp);
Comment on lines +79 to +83
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Oct 6, 2025

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

Verify SQL placeholder replacement pattern consistency.

The placeholder replacement uses different quoting patterns:

  • Line 80: '?order_hash''{}' (quotes in both pattern and replacement)
  • Line 81: '?chain_id'{} (quotes only in pattern)
  • Lines 82-83: ?filter_start_timestamp → filter string (no quotes in pattern)

This inconsistency could lead to SQL syntax errors if not carefully managed.

Run this script to check if other query modules follow the same pattern:


🏁 Script executed:

#!/bin/bash
# Check placeholder patterns in other local_db query modules
fd -e sql -e rs . crates/common/src/raindex_client/local_db/query/ --exec rg -n "replace\(" {} \; | head -50

Length of output: 2071


Extract a helper for SQL placeholder replacement Consolidate the varying .replace() patterns ('?param' vs ?param) and apply quoting consistently via a type-aware utility to remove ad-hoc formatting.

🤖 Prompt for AI Agents
In crates/common/src/raindex_client/local_db/query/fetch_order_trades/mod.rs
around lines 79 to 83, the SQL string is built with multiple ad-hoc .replace()
calls that mix quoted ('?param') and unquoted (?param) patterns and perform
manual quoting/formatting; extract a small helper to centralize placeholder
replacement and quoting (e.g., replace_placeholder(sql: &str, name: &str, value:
impl ToSqlLiteral)) that accepts a parameter name and a type-aware value,
escapes and quotes strings, leaves numeric/boolean types unquoted, and replaces
both "'?name'" and "?name" occurrences consistently; then call that helper for
order_hash, chain_id, filter_start_timestamp, filter_end_timestamp to remove
inline formatting and ensure correct, safe SQL literal insertion.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this is fine for now

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@findolor, understood! I've noted your decision. Feel free to revisit this optimization if needed in the future.


LocalDbQuery::execute_query_json::<Vec<LocalDbOrderTrade>>(db_callback, &sql).await
}
}

#[cfg(test)]
mod tests {
use super::*;

#[cfg(target_family = "wasm")]
mod wasm_tests {
use super::*;
use crate::raindex_client::local_db::query::tests::{
create_sql_capturing_callback, create_success_callback,
};
use wasm_bindgen_test::wasm_bindgen_test;

#[wasm_bindgen_test]
async fn test_fetch_order_trades_parses_rows() {
let row = LocalDbOrderTrade {
trade_kind: "take".into(),
orderbook_address: "0xob".into(),
order_hash: "0xhash".into(),
order_owner: "0xowner".into(),
order_nonce: "1".into(),
transaction_hash: "0xtx".into(),
log_index: 1,
block_number: 10,
block_timestamp: 1000,
transaction_sender: "0xsender".into(),
input_vault_id: "1".into(),
input_token: "0xinput".into(),
input_token_name: Some("Token In".into()),
input_token_symbol: Some("TIN".into()),
input_token_decimals: Some(18),
input_delta: "0x1".into(),
input_running_balance: Some("0x10".into()),
output_vault_id: "2".into(),
output_token: "0xoutput".into(),
output_token_name: Some("Token Out".into()),
output_token_symbol: Some("TOUT".into()),
output_token_decimals: Some(6),
output_delta: "0x2".into(),
output_running_balance: Some("0x20".into()),
trade_id: "0xdead".into(),
};

let callback =
create_success_callback(&serde_json::to_string(&vec![row.clone()]).unwrap());

let result = LocalDbQuery::fetch_order_trades(&callback, 1, "0xABC", None, None).await;

assert!(result.is_ok());
let rows = result.unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].trade_id, row.trade_id);
assert_eq!(rows[0].input_token_symbol, Some("TIN".into()));
}

#[wasm_bindgen_test]
async fn test_fetch_order_trades_replaces_placeholders() {
let captured_sql = std::rc::Rc::new(std::cell::RefCell::new(String::new()));
let callback = create_sql_capturing_callback("[]", captured_sql.clone());

let _ =
LocalDbQuery::fetch_order_trades(&callback, 42161, "0xHASH", Some(100), Some(200))
.await;

let sql = captured_sql.borrow();
assert!(sql.contains("'0xhash'"));
assert!(sql.contains("42161"));
assert!(sql.contains("block_timestamp >= 100"));
assert!(sql.contains("block_timestamp <= 200"));
assert!(!sql.contains("?order_hash"));
}
}
}
Loading