1use crate::{
2 helpers::get_total_amount,
3 market_data::OrderData,
4 order_book::Side,
5};
6use blart::{
7 AsBytes,
8 NoPrefixesBytes,
9 TreeMap,
10};
11use fuels::{
12 tx::TxId,
13 types::{
14 AssetId,
15 Identity,
16 },
17};
18use indexmap::IndexMap;
19use slotmap::{
20 DefaultKey,
21 SlotMap,
22};
23use std::fmt::Debug;
24
25pub type Price = u64;
26pub type Quantity = u64;
27pub type OrderId = DefaultKey;
28pub type Balances = IndexMap<Identity, (u64, u64)>;
29
30#[derive(Debug, Clone)]
32struct PriceKey([u8; 8]);
33
34impl From<Price> for PriceKey {
35 fn from(value: Price) -> Self {
36 Self(value.to_be_bytes())
37 }
38}
39
40impl From<&Price> for PriceKey {
41 fn from(value: &Price) -> Self {
42 Self(value.to_be_bytes())
43 }
44}
45
46impl From<&mut Price> for PriceKey {
47 fn from(value: &mut Price) -> Self {
48 Self(value.to_be_bytes())
49 }
50}
51
52impl From<PriceKey> for Price {
53 fn from(value: PriceKey) -> Self {
54 u64::from_be_bytes(value.0)
55 }
56}
57
58impl From<&PriceKey> for Price {
59 fn from(value: &PriceKey) -> Self {
60 u64::from_be_bytes(value.0)
61 }
62}
63
64impl From<&mut PriceKey> for Price {
65 fn from(value: &mut PriceKey) -> Self {
66 u64::from_be_bytes(value.0)
67 }
68}
69
70impl AsBytes for PriceKey {
71 fn as_bytes(&self) -> &[u8] {
72 &self.0
73 }
74}
75
76unsafe impl NoPrefixesBytes for PriceKey {}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub struct Fill {
80 pub order_id: u64,
81 pub quantity: u64,
82 pub price: u64,
83}
84
85#[derive(Debug)]
86pub struct OrderParams {
87 pub side: Side,
88 pub price: u64,
89 pub quantity: u64,
90}
91
92#[derive(Debug, Clone, PartialEq, Eq)]
93pub struct Order {
94 pub trader_id: Identity,
95 pub order_id: u64,
96 pub side: Side,
97 pub price: u64,
98 pub quantity: Quantity,
99 pub fill: Vec<Fill>,
100 pub tx_id: Option<TxId>,
101}
102
103impl Order {
104 pub fn new(
105 order_id: u64,
106 OrderData {
107 trader_id,
108 side,
109 price,
110 quantity,
111 }: OrderData,
112 ) -> Self {
113 Self {
114 order_id,
115 trader_id,
116 price,
117 quantity,
118 side,
119 fill: vec![],
120 tx_id: None,
121 }
122 }
123
124 pub fn fill(&mut self, order_id: u64, fill_quantity: Quantity, price: Price) -> bool {
125 let filled_order = Fill {
126 quantity: fill_quantity,
127 price,
128 order_id,
129 };
130 self.fill.push(filled_order);
131 self.quantity -= fill_quantity;
132 self.quantity == 0
133 }
134
135 pub fn total(&self, decimals: u64) -> u64 {
136 get_total_amount(self.quantity, self.price, decimals)
137 }
138
139 pub fn filled_total(&self, decimals: u64) -> u64 {
140 self.fill
141 .iter()
142 .map(|f| get_total_amount(f.quantity, f.price, decimals))
143 .sum()
144 }
145
146 pub fn remaining_total(&self, decimals: u64) -> Quantity {
147 match self.side {
148 Side::Buy => {
149 get_total_amount(self.total_quantity(), self.price, decimals)
150 - self.filled_total(decimals)
151 }
152 Side::Sell => 0,
153 }
154 }
155
156 pub fn total_quantity(&self) -> Quantity {
157 self.quantity + self.filled_quantity()
158 }
159
160 pub fn filled_quantity(&self) -> Quantity {
161 self.fill.iter().map(|q| q.quantity).sum::<Quantity>()
162 }
163
164 pub fn filled_price(&self) -> Price {
165 let total_price: Price = self.fill.iter().map(|q| q.price).sum();
166 let count = self.fill.len();
167
168 if count == 0 {
169 0
170 } else {
171 total_price / count as Price
172 }
173 }
174
175 pub fn is_closed(&self) -> bool {
176 self.quantity == 0
177 }
178}
179
180#[derive(Clone, Debug, PartialEq, Eq)]
181pub struct MatchedEvent {
182 pub direction: Side,
183 pub taker_id: u64,
184 pub maker_id: u64,
185 pub quantity: u64,
186 pub price: u64,
187 pub total: u64,
188}
189
190#[derive(Clone, Default)]
191pub struct BookConfig {
192 pub base_asset: AssetId,
193 pub base_decimals: u64,
194 pub quote_asset: AssetId,
195 pub quote_decimals: u64,
196 pub taker_fee: u64,
197 pub maker_fee: u64,
198}
199
200#[derive(Clone)]
201pub struct Book {
202 pub order_id: u64,
203 pub base_asset: AssetId,
204 pub base_decimals: u64,
205 pub quote_asset: AssetId,
206 pub quote_decimals: u64,
207 pub taker_fee: u64,
208 pub maker_fee: u64,
209 pub all_orders: Vec<Order>,
210 pub trades: Vec<MatchedEvent>,
211 pub cancels: Vec<Order>,
212 pub balances: Balances,
213 buys: TreeMap<PriceKey, Vec<OrderId>>,
214 sells: TreeMap<PriceKey, Vec<OrderId>>,
215 orders: SlotMap<OrderId, Order>,
216}
217
218impl Book {
219 pub fn new(config: BookConfig) -> Self {
220 Self {
221 order_id: 0,
222 base_asset: config.base_asset,
223 base_decimals: config.base_decimals,
224 quote_asset: config.quote_asset,
225 quote_decimals: config.quote_decimals,
226 taker_fee: config.taker_fee,
227 maker_fee: config.taker_fee,
228 buys: TreeMap::new(),
229 sells: TreeMap::new(),
230 orders: SlotMap::new(),
231 trades: Vec::new(),
232 cancels: Vec::new(),
233 all_orders: Vec::new(),
234 balances: Balances::new(),
235 }
236 }
237
238 pub fn subtract_fee(&self, is_maker: bool, amount: u64) -> u64 {
239 let fee: u64 = if is_maker {
240 self.maker_fee
241 } else {
242 self.taker_fee
243 };
244 if fee == 0 {
245 return amount;
246 }
247 amount - ((amount * fee) / 1_000_000)
248 }
249
250 pub fn subtract_taker_fee(&self, amount: u64) -> u64 {
251 self.subtract_fee(false, amount)
252 }
253
254 pub fn subtract_maker_fee(&self, amount: u64) -> u64 {
255 self.subtract_fee(true, amount)
256 }
257
258 pub fn set_address_balance(
259 &mut self,
260 trader_id: &Identity,
261 base_balance: u64,
262 quote_balance: u64,
263 ) {
264 self.balances
265 .insert(*trader_id, (base_balance, quote_balance));
266 }
267
268 pub fn set_balances(&mut self, balances: Balances) {
269 self.balances = balances.clone();
270 }
271
272 pub fn change_balance_insert(&mut self, order: &Order) {
273 if let Some(balance) = self.balances.get_mut(&order.trader_id) {
274 if order.side == Side::Buy {
275 balance.1 -= get_total_amount(
276 order.total_quantity(),
277 order.price,
278 self.base_decimals,
279 );
280 } else {
281 balance.0 -= order.total_quantity();
282 };
283 }
284 }
285
286 pub fn change_balance_cancel(&mut self, order: &Order) {
287 if let Some(balance) = self.balances.get_mut(&order.trader_id) {
288 if order.side == Side::Buy {
289 balance.1 +=
290 get_total_amount(order.quantity, order.price, self.base_decimals);
291 } else {
292 balance.0 += order.quantity;
293 };
294 }
295 }
296
297 pub fn change_balance_match(
298 &mut self,
299 maker: &Order,
300 taker: &Order,
301 fill_quantity: u64,
302 ) {
303 let maker_balances = self.balances.get(&maker.trader_id);
304 let taker_balances = self.balances.get(&taker.trader_id);
305
306 if maker_balances.is_none() || taker_balances.is_none() {
307 return;
308 }
309 let mut maker_balances = maker_balances.cloned().unwrap();
310 let mut taker_balances = taker_balances.cloned().unwrap();
311
312 if maker.side == Side::Buy {
313 maker_balances.0 += self.subtract_maker_fee(fill_quantity);
314 taker_balances.1 += self.subtract_taker_fee(get_total_amount(
315 fill_quantity,
316 maker.price,
317 self.base_decimals,
318 ));
319 } else {
320 taker_balances.0 += self.subtract_taker_fee(fill_quantity);
321 maker_balances.1 += self.subtract_maker_fee(get_total_amount(
322 fill_quantity,
323 maker.price,
324 self.base_decimals,
325 ));
326 }
327
328 if taker.is_closed() && taker.remaining_total(self.base_decimals) > 0 {
329 taker_balances.1 += taker.remaining_total(self.base_decimals);
330 }
331 if maker.is_closed() && maker.remaining_total(self.base_decimals) > 0 {
332 taker_balances.1 += taker.remaining_total(self.base_decimals);
333 }
334
335 self.balances.insert(maker.trader_id, maker_balances);
336 self.balances.insert(taker.trader_id, taker_balances);
337 }
338
339 pub fn load(&mut self, orders: Vec<Order>) {
340 for order in orders {
341 if order.quantity == 0 {
342 continue;
343 }
344 self.insert_order(&order);
345 }
346 }
347
348 pub fn get_buys_count(&mut self) -> usize {
349 self.buys.len()
350 }
351
352 pub fn get_sells_count(&mut self) -> usize {
353 self.sells.len()
354 }
355
356 fn insert_order(&mut self, order: &Order) -> OrderId {
357 let price = order.price;
358 let id = self.orders.insert(order.clone());
359 self.change_balance_insert(order);
360
361 match order.side {
362 Side::Sell => {
363 self.sells.entry(price.into()).or_default().push(id);
364 }
365 Side::Buy => {
366 self.buys.entry(price.into()).or_default().push(id);
367 }
368 }
369 self.execute();
370 id
371 }
372
373 pub fn get_orders(&self) -> Vec<Order> {
374 self.orders.values().cloned().collect()
375 }
376
377 pub fn cancel(&mut self, order_id: u64) {
378 if let Some((order_key, order)) =
379 self.orders.iter().find(|o| o.1.order_id == order_id)
380 {
381 self.cancels.push(order.clone());
382 self.change_balance_cancel(&order.clone());
383 self.remove_order(order_key);
384 }
385 }
386
387 fn remove_order(&mut self, order_id: OrderId) {
388 let order = &self.orders[order_id];
389 match order.side {
390 Side::Sell => {
391 if let Some(ref_vec) = self.sells.get_mut(&order.price.into()) {
392 ref_vec.retain(|id| *id != order_id);
393 if ref_vec.is_empty() {
394 self.sells.remove(&order.price.into());
395 }
396 }
397 }
398 Side::Buy => {
399 if let Some(ref_vec) = self.buys.get_mut(&order.price.into()) {
400 ref_vec.retain(|id| *id != order_id);
401 if ref_vec.is_empty() {
402 self.buys.remove(&order.price.into());
403 }
404 }
405 }
406 }
407 self.orders.remove(order_id);
408 }
409
410 fn lowest_sell(&self) -> Option<(&PriceKey, &OrderId)> {
411 match self.sells.first_key_value().map(|(k, v)| (k, v.first())) {
412 Some((k, Some(v))) => Some((k, v)),
413 _ => None,
414 }
415 }
416
417 fn highest_buy(&self) -> Option<(&PriceKey, &OrderId)> {
418 match self.buys.last_key_value().map(|(k, v)| (k, v.first())) {
419 Some((k, Some(v))) => Some((k, v)),
420 _ => None,
421 }
422 }
423
424 pub fn create_order_matched_event(&self, maker_order: &Order) -> MatchedEvent {
425 let last_filled_order = maker_order.fill.last().unwrap();
426 let total = get_total_amount(
427 last_filled_order.quantity,
428 last_filled_order.price,
429 self.base_decimals,
430 );
431 MatchedEvent {
432 direction: maker_order.side,
433 taker_id: last_filled_order.order_id,
434 maker_id: maker_order.order_id,
435 quantity: last_filled_order.quantity,
436 price: last_filled_order.price,
437 total,
438 }
439 }
440
441 pub fn execute(&mut self) {
442 while self
443 .lowest_sell()
444 .zip(self.highest_buy())
445 .map(|((sell, _), (buy, _))| Price::from(buy) >= Price::from(sell))
446 .unwrap_or(false)
447 {
448 let sell_id = *self.lowest_sell().unwrap().1;
449 let sell_quantity = self.orders[sell_id].quantity;
450 let sell_order_id = self.orders[sell_id].order_id;
451 let buy_id = *self.highest_buy().unwrap().1;
452 let buy_quantity = self.orders[buy_id].quantity;
453 let buy_order_id = self.orders[buy_id].order_id;
454 let fill_quantity = sell_quantity.min(buy_quantity);
455
456 let maker_id = if sell_order_id < buy_order_id {
457 sell_id
458 } else {
459 buy_id
460 };
461 let taker_id = if sell_order_id > buy_order_id {
462 sell_id
463 } else {
464 buy_id
465 };
466 let maker_price = self.orders[maker_id].price;
467
468 let sell_is_full =
469 self.orders[sell_id].fill(buy_order_id, fill_quantity, maker_price);
470 let buy_is_full =
471 self.orders[buy_id].fill(sell_order_id, fill_quantity, maker_price);
472
473 self.change_balance_match(
474 &self.orders[maker_id].clone(),
475 &self.orders[taker_id].clone(),
476 fill_quantity,
477 );
478 self.trades
479 .push(self.create_order_matched_event(&self.orders[maker_id]));
480
481 if sell_is_full {
482 self.remove_order(sell_id);
483 }
484 if buy_is_full {
485 self.remove_order(buy_id);
486 }
487 }
488 }
489
490 pub fn create_order(&mut self, order_data: OrderData) -> Order {
491 self.order_id += 1;
492 let order = Order::new(self.order_id, order_data);
493 self.insert_order(&order);
494 self.all_orders.push(order.clone());
495 order
496 }
497
498 pub fn create_orders(&mut self, orders_data: Vec<OrderData>) -> Vec<Order> {
499 let mut orders = Vec::new();
500 for order_data in orders_data {
501 orders.push(self.create_order(order_data));
502 }
503 orders
504 }
505}
506
507#[derive(Clone, Debug)]
508pub struct TestOrderBookState {
509 pub orders: Vec<OrderData>,
510 pub matches: Vec<MatchedEvent>,
511 pub cancels: Vec<Order>,
512}
513
514pub fn get_default_orders_test(
515 base_asset: AssetId,
516 quote_asset: AssetId,
517 initial_balances: Balances,
518) -> (Book, TestOrderBookState) {
519 let mut book: Book = Book::new(BookConfig {
520 base_asset,
521 base_decimals: 9,
522 quote_asset,
523 quote_decimals: 6,
524 taker_fee: 0,
525 maker_fee: 0,
526 });
527 let traders = initial_balances.keys().cloned().collect::<Vec<_>>();
528
529 book.set_balances(initial_balances);
531
532 let mut orders = vec![
533 OrderData {
534 side: Side::Buy,
535 price: 9_500_000,
536 quantity: 20_000_000,
537 trader_id: Identity::default(),
538 },
539 OrderData {
540 side: Side::Sell,
541 price: 20_000_000,
542 quantity: 20_000_000,
543 trader_id: Identity::default(),
544 },
545 OrderData {
546 side: Side::Sell,
547 price: 10_000_000,
548 quantity: 20_000_000,
549 trader_id: Identity::default(),
550 },
551 OrderData {
552 side: Side::Buy,
553 price: 10_000_000,
554 quantity: 40_000_000,
555 trader_id: Identity::default(),
556 },
557 OrderData {
558 side: Side::Sell,
559 price: 9_900_000,
560 quantity: 40_000_000,
561 trader_id: Identity::default(),
562 },
563 OrderData {
564 side: Side::Buy,
565 price: 10_000_000,
566 quantity: 20_000_000,
567 trader_id: Identity::default(),
568 },
569 ];
570
571 for (i, order) in orders.iter_mut().enumerate() {
572 let trader_id = traders.get(i % traders.len()).unwrap();
573 order.trader_id = *trader_id;
574 }
575
576 let matches = vec![
577 MatchedEvent {
578 direction: Side::Sell,
579 maker_id: 3,
580 taker_id: 4,
581 quantity: 20_000_000,
582 price: 10_000_000,
583 total: 200_000,
584 },
585 MatchedEvent {
586 direction: Side::Buy,
587 maker_id: 4,
588 taker_id: 5,
589 quantity: 20_000_000,
590 price: 10_000_000,
591 total: 200_000,
592 },
593 MatchedEvent {
594 direction: Side::Sell,
595 maker_id: 5,
596 taker_id: 6,
597 quantity: 20_000_000,
598 price: 9_900_000,
599 total: 198_000,
600 },
601 ];
602 let cancels = vec![Order::new(
603 1,
604 OrderData {
605 side: Side::Buy,
606 price: 9_500_000,
607 quantity: 20_000_000,
608 trader_id: *traders.first().unwrap(),
609 },
610 )];
611
612 book.create_orders(orders.clone());
614
615 let test_order_book_state = TestOrderBookState {
616 orders,
617 matches,
618 cancels,
619 };
620
621 (book, test_order_book_state)
622}
623
624pub fn get_default_orders_with_fees_test(
625 base_asset: AssetId,
626 quote_asset: AssetId,
627 initial_balances: Balances,
628) -> (Book, TestOrderBookState) {
629 let mut book: Book = Book::new(BookConfig {
630 base_asset,
631 base_decimals: 9,
632 quote_asset,
633 quote_decimals: 6,
634 taker_fee: 2_000,
635 maker_fee: 1_000,
636 });
637 let traders = initial_balances.keys().cloned().collect::<Vec<_>>();
638
639 book.set_balances(initial_balances);
641
642 let mut orders = vec![
643 OrderData {
644 side: Side::Buy,
645 price: 9_500_000,
646 quantity: 20_000_000,
647 trader_id: Identity::default(),
648 },
649 OrderData {
650 side: Side::Sell,
651 price: 20_000_000,
652 quantity: 20_000_000,
653 trader_id: Identity::default(),
654 },
655 OrderData {
656 side: Side::Sell,
657 price: 10_000_000,
658 quantity: 20_000_000,
659 trader_id: Identity::default(),
660 },
661 OrderData {
662 side: Side::Buy,
663 price: 10_000_000,
664 quantity: 40_000_000,
665 trader_id: Identity::default(),
666 },
667 OrderData {
668 side: Side::Sell,
669 price: 9_900_000,
670 quantity: 40_000_000,
671 trader_id: Identity::default(),
672 },
673 OrderData {
674 side: Side::Buy,
675 price: 10_000_000,
676 quantity: 20_000_000,
677 trader_id: Identity::default(),
678 },
679 ];
680
681 for (i, order) in orders.iter_mut().enumerate() {
682 let trader_id = traders.get(i % traders.len()).unwrap();
683 order.trader_id = *trader_id;
684 }
685
686 let matches = vec![
687 MatchedEvent {
688 direction: Side::Sell,
689 maker_id: 3,
690 taker_id: 4,
691 quantity: 20_000_000,
692 price: 10_000_000,
693 total: 200_000,
694 },
695 MatchedEvent {
696 direction: Side::Buy,
697 maker_id: 4,
698 taker_id: 5,
699 quantity: 20_000_000,
700 price: 10_000_000,
701 total: 200_000,
702 },
703 MatchedEvent {
704 direction: Side::Sell,
705 maker_id: 5,
706 taker_id: 6,
707 quantity: 20_000_000,
708 price: 9_900_000,
709 total: 198_000,
710 },
711 ];
712 let cancels = vec![Order::new(
713 1,
714 OrderData {
715 side: Side::Buy,
716 price: 9_500_000,
717 quantity: 20_000_000,
718 trader_id: *traders.first().unwrap(),
719 },
720 )];
721
722 book.create_orders(orders.clone());
724
725 let test_order_book_state = TestOrderBookState {
726 orders,
727 matches,
728 cancels,
729 };
730
731 (book, test_order_book_state)
732}
733
734#[tokio::test]
735async fn test_order_book_data() {
736 use fuels::types::{
737 Address,
738 Identity,
739 };
740 use std::str::FromStr;
741 const USDC: &str =
742 "0x8288f40f7c9ab3b5bc36783b692f4ae96975c5a04d8f0da258886fdada0b26af";
743 const BTC: &str =
744 "0xd0cef4b9cb0706cfa4aa174e75fb006b8ddd2a0ecd6296a27c0a05dcb856f32f";
745 let address_1 = Identity::Address(Address::from([5u8; 32]));
746 let address_2 = Identity::Address(Address::from([6u8; 32]));
747
748 let balances = Balances::from_iter(vec![
749 (address_1, (100_000_000_000_000, 100_000_000_000_000)),
750 (address_2, (100_000_000_000_000, 100_000_000_000_000)),
751 ]);
752
753 let (mut book, test_order_book_state) = get_default_orders_test(
754 AssetId::from_str(BTC).unwrap(),
755 AssetId::from_str(USDC).unwrap(),
756 balances,
757 );
758 book.cancel(1);
759 let balances = Balances::from_iter(vec![
760 (address_1, (99999940000000, 100000000598000)),
761 (address_2, (100000040000000, 99999999402000)),
762 ]);
763
764 assert_eq!(test_order_book_state.matches, book.trades);
765 assert_eq!(test_order_book_state.cancels, book.cancels);
766 assert_eq!(balances, book.balances);
767}
768
769#[tokio::test]
770async fn test_order_book_data_fees() {
771 use fuels::types::{
772 Address,
773 Identity,
774 };
775 use std::str::FromStr;
776 const USDC: &str =
777 "0x8288f40f7c9ab3b5bc36783b692f4ae96975c5a04d8f0da258886fdada0b26af";
778 const BTC: &str =
779 "0xd0cef4b9cb0706cfa4aa174e75fb006b8ddd2a0ecd6296a27c0a05dcb856f32f";
780 let address_1 = Identity::Address(Address::from([5u8; 32]));
781 let address_2 = Identity::Address(Address::from([6u8; 32]));
782
783 let balances = Balances::from_iter(vec![
784 (address_1, (100_000_000_000_000, 100_000_000_000_000)),
785 (address_2, (100_000_000_000_000, 100_000_000_000_000)),
786 ]);
787
788 let (mut book, test_order_book_state) = get_default_orders_with_fees_test(
789 AssetId::from_str(BTC).unwrap(),
790 AssetId::from_str(USDC).unwrap(),
791 balances,
792 );
793 book.cancel(1);
794 let balances = Balances::from_iter(vec![
795 (address_1, (99999940000000, 100000000596804)),
796 (address_2, (100000039880000, 99999999402000)),
797 ]);
798
799 assert_eq!(test_order_book_state.matches, book.trades);
800 assert_eq!(test_order_book_state.cancels, book.cancels);
801 assert_eq!(balances, book.balances);
802}