1use crate::stablecoin_dex::{IStablecoinDEX, error::OrderError};
8use alloy::primitives::{Address, B256};
9use tempo_precompiles_macros::Storable;
10
11#[derive(Debug, Clone, PartialEq, Eq, Storable)]
35pub struct Order {
36 pub order_id: u128,
38 pub maker: Address,
40 pub book_key: B256,
42 pub is_bid: bool,
44 pub tick: i16,
46 pub amount: u128,
48 pub remaining: u128,
50 pub prev: u128,
52 pub next: u128,
54 pub is_flip: bool,
56 pub flip_tick: i16,
60}
61
62impl Order {
63 #[allow(clippy::too_many_arguments)]
65 pub fn new(
66 order_id: u128,
67 maker: Address,
68 book_key: B256,
69 amount: u128,
70 tick: i16,
71 is_bid: bool,
72 is_flip: bool,
73 flip_tick: i16,
74 ) -> Self {
75 Self {
76 order_id,
77 maker,
78 book_key,
79 is_bid,
80 tick,
81 amount,
82 remaining: amount,
83 prev: 0,
84 next: 0,
85 is_flip,
86 flip_tick,
87 }
88 }
89
90 pub fn new_bid(
92 order_id: u128,
93 maker: Address,
94 book_key: B256,
95 amount: u128,
96 tick: i16,
97 ) -> Self {
98 Self::new(order_id, maker, book_key, amount, tick, true, false, 0)
99 }
100
101 pub fn new_ask(
103 order_id: u128,
104 maker: Address,
105 book_key: B256,
106 amount: u128,
107 tick: i16,
108 ) -> Self {
109 Self::new(order_id, maker, book_key, amount, tick, false, false, 0)
110 }
111
112 pub fn new_flip(
119 order_id: u128,
120 maker: Address,
121 book_key: B256,
122 amount: u128,
123 tick: i16,
124 is_bid: bool,
125 flip_tick: i16,
126 ) -> Result<Self, OrderError> {
127 if is_bid {
129 if flip_tick <= tick {
130 return Err(OrderError::InvalidBidFlipTick { tick, flip_tick });
131 }
132 } else if flip_tick >= tick {
133 return Err(OrderError::InvalidAskFlipTick { tick, flip_tick });
134 }
135
136 Ok(Self::new(
137 order_id, maker, book_key, amount, tick, is_bid, true, flip_tick,
138 ))
139 }
140
141 pub fn order_id(&self) -> u128 {
143 self.order_id
144 }
145
146 pub fn maker(&self) -> Address {
148 self.maker
149 }
150
151 pub fn book_key(&self) -> B256 {
153 self.book_key
154 }
155
156 pub fn is_bid(&self) -> bool {
158 self.is_bid
159 }
160
161 pub fn amount(&self) -> u128 {
163 self.amount
164 }
165
166 pub fn remaining(&self) -> u128 {
168 self.remaining
169 }
170
171 fn remaining_mut(&mut self) -> &mut u128 {
173 &mut self.remaining
174 }
175
176 pub fn tick(&self) -> i16 {
178 self.tick
179 }
180
181 pub fn is_ask(&self) -> bool {
183 !self.is_bid
184 }
185
186 pub fn is_flip(&self) -> bool {
188 self.is_flip
189 }
190
191 pub fn flip_tick(&self) -> i16 {
196 self.flip_tick
197 }
198
199 pub fn prev(&self) -> u128 {
201 self.prev
202 }
203
204 pub fn next(&self) -> u128 {
206 self.next
207 }
208
209 pub fn set_prev(&mut self, prev_id: u128) {
211 self.prev = prev_id;
212 }
213
214 pub fn set_next(&mut self, next_id: u128) {
216 self.next = next_id;
217 }
218
219 pub fn is_fully_filled(&self) -> bool {
221 self.remaining == 0
222 }
223
224 pub fn fill(&mut self, fill_amount: u128) -> Result<(), OrderError> {
229 if fill_amount > self.remaining {
230 return Err(OrderError::FillAmountExceedsRemaining {
231 requested: fill_amount,
232 available: self.remaining,
233 });
234 }
235 *self.remaining_mut() = self.remaining.saturating_sub(fill_amount);
236 Ok(())
237 }
238
239 pub fn create_flipped_order(&self, new_order_id: u128) -> Result<Self, OrderError> {
252 if !self.is_flip {
254 return Err(OrderError::NotAFlipOrder);
255 }
256
257 if self.remaining != 0 {
259 return Err(OrderError::OrderNotFullyFilled {
260 remaining: self.remaining,
261 });
262 }
263
264 Ok(Self {
266 order_id: new_order_id,
267 maker: self.maker,
268 book_key: self.book_key,
269 is_bid: !self.is_bid, tick: self.flip_tick, amount: self.amount, remaining: self.amount, prev: 0, next: 0,
275 is_flip: true, flip_tick: self.tick, })
278 }
279}
280
281impl From<Order> for IStablecoinDEX::Order {
282 fn from(value: Order) -> Self {
283 Self {
284 orderId: value.order_id,
285 maker: value.maker,
286 bookKey: value.book_key,
287 isBid: value.is_bid,
288 tick: value.tick,
289 amount: value.amount,
290 remaining: value.remaining,
291 prev: value.prev,
292 next: value.next,
293 isFlip: value.is_flip,
294 flipTick: value.flip_tick,
295 }
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use crate::{
302 stablecoin_dex::StablecoinDEX,
303 storage::{Handler, StorageCtx, hashmap::HashMapStorageProvider},
304 };
305
306 use super::*;
307 use alloy::primitives::{address, b256};
308
309 const TEST_MAKER: Address = address!("0x1111111111111111111111111111111111111111");
310 const TEST_BOOK_KEY: B256 =
311 b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
312
313 #[test]
314 fn test_new_bid_order() {
315 let order = Order::new_bid(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5);
316
317 assert_eq!(order.order_id(), 1);
318 assert_eq!(order.maker(), TEST_MAKER);
319 assert_eq!(order.book_key(), TEST_BOOK_KEY);
320 assert!(order.is_bid());
321 assert_eq!(order.amount(), 1000);
322 assert_eq!(order.remaining(), 1000);
323 assert!(order.is_bid());
324 assert!(!order.is_ask());
325 assert_eq!(order.tick(), 5);
326 assert!(!order.is_flip());
327 assert_eq!(order.flip_tick(), 0);
328 }
329
330 #[test]
331 fn test_new_ask_order() {
332 let order = Order::new_ask(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5);
333
334 assert_eq!(order.order_id(), 1);
335 assert!(!order.is_bid());
336 assert!(!order.is_bid());
337 assert!(order.is_ask());
338 assert!(!order.is_flip());
339 }
340
341 #[test]
342 fn test_new_flip_order_bid() {
343 let order = Order::new_flip(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5, true, 10).unwrap();
344
345 assert!(order.is_flip());
346 assert_eq!(order.flip_tick(), 10);
347 assert_eq!(order.tick(), 5);
348 assert!(order.is_bid());
349 }
350
351 #[test]
352 fn test_new_flip_order_ask() {
353 let order = Order::new_flip(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5, false, 2).unwrap();
354
355 assert!(order.is_flip());
356 assert_eq!(order.flip_tick(), 2);
357 assert_eq!(order.tick(), 5);
358 assert!(!order.is_bid());
359 assert!(order.is_ask());
360 }
361
362 #[test]
363 fn test_new_flip_order_bid_invalid_flip_tick() {
364 let result = Order::new_flip(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5, true, 3);
365
366 assert!(matches!(result, Err(OrderError::InvalidBidFlipTick { .. })));
367 }
368
369 #[test]
370 fn test_new_flip_order_ask_invalid_flip_tick() {
371 let result = Order::new_flip(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5, false, 7);
372
373 assert!(matches!(result, Err(OrderError::InvalidAskFlipTick { .. })));
374 }
375
376 #[test]
377 fn test_fill_bid_order_partial() {
378 let mut order = Order::new_bid(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5);
379
380 assert!(!order.is_fully_filled());
381
382 order.fill(400).unwrap();
383
384 assert_eq!(order.remaining(), 600);
385 assert_eq!(order.amount(), 1000);
386 assert!(!order.is_fully_filled());
387 }
388
389 #[test]
390 fn test_fill_ask_order_complete() {
391 let mut order = Order::new_ask(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5);
392
393 order.fill(1000).unwrap();
394
395 assert_eq!(order.remaining(), 0);
396 assert_eq!(order.amount(), 1000);
397 assert!(order.is_fully_filled());
398 }
399
400 #[test]
401 fn test_fill_order_overfill() {
402 let mut order = Order::new_bid(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5);
403
404 let result = order.fill(1001);
405 assert!(matches!(
406 result,
407 Err(OrderError::FillAmountExceedsRemaining { .. })
408 ));
409 }
410
411 #[test]
412 fn test_create_flipped_order_bid_to_ask() {
413 let mut order = Order::new_flip(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5, true, 10).unwrap();
414
415 order.fill(1000).unwrap();
417 assert!(order.is_fully_filled());
418
419 let flipped = order.create_flipped_order(2).unwrap();
421
422 assert_eq!(flipped.order_id(), 2);
423 assert_eq!(flipped.maker(), order.maker());
424 assert_eq!(flipped.book_key(), order.book_key());
425 assert_eq!(flipped.amount(), 1000); assert_eq!(flipped.remaining(), 1000); assert!(!flipped.is_bid()); assert!(flipped.is_ask());
429 assert_eq!(flipped.tick(), 10); assert_eq!(flipped.flip_tick(), 5); assert!(flipped.is_flip());
432 }
433
434 #[test]
435 fn test_create_flipped_order_ask_to_bid() {
436 let mut order = Order::new_flip(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 10, false, 5).unwrap();
437
438 order.fill(1000).unwrap();
439 let flipped = order.create_flipped_order(2).unwrap();
440
441 assert!(flipped.is_bid()); assert!(!flipped.is_ask());
443 assert_eq!(flipped.tick(), 5); assert_eq!(flipped.flip_tick(), 10); }
446
447 #[test]
448 fn test_create_flipped_order_non_flip() {
449 let mut order = Order::new_bid(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5);
450
451 order.fill(1000).unwrap();
452 let result = order.create_flipped_order(2);
453 assert!(matches!(result, Err(OrderError::NotAFlipOrder)));
454 }
455
456 #[test]
457 fn test_create_flipped_order_not_filled() {
458 let order = Order::new_flip(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5, true, 10).unwrap();
459
460 let result = order.create_flipped_order(2);
461 assert!(matches!(
462 result,
463 Err(OrderError::OrderNotFullyFilled { .. })
464 ));
465 }
466
467 #[test]
468 fn test_multiple_fills() {
469 let mut order = Order::new_bid(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5);
470
471 order.fill(300).unwrap();
473 assert_eq!(order.remaining(), 700);
474
475 order.fill(200).unwrap();
476 assert_eq!(order.remaining(), 500);
477
478 order.fill(500).unwrap();
479 assert_eq!(order.remaining(), 0);
480 assert!(order.is_fully_filled());
481 }
482
483 #[test]
484 fn test_multiple_flips() {
485 let mut order = Order::new_flip(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5, true, 10).unwrap();
487
488 order.fill(1000).unwrap();
490 let mut flipped1 = order.create_flipped_order(2).unwrap();
491
492 assert!(!flipped1.is_bid());
493 assert!(flipped1.is_ask());
494 assert_eq!(flipped1.tick(), 10);
495 assert_eq!(flipped1.flip_tick(), 5);
496
497 flipped1.fill(1000).unwrap();
499 let flipped2 = flipped1.create_flipped_order(3).unwrap();
500
501 assert!(flipped2.is_bid());
502 assert!(!flipped2.is_ask());
503 assert_eq!(flipped2.tick(), 5);
504 assert_eq!(flipped2.flip_tick(), 10);
505 }
506
507 #[test]
508 fn test_tick_price_encoding() {
509 let order_above = Order::new_bid(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 2);
512 assert_eq!(order_above.tick(), 2);
513
514 let order_below = Order::new_ask(2, TEST_MAKER, TEST_BOOK_KEY, 1000, -2);
515 assert_eq!(order_below.tick(), -2);
516
517 let order_par = Order::new_bid(3, TEST_MAKER, TEST_BOOK_KEY, 1000, 0);
518 assert_eq!(order_par.tick(), 0);
519 }
520
521 #[test]
522 fn test_linked_list_pointers_initialization() {
523 let order = Order::new_bid(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5);
524 assert_eq!(order.prev(), 0);
526 assert_eq!(order.next(), 0);
527 }
528
529 #[test]
530 fn test_set_linked_list_pointers() {
531 let mut order = Order::new_bid(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5);
532
533 order.set_prev(42);
535 order.set_next(43);
536
537 assert_eq!(order.prev(), 42);
538 assert_eq!(order.next(), 43);
539 }
540
541 #[test]
542 fn test_flipped_order_resets_linked_list_pointers() {
543 let mut order = Order::new_flip(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5, true, 10).unwrap();
544
545 order.set_prev(100);
547 order.set_next(200);
548
549 order.fill(1000).unwrap();
551
552 let flipped = order.create_flipped_order(2).unwrap();
554
555 assert_eq!(flipped.prev(), 0);
557 assert_eq!(flipped.next(), 0);
558 }
559
560 #[test]
561 fn test_store_order() -> eyre::Result<()> {
562 let mut storage = HashMapStorageProvider::new(1);
563 StorageCtx::enter(&mut storage, || {
564 let mut exchange = StablecoinDEX::new();
565
566 let id = 42;
567 let order = Order::new_flip(id, TEST_MAKER, TEST_BOOK_KEY, 1000, 5, true, 10).unwrap();
568 exchange.orders[id].write(order)?;
569
570 let loaded_order = exchange.orders[id].read()?;
571 assert_eq!(loaded_order.order_id(), 42);
572 assert_eq!(loaded_order.maker(), TEST_MAKER);
573 assert_eq!(loaded_order.book_key(), TEST_BOOK_KEY);
574 assert_eq!(loaded_order.amount(), 1000);
575 assert_eq!(loaded_order.remaining(), 1000);
576 assert_eq!(loaded_order.tick(), 5);
577 assert!(loaded_order.is_bid());
578 assert!(loaded_order.is_flip());
579 assert_eq!(loaded_order.flip_tick(), 10);
580 assert_eq!(loaded_order.prev(), 0);
581 assert_eq!(loaded_order.next(), 0);
582
583 Ok(())
584 })
585 }
586
587 #[test]
588 fn test_delete_order() -> eyre::Result<()> {
589 let mut storage = HashMapStorageProvider::new(1);
590 StorageCtx::enter(&mut storage, || {
591 let mut exchange = StablecoinDEX::new();
592
593 let id = 42;
594 let order = Order::new_flip(id, TEST_MAKER, TEST_BOOK_KEY, 1000, 5, true, 10).unwrap();
595 exchange.orders[id].write(order)?;
596 exchange.orders[id].delete()?;
597
598 let deleted_order = exchange.orders[id].read()?;
599 assert_eq!(deleted_order.order_id(), 0);
600 assert_eq!(deleted_order.maker(), Address::ZERO);
601 assert_eq!(deleted_order.book_key(), B256::ZERO);
602 assert_eq!(deleted_order.amount(), 0);
603 assert_eq!(deleted_order.remaining(), 0);
604 assert_eq!(deleted_order.tick(), 0);
605 assert!(!deleted_order.is_bid());
606 assert!(!deleted_order.is_flip());
607 assert_eq!(deleted_order.flip_tick(), 0);
608 assert_eq!(deleted_order.prev(), 0);
609 assert_eq!(deleted_order.next(), 0);
610
611 Ok(())
612 })
613 }
614}