1use std::iter;
2
3use fastnum::{D64, D256, UD64, UD128};
4use itertools::chain;
5
6use super::*;
7use crate::{
8 Chain,
9 abi::dex::Exchange::ExchangeEvents,
10 stream,
11 types::{EventContext, OrderType},
12};
13
14pub type StateBlockEvents = types::BlockEvents<types::EventContext<Vec<StateEvents>>>;
15
16struct RawMakerFill {
18 perp_id: U256,
19 account_id: U256,
20 order_id: U256,
21 price_pns: U256,
22 lot_lns: U256,
23 fee_cns: U256,
24 builder_fee_cns: U256,
25 locked_balance_cns: U256,
26 balance_cns: U256,
27}
28
29struct RawContractAdded {
33 perp_id: U256,
34 status: u8,
35 price_decimals: U256,
36 lot_decimals: U256,
37 base_price_pns: U256,
38 init_margin_frac_hdths: U256,
39 maint_margin_frac_hdths: U256,
40}
41
42struct RawTakerFill {
44 collat_price_pns: U256,
45 lot_lns: U256,
46 fee_cns: U256,
47 builder_fee_cns: U256,
48 balance_cns: U256,
49}
50
51#[derive(Clone, derive_more::Debug)]
57pub struct Exchange {
58 chain: Chain,
59 instant: types::StateInstant,
60 features: ContractFeatures,
61 collateral_converter: num::Converter,
62 funding_interval_blocks: u32,
63 #[debug("{min_post}")]
64 min_post: UD128,
65 #[debug("{min_settle}")]
66 min_settle: UD128,
67 #[debug("{recycle_fee}")]
68 recycle_fee: UD128,
69 fee_schedules: FeeScheduleRegistry,
70 perpetuals: HashMap<types::PerpetualId, Perpetual>,
71 accounts: HashMap<types::AccountId, Account>,
72 is_halted: bool,
73 track_all_accounts: bool,
74}
75
76impl Exchange {
77 #[allow(clippy::too_many_arguments)]
78 pub(crate) fn new(
79 chain: Chain,
80 instant: types::StateInstant,
81 features: ContractFeatures,
82 collateral_converter: num::Converter,
83 funding_interval_blocks: u32,
84 min_post: UD128,
85 min_settle: UD128,
86 recycle_fee: UD128,
87 fee_schedules: FeeScheduleRegistry,
88 perpetuals: HashMap<types::PerpetualId, Perpetual>,
89 accounts: HashMap<types::AccountId, Account>,
90 is_halted: bool,
91 track_all_accounts: bool,
92 ) -> Self {
93 Self {
94 chain,
95 instant,
96 features,
97 collateral_converter,
98 funding_interval_blocks,
99 min_post,
100 min_settle,
101 recycle_fee,
102 fee_schedules,
103 perpetuals,
104 accounts,
105 is_halted,
106 track_all_accounts,
107 }
108 }
109
110 pub const fn revision() -> &'static str { crate::abi::DEX_REVISION }
112
113 pub fn chain(&self) -> &Chain { &self.chain }
115
116 pub fn instant(&self) -> types::StateInstant { self.instant }
118
119 pub fn features(&self) -> ContractFeatures { self.features }
123
124 pub fn contract_version(&self) -> Option<ContractVersion> { self.features.version() }
127
128 pub fn fee_schedules(&self) -> &FeeScheduleRegistry { &self.fee_schedules }
132
133 pub fn default_fee_schedule(&self) -> FeeSchedule { self.fee_schedules.default_schedule() }
136
137 pub fn rwa_fee_schedule(&self) -> FeeSchedule { self.fee_schedules.rwa_default_schedule() }
139
140 pub fn fee_schedule(&self, key: FeeScheduleKey) -> Option<FeeSchedule> {
144 self.fee_schedules.get(key)
145 }
146
147 pub fn collateral_converter(&self) -> num::Converter { self.collateral_converter }
150
151 pub fn funding_interval_blocks(&self) -> u32 { self.funding_interval_blocks }
156
157 pub fn min_post(&self) -> UD128 { self.min_post }
159
160 pub fn min_settle(&self) -> UD128 { self.min_settle }
162
163 pub fn recycle_fee(&self) -> UD128 { self.recycle_fee }
169
170 pub fn perpetuals(&self) -> &HashMap<types::PerpetualId, Perpetual> { &self.perpetuals }
173
174 pub fn accounts(&self) -> &HashMap<types::AccountId, Account> { &self.accounts }
177
178 pub fn is_halted(&self) -> bool { self.is_halted }
180
181 pub fn apply_events(
221 &mut self,
222 events: &stream::RawBlockEvents,
223 ) -> Result<Option<StateBlockEvents>, DexError> {
224 let next_instant = events.instant();
225 if self.instant >= next_instant {
226 return Ok(None);
228 }
229 if self.instant.block_number() + 1 < next_instant.block_number() {
230 return Err(DexError::BlockOutOfOrder(
232 self.instant.block_number() + 1,
233 next_instant.block_number(),
234 ));
235 }
236
237 let mut order_context: Option<OrderContext> = None;
248 let mut prev_tx_index: Option<u64> = None;
249 let mut state_events = vec![];
250 let mut perp_events = vec![];
251
252 let funding_due: Vec<(types::PerpetualId, D64, D256)> = self
257 .perpetuals
258 .values_mut()
259 .filter_map(|perp| {
260 perp.take_funding_payment(next_instant)
261 .map(|(rate, payment)| (perp.id(), rate, payment))
262 })
263 .collect();
264 for (perp_id, rate, payment) in funding_due {
265 let mut funding_events = vec![];
266 if let Some(perp) = self.perpetuals.get(&perp_id) {
267 funding_events.push(StateEvents::perpetual(
268 perp,
269 PerpetualEventType::FundingEvent { rate, payment_per_unit: payment },
270 ));
271 }
272 for acc in self.accounts.values_mut() {
273 if let Some(pos) = acc.positions_mut().get_mut(&perp_id)
274 && pos.apply_funding_payment(next_instant, payment)
275 {
276 funding_events.push(StateEvents::position(
277 pos,
278 &None,
279 PositionEventType::UnrealizedPnLUpdated {
280 pnl: pos.pnl(),
281 delta_pnl: pos.delta_pnl(),
282 premium_pnl: pos.premium_pnl(),
283 },
284 ));
285 }
286 }
287 if !funding_events.is_empty() {
288 state_events.push(EventContext::empty(funding_events));
289 }
290 }
291
292 for event in events.events() {
295 if prev_tx_index.is_some_and(|idx| idx < event.tx_index()) {
296 order_context.take();
298 }
299 let result = self.apply_raw_event(next_instant, event, &mut order_context)?;
300 if !result.is_empty() {
301 let block_perp_events = result
303 .iter()
304 .filter(|e| e.as_perpetual_event().is_some())
305 .cloned()
306 .collect::<Vec<_>>();
307 if !block_perp_events.is_empty() {
308 perp_events.push(block_perp_events);
309 }
310 state_events.push(event.pass(result));
311 }
312 prev_tx_index = Some(event.tx_index());
313 }
314
315 self.instant = events.instant();
318 for perp in self.perpetuals.values_mut() {
319 perp.update_state_instant(self.instant);
320 }
321
322 for event in perp_events.iter().flatten() {
326 let result = self.apply_state_event(self.instant, event)?;
327 if !result.is_empty() {
328 state_events.push(EventContext::empty(result));
329 }
330 }
331
332 Ok(Some(StateBlockEvents::new(self.instant, state_events)))
333 }
334
335 pub(crate) fn apply_raw_event(
336 &mut self,
337 instant: types::StateInstant,
338 event: &stream::RawEvent,
339 ctx: &mut Option<OrderContext>,
340 ) -> Result<Vec<StateEvents>, DexError> {
341 let cc = self.collateral_converter;
342
343 let must_ctx = || {
344 ctx.as_ref()
345 .ok_or(DexError::OrderContextExpected(event.tx_index(), event.log_index()))
346 };
347
348 Ok(match event.event() {
349 ExchangeEvents::AccountCreated(e) => {
350 if self.track_all_accounts {
351 self.accounts
352 .insert(e.id.to(), Account::created(instant, e.id.to(), e.account));
353 vec![StateEvents::Account(AccountEvent {
354 account_id: e.id.to(),
355 request_id: None,
356 r#type: AccountEventType::Created(e.id.to()),
357 })]
358 } else {
359 vec![]
360 }
361 },
362 ExchangeEvents::AccountFeeTierSet(e) => self
363 .account(e.accountId)
364 .map(|acc| {
365 let tier = e.tier.to();
366 acc.update_fee_tier(instant, tier);
367 StateEvents::account(acc, ctx, AccountEventType::FeeTierUpdated(tier))
368 })
369 .into_iter()
370 .collect(),
371 ExchangeEvents::AccountFreeze(e) => self
372 .account(e.accountId)
373 .map(|acc| {
374 acc.update_frozen(instant, e.status > 0);
375 StateEvents::account(acc, ctx, AccountEventType::Frozen(acc.frozen()))
376 })
377 .into_iter()
378 .collect(),
379 ExchangeEvents::AccountFrozen(_) => self
380 .err_ctx(ctx, event)?
381 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::AccountFrozen))
382 .into_iter()
383 .collect(),
384 ExchangeEvents::AccountLiquidationCredit(e) => self
385 .account(e.accountId)
386 .map(|acc| {
387 acc.update_balance(instant, cc.from_unsigned(e.endBalanceCNS));
388 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
389 })
390 .into_iter()
391 .collect(),
392 ExchangeEvents::AdminChanged(_) => vec![],
393 ExchangeEvents::AdministratorUpdated(_) => vec![],
394 ExchangeEvents::AmountExceedsAvailableBalance(e) => self
395 .err_ctx(ctx, event)?
396 .map(|ctx| {
397 StateEvents::order_error(
398 ctx,
399 OrderErrorType::AmountExceedsAvailableBalance(
400 cc.from_unsigned(e.amountCNS),
401 cc.from_unsigned(e.availableBalanceCNS),
402 ),
403 )
404 })
405 .into_iter()
406 .collect(),
407 ExchangeEvents::BankruptcyPricePreventsDeleverage(_) => vec![],
408 ExchangeEvents::BeaconUpgraded(_) => vec![],
409 ExchangeEvents::BlockStatusChanged(_) => vec![],
410 ExchangeEvents::BorrowMarginNotMetAfterDecCollateral(_) => vec![],
411 ExchangeEvents::BuyToLiquidateSettled(_) => vec![],
412 ExchangeEvents::BuyToLiquidateSlippageExceeded(_) => vec![],
413 ExchangeEvents::BuyToLiquidateStarted(_) => vec![],
414 ExchangeEvents::BuyToLiquidateBuyerRestricted(_) => vec![],
415 ExchangeEvents::BuyToLiquidateParamsUpdated(_) => vec![],
416 ExchangeEvents::BuyToLiquidateThresholdUpdated(_) => vec![],
417 ExchangeEvents::BuyToLiquidateRestrictionUpdated(_) => vec![],
418 ExchangeEvents::CancelExistingInvalidCloseOrders(_) => self
419 .err_ctx(ctx, event)?
420 .map(|ctx| {
421 StateEvents::order_error(ctx, OrderErrorType::CancelExistingInvalidCloseOrders)
422 })
423 .into_iter()
424 .collect(),
425 ExchangeEvents::CannotAdjustEntryPriceToDecCollateral(_) => vec![],
426 ExchangeEvents::CantBuyToLiquidate(_) => vec![],
427 ExchangeEvents::CantChangeCloseOrder(_) => self
428 .err_ctx(ctx, event)?
429 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::CantChangeCloseOrder))
430 .into_iter()
431 .collect(),
432 ExchangeEvents::CantDeleverageAgainstOpposingPositions(_) => vec![],
433 ExchangeEvents::CantLiquidatePosAboveMMR(_) => vec![],
434 ExchangeEvents::ChangeExpiredOrderNeedsNewExpiry(_) => self
435 .err_ctx(ctx, event)?
436 .map(|ctx| {
437 StateEvents::order_error(ctx, OrderErrorType::ChangeExpiredOrderNeedsNewExpiry)
438 })
439 .into_iter()
440 .collect(),
441 ExchangeEvents::ClearingExpiredOrder(e) => chain!(
442 if let Some(perp) = self.perpetual(e.perpId) {
443 let order_id = std::num::NonZeroU16::new(e.orderId.to::<u16>())
444 .expect("orderId in event cannot be 0");
445 let order = perp.remove_order(order_id)?;
446 Some(StateEvents::order(perp, &order, ctx, OrderEventType::Removed))
447 } else {
448 None
449 },
450 self.account(e.accountId).map(|acc| {
451 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
452 StateEvents::account(
453 acc,
454 ctx,
455 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
456 )
457 }),
458 if !e.recyclerAmountCNS.is_zero() {
459 self.account(e.recyclerAccountId).map(|acc| {
460 acc.update_balance(instant, cc.from_unsigned(e.recyclerBalanceCNS));
461 StateEvents::account(
462 acc,
463 ctx,
464 AccountEventType::BalanceUpdated(acc.balance()),
465 )
466 })
467 } else {
468 None
469 },
470 )
471 .collect(),
472 ExchangeEvents::ClearingFrozenAccountOrder(e) => chain!(
473 if let Some(perp) = self.perpetual(e.perpId) {
474 let order_id = std::num::NonZeroU16::new(e.orderId.to::<u16>())
475 .expect("orderId in event cannot be 0");
476 let order = perp.remove_order(order_id)?;
477 Some(StateEvents::order(perp, &order, ctx, OrderEventType::Removed))
478 } else {
479 None
480 },
481 self.account(e.accountId).map(|acc| {
482 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
483 StateEvents::account(
484 acc,
485 ctx,
486 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
487 )
488 }),
489 if !e.recyclerAmountCNS.is_zero() {
490 self.account(e.recyclerAccountId).map(|acc| {
491 acc.update_balance(instant, cc.from_unsigned(e.recyclerBalanceCNS));
492 StateEvents::account(
493 acc,
494 ctx,
495 AccountEventType::BalanceUpdated(acc.balance()),
496 )
497 })
498 } else {
499 None
500 },
501 )
502 .collect(),
503 ExchangeEvents::ClearingInvalidCloseOrder(e) => chain!(
504 if let Some(perp) = self.perpetual(e.perpId) {
505 let order_id = std::num::NonZeroU16::new(e.orderId.to::<u16>())
506 .expect("orderId in event cannot be 0");
507 let order = perp.remove_order(order_id)?;
508 Some(StateEvents::order(perp, &order, ctx, OrderEventType::Removed))
509 } else {
510 None
511 },
512 self.account(e.accountId).map(|acc| {
513 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
514 StateEvents::account(
515 acc,
516 ctx,
517 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
518 )
519 }),
520 if !e.recyclerAmountCNS.is_zero() {
521 self.account(e.recyclerAccountId).map(|acc| {
522 acc.update_balance(instant, cc.from_unsigned(e.recyclerBalanceCNS));
523 StateEvents::account(
524 acc,
525 ctx,
526 AccountEventType::BalanceUpdated(acc.balance()),
527 )
528 })
529 } else {
530 None
531 },
532 )
533 .collect(),
534 ExchangeEvents::ClearingRemainingOrderLockBeyondBalance(e) => {
535 if let Some(ctx) = ctx {
536 ctx.clearing_remaining_order = true;
537 }
538 chain!(if !e.recyclerAmountCNS.is_zero() {
539 self.account(e.recyclerAccountId).map(|acc| {
540 acc.update_balance(instant, cc.from_unsigned(e.recyclerBalanceCNS));
541 StateEvents::account(
542 acc,
543 ctx,
544 AccountEventType::BalanceUpdated(acc.balance()),
545 )
546 })
547 } else {
548 None
549 },)
550 .collect()
551 },
552 ExchangeEvents::ClearingSelfMatchingOrder(e) => chain!(
553 if let Some(perp) = self.perpetual(e.perpId) {
554 let order_id = std::num::NonZeroU16::new(e.orderId.to::<u16>())
555 .expect("orderId in event cannot be 0");
556 let order = perp.remove_order(order_id)?;
557 Some(StateEvents::order(perp, &order, ctx, OrderEventType::Removed))
558 } else {
559 None
560 },
561 self.account(e.accountId).map(|acc| {
562 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
563 StateEvents::account(
564 acc,
565 ctx,
566 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
567 )
568 }),
569 if !e.recyclerAmountCNS.is_zero() {
570 self.account(e.recyclerAccountId).map(|acc| {
571 acc.update_balance(instant, cc.from_unsigned(e.recyclerBalanceCNS));
572 StateEvents::account(
573 acc,
574 ctx,
575 AccountEventType::BalanceUpdated(acc.balance()),
576 )
577 })
578 } else {
579 None
580 },
581 )
582 .collect(),
583 ExchangeEvents::CloseOrderExceedsPosition(_) => self
584 .err_ctx(ctx, event)?
585 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::CloseOrderExceedsPosition))
586 .into_iter()
587 .collect(),
588 ExchangeEvents::CloseOrderPositionMismatch(_) => self
589 .err_ctx(ctx, event)?
590 .map(|ctx| {
591 StateEvents::order_error(ctx, OrderErrorType::CloseOrderPositionMismatch)
592 })
593 .into_iter()
594 .collect(),
595 ExchangeEvents::CollateralDecreaseDeclined(_) => vec![],
596 ExchangeEvents::CollateralDecreaseRequestCancelled(_) => vec![],
597 ExchangeEvents::CollateralDecreaseRequested(_) => vec![],
598 ExchangeEvents::CollateralDecreaseRequestExpired(_) => vec![],
599 ExchangeEvents::CollateralDeposit(e) => self
600 .account(e.accountId)
601 .map(|acc| {
602 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
603 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
604 })
605 .into_iter()
606 .collect(),
607 ExchangeEvents::CollateralWithdrawal(e) => self
608 .account(e.accountId)
609 .map(|acc| {
610 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
611 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
612 })
613 .into_iter()
614 .collect(),
615 ExchangeEvents::ContractAdded(e) => {
619 let fee_converter = num::fee_converter();
620 vec![self.add_perpetual(
621 instant,
622 &e.name,
623 &e.symbol,
624 RawContractAdded {
625 perp_id: e.perpId,
626 status: e.status,
627 price_decimals: e.priceDecimals,
628 lot_decimals: e.lotDecimals,
629 base_price_pns: e.basePricePNS,
630 init_margin_frac_hdths: e.initMarginFracHdths,
631 maint_margin_frac_hdths: e.maintMarginFracHdths,
632 },
633 FeeSchedule::flat(
634 FeeScheduleKey::Default,
635 fee_converter.from_unsigned(e.takerFeePer100K),
636 fee_converter.from_unsigned(e.makerFeePer100K),
637 ),
638 )]
639 },
640 ExchangeEvents::ContractAddedV2(e) => {
641 let key = FeeScheduleKey::from_raw(e.perpFeeSchedId);
646 let fee_schedule = self.fee_schedule(key).ok_or(
647 DexError::FeeScheduleNotFound(key),
652 )?;
653 vec![self.add_perpetual(
654 instant,
655 &e.name,
656 &e.symbol,
657 RawContractAdded {
658 perp_id: e.perpId,
659 status: e.status,
660 price_decimals: e.priceDecimals,
661 lot_decimals: e.lotDecimals,
662 base_price_pns: e.basePricePNS,
663 init_margin_frac_hdths: e.initMarginFracHdths,
664 maint_margin_frac_hdths: e.maintMarginFracHdths,
665 },
666 fee_schedule,
667 )]
668 },
669 ExchangeEvents::ContractNotOperational(_) => self
670 .err_ctx(ctx, event)?
671 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::ContractNotOperational))
672 .into_iter()
673 .collect(),
674 ExchangeEvents::ContractLinkFeedUpdated(e) => self
675 .perpetual(e.perpId)
676 .map(|perp| {
677 perp.update_oracle_feed_id(instant, e.feedId);
678 StateEvents::perpetual(
679 perp,
680 PerpetualEventType::OracleConfigurationUpdated {
681 is_used: perp.is_oracle_used(),
682 feed_id: perp.oracle_feed_id(),
683 },
684 )
685 })
686 .into_iter()
687 .collect(),
688 ExchangeEvents::ContractPaused(e) => {
689 let funding_interval = self.funding_interval_blocks as u64;
692 self.perpetual(e.perpId)
693 .map(|perp| {
694 perp.update_paused(instant, e.paused, funding_interval);
695 StateEvents::perpetual(perp, PerpetualEventType::Paused(perp.is_paused()))
696 })
697 .into_iter()
698 .collect()
699 },
700 ExchangeEvents::ContractRemoved(e) => {
701 let funding_interval = self.funding_interval_blocks as u64;
702 self.perpetual(e.perpId)
703 .map(|perp| {
704 perp.update_paused(instant, true, funding_interval);
705 StateEvents::perpetual(perp, PerpetualEventType::Paused(perp.is_paused()))
706 })
707 .into_iter()
708 .collect()
709 },
710 ExchangeEvents::ContractVersionSet(e) => {
711 let version = ContractVersion::new(e.major.to(), e.minor.to(), e.patch.to());
712 self.features.observe_version(version);
713 vec![StateEvents::Exchange(ExchangeEvent::ContractVersionUpdated(version))]
714 },
715 ExchangeEvents::CrossesBook(_) => self
716 .err_ctx(ctx, event)?
717 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::CrossesBook))
718 .into_iter()
719 .collect(),
720 ExchangeEvents::DcpBorrowThreshUpdated(_) => vec![],
721 ExchangeEvents::DecreaseCollateralBeyondMarkPrice(_) => vec![],
722 ExchangeEvents::DefaultPerpFeeScheduleSet(e) => self.update_fee_schedule(
723 instant,
724 FeeSchedule::new(
725 FeeScheduleKey::Default,
726 e.takerFeesPer100K,
727 e.makerFeesPer100K,
728 num::fee_converter(),
729 ),
730 ),
731 ExchangeEvents::DefaultRwaFeeScheduleSet(e) => self.update_fee_schedule(
732 instant,
733 FeeSchedule::new(
734 FeeScheduleKey::RwaDefault,
735 e.takerFeesPer100K,
736 e.makerFeesPer100K,
737 num::fee_converter(),
738 ),
739 ),
740 ExchangeEvents::DeleveragePositionListEmpty(_) => vec![],
741 ExchangeEvents::ExceedsLastExecutionBlock(_) => self
742 .err_ctx(ctx, event)?
743 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::ExceedsLastExecutionBlock))
744 .into_iter()
745 .collect(),
746 ExchangeEvents::ExchangeHalted(e) => {
747 self.is_halted = e.halted;
748 vec![StateEvents::Exchange(ExchangeEvent::Halted(self.is_halted))]
749 },
750 ExchangeEvents::ExchangeInitialized(_) => vec![],
751 ExchangeEvents::FeeParamsUpdated(_) => vec![],
752 ExchangeEvents::FeeScheduleSet(e) => {
753 self.update_fee_schedule(
759 instant,
760 FeeSchedule::new(
761 FeeScheduleKey::from_raw(e.feeSchedId),
762 e.takerFeesPer100K,
763 e.makerFeesPer100K,
764 num::fee_converter(),
765 ),
766 )
767 },
768 ExchangeEvents::FundingClampPctUpdated(_) => vec![],
769 ExchangeEvents::FundingEventCompleted(e) => {
770 if let Some(perp) = self.perpetual(e.perpId) {
771 perp.update_funding(
772 instant,
773 perp.funding_rate_converter()
774 .from_signed(e.actualRatePct100k),
775 perp.funding_sum_converter()
776 .from_i64(e.fundingPaymentPNS.as_i64()),
777 e.fundingEventBlock.to(),
778 );
779 }
780 vec![]
781 },
782 ExchangeEvents::FundingEventSetTooEarly(_) => vec![],
783 ExchangeEvents::FundingPriceExceedsTol(_) => vec![],
784 ExchangeEvents::FundingSumAlreadySet(_) => vec![],
785 ExchangeEvents::FundingSumScalingExpUpdated(e) => self
786 .perpetual(e.perpId)
787 .map(|perp| {
788 perp.update_funding_sum_scaling_exp(instant, e.newExp.to());
789 StateEvents::perpetual(
790 perp,
791 PerpetualEventType::FundingSumScalingExpUpdated(e.newExp.to()),
792 )
793 })
794 .into_iter()
795 .collect(),
796 ExchangeEvents::IgnoreOracleUpdated(e) => self
797 .perpetual(e.perpId)
798 .map(|perp| {
799 perp.update_is_oracle_used(instant, !e.ignOracle);
800 StateEvents::perpetual(
801 perp,
802 PerpetualEventType::OracleConfigurationUpdated {
803 is_used: perp.is_oracle_used(),
804 feed_id: perp.oracle_feed_id(),
805 },
806 )
807 })
808 .into_iter()
809 .collect(),
810 ExchangeEvents::ImmediateOrCancelExecuted(_) => self
811 .err_ctx(ctx, event)?
812 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::ImmediateOrCancelExecuted))
813 .into_iter()
814 .collect(),
815 ExchangeEvents::IncreasePositionCollateral(e) => chain!(
816 self.position(e.accountId, e.perpId)?.map(|(pos, _)| {
817 pos.update_deposit(instant, cc.from_unsigned(e.positionDepositCNS));
818 StateEvents::position(
819 pos,
820 ctx,
821 PositionEventType::DepositUpdated(pos.deposit()),
822 )
823 }),
824 self.account(e.accountId).map(|acc| {
825 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
826 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
827 }),
828 )
829 .collect(),
830 ExchangeEvents::Initialized(_) => vec![],
831 ExchangeEvents::InitialMarginFractionUpdated(e) => self
832 .perpetual(e.perpId)
833 .map(|perp| {
834 perp.update_initial_margin(
835 instant,
836 perp.leverage_converter()
837 .from_unsigned(e.initMarginFracHdths),
838 );
839 StateEvents::perpetual(
840 perp,
841 PerpetualEventType::InitialMarginFractionUpdated(perp.initial_margin()),
842 )
843 })
844 .into_iter()
845 .collect(),
846 ExchangeEvents::InsolventPositionCannotBeForcedClose(_) => vec![],
847 ExchangeEvents::InsuficientFundsForRecycleFee(_) => self
848 .err_ctx(ctx, event)?
849 .map(|ctx| {
850 StateEvents::order_error(ctx, OrderErrorType::InsuficientFundsForRecycleFee)
851 })
852 .into_iter()
853 .collect(),
854 ExchangeEvents::InsufficientFundsToDecCollateral(_) => vec![],
855 ExchangeEvents::InsurancePaymentForSettlement(_) => vec![],
856 ExchangeEvents::InvalidAccountFrozenOrder(_) => vec![],
857 ExchangeEvents::InvalidBankruptcyPrice(_) => vec![],
858 ExchangeEvents::InvalidExpiryBlock(_) => self
859 .err_ctx(ctx, event)?
860 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::InvalidExpiryBlock))
861 .into_iter()
862 .collect(),
863 ExchangeEvents::InvalidLinkReportForContract(_) => vec![],
864 ExchangeEvents::InvalidLinkReportVersion(_) => vec![],
865 ExchangeEvents::InvalidLiquidationPrice(_) => vec![],
866 ExchangeEvents::InvalidOrderId(_) => self
867 .err_ctx(ctx, event)?
868 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::InvalidOrderId))
869 .into_iter()
870 .collect(),
871 ExchangeEvents::LastForwardedDescIdReset(_) => vec![],
872 ExchangeEvents::LastTriggeredDescIdReset(_) => vec![],
873 ExchangeEvents::LinkDatastreamConfigured(_) => vec![],
874 ExchangeEvents::LinkDsError_0(_) => vec![],
875 ExchangeEvents::LinkDsError_1(_) => vec![],
876 ExchangeEvents::LinkDsPanic(_) => vec![],
877 ExchangeEvents::LinkPriceUpdated(e) => self
878 .perpetual(e.perpId)
879 .map(|perp| {
880 perp.update_oracle_price(
881 instant,
882 perp.price_converter().from_unsigned(e.oraclePricePNS),
883 );
884 StateEvents::perpetual(
885 perp,
886 PerpetualEventType::OraclePriceUpdated(perp.oracle_price()),
887 )
888 })
889 .into_iter()
890 .collect(),
891 ExchangeEvents::LiquidationBuyerUpdated(_) => vec![],
892 ExchangeEvents::LiquidationParamsUpdated(_) => vec![],
893 ExchangeEvents::LotOutOfRange(_) => self
894 .err_ctx(ctx, event)?
895 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::SizeOutOfRange))
896 .into_iter()
897 .collect(),
898 ExchangeEvents::MaintenanceMarginFractionUpdated(e) => self
899 .perpetual(e.perpId)
900 .map(|perp| {
901 perp.update_maintenance_margin(
902 instant,
903 perp.leverage_converter()
904 .from_unsigned(e.maintMarginFracHdths),
905 );
906 StateEvents::perpetual(
907 perp,
908 PerpetualEventType::MaintenanceMarginFractionUpdated(
909 perp.maintenance_margin(),
910 ),
911 )
912 })
913 .into_iter()
914 .collect(),
915 ExchangeEvents::MakerFeeUpdated(e) => self
917 .perpetual(e.perpId)
918 .map(|perp| {
919 perp.update_base_maker_fee(
920 instant,
921 perp.fee_converter().from_unsigned(e.makerFeePer100K),
922 );
923 StateEvents::perpetual(
924 perp,
925 PerpetualEventType::MakerFeeUpdated(perp.maker_fee()),
926 )
927 })
928 .into_iter()
929 .collect(),
930 ExchangeEvents::MakerOrderFilled(e) => self.apply_maker_order_filled(
933 instant,
934 event,
935 ctx,
936 RawMakerFill {
937 perp_id: e.perpId,
938 account_id: e.accountId,
939 order_id: e.orderId,
940 price_pns: e.pricePNS,
941 lot_lns: e.lotLNS,
942 fee_cns: e.feeCNS,
943 builder_fee_cns: U256::ZERO,
944 locked_balance_cns: e.lockedBalanceCNS,
945 balance_cns: e.balanceCNS,
946 },
947 )?,
948 ExchangeEvents::MakerOrderFilledV2(e) => self.apply_maker_order_filled(
949 instant,
950 event,
951 ctx,
952 RawMakerFill {
953 perp_id: e.perpId,
954 account_id: e.accountId,
955 order_id: e.orderId,
956 price_pns: e.pricePNS,
957 lot_lns: e.lotLNS,
958 fee_cns: e.feeCNS,
959 builder_fee_cns: e.builderFeeCNS,
960 locked_balance_cns: e.lockedBalanceCNS,
961 balance_cns: e.balanceCNS,
962 },
963 )?,
964 ExchangeEvents::MakerOrderSettlementFailed(e) => chain!(
965 if let Some(perp) = self.perpetual(e.perpId) {
966 let order_id = std::num::NonZeroU16::new(e.orderId.to::<u16>())
967 .expect("orderId in event cannot be 0");
968 let order = perp.remove_order(order_id)?;
969 chain!(
970 Some(StateEvents::order(perp, &order, ctx, OrderEventType::Removed)),
971 self.err_ctx(ctx, event)?
972 .map(|ctx| StateEvents::affected_order_error(
973 ctx,
974 &order,
975 OrderErrorType::MakerOrderSettlementFailed
976 ))
977 )
978 .collect()
979 } else {
980 vec![]
981 },
982 self.account(e.accountId).map(|acc| {
983 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
984 StateEvents::account(
985 acc,
986 ctx,
987 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
988 )
989 }),
990 if !e.recyclerAmountCNS.is_zero() {
991 self.account(e.recyclerAccountId).map(|acc| {
992 acc.update_balance(instant, cc.from_unsigned(e.recyclerBalanceCNS));
993 StateEvents::account(
994 acc,
995 ctx,
996 AccountEventType::BalanceUpdated(acc.balance()),
997 )
998 })
999 } else {
1000 None
1001 },
1002 )
1003 .collect(),
1004 ExchangeEvents::MarginTolUpdated(_) => vec![],
1005 ExchangeEvents::MarkExceedsTol(_) => vec![],
1006 ExchangeEvents::MarkPriceAgeExceedsMax(_) => vec![],
1007 ExchangeEvents::MarkUpdated(e) => {
1008 let perp_mark = self.perpetual(e.perpId).map(|perp| {
1009 perp.update_mark_price(
1010 instant,
1011 perp.price_converter().from_unsigned(e.pricePNS),
1012 );
1013 (perp.id(), perp.mark_price())
1014 });
1015 if let Some((perp_id, mark_price)) = perp_mark {
1016 chain!(
1017 Some(StateEvents::Perpetual(PerpetualEvent {
1018 perpetual_id: perp_id,
1019 r#type: PerpetualEventType::MarkPriceUpdated(mark_price),
1020 })),
1021 self.accounts.values_mut().filter_map(|acc| {
1023 acc.positions_mut().get_mut(&perp_id).map(|pos| {
1024 pos.apply_mark_price(instant, mark_price);
1025 StateEvents::position(
1026 pos,
1027 &None,
1028 PositionEventType::UnrealizedPnLUpdated {
1029 pnl: pos.pnl(),
1030 delta_pnl: pos.delta_pnl(),
1031 premium_pnl: pos.premium_pnl(),
1032 },
1033 )
1034 })
1035 }),
1036 )
1037 .collect()
1038 } else {
1039 vec![]
1040 }
1041 },
1042 ExchangeEvents::MaxMatchesReached(_) => self
1043 .err_ctx(ctx, event)?
1044 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::MaxMatchesReached))
1045 .into_iter()
1046 .collect(),
1047 ExchangeEvents::MaxOpenInterestUpdated(_) => vec![],
1048 ExchangeEvents::MaximumAccountOrders(_) => self
1049 .err_ctx(ctx, event)?
1050 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::MaximumAccountOrders))
1051 .into_iter()
1052 .collect(),
1053 ExchangeEvents::MinAccountOpenAmountUpdated(_) => vec![],
1054 ExchangeEvents::MinPostUpdated(e) => {
1055 self.min_post = cc.from_unsigned(e.minPostCNS);
1056 vec![StateEvents::Exchange(ExchangeEvent::MinPostUpdated(self.min_post))]
1057 },
1058 ExchangeEvents::MinSettleUpdated(e) => {
1059 self.min_settle = cc.from_unsigned(e.minSettleCNS);
1060 vec![StateEvents::Exchange(ExchangeEvent::MinSettleUpdated(self.min_settle))]
1061 },
1062 ExchangeEvents::MonitorAdministratorUpdated(_) => vec![],
1063 ExchangeEvents::MonitorPauseAttempted(_) => vec![],
1064 ExchangeEvents::OracleAgeExceedsMax(_) => vec![],
1065 ExchangeEvents::OracleDisabled(_) => vec![],
1066 ExchangeEvents::OrderBatchCompleted(_) => {
1067 ctx.take();
1069 vec![]
1070 },
1071 ExchangeEvents::OrderCancelled(e) => {
1072 let c = must_ctx()?;
1073 let order_id = c.order_id.expect("order_id required for OrderCancelled");
1074 chain!(
1075 if let Some(perp) = self.perpetuals.get_mut(&c.perpetual_id) {
1076 let order = perp.remove_order(order_id)?;
1077 Some(StateEvents::order(perp, &order, ctx, OrderEventType::Removed))
1078 } else {
1079 None
1080 },
1081 if let Some(acc) = self.accounts.get_mut(&c.account_id) {
1082 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
1083 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
1084 vec![
1085 StateEvents::account(
1086 acc,
1087 ctx,
1088 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
1089 ),
1090 StateEvents::account(
1091 acc,
1092 ctx,
1093 AccountEventType::BalanceUpdated(acc.balance()),
1094 ),
1095 ]
1096 } else {
1097 vec![]
1098 },
1099 )
1100 .collect()
1101 },
1102 ExchangeEvents::OrderCancelledByAdmin(e) => chain!(
1103 self.order(e.perpId, e.orderId)?.map(|(perp, order)| {
1104 perp.remove_order(order.order_id()).expect("order exists");
1105 StateEvents::order(perp, &order, ctx, OrderEventType::Removed)
1106 }),
1107 self.account(e.accountId).map(|acc| {
1108 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
1109 StateEvents::account(
1110 acc,
1111 ctx,
1112 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
1113 )
1114 }),
1115 )
1116 .collect(),
1117 ExchangeEvents::OrderCancelledByLiquidator(e) => chain!(
1118 self.order(e.perpId, e.orderId)?.map(|(perp, order)| {
1119 perp.remove_order(order.order_id()).expect("order exists");
1120 StateEvents::order(perp, &order, ctx, OrderEventType::Removed)
1121 }),
1122 self.account(e.accountId).map(|acc| {
1123 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
1124 StateEvents::account(
1125 acc,
1126 ctx,
1127 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
1128 )
1129 }),
1130 )
1131 .collect(),
1132 ExchangeEvents::OrderChanged(e) => {
1133 let c = must_ctx()?;
1134 let order_id = c.order_id.expect("order_id required for OrderChanged");
1135 chain!(
1136 if let Some(perp) = self.perpetuals.get_mut(&c.perpetual_id) {
1137 let order = perp
1138 .get_order(order_id)
1139 .copied()
1140 .ok_or(DexError::OrderNotFound(perp.id(), order_id))?;
1141 let new_price = perp.price_converter().from_unsigned(e.pricePNS);
1142 let new_size = perp.size_converter().from_unsigned(e.lotLNS);
1143 let new_expiry_block = e.expiryBlock.to();
1144 let price_update =
1145 if order.price() != new_price { Some(new_price) } else { None };
1146 let size_update =
1147 if order.size() != new_size { Some(new_size) } else { None };
1148 let expiry_block_update = if order.expiry_block() != new_expiry_block {
1149 Some(new_expiry_block)
1150 } else {
1151 None
1152 };
1153 let updated = order.updated(
1154 instant,
1155 ctx,
1156 price_update,
1157 size_update,
1158 size_update,
1159 expiry_block_update,
1160 );
1161 perp.update_order(updated)?;
1162 Some(StateEvents::order(
1163 perp,
1164 &order,
1165 ctx,
1166 OrderEventType::Updated {
1167 price: price_update,
1168 size: size_update,
1169 expiry_block: expiry_block_update,
1170 },
1171 ))
1172 } else {
1173 None
1174 },
1175 if let Some(acc) = self.accounts.get_mut(&c.account_id) {
1176 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
1177 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
1178 vec![
1179 StateEvents::account(
1180 acc,
1181 ctx,
1182 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
1183 ),
1184 StateEvents::account(
1185 acc,
1186 ctx,
1187 AccountEventType::BalanceUpdated(acc.balance()),
1188 ),
1189 ]
1190 } else {
1191 vec![]
1192 },
1193 )
1194 .collect()
1195 },
1196 ExchangeEvents::OrderDescIdTooLow(_) => vec![],
1197 ExchangeEvents::OrderDoesNotExist(_) => self
1198 .err_ctx(ctx, event)?
1199 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::OrderDoesNotExist))
1200 .into_iter()
1201 .collect(),
1202 ExchangeEvents::OrderExtensionRejected(_) => self
1203 .err_ctx(ctx, event)?
1204 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::OrderExtensionRejected))
1205 .into_iter()
1206 .collect(),
1207 ExchangeEvents::OrderForwardingNotAllowed(_) => vec![],
1208 ExchangeEvents::OrderForwardingUpdated(_) => vec![],
1209 ExchangeEvents::OrderPlaced(e) => {
1210 let c = must_ctx()?;
1211 let order_id = std::num::NonZeroU16::new(e.orderId.to::<u16>())
1212 .expect("orderId in OrderPlaced event cannot be 0");
1213 chain!(
1214 if let Some(perp) = self.perpetuals.get_mut(&c.perpetual_id) {
1215 let order = Order::placed(
1216 instant,
1217 c,
1218 order_id,
1219 perp.size_converter().from_unsigned(e.lotLNS),
1220 perp.price_converter(),
1221 perp.leverage_converter(),
1222 );
1223 let event = OrderEventType::Placed {
1224 r#type: order.r#type(),
1225 price: order.price(),
1226 size: order.size(),
1227 expiry_block: order.expiry_block(),
1228 leverage: order.leverage(),
1229 post_only: order.post_only().unwrap_or_default(),
1230 fill_or_kill: order.fill_or_kill().unwrap_or_default(),
1231 immediate_or_cancel: order.immediate_or_cancel().unwrap_or_default(),
1232 };
1233 perp.add_order(order)?;
1234 Some(StateEvents::order(perp, &order, ctx, event))
1235 } else {
1236 None
1237 },
1238 if let Some(acc) = self.accounts.get_mut(&c.account_id) {
1239 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
1240 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
1241 vec![
1242 StateEvents::account(
1243 acc,
1244 ctx,
1245 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
1246 ),
1247 StateEvents::account(
1248 acc,
1249 ctx,
1250 AccountEventType::BalanceUpdated(acc.balance()),
1251 ),
1252 ]
1253 } else {
1254 vec![]
1255 },
1256 )
1257 .collect()
1258 },
1259 ExchangeEvents::OrderPostFailed(e) => self
1260 .err_ctx(ctx, event)?
1261 .map(|ctx| {
1262 StateEvents::order_error(ctx, OrderErrorType::OrderPostFailed(e.reason.to()))
1263 })
1264 .into_iter()
1265 .collect(),
1266 ExchangeEvents::OrderRequest(e) => {
1269 ctx.replace(OrderContext::from(e));
1272 vec![]
1273 },
1274 ExchangeEvents::OrderRequestV2(e) => {
1275 ctx.replace(OrderContext::from(e));
1276 vec![]
1277 },
1278 ExchangeEvents::OrderSettlementImpliesInsolvent(_) => self
1279 .err_ctx(ctx, event)?
1280 .map(|ctx| {
1281 StateEvents::order_error(ctx, OrderErrorType::OrderSettlementImpliesInsolvent)
1282 })
1283 .into_iter()
1284 .collect(),
1285 ExchangeEvents::OrderSizeExceedsAvailableSize(_) => self
1286 .err_ctx(ctx, event)?
1287 .map(|ctx| {
1288 StateEvents::order_error(ctx, OrderErrorType::OrderSizeExceedsAvailableSize)
1289 })
1290 .into_iter()
1291 .collect(),
1292 ExchangeEvents::OverCollatDescentThreshUpdated(_) => vec![],
1293 ExchangeEvents::OwnershipTransferStarted(_) => vec![],
1294 ExchangeEvents::OwnershipTransferred(_) => vec![],
1295 ExchangeEvents::PermissonedCancelParamsUpdated(_) => vec![],
1296 ExchangeEvents::PerpFeeSchedIdSet(e) => {
1297 let key = FeeScheduleKey::from_raw(e.feeSchedId);
1307 let schedule = self.fee_schedules.get(key);
1308 self.perpetual(e.perpId)
1309 .and_then(|perp| match schedule {
1310 Some(schedule) => {
1311 perp.update_fee_schedule(instant, schedule);
1312 Some(StateEvents::perpetual(
1313 perp,
1314 PerpetualEventType::FeeScheduleUpdated(perp.fee_schedule()),
1315 ))
1316 },
1317 None => {
1318 perp.update_fee_schedule_key(instant, key);
1319 None
1320 },
1321 })
1322 .into_iter()
1323 .collect()
1324 },
1325 ExchangeEvents::PerpPositionBalCreditPositiveSevere(_) => vec![],
1326 ExchangeEvents::PositionAdministratorUpdated(_) => vec![],
1327 ExchangeEvents::PositionClosed(e) => {
1328 if let Some(ctx) = ctx {
1329 ctx.position_closed_at_log_index = Some(event.log_index());
1330 }
1331
1332 if let Some((acc, perp)) = self.account_perpetual(e.accountId, e.perpId) {
1333 let pos = acc
1334 .positions_mut()
1335 .remove(&perp.id())
1336 .ok_or(DexError::PositionNotFound(acc.id(), perp.id()))?;
1337
1338 chain!(
1339 Some(StateEvents::position(
1340 &pos,
1341 ctx,
1342 PositionEventType::Closed {
1343 r#type: pos.r#type(),
1344 entry_price: pos.entry_price(),
1345 exit_price: perp.price_converter().from_unsigned(e.pricePNS),
1346 size: pos.size(),
1347 delta_pnl: cc.from_signed(e.deltaPnlCNS),
1348 premium_pnl: cc.from_signed(e.fundingCNS),
1349 }
1350 )),
1351 if PositionType::from(e.positionType) == PositionType::Long {
1352 perp.update_open_interest(instant, pos.size(), UD64::ZERO);
1353 Some(StateEvents::perpetual(
1354 perp,
1355 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1356 ))
1357 } else {
1358 None
1359 },
1360 )
1361 .collect()
1362 } else {
1363 vec![]
1364 }
1365 },
1366 ExchangeEvents::PositionCollateralDecreased(e) => {
1367 if let Some((pos, perp)) = self.position(e.accountId, e.perpId)? {
1368 let prev_entry_price = pos.entry_price();
1369 pos.update_entry_price(instant, e.endEntryPricePNS, 0, perp.price_converter());
1370 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1371 pos.apply_mark_price(instant, perp.mark_price());
1372 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1373 vec![StateEvents::position(
1374 pos,
1375 ctx,
1376 PositionEventType::CollateralDecreased {
1377 prev_entry_price,
1378 new_entry_price: pos.entry_price(),
1379 deposit: pos.deposit(),
1380 },
1381 )]
1382 } else {
1383 vec![]
1384 }
1385 },
1386 ExchangeEvents::PositionDecreased(e) => {
1387 if let Some((pos, perp)) = self.position(e.accountId, e.perpId)? {
1388 let prev_size = pos.size();
1389 pos.update_size(instant, perp.size_converter().from_unsigned(e.endLotLNS));
1390 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1391 pos.apply_mark_price(instant, perp.mark_price());
1392 pos.update_premium_pnl(
1393 instant,
1394 pos.premium_pnl().sub(cc.from_signed(e.fundingCNS)),
1395 );
1396 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1397 chain!(
1398 Some(StateEvents::position(
1399 pos,
1400 ctx,
1401 PositionEventType::Decreased {
1402 prev_size,
1403 new_size: pos.size(),
1404 deposit: pos.deposit(),
1405 delta_pnl: pos.delta_pnl(),
1406 premium_pnl: pos.premium_pnl(),
1407 }
1408 )),
1409 if pos.r#type() == PositionType::Long {
1410 perp.update_open_interest(instant, prev_size, pos.size());
1411 Some(StateEvents::perpetual(
1412 perp,
1413 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1414 ))
1415 } else {
1416 None
1417 },
1418 )
1419 .collect()
1420 } else {
1421 vec![]
1422 }
1423 },
1424 ExchangeEvents::PositionDeleveraged(e) => chain!(
1425 if let Some((pos, perp)) = self.position(e.accountId, e.perpId)? {
1426 let prev_size = pos.size();
1427 pos.update_size(instant, perp.size_converter().from_unsigned(e.endLotLNS));
1428 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1429 pos.apply_mark_price(instant, perp.mark_price());
1430 pos.update_premium_pnl(
1431 instant,
1432 pos.premium_pnl().sub(cc.from_signed(e.fundingCNS)),
1433 );
1434 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1435 chain!(
1436 Some(StateEvents::position(
1437 pos,
1438 ctx,
1439 PositionEventType::Deleveraged {
1440 force_close: e.forceClose,
1441 r#type: pos.r#type(),
1442 entry_price: pos.entry_price(),
1443 exit_price: perp
1444 .price_converter()
1445 .from_unsigned(e.deleveragePricePNS),
1446 prev_size,
1447 new_size: pos.size(),
1448 deposit: pos.deposit(),
1449 delta_pnl: pos.delta_pnl(),
1450 premium_pnl: pos.premium_pnl(),
1451 }
1452 )),
1453 if pos.r#type() == PositionType::Long {
1454 perp.update_open_interest(instant, prev_size, pos.size());
1455 Some(StateEvents::perpetual(
1456 perp,
1457 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1458 ))
1459 } else {
1460 None
1461 },
1462 )
1463 .collect()
1464 } else {
1465 vec![]
1466 },
1467 self.account(e.accountId).map(|acc| {
1468 if e.endLotLNS == U256::ZERO {
1469 acc.positions_mut()
1470 .remove(&e.perpId.to::<types::PerpetualId>());
1471 }
1472 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
1473 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
1474 }),
1475 )
1476 .collect(),
1477 ExchangeEvents::PositionDeleveragedV2(e) => chain!(
1478 if let Some((pos, perp)) = self.position(e.accountId, e.perpId)? {
1479 let prev_size = pos.size();
1480 pos.update_size(instant, perp.size_converter().from_unsigned(e.endLotLNS));
1481 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1482 pos.apply_mark_price(instant, perp.mark_price());
1483 pos.update_premium_pnl(
1484 instant,
1485 pos.premium_pnl().sub(cc.from_signed(e.fundingCNS)),
1486 );
1487 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1488 chain!(
1489 Some(StateEvents::position(
1490 pos,
1491 ctx,
1492 PositionEventType::Deleveraged {
1493 force_close: e.forceClose,
1494 r#type: pos.r#type(),
1495 entry_price: pos.entry_price(),
1496 exit_price: perp
1497 .price_converter()
1498 .from_unsigned(e.deleveragePricePNS),
1499 prev_size,
1500 new_size: pos.size(),
1501 deposit: pos.deposit(),
1502 delta_pnl: pos.delta_pnl(),
1503 premium_pnl: pos.premium_pnl(),
1504 }
1505 )),
1506 if pos.r#type() == PositionType::Long {
1507 perp.update_open_interest(instant, prev_size, pos.size());
1508 Some(StateEvents::perpetual(
1509 perp,
1510 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1511 ))
1512 } else {
1513 None
1514 },
1515 )
1516 .collect()
1517 } else {
1518 vec![]
1519 },
1520 self.account(e.accountId).map(|acc| {
1521 if e.endLotLNS == U256::ZERO {
1522 acc.positions_mut()
1523 .remove(&e.perpId.to::<types::PerpetualId>());
1524 }
1525 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
1526 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
1527 }),
1528 )
1529 .collect(),
1530 ExchangeEvents::PositionDoesNotExist(_) => vec![],
1531 ExchangeEvents::PositionIncreased(e) => {
1532 if let Some((pos, perp)) = self.position(e.accountId, e.perpId)? {
1533 let prev_size = pos.size();
1534 pos.update_entry_price(instant, e.pricePNS, 0, perp.price_converter());
1535 pos.update_size(instant, perp.size_converter().from_unsigned(e.endLotLNS));
1536 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1537 pos.apply_mark_price(instant, perp.mark_price());
1538 pos.update_premium_pnl(instant, D256::ZERO);
1539 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1540
1541 chain!(
1542 Some(StateEvents::position(
1543 pos,
1544 ctx,
1545 PositionEventType::Increased {
1546 entry_price: pos.entry_price(),
1547 prev_size,
1548 new_size: pos.size(),
1549 deposit: pos.deposit(),
1550 }
1551 )),
1552 if pos.r#type() == PositionType::Long {
1553 perp.update_open_interest(instant, prev_size, pos.size());
1554 Some(StateEvents::perpetual(
1555 perp,
1556 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1557 ))
1558 } else {
1559 None
1560 },
1561 )
1562 .collect()
1563 } else {
1564 vec![]
1565 }
1566 },
1567 ExchangeEvents::PositionIncreasedV2(e) => {
1568 if let Some((pos, perp)) = self.position(e.accountId, e.perpId)? {
1569 let prev_size = pos.size();
1570 pos.update_entry_price(
1571 instant,
1572 e.pricePNS,
1573 e.priceResiduePNSQ16.to(),
1574 perp.price_converter(),
1575 );
1576 pos.update_size(instant, perp.size_converter().from_unsigned(e.endLotLNS));
1577 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1578 pos.apply_mark_price(instant, perp.mark_price());
1579 pos.update_premium_pnl(instant, D256::ZERO);
1580 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1581
1582 chain!(
1583 Some(StateEvents::position(
1584 pos,
1585 ctx,
1586 PositionEventType::Increased {
1587 entry_price: pos.entry_price(),
1588 prev_size,
1589 new_size: pos.size(),
1590 deposit: pos.deposit(),
1591 }
1592 )),
1593 if pos.r#type() == PositionType::Long {
1594 perp.update_open_interest(instant, prev_size, pos.size());
1595 Some(StateEvents::perpetual(
1596 perp,
1597 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1598 ))
1599 } else {
1600 None
1601 },
1602 )
1603 .collect()
1604 } else {
1605 vec![]
1606 }
1607 },
1608 ExchangeEvents::PositionInverted(e) => {
1609 if let Some((pos, perp)) = self.position(e.accountId, e.perpId)? {
1610 let prev_type = pos.r#type();
1611 let prev_entry_price = pos.entry_price();
1612 let prev_size = pos.size();
1613 pos.update_type(instant, PositionType::from(e.positionType));
1614 pos.update_entry_price(instant, e.pricePNS, 0, perp.price_converter());
1615 pos.update_size(instant, perp.size_converter().from_unsigned(e.endLotLNS));
1616 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1617 pos.apply_mark_price(instant, perp.mark_price());
1618 pos.update_premium_pnl(instant, D256::ZERO);
1619 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1620 if pos.r#type() == PositionType::Long {
1621 perp.update_open_interest(instant, UD64::ZERO, pos.size());
1622 } else {
1623 perp.update_open_interest(instant, prev_size, UD64::ZERO);
1624 }
1625 vec![
1626 StateEvents::position(
1627 pos,
1628 ctx,
1629 PositionEventType::Closed {
1630 r#type: prev_type,
1631 entry_price: prev_entry_price,
1632 exit_price: pos.entry_price(),
1633 size: prev_size,
1634 delta_pnl: cc.from_signed(e.deltaPnlCNS),
1635 premium_pnl: cc.from_signed(e.fundingCNS),
1636 },
1637 ),
1638 StateEvents::position(
1639 pos,
1640 ctx,
1641 PositionEventType::Inverted {
1642 r#type: pos.r#type(),
1643 entry_price: pos.entry_price(),
1644 prev_size,
1645 new_size: pos.size(),
1646 deposit: pos.deposit(),
1647 delta_pnl: pos.delta_pnl(),
1648 premium_pnl: pos.premium_pnl(),
1649 },
1650 ),
1651 StateEvents::perpetual(
1652 perp,
1653 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1654 ),
1655 ]
1656 } else {
1657 vec![]
1658 }
1659 },
1660 ExchangeEvents::PositionLiquidated(e) => chain!(
1661 if let Some((pos, perp)) = self.position(e.posAccountId, e.perpId)? {
1662 let prev_size = pos.size();
1663 pos.update_size(instant, perp.size_converter().from_unsigned(e.posLotLNS));
1664 pos.update_deposit(instant, cc.from_unsigned(e.posDepositCNS));
1665 pos.apply_mark_price(instant, perp.mark_price());
1666 pos.update_premium_pnl(
1667 instant,
1668 pos.premium_pnl().sub(cc.from_signed(e.fundingCNS)),
1669 );
1670 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1671 chain!(
1672 Some(StateEvents::position(
1673 pos,
1674 ctx,
1675 PositionEventType::Liquidated {
1676 r#type: pos.r#type(),
1677 entry_price: pos.entry_price(),
1678 exit_price: perp.price_converter().from_unsigned(e.liqPricePNS),
1679 prev_size,
1680 liquidated_size: perp.size_converter().from_unsigned(e.liqLotLNS),
1681 new_size: pos.size(),
1682 deposit: pos.deposit(),
1683 delta_pnl: pos.delta_pnl(),
1684 premium_pnl: pos.premium_pnl(),
1685 }
1686 )),
1687 if pos.r#type() == PositionType::Long {
1688 perp.update_open_interest(instant, prev_size, pos.size());
1689 Some(StateEvents::perpetual(
1690 perp,
1691 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1692 ))
1693 } else {
1694 None
1695 },
1696 )
1697 .collect()
1698 } else {
1699 vec![]
1700 },
1701 self.account(e.posAccountId).map(|acc| {
1702 if e.posLotLNS == U256::ZERO {
1703 acc.positions_mut()
1704 .remove(&e.perpId.to::<types::PerpetualId>());
1705 }
1706 acc.update_balance(instant, cc.from_unsigned(e.accBalanceCNS));
1707 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
1708 }),
1709 )
1710 .collect(),
1711 ExchangeEvents::PositionLiquidationCredit(e) => self
1712 .position(e.accountId, e.perpId)?
1713 .map(|(pos, _)| {
1714 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1715 StateEvents::position(
1716 pos,
1717 ctx,
1718 PositionEventType::DepositUpdated(pos.deposit()),
1719 )
1720 })
1721 .into_iter()
1722 .collect(),
1723 ExchangeEvents::PositionOpened(e) => {
1724 if let Some((acc, perp)) = self.account_perpetual(e.accountId, e.perpId) {
1725 let pos = Position::opened(
1726 instant,
1727 perp.id(),
1728 acc.id(),
1729 PositionType::from(e.positionType),
1730 e.pricePNS,
1731 0,
1732 perp.price_converter(),
1733 perp.size_converter().from_unsigned(e.lotLNS),
1734 cc.from_unsigned(e.depositCNS),
1735 perp.maintenance_margin(),
1736 );
1737 let events = chain!(
1738 Some(StateEvents::position(
1739 &pos,
1740 ctx,
1741 PositionEventType::Opened {
1742 r#type: pos.r#type(),
1743 entry_price: pos.entry_price(),
1744 size: pos.size(),
1745 deposit: pos.deposit(),
1746 }
1747 )),
1748 if pos.r#type() == PositionType::Long {
1749 perp.update_open_interest(instant, UD64::ZERO, pos.size());
1750 Some(StateEvents::perpetual(
1751 perp,
1752 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1753 ))
1754 } else {
1755 None
1756 },
1757 )
1758 .collect();
1759 acc.positions_mut().insert(perp.id(), pos);
1760 events
1761 } else {
1762 vec![]
1763 }
1764 },
1765 ExchangeEvents::PositionOpenedV2(e) => {
1766 if let Some((acc, perp)) = self.account_perpetual(e.accountId, e.perpId) {
1767 let pos = Position::opened(
1768 instant,
1769 perp.id(),
1770 acc.id(),
1771 PositionType::from(e.positionType),
1772 e.pricePNS,
1773 e.priceResiduePNSQ16.to(),
1774 perp.price_converter(),
1775 perp.size_converter().from_unsigned(e.lotLNS),
1776 cc.from_unsigned(e.depositCNS),
1777 perp.maintenance_margin(),
1778 );
1779 let events = chain!(
1780 Some(StateEvents::position(
1781 &pos,
1782 ctx,
1783 PositionEventType::Opened {
1784 r#type: pos.r#type(),
1785 entry_price: pos.entry_price(),
1786 size: pos.size(),
1787 deposit: pos.deposit(),
1788 }
1789 )),
1790 if pos.r#type() == PositionType::Long {
1791 perp.update_open_interest(instant, UD64::ZERO, pos.size());
1792 Some(StateEvents::perpetual(
1793 perp,
1794 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1795 ))
1796 } else {
1797 None
1798 },
1799 )
1800 .collect();
1801 acc.positions_mut().insert(perp.id(), pos);
1802 events
1803 } else {
1804 vec![]
1805 }
1806 },
1807 ExchangeEvents::PositionUnwound(e) => {
1808 if let Some((acc, perp)) = self.account_perpetual(e.accountId, e.perpId) {
1809 let pos = acc
1810 .positions_mut()
1811 .remove(&perp.id())
1812 .ok_or(DexError::PositionNotFound(acc.id(), perp.id()))?;
1813 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
1814 chain!(
1815 Some(StateEvents::position(
1816 &pos,
1817 ctx,
1818 PositionEventType::Unwound {
1819 r#type: pos.r#type(),
1820 entry_price: pos.entry_price(),
1821 exit_price: perp.price_converter().from_unsigned(e.pricePNS),
1822 size: pos.size(),
1823 fair_market_value: cc.from_signed(e.positionFmvCNS),
1824 payment: cc.from_unsigned(e.paymentCNS),
1825 }
1826 )),
1827 Some(StateEvents::account(
1828 acc,
1829 ctx,
1830 AccountEventType::BalanceUpdated(acc.balance())
1831 )),
1832 if pos.r#type() == PositionType::Long {
1833 perp.update_open_interest(instant, pos.size(), UD64::ZERO);
1834 Some(StateEvents::perpetual(
1835 perp,
1836 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1837 ))
1838 } else {
1839 None
1840 },
1841 )
1842 .collect()
1843 } else {
1844 vec![]
1845 }
1846 },
1847 ExchangeEvents::PositionUnwoundV2(e) => {
1848 if let Some((acc, perp)) = self.account_perpetual(e.accountId, e.perpId) {
1850 let pos = acc
1851 .positions_mut()
1852 .remove(&perp.id())
1853 .ok_or(DexError::PositionNotFound(acc.id(), perp.id()))?;
1854 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
1855 chain!(
1856 Some(StateEvents::position(
1857 &pos,
1858 ctx,
1859 PositionEventType::Unwound {
1860 r#type: pos.r#type(),
1861 entry_price: pos.entry_price(),
1862 exit_price: perp.price_converter().from_unsigned(e.pricePNS),
1863 size: pos.size(),
1864 fair_market_value: cc.from_signed(e.positionFmvCNS),
1865 payment: cc.from_unsigned(e.paymentCNS),
1866 }
1867 )),
1868 Some(StateEvents::account(
1869 acc,
1870 ctx,
1871 AccountEventType::BalanceUpdated(acc.balance())
1872 )),
1873 if pos.r#type() == PositionType::Long {
1874 perp.update_open_interest(instant, pos.size(), UD64::ZERO);
1875 Some(StateEvents::perpetual(
1876 perp,
1877 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1878 ))
1879 } else {
1880 None
1881 },
1882 )
1883 .collect()
1884 } else {
1885 vec![]
1886 }
1887 },
1888 ExchangeEvents::PositionUnwoundWithoutPayment(e) => {
1889 if let Some((acc, perp)) = self.account_perpetual(e.accountId, e.perpId) {
1890 let pos = acc
1891 .positions_mut()
1892 .remove(&perp.id())
1893 .ok_or(DexError::PositionNotFound(acc.id(), perp.id()))?;
1894 chain!(
1895 Some(StateEvents::position(
1896 &pos,
1897 ctx,
1898 PositionEventType::Unwound {
1899 r#type: pos.r#type(),
1900 entry_price: pos.entry_price(),
1901 exit_price: perp.price_converter().from_unsigned(e.pricePNS),
1902 size: pos.size(),
1903 fair_market_value: cc.from_signed(e.positionFmvCNS),
1904 payment: UD128::ZERO,
1905 }
1906 )),
1907 if pos.r#type() == PositionType::Long {
1908 perp.update_open_interest(instant, pos.size(), UD64::ZERO);
1909 Some(StateEvents::perpetual(
1910 perp,
1911 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1912 ))
1913 } else {
1914 None
1915 },
1916 )
1917 .collect()
1918 } else {
1919 vec![]
1920 }
1921 },
1922 ExchangeEvents::PositionUnwoundWithoutPaymentV2(e) => {
1923 if let Some((acc, perp)) = self.account_perpetual(e.accountId, e.perpId) {
1925 let pos = acc
1926 .positions_mut()
1927 .remove(&perp.id())
1928 .ok_or(DexError::PositionNotFound(acc.id(), perp.id()))?;
1929 chain!(
1930 Some(StateEvents::position(
1931 &pos,
1932 ctx,
1933 PositionEventType::Unwound {
1934 r#type: pos.r#type(),
1935 entry_price: pos.entry_price(),
1936 exit_price: perp.price_converter().from_unsigned(e.pricePNS),
1937 size: pos.size(),
1938 fair_market_value: cc.from_signed(e.positionFmvCNS),
1939 payment: UD128::ZERO,
1940 }
1941 )),
1942 if pos.r#type() == PositionType::Long {
1943 perp.update_open_interest(instant, pos.size(), UD64::ZERO);
1944 Some(StateEvents::perpetual(
1945 perp,
1946 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1947 ))
1948 } else {
1949 None
1950 },
1951 )
1952 .collect()
1953 } else {
1954 vec![]
1955 }
1956 },
1957 ExchangeEvents::PostOrderUnderMinimum(_) => self
1958 .err_ctx(ctx, event)?
1959 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::PostOrderUnderMinimum))
1960 .into_iter()
1961 .collect(),
1962 ExchangeEvents::PriceAdministratorUpdated(_) => vec![],
1963 ExchangeEvents::PriceMaxAgeUpdated(e) => {
1964 if let Some(perp) = self.perpetual(e.perpId) {
1965 perp.update_price_max_age_sec(instant, e.maxAgeSec.to());
1966 }
1967 vec![]
1968 },
1969 ExchangeEvents::PriceOutOfRange(_) => self
1970 .err_ctx(ctx, event)
1971 .ok() .flatten()
1973 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::PriceOutOfRange))
1974 .into_iter()
1975 .collect(),
1976 ExchangeEvents::PriceSetDuringTriggerExec(_) => vec![],
1977 ExchangeEvents::PriceTolUpdated(_) => vec![],
1978 ExchangeEvents::ProtocolBalanceDeposit(_) => vec![],
1979 ExchangeEvents::ProtocolBalanceWithdraw(_) => vec![],
1980 ExchangeEvents::RecycleBalanceInsufficientSevere(_) => vec![],
1981 ExchangeEvents::RecycleFeeToAccount(_) => vec![],
1982 ExchangeEvents::RecycleFeeToProtocol(_) => vec![],
1983 ExchangeEvents::RecycleFeeUpdated(e) => {
1984 self.recycle_fee = cc.from_unsigned(e.recycleFeeCNS);
1985 vec![StateEvents::Exchange(ExchangeEvent::RecycleFeeUpdated(self.recycle_fee()))]
1986 },
1987 ExchangeEvents::ReportAgeExceedsLastUpdate(_) => vec![],
1988 ExchangeEvents::ReportExpiresTooSoon(_) => vec![],
1989 ExchangeEvents::ReportFromFuture(_) => vec![],
1990 ExchangeEvents::ReportPriceIsNegative(_) => vec![],
1991 ExchangeEvents::ResidueBalanceInsufficient(_) => vec![],
1992 ExchangeEvents::ResidueTransferred(_) => vec![],
1993 ExchangeEvents::TakerFeeUpdated(e) => self
1995 .perpetual(e.perpId)
1996 .map(|perp| {
1997 perp.update_base_taker_fee(
1998 instant,
1999 perp.fee_converter().from_unsigned(e.takerFeePer100K),
2000 );
2001 StateEvents::perpetual(
2002 perp,
2003 PerpetualEventType::TakerFeeUpdated(perp.taker_fee()),
2004 )
2005 })
2006 .into_iter()
2007 .collect(),
2008 ExchangeEvents::TakerOrderFilled(e) => self.apply_taker_order_filled(
2011 instant,
2012 event,
2013 ctx,
2014 RawTakerFill {
2015 collat_price_pns: e.collatPricePNS,
2016 lot_lns: e.lotLNS,
2017 fee_cns: e.feeCNS,
2018 builder_fee_cns: U256::ZERO,
2019 balance_cns: e.balanceCNS,
2020 },
2021 )?,
2022 ExchangeEvents::TakerOrderFilledV2(e) => self.apply_taker_order_filled(
2023 instant,
2024 event,
2025 ctx,
2026 RawTakerFill {
2027 collat_price_pns: e.collatPricePNS,
2028 lot_lns: e.lotLNS,
2029 fee_cns: e.feeCNS,
2030 builder_fee_cns: e.builderFeeCNS,
2031 balance_cns: e.balanceCNS,
2032 },
2033 )?,
2034 ExchangeEvents::ToleranceAdministratorUpdated(_) => vec![],
2035 ExchangeEvents::TransferAccountToProtocol(e) => self
2036 .account(e.accountId)
2037 .map(|acc| {
2038 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
2039 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
2040 })
2041 .into_iter()
2042 .collect(),
2043 ExchangeEvents::TransferPerpInsToProtocol(_) => vec![],
2044 ExchangeEvents::TransferPerpPosToProtocol(_) => vec![],
2045 ExchangeEvents::TransferProtocolToAccount(e) => self
2046 .account(e.accountId)
2047 .map(|acc| {
2048 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
2049 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
2050 })
2051 .into_iter()
2052 .collect(),
2053 ExchangeEvents::TransferProtocolToPerp(_) => vec![],
2054 ExchangeEvents::TransferProtocolToRecycleBal(_) => vec![],
2055 ExchangeEvents::TriggerDescIdTooLow(_) => vec![],
2056 ExchangeEvents::TriggerOrderExecution(_) => vec![],
2057 ExchangeEvents::TriggerOrderRequest(_) => vec![],
2058 ExchangeEvents::UnableToCancelOrder(_) => vec![],
2059 ExchangeEvents::UnityDescentThreshUpdated(_) => vec![],
2060 ExchangeEvents::UnspecifiedCollateral(_) => vec![],
2061 ExchangeEvents::UnwindCompleted(_) => vec![],
2062 ExchangeEvents::UnwindContractTrigger(_) => vec![],
2063 ExchangeEvents::UnwindInitializationCleared(_) => vec![],
2064 ExchangeEvents::UnwindInitialized(_) => vec![],
2065 ExchangeEvents::UnwindInsufficientBalance(_) => vec![],
2066 ExchangeEvents::UnwindIterationCompleted(_) => vec![],
2067 ExchangeEvents::UnwindPrepared(_) => vec![],
2068 ExchangeEvents::UnwindPreparationCleared(_) => vec![],
2069 ExchangeEvents::UnwindProcessInProgress(_) => vec![],
2070 ExchangeEvents::UpdateOracleFailed(_) => vec![],
2071 ExchangeEvents::Upgraded(_) => vec![],
2072 ExchangeEvents::ValueExceedsMaximum(_) => self
2073 .err_ctx(ctx, event)?
2074 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::ValueExceedsMaximum))
2075 .into_iter()
2076 .collect(),
2077 ExchangeEvents::WhitelistAddress(_) => vec![],
2078 ExchangeEvents::WhitelistingEnabledChanged(_) => vec![],
2079 ExchangeEvents::WithdrawRateLimitBypassSet(_) => vec![],
2080 ExchangeEvents::WithdrawRateLimitForceReset(_) => vec![],
2081 ExchangeEvents::WRLSMinWithdrawLimitUpdated(_) => vec![],
2082 ExchangeEvents::WRLSThousandthsTvlUpdated(_) => vec![],
2083 ExchangeEvents::WithdrawRateLimitReset(_) => vec![],
2084 ExchangeEvents::WrongAccountForOrder(_) => self
2085 .err_ctx(ctx, event)?
2086 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::WrongAccountForOrder))
2087 .into_iter()
2088 .collect(),
2089 })
2090 }
2091
2092 fn add_perpetual(
2095 &mut self,
2096 instant: types::StateInstant,
2097 name: &str,
2098 symbol: &str,
2099 listing: RawContractAdded,
2100 fee_schedule: FeeSchedule,
2101 ) -> StateEvents {
2102 let perp = Perpetual::added(
2103 instant,
2104 listing.perp_id.to(),
2105 name.to_string(),
2106 symbol.to_string(),
2107 listing.status == 0, listing.price_decimals.to(),
2109 listing.lot_decimals.to(),
2110 listing.base_price_pns,
2111 fee_schedule,
2112 listing.init_margin_frac_hdths,
2113 listing.maint_margin_frac_hdths,
2114 );
2115 let event = StateEvents::perpetual(&perp, PerpetualEventType::Added);
2116 self.perpetuals.insert(perp.id(), perp);
2117 event
2118 }
2119
2120 fn apply_maker_order_filled(
2123 &mut self,
2124 instant: types::StateInstant,
2125 event: &stream::RawEvent,
2126 ctx: &mut Option<OrderContext>,
2127 fill: RawMakerFill,
2128 ) -> Result<Vec<StateEvents>, DexError> {
2129 let cc = self.collateral_converter;
2130 Ok(chain!(
2131 if let Some((perp, order)) = self.order(fill.perp_id, fill.order_id)? {
2132 let fill_price = perp.price_converter().from_unsigned(fill.price_pns);
2133 let fill_size = perp.size_converter().from_unsigned(fill.lot_lns);
2134 let fee = cc.from_unsigned(fill.fee_cns);
2135 let builder_fee = cc.from_unsigned(fill.builder_fee_cns);
2136 perp.update_last_price(instant, fill_price);
2137 let clearing_remaining_order = if let Some(ctx) = ctx {
2138 ctx.maker_fills.push(types::MakerFill {
2139 log_index: event.log_index(),
2140 maker_account_id: order.account_id(),
2141 maker_order_id: order.order_id(),
2142 maker_client_order_id: order.client_order_id(),
2143 price: fill_price,
2144 size: fill_size,
2145 fee,
2146 builder: order.builder(),
2147 builder_fee,
2148 });
2149 let position_closed_by_smart_contract = if event.log_index() > 0 {
2150 match order.r#type() {
2154 OrderType::CloseLong | OrderType::CloseShort => {
2155 Some(event.log_index() - 1) == ctx.position_closed_at_log_index
2156 || Some(event.log_index() - 2)
2157 == ctx.position_closed_at_log_index
2158 },
2159 _ => false,
2160 }
2161 } else {
2162 false
2163 };
2164 ctx.clearing_remaining_order | position_closed_by_smart_contract
2165 } else {
2166 false
2167 };
2168 vec![
2169 if order.size() > fill_size && !clearing_remaining_order {
2170 let new_size = order.size() - fill_size;
2171 perp.update_order(order.updated(
2172 instant,
2173 ctx,
2174 None,
2175 Some(new_size),
2176 None,
2177 None,
2178 ))
2179 .expect("order exists");
2180 StateEvents::order(
2181 perp,
2182 &order,
2183 ctx,
2184 OrderEventType::Updated {
2185 price: None,
2186 size: Some(new_size),
2187 expiry_block: None,
2188 },
2189 )
2190 } else {
2191 perp.remove_order(order.order_id()).expect("order exists");
2192 StateEvents::order(perp, &order, ctx, OrderEventType::Removed)
2193 },
2194 StateEvents::order(
2195 perp,
2196 &order,
2197 ctx,
2198 OrderEventType::Filled {
2199 fill_price,
2200 fill_size,
2201 fee,
2202 builder_fee,
2203 is_maker: true,
2204 },
2205 ),
2206 StateEvents::perpetual(
2207 perp,
2208 PerpetualEventType::LastPriceUpdated(perp.last_price()),
2209 ),
2210 ]
2211 } else {
2212 vec![]
2213 },
2214 self.account(fill.account_id).map(|acc| {
2215 acc.update_locked_balance(instant, cc.from_unsigned(fill.locked_balance_cns));
2216 StateEvents::account(
2217 acc,
2218 ctx,
2219 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
2220 )
2221 }),
2222 self.account(fill.account_id).map(|acc| {
2223 acc.update_balance(instant, cc.from_unsigned(fill.balance_cns));
2224 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
2225 }),
2226 )
2227 .collect())
2228 }
2229
2230 fn apply_taker_order_filled(
2233 &mut self,
2234 instant: types::StateInstant,
2235 event: &stream::RawEvent,
2236 ctx: &mut Option<OrderContext>,
2237 fill: RawTakerFill,
2238 ) -> Result<Vec<StateEvents>, DexError> {
2239 let cc = self.collateral_converter;
2240 let c = ctx
2241 .as_ref()
2242 .ok_or(DexError::OrderContextExpected(event.tx_index(), event.log_index()))?;
2243 let taker_fee = cc.from_unsigned(fill.fee_cns);
2244 let taker_builder_fee = cc.from_unsigned(fill.builder_fee_cns);
2245 Ok(chain!(
2246 self.perpetuals
2247 .get(&c.perpetual_id)
2248 .map(|perp| StateEvents::Order(OrderEvent {
2249 perpetual_id: perp.id(),
2250 account_id: c.account_id,
2251 request_id: Some(c.request_id),
2252 client_order_id: Some(c.request_id),
2253 order_id: None,
2254 builder: c.builder,
2255 r#type: OrderEventType::Filled {
2256 fill_price: perp.price_converter().from_unsigned(fill.collat_price_pns),
2257 fill_size: perp.size_converter().from_unsigned(fill.lot_lns),
2258 fee: taker_fee,
2259 builder_fee: taker_builder_fee,
2260 is_maker: false,
2261 },
2262 })),
2263 self.accounts.get_mut(&c.account_id).map(|acc| {
2264 acc.update_balance(instant, cc.from_unsigned(fill.balance_cns));
2265 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
2266 }),
2267 iter::once(StateEvents::trade(c, taker_fee, taker_builder_fee)),
2268 )
2269 .collect())
2270 }
2271
2272 fn update_fee_schedule(
2277 &mut self,
2278 instant: types::StateInstant,
2279 schedule: FeeSchedule,
2280 ) -> Vec<StateEvents> {
2281 self.fee_schedules.set(schedule);
2282 chain!(
2283 iter::once(StateEvents::Exchange(ExchangeEvent::FeeScheduleUpdated(schedule))),
2284 self.perpetuals
2285 .values_mut()
2286 .filter(|perp| perp.fee_schedule().key() == schedule.key())
2287 .map(|perp| {
2288 perp.update_fee_schedule(instant, schedule);
2289 StateEvents::perpetual(
2290 perp,
2291 PerpetualEventType::FeeScheduleUpdated(perp.fee_schedule()),
2292 )
2293 }),
2294 )
2295 .collect()
2296 }
2297
2298 fn apply_state_event(
2299 &mut self,
2300 instant: types::StateInstant,
2301 event: &StateEvents,
2302 ) -> Result<Vec<StateEvents>, DexError> {
2303 Ok(match event {
2304 StateEvents::Perpetual(pe) => {
2305 match pe.r#type {
2306 PerpetualEventType::MaintenanceMarginFractionUpdated(maintenance_margin) => {
2307 self.accounts
2309 .values_mut()
2310 .filter_map(|acc| {
2311 acc.positions_mut().get_mut(&pe.perpetual_id).map(|pos| {
2312 pos.apply_maintenance_margin(instant, maintenance_margin);
2313 StateEvents::position(
2314 pos,
2315 &None,
2316 PositionEventType::MaintenanceMarginUpdated(
2317 pos.maintenance_margin_requirement(),
2318 ),
2319 )
2320 })
2321 })
2322 .collect()
2323 },
2324 _ => vec![],
2325 }
2326 },
2327 _ => vec![],
2328 })
2329 }
2330
2331 fn err_ctx<'c>(
2332 &self,
2333 ctx: &'c mut Option<OrderContext>,
2334 event: &stream::RawEvent,
2335 ) -> Result<Option<&'c OrderContext>, DexError> {
2336 let c = ctx
2337 .as_ref()
2338 .ok_or(DexError::OrderContextExpected(event.tx_index(), event.log_index()))?;
2339 Ok(self.accounts.contains_key(&c.account_id).then_some(c))
2340 }
2341
2342 fn ensure_account(&mut self, id: U256) {
2343 let id = id.to::<types::AccountId>();
2344 if self.track_all_accounts && !self.accounts.contains_key(&id) {
2345 self.accounts
2346 .insert(id, Account::untracked(types::StateInstant::default(), id));
2347 }
2348 }
2349
2350 fn account(&mut self, id: U256) -> Option<&mut Account> {
2351 self.ensure_account(id);
2352 self.accounts.get_mut(&id.to::<types::AccountId>())
2353 }
2354
2355 fn order(
2356 &mut self,
2357 perp_id: U256,
2358 ord_id: U256,
2359 ) -> Result<Option<(&mut Perpetual, Order)>, DexError> {
2360 let ord_id = std::num::NonZeroU16::new(ord_id.to::<u16>())
2361 .expect("ord_id in order lookup cannot be 0");
2362 Ok(if let Some(perp) = self.perpetuals.get_mut(&perp_id.to::<types::PerpetualId>()) {
2363 let ord = perp
2364 .get_order(ord_id)
2365 .copied()
2366 .ok_or(DexError::OrderNotFound(perp.id(), ord_id))?;
2367 Some((perp, ord))
2368 } else {
2369 None
2370 })
2371 }
2372
2373 fn perpetual(&mut self, id: U256) -> Option<&mut Perpetual> {
2374 self.perpetuals.get_mut(&id.to::<types::PerpetualId>())
2375 }
2376
2377 fn account_perpetual(
2378 &mut self,
2379 acc_id: U256,
2380 perp_id: U256,
2381 ) -> Option<(&mut Account, &mut Perpetual)> {
2382 self.ensure_account(acc_id);
2383 self.accounts
2384 .get_mut(&acc_id.to::<types::AccountId>())
2385 .zip(self.perpetuals.get_mut(&perp_id.to::<types::PerpetualId>()))
2386 }
2387
2388 fn position(
2389 &mut self,
2390 acc_id: U256,
2391 perp_id: U256,
2392 ) -> Result<Option<(&mut Position, &mut Perpetual)>, DexError> {
2393 self.ensure_account(acc_id);
2394 let acc_id = acc_id.to::<types::AccountId>();
2395 let perp_id = perp_id.to::<types::PerpetualId>();
2396 Ok(
2397 if let Some(acc) = self.accounts.get_mut(&acc_id)
2398 && let Some(perp) = self.perpetuals.get_mut(&perp_id)
2399 {
2400 let pos = acc
2401 .positions_mut()
2402 .get_mut(&perp_id)
2403 .ok_or(DexError::PositionNotFound(acc_id, perp_id))?;
2404 Some((pos, perp))
2405 } else {
2406 None
2407 },
2408 )
2409 }
2410}
2411
2412#[cfg(feature = "display")]
2413impl std::fmt::Display for Exchange {
2414 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2415 use colored::Colorize;
2416 use tabled::{Table, settings::Style};
2417
2418 writeln!(
2419 f,
2420 "{}",
2421 format!(
2422 "{} | {}{} | {} (sdk {}) | chain {}",
2423 self.instant,
2424 if self.is_halted { "[HALTED] ".bold().bright_red() } else { Default::default() },
2425 self.chain.exchange(),
2426 self.features,
2427 Self::revision(),
2428 self.chain.chain_id(),
2429 )
2430 .bold()
2431 .purple(),
2432 )?;
2433
2434 let mut params = Table::from_iter(vec![vec![
2435 format!("Min Post: {}", self.min_post),
2436 format!("Min Settle: {}", self.min_settle),
2437 format!("Recycle Fee: {}", self.recycle_fee),
2438 format!("Funding Interval: {}", self.funding_interval_blocks),
2439 ]]);
2440 params.with(Style::modern());
2441 writeln!(f, "{params}")?;
2442
2443 if self.features.keyed_fee_schedules() {
2447 let mut fees = Table::from_iter(chain!(
2448 iter::once(
2449 chain!(
2450 iter::once("Fees (tkr/mkr)".to_string()),
2451 (0..FEE_TIERS).map(|tier| format!("Tier {tier}")),
2452 )
2453 .collect::<Vec<_>>(),
2454 ),
2455 self.fee_schedules.schedules().map(|schedule| chain!(
2456 iter::once(schedule.key().to_string()),
2457 (0..FEE_TIERS as types::FeeTier).map(move |tier| format!(
2458 "{}\n{}",
2459 schedule.taker_fee(tier),
2460 schedule.maker_fee(tier),
2461 )),
2462 )
2463 .collect::<Vec<_>>()),
2464 ));
2465 fees.with(Style::modern());
2466 writeln!(f, "{fees}")?;
2467 }
2468 writeln!(f)?;
2469
2470 if f.alternate() && !self.perpetuals().is_empty() {
2472 let mut perpetuals: Vec<_> = self.perpetuals().values().collect();
2473 perpetuals.sort_by_key(|p| p.id());
2474 for perpetual in perpetuals {
2475 writeln!(f, "{:#}", perpetual)?;
2476 }
2477 }
2478
2479 if f.alternate() && !self.accounts().is_empty() {
2480 let mut accounts: Vec<_> = self.accounts().values().collect();
2481 accounts.sort_by_key(|a| a.id());
2482 for account in accounts {
2483 writeln!(f, "{:#}", account)?;
2484 }
2485 }
2486
2487 Ok(())
2488 }
2489}