vertex_sdk/vertex_utils/
trigger.rs

1use crate::eip712_structs::{Cancellation, ListTriggerOrders};
2use crate::eip712_structs::{CancellationProducts, Order};
3use crate::engine::{ExecuteResponse, PlaceOrder, Status};
4use crate::serialize_utils::{deserialize_i128, serialize_i128, WrappedBytes32};
5use ethers::types::{Bytes, H256};
6use eyre::Result;
7use serde::{Deserialize, Serialize};
8
9#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
10#[serde(rename_all = "snake_case")]
11pub enum CancelReason {
12    UserRequested,
13    LinkedSignerChanged,
14    Expired,
15    AccountHealth,
16    IsolatedSubaccountClosed,
17}
18
19#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
20#[serde(rename_all = "snake_case")]
21pub enum TriggerOrderStatus {
22    Pending,
23    Triggering,
24    Triggered(ExecuteResponse),
25    Cancelled(CancelReason),
26    InternalError(String),
27}
28
29impl TriggerOrderStatus {
30    pub fn pending(&self) -> bool {
31        match self {
32            TriggerOrderStatus::Pending => true,
33            _ => false,
34        }
35    }
36
37    pub fn byte(&self) -> i32 {
38        match self {
39            TriggerOrderStatus::Pending => 0,
40            TriggerOrderStatus::Triggering => 1,
41            TriggerOrderStatus::Triggered(_) => 2,
42            TriggerOrderStatus::Cancelled(_) => 3,
43            TriggerOrderStatus::InternalError(_) => 4,
44        }
45    }
46
47    pub fn byte_from_str(s: &str) -> Result<i32> {
48        match s {
49            "pending" => Ok(0),
50            "triggering" => Ok(1),
51            "triggered" => Ok(2),
52            "cancelled" => Ok(3),
53            "internal_error" => Ok(4),
54            _ => Err(eyre::eyre!("Invalid status string")),
55        }
56    }
57
58    pub fn data(&self) -> Vec<u8> {
59        serde_json::to_vec(self).unwrap()
60    }
61
62    pub fn from_status_data(data: &[u8]) -> Self {
63        serde_json::from_slice(data).unwrap()
64    }
65}
66
67#[derive(Clone, Serialize, Deserialize, Debug)]
68#[serde(rename_all = "snake_case")]
69pub enum TriggerCriteria {
70    // on the oracle price
71    PriceAbove(
72        #[serde(
73            serialize_with = "serialize_i128",
74            deserialize_with = "deserialize_i128"
75        )]
76        i128,
77    ),
78    PriceBelow(
79        #[serde(
80            serialize_with = "serialize_i128",
81            deserialize_with = "deserialize_i128"
82        )]
83        i128,
84    ),
85
86    LastPriceAbove(
87        #[serde(
88            serialize_with = "serialize_i128",
89            deserialize_with = "deserialize_i128"
90        )]
91        i128,
92    ),
93    LastPriceBelow(
94        #[serde(
95            serialize_with = "serialize_i128",
96            deserialize_with = "deserialize_i128"
97        )]
98        i128,
99    ),
100}
101
102impl TriggerCriteria {
103    pub fn byte(&self) -> i32 {
104        match self {
105            TriggerCriteria::PriceAbove(_) => 0,
106            TriggerCriteria::PriceBelow(_) => 1,
107            TriggerCriteria::LastPriceAbove(_) => 2,
108            TriggerCriteria::LastPriceBelow(_) => 3,
109        }
110    }
111
112    pub fn price(&self) -> String {
113        let p = match self {
114            TriggerCriteria::PriceAbove(price) => *price,
115            TriggerCriteria::PriceBelow(price) => *price,
116            TriggerCriteria::LastPriceAbove(price) => *price,
117            TriggerCriteria::LastPriceBelow(price) => *price,
118        };
119        format!("{:0>40}", p)
120    }
121}
122
123#[derive(Clone, Debug, Serialize, Deserialize)]
124#[serde(rename_all = "snake_case")]
125pub struct PlaceTriggerOrder {
126    pub order: Order,
127    pub signature: Bytes,
128    pub product_id: u32,
129    pub spot_leverage: Option<bool>,
130    pub trigger: TriggerCriteria,
131    pub digest: Option<H256>,
132    pub id: Option<u64>,
133}
134
135impl PlaceTriggerOrder {
136    pub fn to_place_order(&self) -> PlaceOrder {
137        PlaceOrder {
138            order: self.order.clone(),
139            signature: self.signature.to_vec(),
140            product_id: self.product_id,
141            digest: None,
142            id: self.id,
143            spot_leverage: self.spot_leverage,
144        }
145    }
146
147    pub fn order_data(&self) -> Vec<u8> {
148        bincode::serialize(self).unwrap()
149    }
150
151    pub fn from_order_data(data: &[u8]) -> Self {
152        #[derive(Clone, Debug, Serialize, Deserialize)]
153        #[serde(rename_all = "snake_case")]
154        pub struct PlaceTriggerOrderOld {
155            pub order: Order,
156            pub signature: Bytes,
157            pub product_id: u32,
158            pub spot_leverage: Option<bool>,
159            pub trigger: TriggerCriteria,
160            pub digest: Option<H256>,
161        }
162
163        match bincode::deserialize(data) {
164            Ok(p) => p,
165            Err(_) => {
166                let p_old = bincode::deserialize::<PlaceTriggerOrderOld>(data).unwrap();
167                PlaceTriggerOrder {
168                    order: p_old.order,
169                    signature: p_old.signature,
170                    product_id: p_old.product_id,
171                    spot_leverage: p_old.spot_leverage,
172                    trigger: p_old.trigger,
173                    digest: p_old.digest,
174                    id: None,
175                }
176            }
177        }
178    }
179
180    pub fn expiration(&self) -> u64 {
181        self.order.expiration()
182    }
183
184    pub fn is_bid(&self) -> bool {
185        self.order.amount.is_positive()
186    }
187}
188
189#[derive(Clone, Serialize, Deserialize, Debug)]
190#[serde(rename_all = "snake_case")]
191// #[ts(export)]ยท
192// #[ts(export_to = "tsBindings/msg/")]
193pub enum Execute {
194    PlaceOrder(PlaceTriggerOrder),
195    CancelOrders {
196        tx: Cancellation,
197        signature: Bytes,
198    },
199    CancelProductOrders {
200        tx: CancellationProducts,
201        signature: Bytes,
202    },
203}
204
205#[derive(Debug, Deserialize, Serialize)]
206#[serde(rename_all = "snake_case")]
207#[serde(tag = "type")]
208pub enum Query {
209    ListTriggerOrders {
210        tx: ListTriggerOrders,
211        signature: Bytes,
212        product_id: Option<u32>,
213        pending: bool,
214        max_update_time: Option<u64>,
215        max_digest: Option<WrappedBytes32>,
216        digests: Option<Vec<WrappedBytes32>>,
217        limit: Option<u32>,
218    },
219}
220
221#[derive(Clone, Debug, Serialize, Deserialize)]
222pub struct TriggerOrderInfo {
223    pub order: PlaceTriggerOrder,
224    pub status: TriggerOrderStatus,
225    pub updated_at: u64,
226}
227
228#[derive(Clone, Debug, Serialize, Deserialize)]
229pub struct ListTriggerOrdersResponse {
230    pub orders: Vec<TriggerOrderInfo>,
231}
232
233impl ListTriggerOrdersResponse {
234    pub fn contains_digest(&self, digest: H256) -> bool {
235        self.orders
236            .iter()
237            .any(|order| order.order.digest == Some(digest))
238    }
239}
240
241#[derive(Clone, Debug, Serialize, Deserialize)]
242#[serde(rename_all = "snake_case")]
243#[serde(untagged)]
244pub enum QueryResponseData {
245    ListTriggerOrders(ListTriggerOrdersResponse),
246    Error(String),
247}
248
249#[derive(Clone, Debug, Serialize, Deserialize)]
250// #[ts(export)]
251// #[ts(export_to = "tsBindings/msgResponses/")]
252#[serde(rename_all = "snake_case")]
253pub struct QueryResponse {
254    pub status: Status,
255    #[serde(skip_serializing_if = "Option::is_none")]
256    pub data: Option<QueryResponseData>,
257    #[serde(skip_serializing_if = "Option::is_none")]
258    pub error_code: Option<i32>,
259    #[serde(skip_serializing_if = "Option::is_none")]
260    pub error: Option<String>,
261    #[serde(skip_serializing_if = "Option::is_none")]
262    pub request_type: Option<String>,
263}