1use std::sync::atomic::AtomicBool;
12use std::sync::Arc;
13
14use tokio::sync::{mpsc, oneshot, Mutex, Notify, RwLock};
15
16use hypercall_runtime_api::boundary::engine::{EngineJournalReader, EngineStateDigestProvider};
17use hypercall_runtime_api::boundary::market_inputs::{GreeksCacheReader, InstrumentsCacheReader};
18use hypercall_runtime_api::boundary::read_models::{
19 EngineBalanceSnapshotProvider, PortfolioCacheApi, TierCacheApi,
20};
21
22use hypercall_runtime_api::recovery_safety::SharedRecoverySafetyReport;
23use hypercall_runtime_api::trading_halt::TradingHaltState;
24use hypercall_runtime_api::{
25 ApiAsyncDb, ApiSyncDb, BuildInfo, EngineQuiesceRequest, QuoteProvider, QuoteProviderCache,
26 StandbyPromoter, UnifiedEngineRequest,
27};
28use hypercall_types::WalletAddress;
29
30#[derive(Clone)]
36pub struct AdminRuntimeConfig {
37 pub wal_path: std::path::PathBuf,
38 pub db_host: String,
39 pub db_name: String,
40 pub testnet_mode: bool,
43 pub portfolio_margin_pool_enabled: bool,
44 pub portfolio_margin_settlement_allowlist: Vec<WalletAddress>,
45}
46
47#[derive(Clone)]
49pub struct AdminState {
50 pub db: Arc<dyn ApiAsyncDb>,
52 pub sync_db: Option<Arc<dyn ApiSyncDb>>,
53
54 pub portfolio_cache: Arc<dyn PortfolioCacheApi>,
56 pub greeks_cache: Arc<dyn GreeksCacheReader>,
57 pub quote_provider: Arc<dyn QuoteProvider>,
58 pub tier_cache: Arc<dyn TierCacheApi>,
59 pub risk_vol_oracle: hypercall_vol_oracle::SharedVolOracle,
60 pub instruments_cache: Arc<dyn InstrumentsCacheReader>,
61 pub engine_state_digest_provider: Arc<dyn EngineStateDigestProvider>,
62 pub engine_journal_reader: Option<Arc<dyn EngineJournalReader>>,
63 pub balance_snapshot_provider: Arc<dyn EngineBalanceSnapshotProvider>,
64 pub recovery_safety_report: SharedRecoverySafetyReport,
65
66 pub qp_cache: Arc<QuoteProviderCache>,
68
69 pub competition: Arc<hypercall_competition::CompetitionService>,
71
72 pub order_sender: mpsc::Sender<UnifiedEngineRequest>,
74 pub engine_quiesce_sender: mpsc::Sender<EngineQuiesceRequest>,
75 pub tier_update_sender: Option<mpsc::Sender<hypercall_runtime_api::TierUpdateRequest>>,
76 pub pm_settlement_admin_sender:
77 Option<mpsc::Sender<hypercall_runtime_api::PmSettlementAdminRequest>>,
78 pub agent_auth_sender: mpsc::Sender<hypercall_runtime_api::AgentAuthRequest>,
79 pub rsm_signer: Option<Arc<dyn hypercall_signer::RsmSigner>>,
80
81 pub trading_halt: Arc<RwLock<TradingHaltState>>,
83 pub standby_promote: Option<Arc<Mutex<Option<oneshot::Sender<()>>>>>,
84 pub standby_controller: Option<Arc<dyn StandbyPromoter>>,
85 pub drain_signal: Arc<Notify>,
86 pub is_draining: Arc<AtomicBool>,
87
88 pub build_info: BuildInfo,
90 pub runtime_config: Arc<AdminRuntimeConfig>,
91 pub server_started_at: chrono::DateTime<chrono::Utc>,
92 pub boot_id: String,
93 pub admin_api_key: Option<String>,
94 pub allow_unauthenticated_monitoring: bool,
95}
96
97pub(crate) const ENGINE_RESPONSE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
98
99impl AdminState {
100 pub async fn submit_tier_update_command(
104 &self,
105 wallet: hypercall_types::WalletAddress,
106 ) -> Result<(), String> {
107 let sender = self
108 .tier_update_sender
109 .as_ref()
110 .ok_or_else(|| "tier update engine channel is not configured".to_string())?;
111
112 let tier = self
113 .tier_cache
114 .get_tier(&wallet)
115 .await
116 .map(|tier| tier.tier)
117 .ok_or_else(|| format!("missing tier for tier update: wallet={wallet}"))?;
118 let margin_mode = self
119 .tier_cache
120 .get_margin_mode(&wallet)
121 .await
122 .map_err(|e| format!("failed to get margin mode for tier update: {}", e))?;
123 let trading_limits = self.tier_cache.get_trading_limits_async(&wallet).await;
124 let (tx, rx) = oneshot::channel();
125
126 sender
127 .send(hypercall_runtime_api::TierUpdateRequest {
128 wallet,
129 margin_mode,
130 tier,
131 trading_limits,
132 timestamp_ms: hypercall_types::utils::get_timestamp_millis(),
133 applied_tx: Some(tx),
134 })
135 .await
136 .map_err(|_| "tier update engine channel closed".to_string())?;
137
138 tokio::time::timeout(ENGINE_RESPONSE_TIMEOUT, rx)
139 .await
140 .map_err(|_| "tier update apply acknowledgement timed out".to_string())?
141 .map_err(|_| "tier update apply acknowledgement dropped".to_string())?
142 }
143}