1use crate::eip712_structs::{Cancellation, DependencyUpdate, ListTriggerOrders};
2use crate::eip712_structs::{CancellationProducts, Order};
3use crate::engine::{ExecuteResponse, PlaceOrder, Status};
4use crate::serialize_utils::{deserialize_i128, serialize_i128, WrappedBytes32, WrappedI128};
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 DependentOrderCancelled,
18}
19
20#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
21pub struct ExecuteStatus {
22 pub execute_response: ExecuteResponse,
23 pub place_time: u64,
24}
25
26#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
27#[serde(rename_all = "snake_case")]
28pub enum TriggerOrderStatus {
29 Cancelled(CancelReason),
30 Triggered(ExecuteResponse),
31 InternalError(String),
32 Triggering,
33 WaitingPrice,
34 WaitingDependency,
35 TwapExecuting {
37 current_execution: u32,
38 total_executions: u32,
39 },
40 TwapCompleted {
41 executed: u32,
42 total: u32,
43 },
44}
45
46impl TriggerOrderStatus {
47 pub fn byte(&self) -> i32 {
48 (match self {
49 Self::Cancelled(_) => TriggerOrderStatusType::Cancelled,
50 Self::Triggered(_) => TriggerOrderStatusType::Triggered,
51 Self::InternalError(_) => TriggerOrderStatusType::InternalError,
52 Self::Triggering => TriggerOrderStatusType::Triggering,
53 Self::WaitingPrice => TriggerOrderStatusType::WaitingPrice,
54 Self::WaitingDependency => TriggerOrderStatusType::WaitingDependency,
55 Self::TwapExecuting { .. } => TriggerOrderStatusType::TwapExecuting,
56 Self::TwapCompleted { .. } => TriggerOrderStatusType::TwapCompleted,
57 }) as i32
58 }
59
60 pub fn byte_from_str(s: &str) -> Result<i32> {
61 match s {
62 "cancelled" => Ok(0),
63 "triggered" => Ok(1),
64 "internal_error" => Ok(2),
65 "triggering" => Ok(3),
66 "waiting_price" => Ok(4),
67 "waiting_dependency" => Ok(5),
68 "twap_executing" => Ok(6),
69 "twap_completed" => Ok(7),
70 _ => Err(eyre::eyre!("Invalid status string")),
71 }
72 }
73
74 pub fn data(&self) -> Vec<u8> {
75 serde_json::to_vec(self).unwrap()
76 }
77
78 pub fn from_status_data(data: &[u8]) -> Self {
79 serde_json::from_slice(data).unwrap()
80 }
81}
82
83#[derive(Clone, Serialize, Deserialize, Debug)]
84#[serde(rename_all = "snake_case")]
85pub enum TriggerCriteria {
86 PriceTrigger {
87 price_requirement: PriceRequirement,
88 dependency: Option<Dependency>,
89 },
90 TimeTrigger {
91 interval: u64,
92 amounts: Option<Vec<WrappedI128>>,
93 },
94}
95
96#[derive(Clone, Serialize, Deserialize, Debug)]
97pub struct Dependency {
98 pub digest: H256,
99 pub on_partial_fill: bool, }
101
102#[derive(Clone, Serialize, Deserialize, Debug)]
103#[serde(rename_all = "snake_case")]
104pub enum PriceRequirement {
105 OraclePriceAbove(
106 #[serde(
107 serialize_with = "serialize_i128",
108 deserialize_with = "deserialize_i128"
109 )]
110 i128,
111 ),
112 OraclePriceBelow(
113 #[serde(
114 serialize_with = "serialize_i128",
115 deserialize_with = "deserialize_i128"
116 )]
117 i128,
118 ),
119
120 LastPriceAbove(
121 #[serde(
122 serialize_with = "serialize_i128",
123 deserialize_with = "deserialize_i128"
124 )]
125 i128,
126 ),
127 LastPriceBelow(
128 #[serde(
129 serialize_with = "serialize_i128",
130 deserialize_with = "deserialize_i128"
131 )]
132 i128,
133 ),
134
135 MidPriceAbove(
136 #[serde(
137 serialize_with = "serialize_i128",
138 deserialize_with = "deserialize_i128"
139 )]
140 i128,
141 ),
142
143 MidPriceBelow(
144 #[serde(
145 serialize_with = "serialize_i128",
146 deserialize_with = "deserialize_i128"
147 )]
148 i128,
149 ),
150}
151
152#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
153#[serde(rename_all = "snake_case")]
154pub enum TriggerType {
155 PriceTrigger = 0,
156 TimeTrigger = 1,
157 None = 2,
158}
159
160impl TriggerType {
161 pub fn from_appendix(appendix: u128) -> Self {
162 match appendix >> 12 & 3 {
163 1 => Self::PriceTrigger,
164 2 | 3 => Self::TimeTrigger,
165 _ => Self::None,
166 }
167 }
168}
169
170#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
171#[serde(rename_all = "snake_case")]
172pub enum TriggerOrderStatusType {
173 Cancelled = 0,
174 Triggered = 1,
175 InternalError = 2,
176 Triggering = 3,
177 WaitingPrice = 4,
178 WaitingDependency = 5,
179 TwapExecuting = 6,
180 TwapCompleted = 7,
181}
182
183impl TriggerOrderStatusType {
184 pub fn pending() -> Vec<Self> {
185 vec![
186 Self::WaitingPrice,
187 Self::WaitingDependency,
188 Self::TwapExecuting,
189 ]
190 }
191 pub fn pending_byte() -> Vec<i32> {
192 Self::pending().into_iter().map(|x| x as i32).collect()
193 }
194}
195
196impl TriggerCriteria {
197 pub fn byte(&self) -> i32 {
198 match self {
199 Self::PriceTrigger { .. } => 0,
200 Self::TimeTrigger { .. } => 1,
201 }
202 }
203
204 pub fn price_str(&self) -> Option<String> {
205 match self {
206 Self::PriceTrigger {
207 price_requirement, ..
208 } => Some(price_requirement.price_str()),
209 _ => None,
210 }
211 }
212
213 pub fn criteria_price_byte(&self) -> Option<i32> {
214 match self {
215 Self::PriceTrigger {
216 price_requirement, ..
217 } => Some(price_requirement.byte()),
218 _ => None,
219 }
220 }
221
222 pub fn dependency(&self) -> Option<[u8; 32]> {
223 match self {
224 Self::PriceTrigger { dependency, .. } => dependency.clone().map(|x| x.digest.0),
225 _ => None,
226 }
227 }
228}
229
230impl PriceRequirement {
231 pub fn byte(&self) -> i32 {
232 match self {
233 Self::OraclePriceAbove(_) => 0,
234 Self::OraclePriceBelow(_) => 1,
235 Self::LastPriceAbove(_) => 2,
236 Self::LastPriceBelow(_) => 3,
237 Self::MidPriceAbove(_) => 4,
238 Self::MidPriceBelow(_) => 5,
239 }
240 }
241
242 pub fn price(&self) -> i128 {
243 match self {
244 Self::OraclePriceAbove(price) => *price,
245 Self::OraclePriceBelow(price) => *price,
246 Self::LastPriceAbove(price) => *price,
247 Self::LastPriceBelow(price) => *price,
248 Self::MidPriceAbove(price) => *price,
249 Self::MidPriceBelow(price) => *price,
250 }
251 }
252
253 pub fn price_str(&self) -> String {
254 format!("{:0>40}", self.price())
256 }
257}
258
259#[derive(Clone, Debug, Serialize, Deserialize)]
260#[serde(rename_all = "snake_case")]
261pub struct PlaceTriggerOrder {
262 pub order: Order,
263 pub signature: Bytes,
264 pub product_id: u32,
265 pub spot_leverage: Option<bool>,
266 pub borrow_margin: Option<bool>,
267 pub trigger: TriggerCriteria,
268 pub digest: Option<H256>,
269 pub id: Option<u64>,
270}
271
272impl PlaceTriggerOrder {
273 pub fn to_place_order(&self) -> PlaceOrder {
274 PlaceOrder {
275 order: self.order.clone(),
276 signature: self.signature.to_vec(),
277 product_id: self.product_id,
278 digest: None,
279 id: self.id,
280 spot_leverage: self.spot_leverage,
281 borrow_margin: self.borrow_margin,
282 }
283 }
284
285 pub fn order_data(&self) -> Vec<u8> {
286 bincode::serialize(self).unwrap()
287 }
288
289 pub fn from_order_data(data: &[u8]) -> Self {
290 bincode::deserialize(data).unwrap()
291 }
292
293 pub fn expiration(&self) -> u64 {
294 self.order.expiration
295 }
296
297 pub fn is_bid(&self) -> bool {
298 self.order.amount.is_positive()
299 }
300
301 pub fn trigger_amount(&self, index: u32) -> Option<i128> {
302 if !self.order.is_twap_random() {
303 None
304 } else {
305 match &self.trigger {
306 TriggerCriteria::TimeTrigger { amounts, .. } => {
307 Some(amounts.as_ref().unwrap()[index as usize].0)
308 }
309 _ => unreachable!(),
310 }
311 }
312 }
313}
314
315#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
316#[serde(rename_all = "snake_case")]
317pub struct PlaceTriggerOrdersItemResponse {
318 pub digest: Option<[u8; 32]>,
319 pub error: Option<String>,
320}
321
322pub type PlaceTriggerOrdersResponse = Vec<PlaceTriggerOrdersItemResponse>;
323
324#[derive(Clone, Serialize, Deserialize, Debug)]
325#[serde(rename_all = "snake_case")]
326pub enum Execute {
329 PlaceOrder(PlaceTriggerOrder),
330 PlaceOrders {
331 orders: Vec<PlaceTriggerOrder>,
332 stop_on_failure: Option<bool>,
333 },
334 CancelOrders {
335 tx: Cancellation,
336 signature: Bytes,
337 },
338 CancelProductOrders {
339 tx: CancellationProducts,
340 signature: Bytes,
341 },
342 UpdateDependency {
343 tx: DependencyUpdate,
344 signature: Bytes,
345 },
346}
347
348#[derive(Debug, Deserialize, Serialize)]
349#[serde(rename_all = "snake_case")]
350#[serde(tag = "type")]
351pub enum Query {
352 ListTriggerOrders {
353 tx: ListTriggerOrders,
354 signature: Bytes,
355 product_ids: Option<Vec<u32>>,
356 trigger_types: Option<Vec<TriggerType>>,
357 status_types: Option<Vec<TriggerOrderStatusType>>,
358 max_update_time: Option<u64>,
359 max_digest: Option<WrappedBytes32>,
360 digests: Option<Vec<WrappedBytes32>>,
361 limit: Option<u32>,
362 reduce_only: Option<bool>,
363 },
364 ListTwapExecutions {
365 digest: WrappedBytes32,
366 },
367}
368
369#[derive(Clone, Debug, Serialize, Deserialize)]
370pub struct TriggerOrderInfo {
371 pub order: PlaceTriggerOrder,
372 pub status: TriggerOrderStatus,
373 pub placed_at: u64,
374 pub updated_at: u64,
375}
376
377#[derive(Clone, Debug, Serialize, Deserialize)]
378pub struct ListTriggerOrdersResponse {
379 pub orders: Vec<TriggerOrderInfo>,
380}
381
382impl ListTriggerOrdersResponse {
383 pub fn contains_digest(&self, digest: H256) -> bool {
384 self.orders
385 .iter()
386 .any(|order| order.order.digest == Some(digest))
387 }
388}
389
390#[derive(Debug, Clone, Serialize, Deserialize)]
391#[serde(rename_all = "snake_case")]
392pub enum TwapExecutionStatusData {
393 Pending,
394 Executing,
395 Executed {
396 executed_time: i64,
397 execute_response: ExecuteResponse,
398 },
399 Failed(String),
400 Cancelled(CancelReason),
401}
402
403impl TwapExecutionStatusData {
404 pub fn data(&self) -> Vec<u8> {
405 serde_json::to_vec(self).unwrap()
406 }
407
408 pub fn from_status_data(data: &[u8]) -> Self {
409 serde_json::from_slice(data).unwrap()
410 }
411}
412
413#[derive(Clone, Debug, Serialize, Deserialize)]
414pub struct ListTwapExecutionsResponse {
415 pub executions: Vec<TwapExecutionDetail>,
416}
417
418#[derive(Clone, Debug, Serialize, Deserialize)]
419pub struct TwapExecutionDetail {
420 pub execution_id: u32,
421 pub scheduled_time: i64,
422 pub status: TwapExecutionStatusData,
423 pub updated_at: i64,
424}
425
426#[derive(Clone, Debug, Serialize, Deserialize)]
427#[serde(rename_all = "snake_case")]
428#[serde(untagged)]
429pub enum QueryResponseData {
430 ListTriggerOrders(ListTriggerOrdersResponse),
431 ListTwapExecutions(ListTwapExecutionsResponse),
432 Error(String),
433}
434
435#[derive(Clone, Debug, Serialize, Deserialize)]
436#[serde(rename_all = "snake_case")]
439pub struct QueryResponse {
440 pub status: Status,
441 #[serde(skip_serializing_if = "Option::is_none")]
442 pub data: Option<QueryResponseData>,
443 #[serde(skip_serializing_if = "Option::is_none")]
444 pub error_code: Option<i32>,
445 #[serde(skip_serializing_if = "Option::is_none")]
446 pub error: Option<String>,
447 #[serde(skip_serializing_if = "Option::is_none")]
448 pub request_type: Option<String>,
449}