1use alloy::primitives::{Bytes, U256};
2use fastnum::{UD64, UD128};
3
4use super::*;
5use crate::{abi::dex::Exchange::OrderDesc, error::DexError, num, state};
6
7#[derive(Clone, Copy, Debug)]
29pub enum RequestType {
30 OpenLong,
31 OpenShort,
32 CloseLong,
33 CloseShort,
34 Cancel,
35 IncreasePositionCollateral,
36 Change,
37}
38
39#[derive(Clone, derive_more::Debug)]
41pub struct OrderRequest {
42 request_id: RequestId,
43 perp_id: PerpetualId,
44 r#type: RequestType,
45 order_id: Option<OrderId>,
46 #[debug("{price}")]
47 price: UD64,
48 #[debug("{size}")]
49 size: UD64,
50 expiry_block: Option<u64>,
51 post_only: bool,
52 fill_or_kill: bool,
53 immediate_or_cancel: bool,
54 max_matches: Option<u32>,
55 #[debug("{leverage}")]
56 leverage: UD64,
57 last_exec_block: Option<u64>,
58 amount: Option<UD128>,
59 max_neg_pnl_collat_bps: u16,
60 builder: Option<BuilderAttribution>,
61}
62
63impl OrderRequest {
64 #[allow(clippy::too_many_arguments)]
75 pub fn new(
76 request_id: RequestId,
77 perp_id: PerpetualId,
78 r#type: RequestType,
79 order_id: Option<OrderId>,
80 price: UD64,
81 size: UD64,
82 expiry_block: Option<u64>,
83 post_only: bool,
84 fill_or_kill: bool,
85 immediate_or_cancel: bool,
86 max_matches: Option<u32>,
87 leverage: UD64,
88 last_exec_block: Option<u64>,
89 amount: Option<UD128>,
90 max_neg_pnl_collat_bps: u16,
91 ) -> Self {
92 Self {
93 request_id,
94 perp_id,
95 r#type,
96 order_id,
97 price,
98 size,
99 expiry_block,
100 post_only,
101 fill_or_kill,
102 immediate_or_cancel,
103 max_matches,
104 leverage,
105 last_exec_block,
106 amount,
107 max_neg_pnl_collat_bps,
108 builder: None,
109 }
110 }
111
112 pub fn with_builder(mut self, builder: BuilderAttribution) -> Self {
120 self.builder = Some(builder);
121 self
122 }
123
124 pub fn builder(&self) -> Option<BuilderAttribution> { self.builder }
126
127 pub fn prepare(&self, exchange: &state::Exchange) -> OrderDesc {
134 let perp = exchange
135 .perpetuals()
136 .get(&self.perp_id)
137 .expect("known perpetual");
138 self.to_order_desc(
139 perp.price_converter(),
140 perp.size_converter(),
141 perp.leverage_converter(),
142 Some(exchange.collateral_converter()),
143 )
144 }
145
146 pub fn prepare_v2(&self, exchange: &state::Exchange) -> Result<(OrderDesc, Bytes), DexError> {
159 let perp = exchange
160 .perpetuals()
161 .get(&self.perp_id)
162 .ok_or(DexError::PerpetualNotTracked(self.perp_id))?;
163 let extension = match self.builder {
164 None => Bytes::new(),
165 Some(builder) => {
166 if !exchange.features().builder_attribution() {
167 return Err(DexError::UnsupportedByContract(
168 "builder attribution",
169 exchange.features(),
170 ));
171 }
172 builder.encode()?
173 },
174 };
175 Ok((
176 self.to_order_desc(
177 perp.price_converter(),
178 perp.size_converter(),
179 perp.leverage_converter(),
180 Some(exchange.collateral_converter()),
181 ),
182 extension,
183 ))
184 }
185
186 pub fn to_order_extension(&self) -> Result<Bytes, OrderExtensionError> {
189 self.builder
190 .map(|builder| builder.encode())
191 .transpose()
192 .map(Option::unwrap_or_default)
193 }
194
195 pub(crate) fn to_order_desc(
196 &self,
197 price_converter: num::Converter,
198 size_converter: num::Converter,
199 leverage_converter: num::Converter,
200 collateral_converter: Option<num::Converter>,
201 ) -> OrderDesc {
202 OrderDesc {
203 orderDescId: U256::from(self.request_id),
204 perpId: U256::from(self.perp_id),
205 orderType: self.r#type as u8,
206 orderId: U256::from(self.order_id.map(|id| id.get()).unwrap_or(0)),
207 pricePNS: price_converter.to_unsigned(self.price),
208 lotLNS: size_converter.to_unsigned(self.size),
209 expiryBlock: U256::from(self.expiry_block.unwrap_or_default()),
210 postOnly: self.post_only,
211 fillOrKill: self.fill_or_kill,
212 immediateOrCancel: self.immediate_or_cancel,
213 maxMatches: U256::from(self.max_matches.unwrap_or_default()),
214 leverageHdths: leverage_converter.to_unsigned(self.leverage),
215 lastExecutionBlock: U256::from(self.last_exec_block.unwrap_or_default()),
216 amountCNS: self
217 .amount
218 .zip(collateral_converter)
219 .map(|(a, conv)| conv.to_unsigned(a))
220 .unwrap_or_default(),
221 maxNegPnlCollatBPS: U256::from(self.max_neg_pnl_collat_bps),
222 }
223 }
224}
225
226impl From<u8> for RequestType {
227 fn from(value: u8) -> Self {
228 match value {
229 0 => RequestType::OpenLong,
230 1 => RequestType::OpenShort,
231 2 => RequestType::CloseLong,
232 3 => RequestType::CloseShort,
233 4 => RequestType::Cancel,
234 5 => RequestType::IncreasePositionCollateral,
235 6 => RequestType::Change,
236 _ => unreachable!(),
237 }
238 }
239}
240
241impl RequestType {
242 pub fn try_side(&self) -> Option<OrderSide> {
248 match self {
249 RequestType::OpenLong | RequestType::CloseShort => Some(OrderSide::Bid),
250 RequestType::OpenShort | RequestType::CloseLong => Some(OrderSide::Ask),
251 _ => None,
252 }
253 }
254}
255
256impl From<RequestType> for OrderType {
257 fn from(value: RequestType) -> Self {
258 match value {
259 RequestType::OpenLong => OrderType::OpenLong,
260 RequestType::OpenShort => OrderType::OpenShort,
261 RequestType::CloseLong => OrderType::CloseLong,
262 RequestType::CloseShort => OrderType::CloseShort,
263 _ => unreachable!(),
264 }
265 }
266}