hypercall/rsm/liquidation_manager.rs
1//! Pre-liquidation order blocking logic.
2//!
3//! During pre-liquidation / in-liquidation state, only reduce-only orders are
4//! allowed. This module contains the checks that determine whether an order is
5//! risk-reducing and should be permitted.
6
7use crate::rsm::engine_deps::EngineDeps;
8use hypercall_types::OrderInfo;
9use hypercall_types::WalletAddress;
10use rust_decimal::Decimal;
11use tracing::debug;
12
13/// Stateless helper for pre-liquidation order checks.
14///
15/// All methods are associated functions that borrow `&EngineDeps` rather than
16/// owning any state.
17pub struct LiquidationManager;
18
19impl LiquidationManager {
20 /// Check if an order should be blocked due to pre-liquidation state.
21 ///
22 /// During pre-liquidation/in-liquidation:
23 /// - Only reduce-only orders are allowed:
24 /// - Orders that close the position entirely (new_position == 0)
25 /// - Orders that reduce position size while keeping the same direction
26 /// - All other orders are blocked.
27 ///
28 /// Returns `Ok(())` if the order is allowed, `Err(reason)` if blocked.
29 pub fn check_preliquidation_order_allowed(
30 deps: &EngineDeps,
31 engine_positions: &std::collections::HashMap<
32 (WalletAddress, String),
33 crate::rsm::engine_deps::EnginePosition,
34 >,
35 wallet: &WalletAddress,
36 order_info: &OrderInfo,
37 ) -> Result<(), String> {
38 use hypercall_types::LiquidationStateType;
39
40 // Check command-sourced liquidation state first (deterministic).
41 let should_block = match deps.liquidation_states.get(wallet) {
42 Some(
43 LiquidationStateType::PreLiquidation
44 | LiquidationStateType::InLiquidation
45 | LiquidationStateType::Liquidated,
46 ) => true,
47 Some(LiquidationStateType::Healthy) => false,
48 None => false,
49 };
50
51 if !should_block {
52 return Ok(());
53 }
54
55 let result = hypercall_engine::admission::validate_preliquidation_order_allowed(
56 should_block,
57 engine_positions,
58 wallet,
59 order_info,
60 );
61 if result.is_ok() && should_block {
62 debug!(
63 "Pre-liquidation: allowing reduce-only order for wallet {} on {}",
64 wallet, order_info.symbol
65 );
66 }
67 result
68 }
69
70 /// Check if an order is reduce-only (allowed during pre-liquidation).
71 ///
72 /// An order is reduce-only if after the order:
73 /// - The position is completely closed (new_position == 0), OR
74 /// - The position is reduced but stays on the same side
75 pub fn is_reduce_only_order(
76 engine_positions: &std::collections::HashMap<
77 (WalletAddress, String),
78 crate::rsm::engine_deps::EnginePosition,
79 >,
80 wallet: &WalletAddress,
81 order_info: &OrderInfo,
82 ) -> bool {
83 hypercall_engine::admission::is_reduce_only_order(engine_positions, wallet, order_info)
84 }
85
86 /// Check if a position change is reduce-only.
87 ///
88 /// A position change is reduce-only if:
89 /// - new_position == 0 (complete close), OR
90 /// - sign(new) == sign(current) AND abs(new) < abs(current)
91 pub fn is_position_reduce_only(current: Decimal, new: Decimal) -> bool {
92 hypercall_engine::admission::is_position_reduce_only(current, new)
93 }
94}