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();
622 vec![self.add_perpetual(
623 instant,
624 &e.name,
625 &e.symbol,
626 RawContractAdded {
627 perp_id: e.perpId,
628 status: e.status,
629 price_decimals: e.priceDecimals,
630 lot_decimals: e.lotDecimals,
631 base_price_pns: e.basePricePNS,
632 init_margin_frac_hdths: e.initMarginFracHdths,
633 maint_margin_frac_hdths: e.maintMarginFracHdths,
634 },
635 FeeSchedule::flat(
636 FeeScheduleKey::Default,
637 fee_converter.from_unsigned(e.takerFeePer100K),
638 fee_converter.from_unsigned(e.makerFeePer100K),
639 ),
640 )]
641 },
642 ExchangeEvents::ContractAddedV2(e) => {
643 let key = FeeScheduleKey::from_raw(e.perpFeeSchedId);
648 let fee_schedule = self.fee_schedule(key).ok_or(
649 DexError::FeeScheduleNotFound(key),
654 )?;
655 vec![self.add_perpetual(
656 instant,
657 &e.name,
658 &e.symbol,
659 RawContractAdded {
660 perp_id: e.perpId,
661 status: e.status,
662 price_decimals: e.priceDecimals,
663 lot_decimals: e.lotDecimals,
664 base_price_pns: e.basePricePNS,
665 init_margin_frac_hdths: e.initMarginFracHdths,
666 maint_margin_frac_hdths: e.maintMarginFracHdths,
667 },
668 fee_schedule,
669 )]
670 },
671 ExchangeEvents::ContractNotOperational(_) => self
672 .err_ctx(ctx, event)?
673 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::ContractNotOperational))
674 .into_iter()
675 .collect(),
676 ExchangeEvents::ContractLinkFeedUpdated(e) => self
677 .perpetual(e.perpId)
678 .map(|perp| {
679 perp.update_oracle_feed_id(instant, e.feedId);
680 StateEvents::perpetual(
681 perp,
682 PerpetualEventType::OracleConfigurationUpdated {
683 is_used: perp.is_oracle_used(),
684 feed_id: perp.oracle_feed_id(),
685 },
686 )
687 })
688 .into_iter()
689 .collect(),
690 ExchangeEvents::ContractPaused(e) => {
691 let funding_interval = self.funding_interval_blocks as u64;
694 self.perpetual(e.perpId)
695 .map(|perp| {
696 perp.update_paused(instant, e.paused, funding_interval);
697 StateEvents::perpetual(perp, PerpetualEventType::Paused(perp.is_paused()))
698 })
699 .into_iter()
700 .collect()
701 },
702 ExchangeEvents::ContractRemoved(e) => {
703 let funding_interval = self.funding_interval_blocks as u64;
704 self.perpetual(e.perpId)
705 .map(|perp| {
706 perp.update_paused(instant, true, funding_interval);
707 StateEvents::perpetual(perp, PerpetualEventType::Paused(perp.is_paused()))
708 })
709 .into_iter()
710 .collect()
711 },
712 ExchangeEvents::ContractVersionSet(e) => {
713 let version = ContractVersion::new(e.major.to(), e.minor.to(), e.patch.to());
714 self.features.observe_version(version);
715 vec![StateEvents::Exchange(ExchangeEvent::ContractVersionUpdated(version))]
716 },
717 ExchangeEvents::CrossesBook(_) => self
718 .err_ctx(ctx, event)?
719 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::CrossesBook))
720 .into_iter()
721 .collect(),
722 ExchangeEvents::DcpBorrowThreshUpdated(_) => vec![],
723 ExchangeEvents::DecreaseCollateralBeyondMarkPrice(_) => vec![],
724 ExchangeEvents::DefaultPerpFeeScheduleSet(e) => self.update_fee_schedule(
729 instant,
730 FeeSchedule::new(
731 FeeScheduleKey::Default,
732 e.takerFeesPer100K,
733 e.makerFeesPer100K,
734 self.features.fee_rate_converter(),
735 ),
736 ),
737 ExchangeEvents::DefaultRwaFeeScheduleSet(e) => self.update_fee_schedule(
738 instant,
739 FeeSchedule::new(
740 FeeScheduleKey::RwaDefault,
741 e.takerFeesPer100K,
742 e.makerFeesPer100K,
743 self.features.fee_rate_converter(),
744 ),
745 ),
746 ExchangeEvents::DeleveragePositionListEmpty(_) => vec![],
747 ExchangeEvents::ExceedsLastExecutionBlock(_) => self
748 .err_ctx(ctx, event)?
749 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::ExceedsLastExecutionBlock))
750 .into_iter()
751 .collect(),
752 ExchangeEvents::ExchangeHalted(e) => {
753 self.is_halted = e.halted;
754 vec![StateEvents::Exchange(ExchangeEvent::Halted(self.is_halted))]
755 },
756 ExchangeEvents::ExchangeInitialized(_) => vec![],
757 ExchangeEvents::FeeParamsUpdated(_) => vec![],
758 ExchangeEvents::FeeScheduleSet(e) => {
759 self.update_fee_schedule(
765 instant,
766 FeeSchedule::new(
767 FeeScheduleKey::from_raw(e.feeSchedId),
768 e.takerFeesPer100K,
769 e.makerFeesPer100K,
770 self.features.fee_rate_converter(),
771 ),
772 )
773 },
774 ExchangeEvents::FeeScheduleMigrated(_) => vec![],
780 ExchangeEvents::FeeUnitRedenominated(_) => vec![],
781 ExchangeEvents::FundingClampPctUpdated(_) => vec![],
782 ExchangeEvents::FundingEventCompleted(e) => {
783 if let Some(perp) = self.perpetual(e.perpId) {
784 perp.update_funding(
785 instant,
786 perp.funding_rate_converter()
787 .from_signed(e.actualRatePct100k),
788 perp.funding_sum_converter()
789 .from_i64(e.fundingPaymentPNS.as_i64()),
790 e.fundingEventBlock.to(),
791 );
792 }
793 vec![]
794 },
795 ExchangeEvents::FundingEventSetTooEarly(_) => vec![],
796 ExchangeEvents::FundingPriceExceedsTol(_) => vec![],
797 ExchangeEvents::FundingSumAlreadySet(_) => vec![],
798 ExchangeEvents::FundingSumScalingExpUpdated(e) => self
799 .perpetual(e.perpId)
800 .map(|perp| {
801 perp.update_funding_sum_scaling_exp(instant, e.newExp.to());
802 StateEvents::perpetual(
803 perp,
804 PerpetualEventType::FundingSumScalingExpUpdated(e.newExp.to()),
805 )
806 })
807 .into_iter()
808 .collect(),
809 ExchangeEvents::IgnoreOracleUpdated(e) => self
810 .perpetual(e.perpId)
811 .map(|perp| {
812 perp.update_is_oracle_used(instant, !e.ignOracle);
813 StateEvents::perpetual(
814 perp,
815 PerpetualEventType::OracleConfigurationUpdated {
816 is_used: perp.is_oracle_used(),
817 feed_id: perp.oracle_feed_id(),
818 },
819 )
820 })
821 .into_iter()
822 .collect(),
823 ExchangeEvents::ImmediateOrCancelExecuted(_) => self
824 .err_ctx(ctx, event)?
825 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::ImmediateOrCancelExecuted))
826 .into_iter()
827 .collect(),
828 ExchangeEvents::IncreasePositionCollateral(e) => chain!(
829 self.position(e.accountId, e.perpId)?.map(|(pos, _)| {
830 pos.update_deposit(instant, cc.from_unsigned(e.positionDepositCNS));
831 StateEvents::position(
832 pos,
833 ctx,
834 PositionEventType::DepositUpdated(pos.deposit()),
835 )
836 }),
837 self.account(e.accountId).map(|acc| {
838 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
839 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
840 }),
841 )
842 .collect(),
843 ExchangeEvents::Initialized(_) => vec![],
844 ExchangeEvents::InitialMarginFractionUpdated(e) => self
845 .perpetual(e.perpId)
846 .map(|perp| {
847 perp.update_initial_margin(
848 instant,
849 perp.leverage_converter()
850 .from_unsigned(e.initMarginFracHdths),
851 );
852 StateEvents::perpetual(
853 perp,
854 PerpetualEventType::InitialMarginFractionUpdated(perp.initial_margin()),
855 )
856 })
857 .into_iter()
858 .collect(),
859 ExchangeEvents::InsolventPositionCannotBeForcedClose(_) => vec![],
860 ExchangeEvents::InsuficientFundsForRecycleFee(_) => self
861 .err_ctx(ctx, event)?
862 .map(|ctx| {
863 StateEvents::order_error(ctx, OrderErrorType::InsuficientFundsForRecycleFee)
864 })
865 .into_iter()
866 .collect(),
867 ExchangeEvents::InsufficientFundsToDecCollateral(_) => vec![],
868 ExchangeEvents::InsurancePaymentForSettlement(_) => vec![],
869 ExchangeEvents::InvalidAccountFrozenOrder(_) => vec![],
870 ExchangeEvents::InvalidBankruptcyPrice(_) => vec![],
871 ExchangeEvents::InvalidExpiryBlock(_) => self
872 .err_ctx(ctx, event)?
873 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::InvalidExpiryBlock))
874 .into_iter()
875 .collect(),
876 ExchangeEvents::InvalidLinkReportForContract(_) => vec![],
877 ExchangeEvents::InvalidLinkReportVersion(_) => vec![],
878 ExchangeEvents::InvalidLiquidationPrice(_) => vec![],
879 ExchangeEvents::InvalidOrderId(_) => self
880 .err_ctx(ctx, event)?
881 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::InvalidOrderId))
882 .into_iter()
883 .collect(),
884 ExchangeEvents::LastForwardedDescIdReset(_) => vec![],
885 ExchangeEvents::LastTriggeredDescIdReset(_) => vec![],
886 ExchangeEvents::LinkDatastreamConfigured(_) => vec![],
887 ExchangeEvents::LinkDsError_0(_) => vec![],
888 ExchangeEvents::LinkDsError_1(_) => vec![],
889 ExchangeEvents::LinkDsPanic(_) => vec![],
890 ExchangeEvents::LinkPriceUpdated(e) => self
891 .perpetual(e.perpId)
892 .map(|perp| {
893 perp.update_oracle_price(
894 instant,
895 perp.price_converter().from_unsigned(e.oraclePricePNS),
896 );
897 StateEvents::perpetual(
898 perp,
899 PerpetualEventType::OraclePriceUpdated(perp.oracle_price()),
900 )
901 })
902 .into_iter()
903 .collect(),
904 ExchangeEvents::LiquidationBuyerUpdated(_) => vec![],
905 ExchangeEvents::LiquidationParamsUpdated(_) => vec![],
906 ExchangeEvents::LotOutOfRange(_) => self
907 .err_ctx(ctx, event)?
908 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::SizeOutOfRange))
909 .into_iter()
910 .collect(),
911 ExchangeEvents::MaintenanceMarginFractionUpdated(e) => self
912 .perpetual(e.perpId)
913 .map(|perp| {
914 perp.update_maintenance_margin(
915 instant,
916 perp.leverage_converter()
917 .from_unsigned(e.maintMarginFracHdths),
918 );
919 StateEvents::perpetual(
920 perp,
921 PerpetualEventType::MaintenanceMarginFractionUpdated(
922 perp.maintenance_margin(),
923 ),
924 )
925 })
926 .into_iter()
927 .collect(),
928 ExchangeEvents::MakerFeeUpdated(e) => self
930 .perpetual(e.perpId)
931 .map(|perp| {
932 perp.update_base_maker_fee(
933 instant,
934 perp.fee_converter().from_unsigned(e.makerFeePer100K),
935 );
936 StateEvents::perpetual(
937 perp,
938 PerpetualEventType::MakerFeeUpdated(perp.maker_fee()),
939 )
940 })
941 .into_iter()
942 .collect(),
943 ExchangeEvents::MakerOrderFilled(e) => self.apply_maker_order_filled(
946 instant,
947 event,
948 ctx,
949 RawMakerFill {
950 perp_id: e.perpId,
951 account_id: e.accountId,
952 order_id: e.orderId,
953 price_pns: e.pricePNS,
954 lot_lns: e.lotLNS,
955 fee_cns: e.feeCNS,
956 builder_fee_cns: U256::ZERO,
957 locked_balance_cns: e.lockedBalanceCNS,
958 balance_cns: e.balanceCNS,
959 },
960 )?,
961 ExchangeEvents::MakerOrderFilledV2(e) => self.apply_maker_order_filled(
962 instant,
963 event,
964 ctx,
965 RawMakerFill {
966 perp_id: e.perpId,
967 account_id: e.accountId,
968 order_id: e.orderId,
969 price_pns: e.pricePNS,
970 lot_lns: e.lotLNS,
971 fee_cns: e.feeCNS,
972 builder_fee_cns: e.builderFeeCNS,
973 locked_balance_cns: e.lockedBalanceCNS,
974 balance_cns: e.balanceCNS,
975 },
976 )?,
977 ExchangeEvents::MakerOrderSettlementFailed(e) => chain!(
978 if let Some(perp) = self.perpetual(e.perpId) {
979 let order_id = std::num::NonZeroU16::new(e.orderId.to::<u16>())
980 .expect("orderId in event cannot be 0");
981 let order = perp.remove_order(order_id)?;
982 chain!(
983 Some(StateEvents::order(perp, &order, ctx, OrderEventType::Removed)),
984 self.err_ctx(ctx, event)?
985 .map(|ctx| StateEvents::affected_order_error(
986 ctx,
987 &order,
988 OrderErrorType::MakerOrderSettlementFailed
989 ))
990 )
991 .collect()
992 } else {
993 vec![]
994 },
995 self.account(e.accountId).map(|acc| {
996 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
997 StateEvents::account(
998 acc,
999 ctx,
1000 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
1001 )
1002 }),
1003 if !e.recyclerAmountCNS.is_zero() {
1004 self.account(e.recyclerAccountId).map(|acc| {
1005 acc.update_balance(instant, cc.from_unsigned(e.recyclerBalanceCNS));
1006 StateEvents::account(
1007 acc,
1008 ctx,
1009 AccountEventType::BalanceUpdated(acc.balance()),
1010 )
1011 })
1012 } else {
1013 None
1014 },
1015 )
1016 .collect(),
1017 ExchangeEvents::MarginTolUpdated(_) => vec![],
1018 ExchangeEvents::MarkExceedsTol(_) => vec![],
1019 ExchangeEvents::MarkPriceAgeExceedsMax(_) => vec![],
1020 ExchangeEvents::MarkUpdated(e) => {
1021 let perp_mark = self.perpetual(e.perpId).map(|perp| {
1022 perp.update_mark_price(
1023 instant,
1024 perp.price_converter().from_unsigned(e.pricePNS),
1025 );
1026 (perp.id(), perp.mark_price())
1027 });
1028 if let Some((perp_id, mark_price)) = perp_mark {
1029 chain!(
1030 Some(StateEvents::Perpetual(PerpetualEvent {
1031 perpetual_id: perp_id,
1032 r#type: PerpetualEventType::MarkPriceUpdated(mark_price),
1033 })),
1034 self.accounts.values_mut().filter_map(|acc| {
1036 acc.positions_mut().get_mut(&perp_id).map(|pos| {
1037 pos.apply_mark_price(instant, mark_price);
1038 StateEvents::position(
1039 pos,
1040 &None,
1041 PositionEventType::UnrealizedPnLUpdated {
1042 pnl: pos.pnl(),
1043 delta_pnl: pos.delta_pnl(),
1044 premium_pnl: pos.premium_pnl(),
1045 },
1046 )
1047 })
1048 }),
1049 )
1050 .collect()
1051 } else {
1052 vec![]
1053 }
1054 },
1055 ExchangeEvents::MaxMatchesReached(_) => self
1056 .err_ctx(ctx, event)?
1057 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::MaxMatchesReached))
1058 .into_iter()
1059 .collect(),
1060 ExchangeEvents::MaxOpenInterestUpdated(_) => vec![],
1061 ExchangeEvents::MaximumAccountOrders(_) => self
1062 .err_ctx(ctx, event)?
1063 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::MaximumAccountOrders))
1064 .into_iter()
1065 .collect(),
1066 ExchangeEvents::MinAccountOpenAmountUpdated(_) => vec![],
1067 ExchangeEvents::MinPostUpdated(e) => {
1068 self.min_post = cc.from_unsigned(e.minPostCNS);
1069 vec![StateEvents::Exchange(ExchangeEvent::MinPostUpdated(self.min_post))]
1070 },
1071 ExchangeEvents::MinSettleUpdated(e) => {
1072 self.min_settle = cc.from_unsigned(e.minSettleCNS);
1073 vec![StateEvents::Exchange(ExchangeEvent::MinSettleUpdated(self.min_settle))]
1074 },
1075 ExchangeEvents::MonitorAdministratorUpdated(_) => vec![],
1076 ExchangeEvents::MonitorPauseAttempted(_) => vec![],
1077 ExchangeEvents::OracleAgeExceedsMax(_) => vec![],
1078 ExchangeEvents::OracleDisabled(_) => vec![],
1079 ExchangeEvents::OrderBatchCompleted(_) => {
1080 ctx.take();
1082 vec![]
1083 },
1084 ExchangeEvents::OrderCancelled(e) => {
1085 let c = must_ctx()?;
1086 let order_id = c.order_id.expect("order_id required for OrderCancelled");
1087 chain!(
1088 if let Some(perp) = self.perpetuals.get_mut(&c.perpetual_id) {
1089 let order = perp.remove_order(order_id)?;
1090 Some(StateEvents::order(perp, &order, ctx, OrderEventType::Removed))
1091 } else {
1092 None
1093 },
1094 if let Some(acc) = self.accounts.get_mut(&c.account_id) {
1095 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
1096 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
1097 vec![
1098 StateEvents::account(
1099 acc,
1100 ctx,
1101 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
1102 ),
1103 StateEvents::account(
1104 acc,
1105 ctx,
1106 AccountEventType::BalanceUpdated(acc.balance()),
1107 ),
1108 ]
1109 } else {
1110 vec![]
1111 },
1112 )
1113 .collect()
1114 },
1115 ExchangeEvents::OrderCancelledByAdmin(e) => chain!(
1116 self.order(e.perpId, e.orderId)?.map(|(perp, order)| {
1117 perp.remove_order(order.order_id()).expect("order exists");
1118 StateEvents::order(perp, &order, ctx, OrderEventType::Removed)
1119 }),
1120 self.account(e.accountId).map(|acc| {
1121 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
1122 StateEvents::account(
1123 acc,
1124 ctx,
1125 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
1126 )
1127 }),
1128 )
1129 .collect(),
1130 ExchangeEvents::OrderCancelledByLiquidator(e) => chain!(
1131 self.order(e.perpId, e.orderId)?.map(|(perp, order)| {
1132 perp.remove_order(order.order_id()).expect("order exists");
1133 StateEvents::order(perp, &order, ctx, OrderEventType::Removed)
1134 }),
1135 self.account(e.accountId).map(|acc| {
1136 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
1137 StateEvents::account(
1138 acc,
1139 ctx,
1140 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
1141 )
1142 }),
1143 )
1144 .collect(),
1145 ExchangeEvents::OrderChanged(e) => {
1146 let c = must_ctx()?;
1147 let order_id = c.order_id.expect("order_id required for OrderChanged");
1148 chain!(
1149 if let Some(perp) = self.perpetuals.get_mut(&c.perpetual_id) {
1150 let order = perp
1151 .get_order(order_id)
1152 .copied()
1153 .ok_or(DexError::OrderNotFound(perp.id(), order_id))?;
1154 let new_price = perp.price_converter().from_unsigned(e.pricePNS);
1155 let new_size = perp.size_converter().from_unsigned(e.lotLNS);
1156 let new_expiry_block = e.expiryBlock.to();
1157 let price_update =
1158 if order.price() != new_price { Some(new_price) } else { None };
1159 let size_update =
1160 if order.size() != new_size { Some(new_size) } else { None };
1161 let expiry_block_update = if order.expiry_block() != new_expiry_block {
1162 Some(new_expiry_block)
1163 } else {
1164 None
1165 };
1166 let updated = order.updated(
1167 instant,
1168 ctx,
1169 price_update,
1170 size_update,
1171 size_update,
1172 expiry_block_update,
1173 );
1174 perp.update_order(updated)?;
1175 Some(StateEvents::order(
1176 perp,
1177 &order,
1178 ctx,
1179 OrderEventType::Updated {
1180 price: price_update,
1181 size: size_update,
1182 expiry_block: expiry_block_update,
1183 },
1184 ))
1185 } else {
1186 None
1187 },
1188 if let Some(acc) = self.accounts.get_mut(&c.account_id) {
1189 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
1190 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
1191 vec![
1192 StateEvents::account(
1193 acc,
1194 ctx,
1195 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
1196 ),
1197 StateEvents::account(
1198 acc,
1199 ctx,
1200 AccountEventType::BalanceUpdated(acc.balance()),
1201 ),
1202 ]
1203 } else {
1204 vec![]
1205 },
1206 )
1207 .collect()
1208 },
1209 ExchangeEvents::OrderDescIdTooLow(_) => vec![],
1210 ExchangeEvents::OrderDoesNotExist(_) => self
1211 .err_ctx(ctx, event)?
1212 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::OrderDoesNotExist))
1213 .into_iter()
1214 .collect(),
1215 ExchangeEvents::OrderExtensionRejected(_) => self
1216 .err_ctx(ctx, event)?
1217 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::OrderExtensionRejected))
1218 .into_iter()
1219 .collect(),
1220 ExchangeEvents::OrderForwardingNotAllowed(_) => vec![],
1221 ExchangeEvents::OrderForwardingUpdated(_) => vec![],
1222 ExchangeEvents::OrderPlaced(e) => {
1223 let c = must_ctx()?;
1224 let order_id = std::num::NonZeroU16::new(e.orderId.to::<u16>())
1225 .expect("orderId in OrderPlaced event cannot be 0");
1226 chain!(
1227 if let Some(perp) = self.perpetuals.get_mut(&c.perpetual_id) {
1228 let order = Order::placed(
1229 instant,
1230 c,
1231 order_id,
1232 perp.size_converter().from_unsigned(e.lotLNS),
1233 perp.price_converter(),
1234 perp.leverage_converter(),
1235 );
1236 let event = OrderEventType::Placed {
1237 r#type: order.r#type(),
1238 price: order.price(),
1239 size: order.size(),
1240 expiry_block: order.expiry_block(),
1241 leverage: order.leverage(),
1242 post_only: order.post_only().unwrap_or_default(),
1243 fill_or_kill: order.fill_or_kill().unwrap_or_default(),
1244 immediate_or_cancel: order.immediate_or_cancel().unwrap_or_default(),
1245 };
1246 perp.add_order(order)?;
1247 Some(StateEvents::order(perp, &order, ctx, event))
1248 } else {
1249 None
1250 },
1251 if let Some(acc) = self.accounts.get_mut(&c.account_id) {
1252 acc.update_locked_balance(instant, cc.from_unsigned(e.lockedBalanceCNS));
1253 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
1254 vec![
1255 StateEvents::account(
1256 acc,
1257 ctx,
1258 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
1259 ),
1260 StateEvents::account(
1261 acc,
1262 ctx,
1263 AccountEventType::BalanceUpdated(acc.balance()),
1264 ),
1265 ]
1266 } else {
1267 vec![]
1268 },
1269 )
1270 .collect()
1271 },
1272 ExchangeEvents::OrderPostFailed(e) => self
1273 .err_ctx(ctx, event)?
1274 .map(|ctx| {
1275 StateEvents::order_error(ctx, OrderErrorType::OrderPostFailed(e.reason.to()))
1276 })
1277 .into_iter()
1278 .collect(),
1279 ExchangeEvents::OrderRequest(e) => {
1282 ctx.replace(OrderContext::from(e));
1285 vec![]
1286 },
1287 ExchangeEvents::OrderRequestV2(e) => {
1288 ctx.replace(OrderContext::from(e));
1289 vec![]
1290 },
1291 ExchangeEvents::OrderSettlementImpliesInsolvent(_) => self
1292 .err_ctx(ctx, event)?
1293 .map(|ctx| {
1294 StateEvents::order_error(ctx, OrderErrorType::OrderSettlementImpliesInsolvent)
1295 })
1296 .into_iter()
1297 .collect(),
1298 ExchangeEvents::OrderSizeExceedsAvailableSize(_) => self
1299 .err_ctx(ctx, event)?
1300 .map(|ctx| {
1301 StateEvents::order_error(ctx, OrderErrorType::OrderSizeExceedsAvailableSize)
1302 })
1303 .into_iter()
1304 .collect(),
1305 ExchangeEvents::OverCollatDescentThreshUpdated(_) => vec![],
1306 ExchangeEvents::OwnershipTransferStarted(_) => vec![],
1307 ExchangeEvents::OwnershipTransferred(_) => vec![],
1308 ExchangeEvents::PermissonedCancelParamsUpdated(_) => vec![],
1309 ExchangeEvents::PerpFeeSchedIdSet(e) => {
1310 let key = FeeScheduleKey::from_raw(e.feeSchedId);
1320 let schedule = self.fee_schedules.get(key);
1321 self.perpetual(e.perpId)
1322 .and_then(|perp| match schedule {
1323 Some(schedule) => {
1324 perp.update_fee_schedule(instant, schedule);
1325 Some(StateEvents::perpetual(
1326 perp,
1327 PerpetualEventType::FeeScheduleUpdated(perp.fee_schedule()),
1328 ))
1329 },
1330 None => {
1331 perp.update_fee_schedule_key(instant, key);
1332 None
1333 },
1334 })
1335 .into_iter()
1336 .collect()
1337 },
1338 ExchangeEvents::PerpPositionBalCreditPositiveSevere(_) => vec![],
1339 ExchangeEvents::PositionAdministratorUpdated(_) => vec![],
1340 ExchangeEvents::PositionClosed(e) => {
1341 if let Some(ctx) = ctx {
1342 ctx.position_closed_at_log_index = Some(event.log_index());
1343 }
1344
1345 if let Some((acc, perp)) = self.account_perpetual(e.accountId, e.perpId) {
1346 let pos = acc
1347 .positions_mut()
1348 .remove(&perp.id())
1349 .ok_or(DexError::PositionNotFound(acc.id(), perp.id()))?;
1350
1351 chain!(
1352 Some(StateEvents::position(
1353 &pos,
1354 ctx,
1355 PositionEventType::Closed {
1356 r#type: pos.r#type(),
1357 entry_price: pos.entry_price(),
1358 exit_price: perp.price_converter().from_unsigned(e.pricePNS),
1359 size: pos.size(),
1360 delta_pnl: cc.from_signed(e.deltaPnlCNS),
1361 premium_pnl: cc.from_signed(e.fundingCNS),
1362 }
1363 )),
1364 if PositionType::from(e.positionType) == PositionType::Long {
1365 perp.update_open_interest(instant, pos.size(), UD64::ZERO);
1366 Some(StateEvents::perpetual(
1367 perp,
1368 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1369 ))
1370 } else {
1371 None
1372 },
1373 )
1374 .collect()
1375 } else {
1376 vec![]
1377 }
1378 },
1379 ExchangeEvents::PositionCollateralDecreased(e) => {
1380 if let Some((pos, perp)) = self.position(e.accountId, e.perpId)? {
1381 let prev_entry_price = pos.entry_price();
1382 pos.update_entry_price(instant, e.endEntryPricePNS, 0, perp.price_converter());
1383 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1384 pos.apply_mark_price(instant, perp.mark_price());
1385 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1386 vec![StateEvents::position(
1387 pos,
1388 ctx,
1389 PositionEventType::CollateralDecreased {
1390 prev_entry_price,
1391 new_entry_price: pos.entry_price(),
1392 deposit: pos.deposit(),
1393 },
1394 )]
1395 } else {
1396 vec![]
1397 }
1398 },
1399 ExchangeEvents::PositionDecreased(e) => {
1400 if let Some((pos, perp)) = self.position(e.accountId, e.perpId)? {
1401 let prev_size = pos.size();
1402 pos.update_size(instant, perp.size_converter().from_unsigned(e.endLotLNS));
1403 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1404 pos.apply_mark_price(instant, perp.mark_price());
1405 pos.update_premium_pnl(
1406 instant,
1407 pos.premium_pnl().sub(cc.from_signed(e.fundingCNS)),
1408 );
1409 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1410 chain!(
1411 Some(StateEvents::position(
1412 pos,
1413 ctx,
1414 PositionEventType::Decreased {
1415 prev_size,
1416 new_size: pos.size(),
1417 deposit: pos.deposit(),
1418 delta_pnl: pos.delta_pnl(),
1419 premium_pnl: pos.premium_pnl(),
1420 }
1421 )),
1422 if pos.r#type() == PositionType::Long {
1423 perp.update_open_interest(instant, prev_size, pos.size());
1424 Some(StateEvents::perpetual(
1425 perp,
1426 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1427 ))
1428 } else {
1429 None
1430 },
1431 )
1432 .collect()
1433 } else {
1434 vec![]
1435 }
1436 },
1437 ExchangeEvents::PositionDeleveraged(e) => chain!(
1438 if let Some((pos, perp)) = self.position(e.accountId, e.perpId)? {
1439 let prev_size = pos.size();
1440 pos.update_size(instant, perp.size_converter().from_unsigned(e.endLotLNS));
1441 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1442 pos.apply_mark_price(instant, perp.mark_price());
1443 pos.update_premium_pnl(
1444 instant,
1445 pos.premium_pnl().sub(cc.from_signed(e.fundingCNS)),
1446 );
1447 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1448 chain!(
1449 Some(StateEvents::position(
1450 pos,
1451 ctx,
1452 PositionEventType::Deleveraged {
1453 force_close: e.forceClose,
1454 r#type: pos.r#type(),
1455 entry_price: pos.entry_price(),
1456 exit_price: perp
1457 .price_converter()
1458 .from_unsigned(e.deleveragePricePNS),
1459 prev_size,
1460 new_size: pos.size(),
1461 deposit: pos.deposit(),
1462 delta_pnl: pos.delta_pnl(),
1463 premium_pnl: pos.premium_pnl(),
1464 }
1465 )),
1466 if pos.r#type() == PositionType::Long {
1467 perp.update_open_interest(instant, prev_size, pos.size());
1468 Some(StateEvents::perpetual(
1469 perp,
1470 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1471 ))
1472 } else {
1473 None
1474 },
1475 )
1476 .collect()
1477 } else {
1478 vec![]
1479 },
1480 self.account(e.accountId).map(|acc| {
1481 if e.endLotLNS == U256::ZERO {
1482 acc.positions_mut()
1483 .remove(&e.perpId.to::<types::PerpetualId>());
1484 }
1485 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
1486 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
1487 }),
1488 )
1489 .collect(),
1490 ExchangeEvents::PositionDeleveragedV2(e) => chain!(
1491 if let Some((pos, perp)) = self.position(e.accountId, e.perpId)? {
1492 let prev_size = pos.size();
1493 pos.update_size(instant, perp.size_converter().from_unsigned(e.endLotLNS));
1494 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1495 pos.apply_mark_price(instant, perp.mark_price());
1496 pos.update_premium_pnl(
1497 instant,
1498 pos.premium_pnl().sub(cc.from_signed(e.fundingCNS)),
1499 );
1500 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1501 chain!(
1502 Some(StateEvents::position(
1503 pos,
1504 ctx,
1505 PositionEventType::Deleveraged {
1506 force_close: e.forceClose,
1507 r#type: pos.r#type(),
1508 entry_price: pos.entry_price(),
1509 exit_price: perp
1510 .price_converter()
1511 .from_unsigned(e.deleveragePricePNS),
1512 prev_size,
1513 new_size: pos.size(),
1514 deposit: pos.deposit(),
1515 delta_pnl: pos.delta_pnl(),
1516 premium_pnl: pos.premium_pnl(),
1517 }
1518 )),
1519 if pos.r#type() == PositionType::Long {
1520 perp.update_open_interest(instant, prev_size, pos.size());
1521 Some(StateEvents::perpetual(
1522 perp,
1523 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1524 ))
1525 } else {
1526 None
1527 },
1528 )
1529 .collect()
1530 } else {
1531 vec![]
1532 },
1533 self.account(e.accountId).map(|acc| {
1534 if e.endLotLNS == U256::ZERO {
1535 acc.positions_mut()
1536 .remove(&e.perpId.to::<types::PerpetualId>());
1537 }
1538 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
1539 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
1540 }),
1541 )
1542 .collect(),
1543 ExchangeEvents::PositionDoesNotExist(_) => vec![],
1544 ExchangeEvents::PositionIncreased(e) => {
1545 if let Some((pos, perp)) = self.position(e.accountId, e.perpId)? {
1546 let prev_size = pos.size();
1547 pos.update_entry_price(instant, e.pricePNS, 0, perp.price_converter());
1548 pos.update_size(instant, perp.size_converter().from_unsigned(e.endLotLNS));
1549 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1550 pos.apply_mark_price(instant, perp.mark_price());
1551 pos.update_premium_pnl(instant, D256::ZERO);
1552 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1553
1554 chain!(
1555 Some(StateEvents::position(
1556 pos,
1557 ctx,
1558 PositionEventType::Increased {
1559 entry_price: pos.entry_price(),
1560 prev_size,
1561 new_size: pos.size(),
1562 deposit: pos.deposit(),
1563 }
1564 )),
1565 if pos.r#type() == PositionType::Long {
1566 perp.update_open_interest(instant, prev_size, pos.size());
1567 Some(StateEvents::perpetual(
1568 perp,
1569 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1570 ))
1571 } else {
1572 None
1573 },
1574 )
1575 .collect()
1576 } else {
1577 vec![]
1578 }
1579 },
1580 ExchangeEvents::PositionIncreasedV2(e) => {
1581 if let Some((pos, perp)) = self.position(e.accountId, e.perpId)? {
1582 let prev_size = pos.size();
1583 pos.update_entry_price(
1584 instant,
1585 e.pricePNS,
1586 e.priceResiduePNSQ16.to(),
1587 perp.price_converter(),
1588 );
1589 pos.update_size(instant, perp.size_converter().from_unsigned(e.endLotLNS));
1590 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1591 pos.apply_mark_price(instant, perp.mark_price());
1592 pos.update_premium_pnl(instant, D256::ZERO);
1593 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1594
1595 chain!(
1596 Some(StateEvents::position(
1597 pos,
1598 ctx,
1599 PositionEventType::Increased {
1600 entry_price: pos.entry_price(),
1601 prev_size,
1602 new_size: pos.size(),
1603 deposit: pos.deposit(),
1604 }
1605 )),
1606 if pos.r#type() == PositionType::Long {
1607 perp.update_open_interest(instant, prev_size, pos.size());
1608 Some(StateEvents::perpetual(
1609 perp,
1610 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1611 ))
1612 } else {
1613 None
1614 },
1615 )
1616 .collect()
1617 } else {
1618 vec![]
1619 }
1620 },
1621 ExchangeEvents::PositionInverted(e) => {
1622 if let Some((pos, perp)) = self.position(e.accountId, e.perpId)? {
1623 let prev_type = pos.r#type();
1624 let prev_entry_price = pos.entry_price();
1625 let prev_size = pos.size();
1626 pos.update_type(instant, PositionType::from(e.positionType));
1627 pos.update_entry_price(instant, e.pricePNS, 0, perp.price_converter());
1628 pos.update_size(instant, perp.size_converter().from_unsigned(e.endLotLNS));
1629 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1630 pos.apply_mark_price(instant, perp.mark_price());
1631 pos.update_premium_pnl(instant, D256::ZERO);
1632 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1633 if pos.r#type() == PositionType::Long {
1634 perp.update_open_interest(instant, UD64::ZERO, pos.size());
1635 } else {
1636 perp.update_open_interest(instant, prev_size, UD64::ZERO);
1637 }
1638 vec![
1639 StateEvents::position(
1640 pos,
1641 ctx,
1642 PositionEventType::Closed {
1643 r#type: prev_type,
1644 entry_price: prev_entry_price,
1645 exit_price: pos.entry_price(),
1646 size: prev_size,
1647 delta_pnl: cc.from_signed(e.deltaPnlCNS),
1648 premium_pnl: cc.from_signed(e.fundingCNS),
1649 },
1650 ),
1651 StateEvents::position(
1652 pos,
1653 ctx,
1654 PositionEventType::Inverted {
1655 r#type: pos.r#type(),
1656 entry_price: pos.entry_price(),
1657 prev_size,
1658 new_size: pos.size(),
1659 deposit: pos.deposit(),
1660 delta_pnl: pos.delta_pnl(),
1661 premium_pnl: pos.premium_pnl(),
1662 },
1663 ),
1664 StateEvents::perpetual(
1665 perp,
1666 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1667 ),
1668 ]
1669 } else {
1670 vec![]
1671 }
1672 },
1673 ExchangeEvents::PositionLiquidated(e) => chain!(
1674 if let Some((pos, perp)) = self.position(e.posAccountId, e.perpId)? {
1675 let prev_size = pos.size();
1676 pos.update_size(instant, perp.size_converter().from_unsigned(e.posLotLNS));
1677 pos.update_deposit(instant, cc.from_unsigned(e.posDepositCNS));
1678 pos.apply_mark_price(instant, perp.mark_price());
1679 pos.update_premium_pnl(
1680 instant,
1681 pos.premium_pnl().sub(cc.from_signed(e.fundingCNS)),
1682 );
1683 pos.apply_maintenance_margin(instant, perp.maintenance_margin());
1684 chain!(
1685 Some(StateEvents::position(
1686 pos,
1687 ctx,
1688 PositionEventType::Liquidated {
1689 r#type: pos.r#type(),
1690 entry_price: pos.entry_price(),
1691 exit_price: perp.price_converter().from_unsigned(e.liqPricePNS),
1692 prev_size,
1693 liquidated_size: perp.size_converter().from_unsigned(e.liqLotLNS),
1694 new_size: pos.size(),
1695 deposit: pos.deposit(),
1696 delta_pnl: pos.delta_pnl(),
1697 premium_pnl: pos.premium_pnl(),
1698 }
1699 )),
1700 if pos.r#type() == PositionType::Long {
1701 perp.update_open_interest(instant, prev_size, pos.size());
1702 Some(StateEvents::perpetual(
1703 perp,
1704 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1705 ))
1706 } else {
1707 None
1708 },
1709 )
1710 .collect()
1711 } else {
1712 vec![]
1713 },
1714 self.account(e.posAccountId).map(|acc| {
1715 if e.posLotLNS == U256::ZERO {
1716 acc.positions_mut()
1717 .remove(&e.perpId.to::<types::PerpetualId>());
1718 }
1719 acc.update_balance(instant, cc.from_unsigned(e.accBalanceCNS));
1720 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
1721 }),
1722 )
1723 .collect(),
1724 ExchangeEvents::PositionLiquidationCredit(e) => self
1725 .position(e.accountId, e.perpId)?
1726 .map(|(pos, _)| {
1727 pos.update_deposit(instant, cc.from_unsigned(e.endDepositCNS));
1728 StateEvents::position(
1729 pos,
1730 ctx,
1731 PositionEventType::DepositUpdated(pos.deposit()),
1732 )
1733 })
1734 .into_iter()
1735 .collect(),
1736 ExchangeEvents::PositionOpened(e) => {
1737 if let Some((acc, perp)) = self.account_perpetual(e.accountId, e.perpId) {
1738 let pos = Position::opened(
1739 instant,
1740 perp.id(),
1741 acc.id(),
1742 PositionType::from(e.positionType),
1743 e.pricePNS,
1744 0,
1745 perp.price_converter(),
1746 perp.size_converter().from_unsigned(e.lotLNS),
1747 cc.from_unsigned(e.depositCNS),
1748 perp.maintenance_margin(),
1749 );
1750 let events = chain!(
1751 Some(StateEvents::position(
1752 &pos,
1753 ctx,
1754 PositionEventType::Opened {
1755 r#type: pos.r#type(),
1756 entry_price: pos.entry_price(),
1757 size: pos.size(),
1758 deposit: pos.deposit(),
1759 }
1760 )),
1761 if pos.r#type() == PositionType::Long {
1762 perp.update_open_interest(instant, UD64::ZERO, pos.size());
1763 Some(StateEvents::perpetual(
1764 perp,
1765 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1766 ))
1767 } else {
1768 None
1769 },
1770 )
1771 .collect();
1772 acc.positions_mut().insert(perp.id(), pos);
1773 events
1774 } else {
1775 vec![]
1776 }
1777 },
1778 ExchangeEvents::PositionOpenedV2(e) => {
1779 if let Some((acc, perp)) = self.account_perpetual(e.accountId, e.perpId) {
1780 let pos = Position::opened(
1781 instant,
1782 perp.id(),
1783 acc.id(),
1784 PositionType::from(e.positionType),
1785 e.pricePNS,
1786 e.priceResiduePNSQ16.to(),
1787 perp.price_converter(),
1788 perp.size_converter().from_unsigned(e.lotLNS),
1789 cc.from_unsigned(e.depositCNS),
1790 perp.maintenance_margin(),
1791 );
1792 let events = chain!(
1793 Some(StateEvents::position(
1794 &pos,
1795 ctx,
1796 PositionEventType::Opened {
1797 r#type: pos.r#type(),
1798 entry_price: pos.entry_price(),
1799 size: pos.size(),
1800 deposit: pos.deposit(),
1801 }
1802 )),
1803 if pos.r#type() == PositionType::Long {
1804 perp.update_open_interest(instant, UD64::ZERO, pos.size());
1805 Some(StateEvents::perpetual(
1806 perp,
1807 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1808 ))
1809 } else {
1810 None
1811 },
1812 )
1813 .collect();
1814 acc.positions_mut().insert(perp.id(), pos);
1815 events
1816 } else {
1817 vec![]
1818 }
1819 },
1820 ExchangeEvents::PositionUnwound(e) => {
1821 if let Some((acc, perp)) = self.account_perpetual(e.accountId, e.perpId) {
1822 let pos = acc
1823 .positions_mut()
1824 .remove(&perp.id())
1825 .ok_or(DexError::PositionNotFound(acc.id(), perp.id()))?;
1826 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
1827 chain!(
1828 Some(StateEvents::position(
1829 &pos,
1830 ctx,
1831 PositionEventType::Unwound {
1832 r#type: pos.r#type(),
1833 entry_price: pos.entry_price(),
1834 exit_price: perp.price_converter().from_unsigned(e.pricePNS),
1835 size: pos.size(),
1836 fair_market_value: cc.from_signed(e.positionFmvCNS),
1837 payment: cc.from_unsigned(e.paymentCNS),
1838 }
1839 )),
1840 Some(StateEvents::account(
1841 acc,
1842 ctx,
1843 AccountEventType::BalanceUpdated(acc.balance())
1844 )),
1845 if pos.r#type() == PositionType::Long {
1846 perp.update_open_interest(instant, pos.size(), UD64::ZERO);
1847 Some(StateEvents::perpetual(
1848 perp,
1849 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1850 ))
1851 } else {
1852 None
1853 },
1854 )
1855 .collect()
1856 } else {
1857 vec![]
1858 }
1859 },
1860 ExchangeEvents::PositionUnwoundV2(e) => {
1861 if let Some((acc, perp)) = self.account_perpetual(e.accountId, e.perpId) {
1863 let pos = acc
1864 .positions_mut()
1865 .remove(&perp.id())
1866 .ok_or(DexError::PositionNotFound(acc.id(), perp.id()))?;
1867 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
1868 chain!(
1869 Some(StateEvents::position(
1870 &pos,
1871 ctx,
1872 PositionEventType::Unwound {
1873 r#type: pos.r#type(),
1874 entry_price: pos.entry_price(),
1875 exit_price: perp.price_converter().from_unsigned(e.pricePNS),
1876 size: pos.size(),
1877 fair_market_value: cc.from_signed(e.positionFmvCNS),
1878 payment: cc.from_unsigned(e.paymentCNS),
1879 }
1880 )),
1881 Some(StateEvents::account(
1882 acc,
1883 ctx,
1884 AccountEventType::BalanceUpdated(acc.balance())
1885 )),
1886 if pos.r#type() == PositionType::Long {
1887 perp.update_open_interest(instant, pos.size(), UD64::ZERO);
1888 Some(StateEvents::perpetual(
1889 perp,
1890 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1891 ))
1892 } else {
1893 None
1894 },
1895 )
1896 .collect()
1897 } else {
1898 vec![]
1899 }
1900 },
1901 ExchangeEvents::PositionUnwoundWithoutPayment(e) => {
1902 if let Some((acc, perp)) = self.account_perpetual(e.accountId, e.perpId) {
1903 let pos = acc
1904 .positions_mut()
1905 .remove(&perp.id())
1906 .ok_or(DexError::PositionNotFound(acc.id(), perp.id()))?;
1907 chain!(
1908 Some(StateEvents::position(
1909 &pos,
1910 ctx,
1911 PositionEventType::Unwound {
1912 r#type: pos.r#type(),
1913 entry_price: pos.entry_price(),
1914 exit_price: perp.price_converter().from_unsigned(e.pricePNS),
1915 size: pos.size(),
1916 fair_market_value: cc.from_signed(e.positionFmvCNS),
1917 payment: UD128::ZERO,
1918 }
1919 )),
1920 if pos.r#type() == PositionType::Long {
1921 perp.update_open_interest(instant, pos.size(), UD64::ZERO);
1922 Some(StateEvents::perpetual(
1923 perp,
1924 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1925 ))
1926 } else {
1927 None
1928 },
1929 )
1930 .collect()
1931 } else {
1932 vec![]
1933 }
1934 },
1935 ExchangeEvents::PositionUnwoundWithoutPaymentV2(e) => {
1936 if let Some((acc, perp)) = self.account_perpetual(e.accountId, e.perpId) {
1938 let pos = acc
1939 .positions_mut()
1940 .remove(&perp.id())
1941 .ok_or(DexError::PositionNotFound(acc.id(), perp.id()))?;
1942 chain!(
1943 Some(StateEvents::position(
1944 &pos,
1945 ctx,
1946 PositionEventType::Unwound {
1947 r#type: pos.r#type(),
1948 entry_price: pos.entry_price(),
1949 exit_price: perp.price_converter().from_unsigned(e.pricePNS),
1950 size: pos.size(),
1951 fair_market_value: cc.from_signed(e.positionFmvCNS),
1952 payment: UD128::ZERO,
1953 }
1954 )),
1955 if pos.r#type() == PositionType::Long {
1956 perp.update_open_interest(instant, pos.size(), UD64::ZERO);
1957 Some(StateEvents::perpetual(
1958 perp,
1959 PerpetualEventType::OpenInterestUpdated(perp.open_interest()),
1960 ))
1961 } else {
1962 None
1963 },
1964 )
1965 .collect()
1966 } else {
1967 vec![]
1968 }
1969 },
1970 ExchangeEvents::PostOrderUnderMinimum(_) => self
1971 .err_ctx(ctx, event)?
1972 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::PostOrderUnderMinimum))
1973 .into_iter()
1974 .collect(),
1975 ExchangeEvents::PriceAdministratorUpdated(_) => vec![],
1976 ExchangeEvents::PriceMaxAgeUpdated(e) => {
1977 if let Some(perp) = self.perpetual(e.perpId) {
1978 perp.update_price_max_age_sec(instant, e.maxAgeSec.to());
1979 }
1980 vec![]
1981 },
1982 ExchangeEvents::PriceOutOfRange(_) => self
1983 .err_ctx(ctx, event)
1984 .ok() .flatten()
1986 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::PriceOutOfRange))
1987 .into_iter()
1988 .collect(),
1989 ExchangeEvents::PriceSetDuringTriggerExec(_) => vec![],
1990 ExchangeEvents::PriceTolUpdated(_) => vec![],
1991 ExchangeEvents::ProtocolBalanceDeposit(_) => vec![],
1992 ExchangeEvents::ProtocolBalanceWithdraw(_) => vec![],
1993 ExchangeEvents::RecycleBalanceInsufficientSevere(_) => vec![],
1994 ExchangeEvents::RecycleFeeToAccount(_) => vec![],
1995 ExchangeEvents::RecycleFeeToProtocol(_) => vec![],
1996 ExchangeEvents::RecycleFeeUpdated(e) => {
1997 self.recycle_fee = cc.from_unsigned(e.recycleFeeCNS);
1998 vec![StateEvents::Exchange(ExchangeEvent::RecycleFeeUpdated(self.recycle_fee()))]
1999 },
2000 ExchangeEvents::ReportAgeExceedsLastUpdate(_) => vec![],
2001 ExchangeEvents::ReportExpiresTooSoon(_) => vec![],
2002 ExchangeEvents::ReportFromFuture(_) => vec![],
2003 ExchangeEvents::ReportPriceIsNegative(_) => vec![],
2004 ExchangeEvents::ResidueBalanceInsufficient(_) => vec![],
2005 ExchangeEvents::ResidueTransferred(_) => vec![],
2006 ExchangeEvents::TakerFeeUpdated(e) => self
2008 .perpetual(e.perpId)
2009 .map(|perp| {
2010 perp.update_base_taker_fee(
2011 instant,
2012 perp.fee_converter().from_unsigned(e.takerFeePer100K),
2013 );
2014 StateEvents::perpetual(
2015 perp,
2016 PerpetualEventType::TakerFeeUpdated(perp.taker_fee()),
2017 )
2018 })
2019 .into_iter()
2020 .collect(),
2021 ExchangeEvents::TakerOrderFilled(e) => self.apply_taker_order_filled(
2024 instant,
2025 event,
2026 ctx,
2027 RawTakerFill {
2028 collat_price_pns: e.collatPricePNS,
2029 lot_lns: e.lotLNS,
2030 fee_cns: e.feeCNS,
2031 builder_fee_cns: U256::ZERO,
2032 balance_cns: e.balanceCNS,
2033 },
2034 )?,
2035 ExchangeEvents::TakerOrderFilledV2(e) => self.apply_taker_order_filled(
2036 instant,
2037 event,
2038 ctx,
2039 RawTakerFill {
2040 collat_price_pns: e.collatPricePNS,
2041 lot_lns: e.lotLNS,
2042 fee_cns: e.feeCNS,
2043 builder_fee_cns: e.builderFeeCNS,
2044 balance_cns: e.balanceCNS,
2045 },
2046 )?,
2047 ExchangeEvents::ToleranceAdministratorUpdated(_) => vec![],
2048 ExchangeEvents::TransferAccountToProtocol(e) => self
2049 .account(e.accountId)
2050 .map(|acc| {
2051 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
2052 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
2053 })
2054 .into_iter()
2055 .collect(),
2056 ExchangeEvents::TransferPerpInsToProtocol(_) => vec![],
2057 ExchangeEvents::TransferPerpPosToProtocol(_) => vec![],
2058 ExchangeEvents::TransferProtocolToAccount(e) => self
2059 .account(e.accountId)
2060 .map(|acc| {
2061 acc.update_balance(instant, cc.from_unsigned(e.balanceCNS));
2062 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
2063 })
2064 .into_iter()
2065 .collect(),
2066 ExchangeEvents::TransferProtocolToPerp(_) => vec![],
2067 ExchangeEvents::TransferProtocolToRecycleBal(_) => vec![],
2068 ExchangeEvents::TriggerDescIdTooLow(_) => vec![],
2069 ExchangeEvents::TriggerOrderExecution(_) => vec![],
2070 ExchangeEvents::TriggerOrderRequest(_) => vec![],
2071 ExchangeEvents::UnableToCancelOrder(_) => vec![],
2072 ExchangeEvents::UnityDescentThreshUpdated(_) => vec![],
2073 ExchangeEvents::UnspecifiedCollateral(_) => vec![],
2074 ExchangeEvents::UnwindCompleted(_) => vec![],
2075 ExchangeEvents::UnwindContractTrigger(_) => vec![],
2076 ExchangeEvents::UnwindInitializationCleared(_) => vec![],
2077 ExchangeEvents::UnwindInitialized(_) => vec![],
2078 ExchangeEvents::UnwindInsufficientBalance(_) => vec![],
2079 ExchangeEvents::UnwindIterationCompleted(_) => vec![],
2080 ExchangeEvents::UnwindPrepared(_) => vec![],
2081 ExchangeEvents::UnwindPreparationCleared(_) => vec![],
2082 ExchangeEvents::UnwindProcessInProgress(_) => vec![],
2083 ExchangeEvents::UpdateOracleFailed(_) => vec![],
2084 ExchangeEvents::Upgraded(_) => vec![],
2085 ExchangeEvents::ValueExceedsMaximum(_) => self
2086 .err_ctx(ctx, event)?
2087 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::ValueExceedsMaximum))
2088 .into_iter()
2089 .collect(),
2090 ExchangeEvents::WhitelistAddress(_) => vec![],
2091 ExchangeEvents::WhitelistingEnabledChanged(_) => vec![],
2092 ExchangeEvents::WithdrawRateLimitBypassSet(_) => vec![],
2093 ExchangeEvents::WithdrawRateLimitForceReset(_) => vec![],
2094 ExchangeEvents::WRLSMinWithdrawLimitUpdated(_) => vec![],
2095 ExchangeEvents::WRLSThousandthsTvlUpdated(_) => vec![],
2096 ExchangeEvents::WithdrawRateLimitReset(_) => vec![],
2097 ExchangeEvents::WrongAccountForOrder(_) => self
2098 .err_ctx(ctx, event)?
2099 .map(|ctx| StateEvents::order_error(ctx, OrderErrorType::WrongAccountForOrder))
2100 .into_iter()
2101 .collect(),
2102 })
2103 }
2104
2105 fn add_perpetual(
2108 &mut self,
2109 instant: types::StateInstant,
2110 name: &str,
2111 symbol: &str,
2112 listing: RawContractAdded,
2113 fee_schedule: FeeSchedule,
2114 ) -> StateEvents {
2115 let perp = Perpetual::added(
2116 instant,
2117 listing.perp_id.to(),
2118 name.to_string(),
2119 symbol.to_string(),
2120 listing.status == 0, listing.price_decimals.to(),
2122 listing.lot_decimals.to(),
2123 listing.base_price_pns,
2124 fee_schedule,
2125 listing.init_margin_frac_hdths,
2126 listing.maint_margin_frac_hdths,
2127 );
2128 let event = StateEvents::perpetual(&perp, PerpetualEventType::Added);
2129 self.perpetuals.insert(perp.id(), perp);
2130 event
2131 }
2132
2133 fn apply_maker_order_filled(
2136 &mut self,
2137 instant: types::StateInstant,
2138 event: &stream::RawEvent,
2139 ctx: &mut Option<OrderContext>,
2140 fill: RawMakerFill,
2141 ) -> Result<Vec<StateEvents>, DexError> {
2142 let cc = self.collateral_converter;
2143 Ok(chain!(
2144 if let Some((perp, order)) = self.order(fill.perp_id, fill.order_id)? {
2145 let fill_price = perp.price_converter().from_unsigned(fill.price_pns);
2146 let fill_size = perp.size_converter().from_unsigned(fill.lot_lns);
2147 let fee = cc.from_unsigned(fill.fee_cns);
2148 let builder_fee = cc.from_unsigned(fill.builder_fee_cns);
2149 perp.update_last_price(instant, fill_price);
2150 let clearing_remaining_order = if let Some(ctx) = ctx {
2151 ctx.maker_fills.push(types::MakerFill {
2152 log_index: event.log_index(),
2153 maker_account_id: order.account_id(),
2154 maker_order_id: order.order_id(),
2155 maker_client_order_id: order.client_order_id(),
2156 price: fill_price,
2157 size: fill_size,
2158 fee,
2159 builder: order.builder(),
2160 builder_fee,
2161 });
2162 let position_closed_by_smart_contract = if event.log_index() > 0 {
2163 match order.r#type() {
2167 OrderType::CloseLong | OrderType::CloseShort => {
2168 Some(event.log_index() - 1) == ctx.position_closed_at_log_index
2169 || Some(event.log_index() - 2)
2170 == ctx.position_closed_at_log_index
2171 },
2172 _ => false,
2173 }
2174 } else {
2175 false
2176 };
2177 ctx.clearing_remaining_order | position_closed_by_smart_contract
2178 } else {
2179 false
2180 };
2181 vec![
2182 if order.size() > fill_size && !clearing_remaining_order {
2183 let new_size = order.size() - fill_size;
2184 perp.update_order(order.updated(
2185 instant,
2186 ctx,
2187 None,
2188 Some(new_size),
2189 None,
2190 None,
2191 ))
2192 .expect("order exists");
2193 StateEvents::order(
2194 perp,
2195 &order,
2196 ctx,
2197 OrderEventType::Updated {
2198 price: None,
2199 size: Some(new_size),
2200 expiry_block: None,
2201 },
2202 )
2203 } else {
2204 perp.remove_order(order.order_id()).expect("order exists");
2205 StateEvents::order(perp, &order, ctx, OrderEventType::Removed)
2206 },
2207 StateEvents::order(
2208 perp,
2209 &order,
2210 ctx,
2211 OrderEventType::Filled {
2212 fill_price,
2213 fill_size,
2214 fee,
2215 builder_fee,
2216 is_maker: true,
2217 },
2218 ),
2219 StateEvents::perpetual(
2220 perp,
2221 PerpetualEventType::LastPriceUpdated(perp.last_price()),
2222 ),
2223 ]
2224 } else {
2225 vec![]
2226 },
2227 self.account(fill.account_id).map(|acc| {
2228 acc.update_locked_balance(instant, cc.from_unsigned(fill.locked_balance_cns));
2229 StateEvents::account(
2230 acc,
2231 ctx,
2232 AccountEventType::LockedBalanceUpdated(acc.locked_balance()),
2233 )
2234 }),
2235 self.account(fill.account_id).map(|acc| {
2236 acc.update_balance(instant, cc.from_unsigned(fill.balance_cns));
2237 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
2238 }),
2239 )
2240 .collect())
2241 }
2242
2243 fn apply_taker_order_filled(
2246 &mut self,
2247 instant: types::StateInstant,
2248 event: &stream::RawEvent,
2249 ctx: &mut Option<OrderContext>,
2250 fill: RawTakerFill,
2251 ) -> Result<Vec<StateEvents>, DexError> {
2252 let cc = self.collateral_converter;
2253 let c = ctx
2254 .as_ref()
2255 .ok_or(DexError::OrderContextExpected(event.tx_index(), event.log_index()))?;
2256 let taker_fee = cc.from_unsigned(fill.fee_cns);
2257 let taker_builder_fee = cc.from_unsigned(fill.builder_fee_cns);
2258 Ok(chain!(
2259 self.perpetuals
2260 .get(&c.perpetual_id)
2261 .map(|perp| StateEvents::Order(OrderEvent {
2262 perpetual_id: perp.id(),
2263 account_id: c.account_id,
2264 request_id: Some(c.request_id),
2265 client_order_id: Some(c.request_id),
2266 order_id: None,
2267 builder: c.builder,
2268 r#type: OrderEventType::Filled {
2269 fill_price: perp.price_converter().from_unsigned(fill.collat_price_pns),
2270 fill_size: perp.size_converter().from_unsigned(fill.lot_lns),
2271 fee: taker_fee,
2272 builder_fee: taker_builder_fee,
2273 is_maker: false,
2274 },
2275 })),
2276 self.accounts.get_mut(&c.account_id).map(|acc| {
2277 acc.update_balance(instant, cc.from_unsigned(fill.balance_cns));
2278 StateEvents::account(acc, ctx, AccountEventType::BalanceUpdated(acc.balance()))
2279 }),
2280 iter::once(StateEvents::trade(c, taker_fee, taker_builder_fee)),
2281 )
2282 .collect())
2283 }
2284
2285 fn update_fee_schedule(
2290 &mut self,
2291 instant: types::StateInstant,
2292 schedule: FeeSchedule,
2293 ) -> Vec<StateEvents> {
2294 self.fee_schedules.set(schedule);
2295 chain!(
2296 iter::once(StateEvents::Exchange(ExchangeEvent::FeeScheduleUpdated(schedule))),
2297 self.perpetuals
2298 .values_mut()
2299 .filter(|perp| perp.fee_schedule().key() == schedule.key())
2300 .map(|perp| {
2301 perp.update_fee_schedule(instant, schedule);
2302 StateEvents::perpetual(
2303 perp,
2304 PerpetualEventType::FeeScheduleUpdated(perp.fee_schedule()),
2305 )
2306 }),
2307 )
2308 .collect()
2309 }
2310
2311 fn apply_state_event(
2312 &mut self,
2313 instant: types::StateInstant,
2314 event: &StateEvents,
2315 ) -> Result<Vec<StateEvents>, DexError> {
2316 Ok(match event {
2317 StateEvents::Perpetual(pe) => {
2318 match pe.r#type {
2319 PerpetualEventType::MaintenanceMarginFractionUpdated(maintenance_margin) => {
2320 self.accounts
2322 .values_mut()
2323 .filter_map(|acc| {
2324 acc.positions_mut().get_mut(&pe.perpetual_id).map(|pos| {
2325 pos.apply_maintenance_margin(instant, maintenance_margin);
2326 StateEvents::position(
2327 pos,
2328 &None,
2329 PositionEventType::MaintenanceMarginUpdated(
2330 pos.maintenance_margin_requirement(),
2331 ),
2332 )
2333 })
2334 })
2335 .collect()
2336 },
2337 _ => vec![],
2338 }
2339 },
2340 _ => vec![],
2341 })
2342 }
2343
2344 fn err_ctx<'c>(
2345 &self,
2346 ctx: &'c mut Option<OrderContext>,
2347 event: &stream::RawEvent,
2348 ) -> Result<Option<&'c OrderContext>, DexError> {
2349 let c = ctx
2350 .as_ref()
2351 .ok_or(DexError::OrderContextExpected(event.tx_index(), event.log_index()))?;
2352 Ok(self.accounts.contains_key(&c.account_id).then_some(c))
2353 }
2354
2355 fn ensure_account(&mut self, id: U256) {
2356 let id = id.to::<types::AccountId>();
2357 if self.track_all_accounts && !self.accounts.contains_key(&id) {
2358 self.accounts
2359 .insert(id, Account::untracked(types::StateInstant::default(), id));
2360 }
2361 }
2362
2363 fn account(&mut self, id: U256) -> Option<&mut Account> {
2364 self.ensure_account(id);
2365 self.accounts.get_mut(&id.to::<types::AccountId>())
2366 }
2367
2368 fn order(
2369 &mut self,
2370 perp_id: U256,
2371 ord_id: U256,
2372 ) -> Result<Option<(&mut Perpetual, Order)>, DexError> {
2373 let ord_id = std::num::NonZeroU16::new(ord_id.to::<u16>())
2374 .expect("ord_id in order lookup cannot be 0");
2375 Ok(if let Some(perp) = self.perpetuals.get_mut(&perp_id.to::<types::PerpetualId>()) {
2376 let ord = perp
2377 .get_order(ord_id)
2378 .copied()
2379 .ok_or(DexError::OrderNotFound(perp.id(), ord_id))?;
2380 Some((perp, ord))
2381 } else {
2382 None
2383 })
2384 }
2385
2386 fn perpetual(&mut self, id: U256) -> Option<&mut Perpetual> {
2387 self.perpetuals.get_mut(&id.to::<types::PerpetualId>())
2388 }
2389
2390 fn account_perpetual(
2391 &mut self,
2392 acc_id: U256,
2393 perp_id: U256,
2394 ) -> Option<(&mut Account, &mut Perpetual)> {
2395 self.ensure_account(acc_id);
2396 self.accounts
2397 .get_mut(&acc_id.to::<types::AccountId>())
2398 .zip(self.perpetuals.get_mut(&perp_id.to::<types::PerpetualId>()))
2399 }
2400
2401 fn position(
2402 &mut self,
2403 acc_id: U256,
2404 perp_id: U256,
2405 ) -> Result<Option<(&mut Position, &mut Perpetual)>, DexError> {
2406 self.ensure_account(acc_id);
2407 let acc_id = acc_id.to::<types::AccountId>();
2408 let perp_id = perp_id.to::<types::PerpetualId>();
2409 Ok(
2410 if let Some(acc) = self.accounts.get_mut(&acc_id)
2411 && let Some(perp) = self.perpetuals.get_mut(&perp_id)
2412 {
2413 let pos = acc
2414 .positions_mut()
2415 .get_mut(&perp_id)
2416 .ok_or(DexError::PositionNotFound(acc_id, perp_id))?;
2417 Some((pos, perp))
2418 } else {
2419 None
2420 },
2421 )
2422 }
2423}
2424
2425#[cfg(feature = "display")]
2426impl std::fmt::Display for Exchange {
2427 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2428 use colored::Colorize;
2429 use tabled::{Table, settings::Style};
2430
2431 writeln!(
2432 f,
2433 "{}",
2434 format!(
2435 "{} | {}{} | {} (sdk {}) | chain {}",
2436 self.instant,
2437 if self.is_halted { "[HALTED] ".bold().bright_red() } else { Default::default() },
2438 self.chain.exchange(),
2439 self.features,
2440 Self::revision(),
2441 self.chain.chain_id(),
2442 )
2443 .bold()
2444 .purple(),
2445 )?;
2446
2447 let mut params = Table::from_iter(vec![vec![
2448 format!("Min Post: {}", self.min_post),
2449 format!("Min Settle: {}", self.min_settle),
2450 format!("Recycle Fee: {}", self.recycle_fee),
2451 format!("Funding Interval: {}", self.funding_interval_blocks),
2452 ]]);
2453 params.with(Style::modern());
2454 writeln!(f, "{params}")?;
2455
2456 if f.alternate() && self.features.keyed_fee_schedules() {
2464 let mut fees = Table::from_iter(chain!(
2465 iter::once(
2466 chain!(
2467 iter::once("Fees (tkr/mkr)".to_string()),
2468 (0..FEE_TIERS).map(|tier| format!("Tier {tier}")),
2469 )
2470 .collect::<Vec<_>>(),
2471 ),
2472 self.fee_schedules.schedules().map(|schedule| chain!(
2473 iter::once(schedule.key().to_string()),
2474 (0..FEE_TIERS as types::FeeTier).map(move |tier| format!(
2475 "{}\n{}",
2476 schedule.taker_fee(tier),
2477 schedule.maker_fee(tier),
2478 )),
2479 )
2480 .collect::<Vec<_>>()),
2481 ));
2482 fees.with(Style::modern());
2483 writeln!(f, "{fees}")?;
2484 }
2485 writeln!(f)?;
2486
2487 if f.alternate() && !self.perpetuals().is_empty() {
2489 let mut perpetuals: Vec<_> = self.perpetuals().values().collect();
2490 perpetuals.sort_by_key(|p| p.id());
2491 for perpetual in perpetuals {
2492 writeln!(f, "{:#}", perpetual)?;
2493 }
2494 }
2495
2496 if f.alternate() && !self.accounts().is_empty() {
2497 let mut accounts: Vec<_> = self.accounts().values().collect();
2498 accounts.sort_by_key(|a| a.id());
2499 for account in accounts {
2500 writeln!(f, "{:#}", account)?;
2501 }
2502 }
2503
2504 Ok(())
2505 }
2506}