Skip to main content

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}