hypercall/shared/env.rs
1//! Environment and mode detection utilities.
2//!
3//! Three separate concerns:
4//! - `is_testnet_mode()`, "which network?" Controls chain IDs, the default catalog
5//! path selection, and other network-specific settings.
6//! Set on all testnet and staging deployments.
7//! - `test_endpoints_enabled()`, "allow dangerous /test/* endpoints?" Controls
8//! spot-price manipulation, IV overrides, instrument expiry, etc.
9//! Should only be set in local dev and CI, never on deployed environments.
10//! - `faucet` cargo feature: compile-time gate for the `/faucet` route.
11//! Enabled by `test-endpoints` (local dev, CI) or independently for testnet deploys.
12//! Never enabled for real-money builds (AWS staging, mainnet).
13
14use crate::backend_config::ModesConfig;
15use std::future::Future;
16
17/// Returns true if running in testnet/dev mode.
18pub fn is_testnet_mode(modes: &ModesConfig) -> bool {
19 modes.testnet_mode
20}
21
22/// Returns true if dangerous test manipulation endpoints are enabled.
23///
24/// This is now a compile-time check via the `test-endpoints` cargo feature.
25/// The feature gates mock feeds, mock oracles, and `/test/*` endpoints.
26/// Should only be enabled for local dev and CI builds, never in deployed binaries.
27pub fn test_endpoints_enabled(_modes: &ModesConfig) -> bool {
28 cfg!(feature = "test-endpoints")
29}
30
31/// Run an async block synchronously from sync code.
32///
33/// Used for testnet-only initialization (funding test accounts in engine builder).
34/// Handles both multi-threaded and single-threaded tokio runtimes:
35/// - Multi-threaded: uses `block_in_place` (efficient, does not block other tasks)
36/// - Single-threaded: spawns a thread with new runtime (avoids deadlock)
37/// - No runtime: creates a temporary runtime
38///
39/// NOTE: This is only called when `modes.testnet_mode=true`. In production, the code path
40/// that uses this is skipped entirely.
41pub fn block_on_async<F, T>(f: F) -> T
42where
43 F: Future<Output = T> + Send + 'static,
44 T: Send + 'static,
45{
46 // Try to get current runtime handle
47 if let Ok(handle) = tokio::runtime::Handle::try_current() {
48 // We're inside a tokio runtime - check if multi-threaded
49 // block_in_place only works on multi-threaded runtime
50 match handle.runtime_flavor() {
51 tokio::runtime::RuntimeFlavor::MultiThread => {
52 tokio::task::block_in_place(|| handle.block_on(f))
53 }
54 _ => {
55 // Single-threaded runtime: spawn blocking thread
56 std::thread::scope(|s| {
57 s.spawn(|| {
58 let rt = tokio::runtime::Builder::new_current_thread()
59 .enable_all()
60 .build()
61 .expect("Failed to create runtime");
62 rt.block_on(f)
63 })
64 .join()
65 .expect("Thread panicked")
66 })
67 }
68 }
69 } else {
70 // Not inside tokio runtime - create one
71 let rt = tokio::runtime::Builder::new_current_thread()
72 .enable_all()
73 .build()
74 .expect("Failed to create runtime");
75 rt.block_on(f)
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn test_mode_helpers_return_config_values() {
85 let modes = ModesConfig {
86 testnet_mode: true,
87 enable_test_endpoints: false,
88 skip_external_oracle: false,
89 };
90
91 assert!(is_testnet_mode(&modes));
92 // test_endpoints_enabled is now compile-time via cfg!(feature = "test-endpoints")
93 assert_eq!(
94 test_endpoints_enabled(&modes),
95 cfg!(feature = "test-endpoints")
96 );
97 }
98
99 #[test]
100 fn test_block_on_async_outside_runtime() {
101 let result = block_on_async(async { 42 });
102 assert_eq!(result, 42);
103 }
104
105 #[tokio::test]
106 async fn test_block_on_async_inside_runtime() {
107 let result = block_on_async(async { 42 });
108 assert_eq!(result, 42);
109 }
110}