use std::any::Any;
use nautilus_common::factories::ClientConfig;
use nautilus_infrastructure::sql::pg::PostgresConnectOptions;
use nautilus_model::{
defi::{Chain, DexType, SharedChain},
identifiers::{AccountId, TraderId},
};
use nautilus_network::websocket::TransportBackend;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, bon::Builder)]
#[serde(default, deny_unknown_fields)]
#[cfg_attr(
feature = "python",
pyo3::pyclass(
module = "nautilus_trader.core.nautilus_pyo3.blockchain",
from_py_object
)
)]
#[cfg_attr(
feature = "python",
pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.blockchain")
)]
pub struct DexPoolFilters {
#[builder(default = true)]
pub remove_pools_with_empty_erc20fields: bool,
}
impl Default for DexPoolFilters {
fn default() -> Self {
Self::builder().build()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, bon::Builder)]
#[serde(deny_unknown_fields)]
#[cfg_attr(
feature = "python",
pyo3::pyclass(
module = "nautilus_trader.core.nautilus_pyo3.blockchain",
from_py_object
)
)]
#[cfg_attr(
feature = "python",
pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.adapters.blockchain")
)]
pub struct BlockchainDataClientConfig {
pub chain: SharedChain,
#[builder(default)]
#[serde(default)]
pub dex_ids: Vec<DexType>,
#[builder(default)]
#[serde(default)]
pub use_hypersync_for_live_data: bool,
pub http_rpc_url: String,
pub rpc_requests_per_second: Option<u32>,
#[builder(default = 200)]
#[serde(default = "default_multicall_calls_per_rpc_request")]
pub multicall_calls_per_rpc_request: u32,
pub wss_rpc_url: Option<String>,
pub proxy_url: Option<String>,
pub from_block: Option<u64>,
#[builder(default)]
#[serde(default)]
pub pool_filters: DexPoolFilters,
pub postgres_cache_database_config: Option<PostgresConnectOptions>,
#[builder(default)]
#[serde(default)]
pub transport_backend: TransportBackend,
}
const fn default_multicall_calls_per_rpc_request() -> u32 {
200
}
#[derive(Debug, Clone, Serialize, Deserialize, bon::Builder)]
#[serde(deny_unknown_fields)]
pub struct BlockchainExecutionClientConfig {
pub trader_id: TraderId,
pub client_id: AccountId,
pub chain: Chain,
pub wallet_address: String,
pub tokens: Option<Vec<String>>,
pub http_rpc_url: String,
pub rpc_requests_per_second: Option<u32>,
#[builder(default)]
#[serde(default)]
pub transport_backend: TransportBackend,
}
impl ClientConfig for BlockchainExecutionClientConfig {
fn as_any(&self) -> &dyn Any {
self
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
#[rstest]
fn test_data_config_toml_minimal() {
let config: BlockchainDataClientConfig = toml::from_str(
r#"
http_rpc_url = "https://eth-mainnet.example.com"
[chain]
name = "Ethereum"
chain_id = 1
hypersync_url = "https://1.hypersync.xyz"
native_currency_decimals = 18
"#,
)
.unwrap();
assert_eq!(config.http_rpc_url, "https://eth-mainnet.example.com");
assert_eq!(config.chain.chain_id, 1);
assert!(config.dex_ids.is_empty());
assert!(!config.use_hypersync_for_live_data);
assert_eq!(config.multicall_calls_per_rpc_request, 200);
assert!(config.pool_filters.remove_pools_with_empty_erc20fields);
assert_eq!(config.transport_backend, TransportBackend::default());
}
#[rstest]
fn test_execution_config_toml_minimal() {
let config: BlockchainExecutionClientConfig = toml::from_str(
r#"
trader_id = "TRADER-001"
client_id = "BLOCKCHAIN-001"
wallet_address = "0x0000000000000000000000000000000000000000"
http_rpc_url = "https://eth-mainnet.example.com"
[chain]
name = "Ethereum"
chain_id = 1
hypersync_url = "https://1.hypersync.xyz"
native_currency_decimals = 18
"#,
)
.unwrap();
assert_eq!(config.http_rpc_url, "https://eth-mainnet.example.com");
assert_eq!(config.chain.chain_id, 1);
assert_eq!(
config.wallet_address,
"0x0000000000000000000000000000000000000000",
);
assert!(config.tokens.is_none());
assert!(config.rpc_requests_per_second.is_none());
assert_eq!(config.transport_backend, TransportBackend::default());
}
}