Skip to main content

hypercall_db/traits/
settlements.rs

1//! Settlement persistence traits.
2//!
3//! Settlement is the highest-risk persistence domain: every in-memory PnL
4//! mutation MUST have a corresponding DB write (CALL-500 post-mortem).
5//! The `try_apply_settlement_sync` method is idempotent and atomic.
6
7use anyhow::Result;
8use hypercall_types::{MarginMode, WalletAddress};
9use rust_decimal::Decimal;
10use std::collections::HashSet;
11
12use crate::SettlementResult;
13
14/// Read-only settlement queries.
15pub trait SettlementReader: Send + Sync {
16    /// Check if a specific settlement has been applied to the ledger.
17    fn is_settlement_ledger_applied_sync(
18        &self,
19        wallet: &WalletAddress,
20        symbol: &str,
21    ) -> Result<bool>;
22
23    /// Return the subset of symbols that have applied settlement payouts for a wallet.
24    fn get_applied_settlement_symbols_sync(
25        &self,
26        wallet: &WalletAddress,
27        symbols: &[String],
28    ) -> Result<HashSet<String>>;
29
30    /// Get total fill volume: (fill_count, total_notional).
31    fn get_total_fill_volume_sync(&self) -> Result<(i64, Decimal)>;
32}
33
34/// Settlement mutations. Atomic and idempotent.
35pub trait SettlementWriter: SettlementReader {
36    /// Idempotent settlement audit: expiration row, payout row, and ledger event.
37    /// Returns whether this was a new settlement or a duplicate replay.
38    fn try_apply_settlement_sync(
39        &self,
40        wallet: &WalletAddress,
41        symbol: &str,
42        position_size: Decimal,
43        settlement_price: Decimal,
44        settlement_value: Decimal,
45        margin_mode: MarginMode,
46        event_ts_ms: i64,
47        settlement_entry_price: Option<Decimal>,
48        cost_basis: Option<Decimal>,
49        net_pnl: Option<Decimal>,
50    ) -> Result<SettlementResult>;
51
52    /// Read and validate an already-applied settlement without writing.
53    ///
54    /// Standby replay uses this after primary NATS publication, which happens
55    /// only after the primary durable settlement effect succeeds.
56    fn observe_applied_settlement_sync(
57        &self,
58        wallet: &WalletAddress,
59        symbol: &str,
60        position_size: Decimal,
61        settlement_price: Decimal,
62        settlement_value: Decimal,
63        margin_mode: MarginMode,
64        settlement_entry_price: Option<Decimal>,
65        cost_basis: Option<Decimal>,
66        net_pnl: Option<Decimal>,
67    ) -> Result<SettlementResult>;
68}