Skip to main content

df_hyperliquid_core/trade/
modify.rs

1//! `modify` and `batchModify`: a resting order replaced in place.
2
3use crate::trade::order::{Cloid, Order};
4use crate::utils::sign::{L1Action, Map, Value};
5
6/// The order a modify replaces: the venue's id, or the client's.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum OrderRef {
9    Oid(u64),
10    Cloid(Cloid),
11}
12
13impl OrderRef {
14    /// An oid as a number, a cloid as its hex.
15    #[must_use]
16    pub fn wire(self) -> Value {
17        match self {
18            OrderRef::Oid(oid) => Value::Uint(oid),
19            OrderRef::Cloid(cloid) => Value::from(cloid.to_hex()),
20        }
21    }
22}
23
24/// `modify`: `{type, oid, order, a?}`, one order. `a` (always place) is written last and only
25/// when set, and set whenever the new order is a trigger: the venue refuses a trigger without it
26/// and an action hashed with `a: false`. A `batchModify` entry never carries it; the batch does.
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct Modify {
29    target: OrderRef,
30    order: Order,
31    always_place: bool,
32}
33
34impl Modify {
35    #[must_use]
36    pub const fn new(target: OrderRef, order: Order) -> Modify {
37        Modify { target, order, always_place: order.is_trigger() }
38    }
39
40    /// Places the new order even where the old one is gone.
41    #[must_use]
42    pub const fn always_place(mut self) -> Modify {
43        self.always_place = true;
44        self
45    }
46
47    #[must_use]
48    pub const fn is_always_place(&self) -> bool {
49        self.always_place
50    }
51
52    #[must_use]
53    pub const fn target(&self) -> OrderRef {
54        self.target
55    }
56
57    #[must_use]
58    pub const fn order(&self) -> &Order {
59        &self.order
60    }
61
62    /// `{oid, order}`, one entry of a `batchModify`.
63    #[must_use]
64    pub fn entry(&self) -> Map {
65        Map::new().put("oid", self.target.wire()).put("order", self.order.wire())
66    }
67}
68
69impl L1Action for Modify {
70    fn action(&self) -> Map {
71        Map::new()
72            .put("type", "modify")
73            .put("oid", self.target.wire())
74            .put("order", self.order.wire())
75            .put_some("a", self.always_place.then_some(true))
76    }
77}
78
79/// `batchModify`: `{type, modifies: [{oid, order}], a?}`. `a` (always place: the new order goes on
80/// whether or not the old one was still there) is written last and only when set, and set
81/// whenever a new order is a trigger, since the venue refuses a trigger without it.
82#[derive(Debug, Clone, PartialEq, Eq)]
83pub struct BatchModify {
84    modifies: Vec<Modify>,
85    always_place: bool,
86}
87
88impl BatchModify {
89    #[must_use]
90    pub fn new(modifies: Vec<Modify>) -> BatchModify {
91        let always_place = modifies.iter().any(|modify| modify.order.is_trigger());
92        BatchModify { modifies, always_place }
93    }
94
95    /// Places the new orders even where the old ones are gone.
96    #[must_use]
97    pub fn always_place(mut self) -> BatchModify {
98        self.always_place = true;
99        self
100    }
101
102    #[must_use]
103    pub fn modifies(&self) -> &[Modify] {
104        &self.modifies
105    }
106
107    #[must_use]
108    pub const fn is_always_place(&self) -> bool {
109        self.always_place
110    }
111}
112
113impl L1Action for BatchModify {
114    fn action(&self) -> Map {
115        Map::new()
116            .put("type", "batchModify")
117            .put("modifies", Value::array(self.modifies.iter().map(Modify::entry)))
118            .put_some("a", self.always_place.then_some(true))
119    }
120}