tempo_node/rpc/dex/
orders.rs

1use alloy::primitives::Address;
2use serde::{Deserialize, Serialize};
3use tempo_alloy::rpc::pagination::{FilterRange, PaginationParams};
4
5pub type OrdersParams = PaginationParams<OrdersFilters>;
6pub type Tick = i16;
7
8#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
9#[serde(rename_all = "camelCase")]
10pub struct OrdersFilters {
11    /// Filter by specific base token
12    pub base_token: Option<Address>,
13    /// Filter by order side (true=buy, false=sell)
14    pub is_bid: Option<bool>,
15    /// Filter flip orders
16    pub is_flip: Option<bool>,
17    /// Filter by maker address
18    pub maker: Option<Address>,
19    /// Filter by quote token
20    pub quote_token: Option<Address>,
21    /// Remaining amount in range
22    pub remaining: Option<RemainingFilterRange>,
23    /// Tick in range (from -2000 to 2000)
24    pub tick: Option<FilterRange<Tick>>,
25}
26
27/// FilterRange type for u128, so that we can serialize the u128s as QUANTITY.
28#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
29#[serde(rename_all = "camelCase")]
30pub struct RemainingFilterRange {
31    #[serde(with = "alloy_serde::quantity::opt")]
32    pub min: Option<u128>,
33    #[serde(with = "alloy_serde::quantity::opt")]
34    pub max: Option<u128>,
35}
36
37impl RemainingFilterRange {
38    /// Checks if a value is within this range (inclusive)
39    pub fn in_range(&self, value: u128) -> bool {
40        if self.min.as_ref().is_some_and(|min| &value < min) {
41            return false;
42        }
43
44        if self.max.as_ref().is_some_and(|max| &value > max) {
45            return false;
46        }
47
48        true
49    }
50}
51
52#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
53#[serde(rename_all = "camelCase")]
54pub struct OrdersResponse {
55    pub next_cursor: Option<String>,
56    pub orders: Vec<Order>,
57}
58
59#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
60#[serde(rename_all = "camelCase")]
61pub struct Order {
62    /// Original order amount
63    #[serde(with = "alloy_serde::quantity")]
64    pub amount: u128,
65    /// Target tick to flip to when order is filled
66    pub flip_tick: i16,
67    /// Order side: true for buy (bid), false for sell (ask)
68    pub is_bid: bool,
69    /// Whether this is a flip order that auto-flips when filled
70    pub is_flip: bool,
71    /// Address of order maker
72    pub maker: Address,
73    /// Next order ID in FIFO queue
74    #[serde(with = "alloy_serde::quantity")]
75    pub next: u128,
76    /// Unique order ID
77    #[serde(with = "alloy_serde::quantity")]
78    pub order_id: u128,
79    /// Previous order ID in FIFO queue
80    #[serde(with = "alloy_serde::quantity")]
81    pub prev: u128,
82    /// Remaining amount to fill
83    #[serde(with = "alloy_serde::quantity")]
84    pub remaining: u128,
85    /// Price tick
86    pub tick: i16,
87    /// Address of the base token
88    pub base_token: Address,
89    /// Address of the quote token
90    pub quote_token: Address,
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test_case::test_case(
98        OrdersParams::default();
99        "None filled"
100    )]
101    fn test_serialize_and_deserialize_is_identical(expected_params: OrdersParams) {
102        let json = serde_json::to_string(&expected_params).unwrap();
103        let actual_params: OrdersParams = serde_json::from_str(&json).unwrap();
104
105        assert_eq!(actual_params, expected_params);
106    }
107
108    #[test_case::test_case(
109        "{}";
110        "None filled"
111    )]
112    fn test_deserialize_and_serialize_is_identical(expected_json: &str) {
113        let params: OrdersParams = serde_json::from_str(expected_json).unwrap();
114        let actual_json = serde_json::to_string(&params).unwrap();
115
116        assert_eq!(actual_json, expected_json);
117    }
118}