Skip to main content

tempo_precompiles/stablecoin_dex/
order.rs

1//! Limit order type for the stablecoin DEX.
2//!
3//! This module defines the core `Order` type used in the stablecoin DEX orderbook.
4//! Orders support price-time priority matching, partial fills, and flip orders that
5//! automatically place opposite-side orders when filled.
6
7use crate::stablecoin_dex::{IStablecoinDEX, error::OrderError};
8use alloy::primitives::{Address, B256};
9use tempo_chainspec::hardfork::TempoHardfork;
10use tempo_precompiles_macros::Storable;
11
12/// Represents an order in the stablecoin DEX orderbook.
13///
14/// This struct matches the Solidity reference implementation in StablecoinDEX.sol.
15///
16/// # Order Types
17/// - **Regular orders**: Orders with `is_flip = false`
18/// - **Flip orders**: Orders with `is_flip = true` that automatically create
19///   a new order on the opposite side when fully filled
20///
21/// # Order Lifecycle
22/// 1. Order is placed via `place()` or `placeFlip()` and immediately added to the orderbook
23/// 2. Orders can be filled (fully or partially) by swaps
24/// 3. Flip orders automatically create a new order on the opposite side when fully filled
25/// 4. Orders can be cancelled, removing them from the book and refunding escrow
26///
27/// # Price-Time Priority
28/// Orders are sorted by price (tick), then by insertion time.
29/// The doubly linked list maintains insertion order - orders are added at the tail,
30/// so traversing from head to tail gives price-time priority.
31///
32/// # Onchain Storage
33/// Orders are stored onchain in doubly linked lists organized by tick.
34/// Each tick maintains a FIFO queue of orders using `prev` and `next` pointers.
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Storable)]
36pub struct Order {
37    /// Unique identifier for this order
38    pub order_id: u128,
39    /// Address of the user who placed this order
40    pub maker: Address,
41    /// Orderbook key (identifies the trading pair)
42    pub book_key: B256,
43    /// Whether this is a bid (true) or ask (false) order
44    pub is_bid: bool,
45    /// Price tick
46    pub tick: i16,
47    /// Original order amount
48    pub amount: u128,
49    /// Remaining amount to be filled
50    pub remaining: u128,
51    /// Previous order ID in the doubly linked list (0 if head)
52    pub prev: u128,
53    /// Next order ID in the doubly linked list (0 if tail)
54    pub next: u128,
55    /// Whether this is a flip order
56    pub is_flip: bool,
57    /// Tick to flip to when fully filled (for flip orders, 0 for regular orders).
58    /// Pre-T5: for bid flips `flip_tick > tick`; for ask flips `flip_tick < tick`.
59    /// T5+ (TIP-1030): for bid flips `flip_tick >= tick`; for ask flips `flip_tick <= tick`.
60    pub flip_tick: i16,
61}
62
63impl Order {
64    /// Creates a new [`Order`] with `prev` and `next` initialized to 0.
65    #[allow(clippy::too_many_arguments)]
66    pub fn new(
67        order_id: u128,
68        maker: Address,
69        book_key: B256,
70        amount: u128,
71        tick: i16,
72        is_bid: bool,
73        is_flip: bool,
74        flip_tick: i16,
75    ) -> Self {
76        Self {
77            order_id,
78            maker,
79            book_key,
80            is_bid,
81            tick,
82            amount,
83            remaining: amount,
84            prev: 0,
85            next: 0,
86            is_flip,
87            flip_tick,
88        }
89    }
90
91    /// Creates a new bid order
92    pub fn new_bid(
93        order_id: u128,
94        maker: Address,
95        book_key: B256,
96        amount: u128,
97        tick: i16,
98    ) -> Self {
99        Self::new(order_id, maker, book_key, amount, tick, true, false, 0)
100    }
101
102    /// Creates a new ask order
103    pub fn new_ask(
104        order_id: u128,
105        maker: Address,
106        book_key: B256,
107        amount: u128,
108        tick: i16,
109    ) -> Self {
110        Self::new(order_id, maker, book_key, amount, tick, false, false, 0)
111    }
112
113    /// Creates a new flip order with `prev` and `next` initialized to 0.
114    /// The orderbook sets linked-list pointers when inserting.
115    ///
116    /// The `hardfork` parameter controls flip-tick validation:
117    /// - Pre-T5: for bid flips `flip_tick > tick`; for ask flips `flip_tick < tick`.
118    /// - T5+ (TIP-1030): for bid flips `flip_tick >= tick`; for ask flips `flip_tick <= tick`.
119    ///
120    /// # Errors
121    /// - `InvalidBidFlipTick` - `is_bid` is true and `flip_tick < tick`
122    /// - `InvalidAskFlipTick` - `is_bid` is false and `flip_tick > tick`
123    #[allow(clippy::too_many_arguments)]
124    pub fn new_flip(
125        order_id: u128,
126        maker: Address,
127        book_key: B256,
128        amount: u128,
129        tick: i16,
130        is_bid: bool,
131        flip_tick: i16,
132        hardfork: TempoHardfork,
133    ) -> Result<Self, OrderError> {
134        // TIP-1030 (T5+) relaxes the constraint to allow `flip_tick == tick`.
135        let t5_active = hardfork.is_t5();
136        let invalid = if is_bid {
137            flip_tick < tick || (!t5_active && flip_tick == tick)
138        } else {
139            flip_tick > tick || (!t5_active && flip_tick == tick)
140        };
141
142        if invalid {
143            return Err(if is_bid {
144                OrderError::InvalidBidFlipTick { tick, flip_tick }
145            } else {
146                OrderError::InvalidAskFlipTick { tick, flip_tick }
147            });
148        }
149
150        Ok(Self::new(
151            order_id, maker, book_key, amount, tick, is_bid, true, flip_tick,
152        ))
153    }
154
155    /// Returns the order ID.
156    pub fn order_id(&self) -> u128 {
157        self.order_id
158    }
159
160    /// Returns the maker address.
161    pub fn maker(&self) -> Address {
162        self.maker
163    }
164
165    /// Returns the orderbook key.
166    pub fn book_key(&self) -> B256 {
167        self.book_key
168    }
169
170    /// Returns whether this is a bid order.
171    pub fn is_bid(&self) -> bool {
172        self.is_bid
173    }
174
175    /// Returns the original amount.
176    pub fn amount(&self) -> u128 {
177        self.amount
178    }
179
180    /// Returns the remaining amount.
181    pub fn remaining(&self) -> u128 {
182        self.remaining
183    }
184
185    /// Returns a mutable reference to the remaining amount.
186    fn remaining_mut(&mut self) -> &mut u128 {
187        &mut self.remaining
188    }
189
190    /// Returns the tick price.
191    pub fn tick(&self) -> i16 {
192        self.tick
193    }
194
195    /// Returns true if this is an ask order (selling base token).
196    pub fn is_ask(&self) -> bool {
197        !self.is_bid
198    }
199
200    /// Returns true if this is a flip order.
201    pub fn is_flip(&self) -> bool {
202        self.is_flip
203    }
204
205    /// Returns the flip tick.
206    ///
207    /// For non-flip orders, this is always 0.
208    /// For flip orders, this can be any valid tick value including 0 (peg price).
209    pub fn flip_tick(&self) -> i16 {
210        self.flip_tick
211    }
212
213    /// Returns the previous order ID in the doubly linked list (0 if head).
214    pub fn prev(&self) -> u128 {
215        self.prev
216    }
217
218    /// Returns the next order ID in the doubly linked list (0 if tail).
219    pub fn next(&self) -> u128 {
220        self.next
221    }
222
223    /// Sets the previous order ID in the doubly linked list.
224    pub fn set_prev(&mut self, prev_id: u128) {
225        self.prev = prev_id;
226    }
227
228    /// Sets the next order ID in the doubly linked list.
229    pub fn set_next(&mut self, next_id: u128) {
230        self.next = next_id;
231    }
232
233    /// Returns true if the order is completely filled (no remaining amount).
234    pub fn is_fully_filled(&self) -> bool {
235        self.remaining == 0
236    }
237
238    /// Fills the order by the specified amount, reducing `remaining` accordingly.
239    ///
240    /// # Errors
241    /// - `FillAmountExceedsRemaining` — `fill_amount` is greater than `remaining`
242    pub fn fill(&mut self, fill_amount: u128) -> Result<(), OrderError> {
243        if fill_amount > self.remaining {
244            return Err(OrderError::FillAmountExceedsRemaining {
245                requested: fill_amount,
246                available: self.remaining,
247            });
248        }
249        *self.remaining_mut() = self.remaining.saturating_sub(fill_amount);
250        Ok(())
251    }
252
253    /// Creates a flipped order from a fully filled flip order.
254    ///
255    /// When a flip order is completely filled, it creates a new order on the opposite side:
256    /// - Sides are swapped (bid -> ask, ask -> bid)
257    /// - New price = original flip_tick
258    /// - New flip_tick = original tick
259    /// - Amount is the same as original
260    /// - Linked list pointers are reset to 0 (will be set by orderbook on insertion)
261    pub(crate) fn create_flipped_order(&self, new_order_id: u128) -> Self {
262        debug_assert!(self.is_flip());
263
264        // Create flipped order
265        Self {
266            order_id: new_order_id,
267            maker: self.maker,
268            book_key: self.book_key,
269            is_bid: !self.is_bid,   // Flip the side
270            tick: self.flip_tick,   // Old flip_tick becomes new tick
271            amount: self.amount,    // Same as original
272            remaining: self.amount, // Reset remaining to original amount
273            prev: 0,                // Reset linked list pointers
274            next: 0,
275            is_flip: true,        // Keep as flip order
276            flip_tick: self.tick, // Old tick becomes new flip_tick
277        }
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(
344            1,
345            TEST_MAKER,
346            TEST_BOOK_KEY,
347            1000,
348            5,
349            true,
350            10,
351            TempoHardfork::T4,
352        )
353        .unwrap();
354
355        assert!(order.is_flip());
356        assert_eq!(order.flip_tick(), 10);
357        assert_eq!(order.tick(), 5);
358        assert!(order.is_bid());
359    }
360
361    #[test]
362    fn test_new_flip_order_ask() {
363        let order = Order::new_flip(
364            1,
365            TEST_MAKER,
366            TEST_BOOK_KEY,
367            1000,
368            5,
369            false,
370            2,
371            TempoHardfork::T4,
372        )
373        .unwrap();
374
375        assert!(order.is_flip());
376        assert_eq!(order.flip_tick(), 2);
377        assert_eq!(order.tick(), 5);
378        assert!(!order.is_bid());
379        assert!(order.is_ask());
380    }
381
382    #[test]
383    fn test_new_flip_order_bid_invalid_flip_tick() {
384        let result = Order::new_flip(
385            1,
386            TEST_MAKER,
387            TEST_BOOK_KEY,
388            1000,
389            5,
390            true,
391            3,
392            TempoHardfork::T4,
393        );
394
395        assert!(matches!(result, Err(OrderError::InvalidBidFlipTick { .. })));
396    }
397
398    #[test]
399    fn test_new_flip_order_ask_invalid_flip_tick() {
400        let result = Order::new_flip(
401            1,
402            TEST_MAKER,
403            TEST_BOOK_KEY,
404            1000,
405            5,
406            false,
407            7,
408            TempoHardfork::T4,
409        );
410
411        assert!(matches!(result, Err(OrderError::InvalidAskFlipTick { .. })));
412    }
413
414    #[test]
415    fn test_new_flip_order_bid_same_tick_rejected() {
416        // Pre-T5: same-tick bid flip is rejected
417        let result = Order::new_flip(
418            1,
419            TEST_MAKER,
420            TEST_BOOK_KEY,
421            1000,
422            5,
423            true,
424            5,
425            TempoHardfork::T4,
426        );
427        assert!(matches!(result, Err(OrderError::InvalidBidFlipTick { .. })));
428    }
429
430    #[test]
431    fn test_new_flip_order_ask_same_tick_rejected() {
432        // Pre-T5: same-tick ask flip is rejected
433        let result = Order::new_flip(
434            1,
435            TEST_MAKER,
436            TEST_BOOK_KEY,
437            1000,
438            5,
439            false,
440            5,
441            TempoHardfork::T4,
442        );
443        assert!(matches!(result, Err(OrderError::InvalidAskFlipTick { .. })));
444    }
445
446    #[test]
447    fn test_new_flip_order_bid_same_tick_accepted() {
448        // TIP-1030 (T5+): same-tick bid flip is accepted
449        let order = Order::new_flip(
450            1,
451            TEST_MAKER,
452            TEST_BOOK_KEY,
453            1000,
454            5,
455            true,
456            5,
457            TempoHardfork::T5,
458        )
459        .unwrap();
460        assert!(order.is_flip());
461        assert_eq!(order.tick(), 5);
462        assert_eq!(order.flip_tick(), 5);
463        assert!(order.is_bid());
464    }
465
466    #[test]
467    fn test_new_flip_t5_still_rejects_wrong_side() {
468        // TIP-1030 (T5+): flip_tick < tick still rejected for bids
469        let result = Order::new_flip(
470            1,
471            TEST_MAKER,
472            TEST_BOOK_KEY,
473            1000,
474            5,
475            true,
476            3,
477            TempoHardfork::T5,
478        );
479        assert!(matches!(result, Err(OrderError::InvalidBidFlipTick { .. })));
480
481        // TIP-1030 (T5+): flip_tick > tick still rejected for asks
482        let result = Order::new_flip(
483            1,
484            TEST_MAKER,
485            TEST_BOOK_KEY,
486            1000,
487            5,
488            false,
489            7,
490            TempoHardfork::T5,
491        );
492        assert!(matches!(result, Err(OrderError::InvalidAskFlipTick { .. })));
493    }
494
495    #[test]
496    fn test_new_flip_order_ask_same_tick_accepted() {
497        // TIP-1030 (T5+): same-tick ask flip is accepted
498        let order = Order::new_flip(
499            1,
500            TEST_MAKER,
501            TEST_BOOK_KEY,
502            1000,
503            5,
504            false,
505            5,
506            TempoHardfork::T5,
507        )
508        .unwrap();
509        assert!(order.is_flip());
510        assert_eq!(order.tick(), 5);
511        assert_eq!(order.flip_tick(), 5);
512        assert!(order.is_ask());
513    }
514
515    #[test]
516    fn test_fill_bid_order_partial() {
517        let mut order = Order::new_bid(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5);
518
519        assert!(!order.is_fully_filled());
520
521        order.fill(400).unwrap();
522
523        assert_eq!(order.remaining(), 600);
524        assert_eq!(order.amount(), 1000);
525        assert!(!order.is_fully_filled());
526    }
527
528    #[test]
529    fn test_fill_ask_order_complete() {
530        let mut order = Order::new_ask(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5);
531
532        order.fill(1000).unwrap();
533
534        assert_eq!(order.remaining(), 0);
535        assert_eq!(order.amount(), 1000);
536        assert!(order.is_fully_filled());
537    }
538
539    #[test]
540    fn test_fill_order_overfill() {
541        let mut order = Order::new_bid(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5);
542
543        let result = order.fill(1001);
544        assert!(matches!(
545            result,
546            Err(OrderError::FillAmountExceedsRemaining { .. })
547        ));
548    }
549
550    #[test]
551    fn test_create_flipped_order_bid_to_ask() {
552        let mut order = Order::new_flip(
553            1,
554            TEST_MAKER,
555            TEST_BOOK_KEY,
556            1000,
557            5,
558            true,
559            10,
560            TempoHardfork::T4,
561        )
562        .unwrap();
563
564        // Fully fill the order
565        order.fill(1000).unwrap();
566        assert!(order.is_fully_filled());
567
568        // Create flipped order
569        let flipped = order.create_flipped_order(2);
570
571        assert_eq!(flipped.order_id(), 2);
572        assert_eq!(flipped.maker(), order.maker());
573        assert_eq!(flipped.book_key(), order.book_key());
574        assert_eq!(flipped.amount(), 1000); // Same as original
575        assert_eq!(flipped.remaining(), 1000); // Reset to full amount
576        assert!(!flipped.is_bid()); // Flipped from bid to ask
577        assert!(flipped.is_ask());
578        assert_eq!(flipped.tick(), 10); // Old flip_tick
579        assert_eq!(flipped.flip_tick(), 5); // Old tick
580        assert!(flipped.is_flip());
581    }
582
583    #[test]
584    fn test_create_flipped_order_ask_to_bid() {
585        let mut order = Order::new_flip(
586            1,
587            TEST_MAKER,
588            TEST_BOOK_KEY,
589            1000,
590            10,
591            false,
592            5,
593            TempoHardfork::T4,
594        )
595        .unwrap();
596
597        order.fill(1000).unwrap();
598        let flipped = order.create_flipped_order(2);
599
600        assert!(flipped.is_bid()); // Flipped from ask to bid
601        assert!(!flipped.is_ask());
602        assert_eq!(flipped.tick(), 5); // Old flip_tick
603        assert_eq!(flipped.flip_tick(), 10); // Old tick
604    }
605
606    #[test]
607    fn test_multiple_fills() {
608        let mut order = Order::new_bid(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5);
609
610        // Multiple partial fills
611        order.fill(300).unwrap();
612        assert_eq!(order.remaining(), 700);
613
614        order.fill(200).unwrap();
615        assert_eq!(order.remaining(), 500);
616
617        order.fill(500).unwrap();
618        assert_eq!(order.remaining(), 0);
619        assert!(order.is_fully_filled());
620    }
621
622    #[test]
623    fn test_multiple_flips() {
624        // Test that an order can flip multiple times
625        let mut order = Order::new_flip(
626            1,
627            TEST_MAKER,
628            TEST_BOOK_KEY,
629            1000,
630            5,
631            true,
632            10,
633            TempoHardfork::T4,
634        )
635        .unwrap();
636
637        // First flip: bid -> ask
638        order.fill(1000).unwrap();
639        let mut flipped1 = order.create_flipped_order(2);
640
641        assert!(!flipped1.is_bid());
642        assert!(flipped1.is_ask());
643        assert_eq!(flipped1.tick(), 10);
644        assert_eq!(flipped1.flip_tick(), 5);
645
646        // Second flip: ask -> bid
647        flipped1.fill(1000).unwrap();
648        let flipped2 = flipped1.create_flipped_order(3);
649
650        assert!(flipped2.is_bid());
651        assert!(!flipped2.is_ask());
652        assert_eq!(flipped2.tick(), 5);
653        assert_eq!(flipped2.flip_tick(), 10);
654    }
655
656    #[test]
657    fn test_tick_price_encoding() {
658        // Tick represents price offset from peg
659
660        let order_above = Order::new_bid(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 2);
661        assert_eq!(order_above.tick(), 2);
662
663        let order_below = Order::new_ask(2, TEST_MAKER, TEST_BOOK_KEY, 1000, -2);
664        assert_eq!(order_below.tick(), -2);
665
666        let order_par = Order::new_bid(3, TEST_MAKER, TEST_BOOK_KEY, 1000, 0);
667        assert_eq!(order_par.tick(), 0);
668    }
669
670    #[test]
671    fn test_linked_list_pointers_initialization() {
672        let order = Order::new_bid(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5);
673        // Linked list pointers should be initialized to 0
674        assert_eq!(order.prev(), 0);
675        assert_eq!(order.next(), 0);
676    }
677
678    #[test]
679    fn test_set_linked_list_pointers() {
680        let mut order = Order::new_bid(1, TEST_MAKER, TEST_BOOK_KEY, 1000, 5);
681
682        // Set prev and next pointers
683        order.set_prev(42);
684        order.set_next(43);
685
686        assert_eq!(order.prev(), 42);
687        assert_eq!(order.next(), 43);
688    }
689
690    #[test]
691    fn test_flipped_order_resets_linked_list_pointers() {
692        let mut order = Order::new_flip(
693            1,
694            TEST_MAKER,
695            TEST_BOOK_KEY,
696            1000,
697            5,
698            true,
699            10,
700            TempoHardfork::T4,
701        )
702        .unwrap();
703
704        // Set linked list pointers on original order
705        order.set_prev(100);
706        order.set_next(200);
707
708        // Fill the order
709        order.fill(1000).unwrap();
710
711        // Create flipped order
712        let flipped = order.create_flipped_order(2);
713
714        // Flipped order should have reset pointers
715        assert_eq!(flipped.prev(), 0);
716        assert_eq!(flipped.next(), 0);
717    }
718
719    #[test]
720    fn test_store_order() -> eyre::Result<()> {
721        let mut storage = HashMapStorageProvider::new(1);
722        StorageCtx::enter(&mut storage, || {
723            let mut exchange = StablecoinDEX::new();
724
725            let id = 42;
726            let order = Order::new_flip(
727                id,
728                TEST_MAKER,
729                TEST_BOOK_KEY,
730                1000,
731                5,
732                true,
733                10,
734                TempoHardfork::T4,
735            )
736            .unwrap();
737            exchange.orders[id].write(order)?;
738
739            let loaded_order = exchange.orders[id].read()?;
740            assert_eq!(loaded_order.order_id(), 42);
741            assert_eq!(loaded_order.maker(), TEST_MAKER);
742            assert_eq!(loaded_order.book_key(), TEST_BOOK_KEY);
743            assert_eq!(loaded_order.amount(), 1000);
744            assert_eq!(loaded_order.remaining(), 1000);
745            assert_eq!(loaded_order.tick(), 5);
746            assert!(loaded_order.is_bid());
747            assert!(loaded_order.is_flip());
748            assert_eq!(loaded_order.flip_tick(), 10);
749            assert_eq!(loaded_order.prev(), 0);
750            assert_eq!(loaded_order.next(), 0);
751
752            Ok(())
753        })
754    }
755
756    #[test]
757    fn test_delete_order() -> eyre::Result<()> {
758        let mut storage = HashMapStorageProvider::new(1);
759        StorageCtx::enter(&mut storage, || {
760            let mut exchange = StablecoinDEX::new();
761
762            let id = 42;
763            let order = Order::new_flip(
764                id,
765                TEST_MAKER,
766                TEST_BOOK_KEY,
767                1000,
768                5,
769                true,
770                10,
771                TempoHardfork::T4,
772            )
773            .unwrap();
774            exchange.orders[id].write(order)?;
775            exchange.orders[id].delete()?;
776
777            let deleted_order = exchange.orders[id].read()?;
778            assert_eq!(deleted_order.order_id(), 0);
779            assert_eq!(deleted_order.maker(), Address::ZERO);
780            assert_eq!(deleted_order.book_key(), B256::ZERO);
781            assert_eq!(deleted_order.amount(), 0);
782            assert_eq!(deleted_order.remaining(), 0);
783            assert_eq!(deleted_order.tick(), 0);
784            assert!(!deleted_order.is_bid());
785            assert!(!deleted_order.is_flip());
786            assert_eq!(deleted_order.flip_tick(), 0);
787            assert_eq!(deleted_order.prev(), 0);
788            assert_eq!(deleted_order.next(), 0);
789
790            Ok(())
791        })
792    }
793}