1use std::fmt;
4
5use cow_errors::CowError;
6
7use super::{
8 gat::{GatOrder, decode_gat_static_input},
9 stop_loss::{STOP_LOSS_HANDLER_ADDRESS, StopLossOrder, decode_stop_loss_static_input},
10 twap::{TwapOrder, decode_twap_static_input},
11 types::{ConditionalOrderParams, TWAP_HANDLER_ADDRESS},
12};
13
14#[derive(Debug, Clone)]
16pub enum ConditionalOrderKind {
17 Twap(TwapOrder),
19 StopLoss(StopLossOrder),
21 GoodAfterTime(GatOrder),
23 Unknown(ConditionalOrderParams),
25}
26
27impl ConditionalOrderKind {
28 #[must_use]
35 pub const fn as_str(&self) -> &'static str {
36 match self {
37 Self::Twap(_) => "twap",
38 Self::StopLoss(_) => "stop-loss",
39 Self::GoodAfterTime(_) => "good-after-time",
40 Self::Unknown(_) => "unknown",
41 }
42 }
43
44 #[must_use]
66 pub const fn is_twap(&self) -> bool {
67 matches!(self, Self::Twap(_))
68 }
69
70 #[must_use]
84 pub const fn is_stop_loss(&self) -> bool {
85 matches!(self, Self::StopLoss(_))
86 }
87
88 #[must_use]
102 pub const fn is_good_after_time(&self) -> bool {
103 matches!(self, Self::GoodAfterTime(_))
104 }
105
106 #[must_use]
113 pub const fn is_unknown(&self) -> bool {
114 matches!(self, Self::Unknown(_))
115 }
116}
117
118impl fmt::Display for ConditionalOrderKind {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 match self {
121 Self::Twap(order) => write!(f, "twap({order})"),
122 Self::StopLoss(_) => f.write_str("stop-loss"),
123 Self::GoodAfterTime(_) => f.write_str("good-after-time"),
124 Self::Unknown(params) => write!(f, "unknown({:#x})", params.handler),
125 }
126 }
127}
128
129#[derive(Debug, Clone, Default)]
136pub struct ConditionalOrderFactory;
137
138impl ConditionalOrderFactory {
139 #[must_use]
146 pub const fn new() -> Self {
147 Self
148 }
149
150 pub fn from_params(
168 &self,
169 params: ConditionalOrderParams,
170 ) -> Result<ConditionalOrderKind, CowError> {
171 if params.handler == TWAP_HANDLER_ADDRESS {
172 if params.static_input.len() == 14 * 32 {
176 let data = decode_gat_static_input(¶ms.static_input)?;
177 return Ok(ConditionalOrderKind::GoodAfterTime(GatOrder::with_salt(
178 data,
179 params.salt,
180 )));
181 }
182 let data = decode_twap_static_input(¶ms.static_input)?;
183 return Ok(ConditionalOrderKind::Twap(TwapOrder::with_salt(data, params.salt)));
184 }
185 if params.handler == STOP_LOSS_HANDLER_ADDRESS {
186 let data = decode_stop_loss_static_input(¶ms.static_input)?;
187 return Ok(ConditionalOrderKind::StopLoss(StopLossOrder::with_salt(data, params.salt)));
188 }
189 Ok(ConditionalOrderKind::Unknown(params))
190 }
191}
192impl fmt::Display for ConditionalOrderFactory {
193 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194 f.write_str("conditional-order-factory")
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use alloy_primitives::{Address, B256};
201
202 use super::*;
203
204 #[test]
205 fn factory_new() {
206 let factory = ConditionalOrderFactory::new();
207 assert_eq!(factory.to_string(), "conditional-order-factory");
208 }
209
210 #[test]
211 fn factory_unknown_handler() {
212 let factory = ConditionalOrderFactory::new();
213 let params = ConditionalOrderParams {
214 handler: Address::ZERO,
215 salt: B256::ZERO,
216 static_input: vec![],
217 };
218 let result = factory.from_params(params).unwrap();
219 assert!(result.is_unknown());
220 assert!(!result.is_twap());
221 assert!(!result.is_stop_loss());
222 assert!(!result.is_good_after_time());
223 assert_eq!(result.as_str(), "unknown");
224 }
225
226 #[test]
227 fn factory_twap_handler_empty_static_input_errors() {
228 let factory = ConditionalOrderFactory::new();
229 let params = ConditionalOrderParams {
230 handler: TWAP_HANDLER_ADDRESS,
231 salt: B256::ZERO,
232 static_input: vec![],
233 };
234 assert!(factory.from_params(params).is_err());
236 }
237
238 #[test]
239 fn factory_stop_loss_handler_empty_static_input_errors() {
240 let factory = ConditionalOrderFactory::new();
241 let params = ConditionalOrderParams {
242 handler: STOP_LOSS_HANDLER_ADDRESS,
243 salt: B256::ZERO,
244 static_input: vec![],
245 };
246 assert!(factory.from_params(params).is_err());
247 }
248
249 #[test]
250 fn conditional_order_kind_display_unknown() {
251 let kind = ConditionalOrderKind::Unknown(ConditionalOrderParams {
252 handler: Address::ZERO,
253 salt: B256::ZERO,
254 static_input: vec![],
255 });
256 let s = kind.to_string();
257 assert!(s.contains("unknown"));
258 }
259
260 #[test]
261 fn conditional_order_kind_display_stop_loss() {
262 let kind = ConditionalOrderKind::Unknown(ConditionalOrderParams {
263 handler: Address::ZERO,
264 salt: B256::ZERO,
265 static_input: vec![],
266 });
267 assert_eq!(kind.as_str(), "unknown");
270 }
271
272 fn sample_twap() -> TwapOrder {
275 use alloy_primitives::U256;
276
277 use crate::types::TwapData;
278 let data = TwapData::sell(
279 Address::repeat_byte(0x11),
280 Address::repeat_byte(0x22),
281 U256::from(1_000u64),
282 4,
283 3_600,
284 )
285 .with_buy_amount(U256::from(800u64));
286 TwapOrder::new(data)
287 }
288
289 fn sample_stop_loss() -> StopLossOrder {
290 use alloy_primitives::U256;
291
292 use crate::stop_loss::StopLossData;
293 StopLossOrder::new(StopLossData {
294 sell_token: Address::repeat_byte(0x01),
295 buy_token: Address::repeat_byte(0x02),
296 sell_amount: U256::from(1_000u64),
297 buy_amount: U256::from(900u64),
298 app_data: B256::ZERO,
299 receiver: Address::ZERO,
300 is_sell_order: true,
301 is_partially_fillable: false,
302 valid_to: 9_999_999,
303 strike_price: U256::from(1_000_000_000_000_000_000u64),
304 sell_token_price_oracle: Address::repeat_byte(0x03),
305 buy_token_price_oracle: Address::repeat_byte(0x04),
306 token_amount_in_eth: false,
307 })
308 }
309
310 fn sample_gat() -> GatOrder {
311 use alloy_primitives::U256;
312
313 use crate::{gat::GatData, types::GpV2OrderStruct};
314 let order = GpV2OrderStruct {
315 sell_token: Address::repeat_byte(0x01),
316 buy_token: Address::repeat_byte(0x02),
317 receiver: Address::ZERO,
318 sell_amount: U256::from(1_000u64),
319 buy_amount: U256::from(900u64),
320 valid_to: 9_999_999,
321 app_data: B256::ZERO,
322 fee_amount: U256::ZERO,
323 kind: B256::ZERO,
324 partially_fillable: false,
325 sell_token_balance: B256::ZERO,
326 buy_token_balance: B256::ZERO,
327 };
328 GatOrder::new(GatData { order, start_time: 1_000_000, tx_deadline: 2_000_000 })
329 }
330
331 #[test]
332 fn conditional_order_kind_display_typed_variants() {
333 assert!(ConditionalOrderKind::Twap(sample_twap()).to_string().starts_with("twap("));
336 assert_eq!(ConditionalOrderKind::StopLoss(sample_stop_loss()).to_string(), "stop-loss");
337 assert_eq!(
338 ConditionalOrderKind::GoodAfterTime(sample_gat()).to_string(),
339 "good-after-time",
340 );
341 }
342}