scope/compliance/
etherscan.rs1use super::datasource::BlockchainDataClient;
7use super::risk::RiskEngine;
8
9pub const ETHERSCAN_KEY_ENV: &str = "ETHERSCAN_API_KEY";
11
12pub fn etherscan_key() -> Option<String> {
17 let configured = || {
18 crate::config::Config::load(None)
19 .ok()
20 .and_then(|c| c.chains.api_keys.get("etherscan").cloned())
21 };
22 key_from(std::env::var(ETHERSCAN_KEY_ENV).ok(), configured)
23}
24
25fn key_from(env: Option<String>, configured: impl FnOnce() -> Option<String>) -> Option<String> {
27 env.filter(|k| !k.trim().is_empty())
28 .or_else(configured)
29 .filter(|k| !k.trim().is_empty())
30}
31
32impl RiskEngine {
33 pub fn with_optional_client(client: Option<BlockchainDataClient>) -> Self {
36 match client {
37 Some(c) => Self::with_data_client(c),
38 None => Self::new(),
39 }
40 }
41
42 pub fn from_env() -> Self {
44 Self::with_optional_client(BlockchainDataClient::from_env_opt())
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51 use crate::compliance::datasource::DataSources;
52
53 #[test]
54 fn test_optional_client_picks_the_engine() {
55 let _basic = RiskEngine::with_optional_client(None);
57 let client = BlockchainDataClient::new(DataSources::new("k".to_string()));
58 let _rich = RiskEngine::with_optional_client(Some(client));
59 assert_eq!(ETHERSCAN_KEY_ENV, "ETHERSCAN_API_KEY");
60 }
61
62 #[test]
63 fn test_the_variable_wins_and_the_config_key_is_the_fallback() {
64 let config = || Some("from-config".to_string());
65 assert_eq!(
66 key_from(Some("from-env".into()), config).as_deref(),
67 Some("from-env")
68 );
69 assert_eq!(key_from(None, config).as_deref(), Some("from-config"));
71 assert_eq!(
73 key_from(Some(" ".into()), config).as_deref(),
74 Some("from-config")
75 );
76 assert_eq!(key_from(None, || Some(String::new())), None);
77 }
78}