openzeppelin_monitor/services/blockchain/
pool.rs1use crate::utils::client_storage::ClientStorage;
14use crate::{
15 models::{BlockChainType, Network},
16 services::blockchain::{
17 BlockChainClient, BlockFilterFactory, EVMTransportClient, EvmClient, EvmClientTrait,
18 StellarClient, StellarClientTrait, StellarTransportClient,
19 },
20};
21use anyhow::Context;
22use async_trait::async_trait;
23use futures::future::BoxFuture;
24use std::{any::Any, collections::HashMap, sync::Arc};
25
26#[async_trait]
28pub trait ClientPoolTrait: Send + Sync {
29 type EvmClient: EvmClientTrait + BlockChainClient + BlockFilterFactory<Self::EvmClient>;
30 type StellarClient: StellarClientTrait
31 + BlockChainClient
32 + BlockFilterFactory<Self::StellarClient>;
33
34 async fn get_evm_client(
35 &self,
36 network: &Network,
37 ) -> Result<Arc<Self::EvmClient>, anyhow::Error>;
38 async fn get_stellar_client(
39 &self,
40 network: &Network,
41 ) -> Result<Arc<Self::StellarClient>, anyhow::Error>;
42}
43
44pub struct ClientPool {
50 pub storages: HashMap<BlockChainType, Box<dyn Any + Send + Sync>>,
52}
53
54impl ClientPool {
55 pub fn new() -> Self {
59 let mut pool = Self {
60 storages: HashMap::new(),
61 };
62
63 pool.register_client_type::<EvmClient<EVMTransportClient>>(BlockChainType::EVM);
65 pool.register_client_type::<StellarClient<StellarTransportClient>>(BlockChainType::Stellar);
66
67 pool
68 }
69
70 fn register_client_type<T: 'static + Send + Sync>(&mut self, client_type: BlockChainType) {
71 self.storages
72 .insert(client_type, Box::new(ClientStorage::<T>::new()));
73 }
74
75 async fn get_or_create_client<T: BlockChainClient + 'static>(
84 &self,
85 client_type: BlockChainType,
86 network: &Network,
87 create_fn: impl Fn(&Network) -> BoxFuture<'static, Result<T, anyhow::Error>>,
88 ) -> Result<Arc<T>, anyhow::Error> {
89 let storage = self
90 .storages
91 .get(&client_type)
92 .and_then(|s| s.downcast_ref::<ClientStorage<T>>())
93 .with_context(|| "Invalid client type")?;
94
95 if let Some(client) = storage.clients.read().await.get(&network.slug) {
97 return Ok(client.clone());
98 }
99
100 let mut clients = storage.clients.write().await;
102 let client = Arc::new(create_fn(network).await?);
103 clients.insert(network.slug.clone(), client.clone());
104 Ok(client)
105 }
106
107 pub async fn get_client_count<T: 'static>(&self, client_type: BlockChainType) -> usize {
109 match self
110 .storages
111 .get(&client_type)
112 .and_then(|s| s.downcast_ref::<ClientStorage<T>>())
113 {
114 Some(storage) => storage.clients.read().await.len(),
115 None => 0,
116 }
117 }
118}
119
120#[async_trait]
121impl ClientPoolTrait for ClientPool {
122 type EvmClient = EvmClient<EVMTransportClient>;
123 type StellarClient = StellarClient<StellarTransportClient>;
124
125 async fn get_evm_client(
130 &self,
131 network: &Network,
132 ) -> Result<Arc<Self::EvmClient>, anyhow::Error> {
133 self.get_or_create_client(BlockChainType::EVM, network, |n| {
134 let network = n.clone();
135 Box::pin(async move { Self::EvmClient::new(&network).await })
136 })
137 .await
138 .with_context(|| "Failed to get or create EVM client")
139 }
140
141 async fn get_stellar_client(
146 &self,
147 network: &Network,
148 ) -> Result<Arc<Self::StellarClient>, anyhow::Error> {
149 self.get_or_create_client(BlockChainType::Stellar, network, |n| {
150 let network = n.clone();
151 Box::pin(async move { Self::StellarClient::new(&network).await })
152 })
153 .await
154 .with_context(|| "Failed to get or create Stellar client")
155 }
156}
157
158impl Default for ClientPool {
159 fn default() -> Self {
160 Self::new()
161 }
162}