blueprint_client_eigenlayer/
client.rs1use crate::error::Result;
2use alloy_primitives::{Address, Bytes, U256};
3use alloy_provider::{Provider, RootProvider};
4use blueprint_evm_extra::util::{get_provider_http, get_wallet_provider_http};
5use blueprint_runner::config::BlueprintEnvironment;
6use blueprint_std::collections::HashMap;
7use eigensdk::client_avsregistry::reader::AvsRegistryReader;
8use eigensdk::common::get_ws_provider;
9use eigensdk::utils::rewardsv2::middleware::registry_coordinator::RegistryCoordinator;
10use eigensdk::utils::rewardsv2::middleware::stake_registry::{IStakeRegistry, StakeRegistry};
11use eigensdk::utils::slashing::core::allocation_manager::{
12 AllocationManager, IAllocationManagerTypes,
13};
14use eigensdk::utils::slashing::core::delegation_manager::DelegationManager;
15use eigensdk::utils::slashing::middleware::operator_state_retriever::OperatorStateRetriever;
16use num_bigint::BigInt;
17
18#[derive(Clone)]
20pub struct EigenlayerClient {
21 pub config: BlueprintEnvironment,
22}
23
24impl EigenlayerClient {
25 #[must_use]
27 pub fn new(config: BlueprintEnvironment) -> Self {
28 Self { config }
29 }
30
31 #[must_use]
33 pub fn config(&self) -> &BlueprintEnvironment {
34 &self.config
35 }
36
37 #[must_use]
42 pub fn get_provider_http(&self) -> RootProvider {
43 get_provider_http(self.config.http_rpc_endpoint.clone())
44 }
45
46 #[must_use]
53 pub fn get_wallet_provider_http(&self, wallet: alloy_network::EthereumWallet) -> RootProvider {
54 get_wallet_provider_http(self.config.http_rpc_endpoint.clone(), wallet)
55 }
56
57 pub async fn get_provider_ws(&self) -> Result<RootProvider> {
66 get_ws_provider(self.config.ws_rpc_endpoint.as_str())
67 .await
68 .map_err(Into::into)
69 }
70
71 pub async fn avs_registry_reader(
100 &self,
101 ) -> Result<eigensdk::client_avsregistry::reader::AvsRegistryChainReader> {
102 let http_rpc_endpoint = self.config.http_rpc_endpoint.clone();
103 let contract_addresses = self.config.protocol_settings.eigenlayer()?;
104 let registry_coordinator_address = contract_addresses.registry_coordinator_address;
105 let operator_state_retriever_address = contract_addresses.operator_state_retriever_address;
106 eigensdk::client_avsregistry::reader::AvsRegistryChainReader::new(
107 registry_coordinator_address,
108 operator_state_retriever_address,
109 http_rpc_endpoint.to_string(),
110 )
111 .await
112 .map_err(Into::into)
113 }
114
115 pub async fn avs_registry_writer(
124 &self,
125 private_key: String,
126 ) -> Result<eigensdk::client_avsregistry::writer::AvsRegistryChainWriter> {
127 let contract_addresses = self.config.protocol_settings.eigenlayer()?;
128 let registry_coordinator_address = contract_addresses.registry_coordinator_address;
129 let service_manager_address = contract_addresses.service_manager_address;
130
131 eigensdk::client_avsregistry::writer::AvsRegistryChainWriter::build_avs_registry_chain_writer(
132 self.config.http_rpc_endpoint.to_string(),
133 private_key,
134 registry_coordinator_address,
135 service_manager_address,
136 ).await
137 .map_err(Into::into)
138 }
139
140 pub async fn operator_info_service_in_memory(
149 &self,
150 ) -> Result<(
151 eigensdk::services_operatorsinfo::operatorsinfo_inmemory::OperatorInfoServiceInMemory,
152 tokio::sync::mpsc::UnboundedReceiver<
153 eigensdk::services_operatorsinfo::operatorsinfo_inmemory::OperatorInfoServiceError,
154 >,
155 )> {
156 let avs_registry_reader = self.avs_registry_reader().await?;
157
158 eigensdk::services_operatorsinfo::operatorsinfo_inmemory::OperatorInfoServiceInMemory::new(
159 avs_registry_reader,
160 self.config.ws_rpc_endpoint.to_string(),
161 )
162 .await
163 .map_err(Into::into)
164 }
165
166 pub async fn avs_registry_service_chain_caller_in_memory(
173 &self,
174 ) -> Result<
175 eigensdk::services_avsregistry::chaincaller::AvsRegistryServiceChainCaller<
176 eigensdk::client_avsregistry::reader::AvsRegistryChainReader,
177 eigensdk::services_operatorsinfo::operatorsinfo_inmemory::OperatorInfoServiceInMemory,
178 >,
179 > {
180 let avs_registry_reader = self.avs_registry_reader().await?;
181 let (operator_info_service, _) = self.operator_info_service_in_memory().await?;
182
183 let cancellation_token = tokio_util::sync::CancellationToken::new();
184 let token_clone = cancellation_token.clone();
185 let provider = self.get_provider_http();
186 let current_block = provider.get_block_number().await?;
187 let operator_info_clone = operator_info_service.clone();
188
189 tokio::task::spawn(async move {
190 operator_info_clone
191 .start_service(&token_clone, 0, current_block)
192 .await
193 });
194
195 Ok(
196 eigensdk::services_avsregistry::chaincaller::AvsRegistryServiceChainCaller::new(
197 avs_registry_reader,
198 operator_info_service,
199 ),
200 )
201 }
202
203 pub async fn bls_aggregation_service_in_memory(&self) -> Result<eigensdk::services_blsaggregation::bls_agg::BlsAggregatorService<
209 eigensdk::services_avsregistry::chaincaller::AvsRegistryServiceChainCaller<
210 eigensdk::client_avsregistry::reader::AvsRegistryChainReader,
211 eigensdk::services_operatorsinfo::operatorsinfo_inmemory::OperatorInfoServiceInMemory
212 >
213 >>{
214 let avs_registry_service = self.avs_registry_service_chain_caller_in_memory().await?;
215 Ok(
216 eigensdk::services_blsaggregation::bls_agg::BlsAggregatorService::new(
217 avs_registry_service,
218 ),
219 )
220 }
221
222 pub async fn get_operator_stake_in_quorums_at_block(
231 &self,
232 block_number: u32,
233 quorum_numbers: Bytes,
234 ) -> Result<Vec<Vec<OperatorStateRetriever::Operator>>> {
235 self.avs_registry_reader()
236 .await?
237 .get_operators_stake_in_quorums_at_block(block_number.into(), quorum_numbers)
238 .await
239 .map_err(Into::into)
240 }
241
242 pub async fn get_operator_stake_in_quorums_at_current_block(
251 &self,
252 operator_id: alloy_primitives::FixedBytes<32>,
253 ) -> Result<HashMap<u8, BigInt>> {
254 self.avs_registry_reader()
255 .await?
256 .get_operator_stake_in_quorums_of_operator_at_current_block(operator_id)
257 .await
258 .map_err(Into::into)
259 }
260
261 pub async fn get_operator_by_id(&self, operator_id: [u8; 32]) -> Result<Address> {
268 self.avs_registry_reader()
269 .await?
270 .get_operator_from_id(operator_id)
271 .await
272 .map_err(Into::into)
273 }
274
275 pub async fn get_operator_stake_history(
281 &self,
282 operator_id: alloy_primitives::FixedBytes<32>,
283 quorum_number: u8,
284 ) -> Result<Vec<IStakeRegistry::StakeUpdate>> {
285 let contract_addresses = self.config.protocol_settings.eigenlayer()?;
286 let provider = self.get_provider_http();
287 let registry_coordinator = RegistryCoordinator::new(
288 contract_addresses.registry_coordinator_address,
289 provider.clone(),
290 );
291 let stake_registry_address = registry_coordinator.stakeRegistry().call().await?;
292 let instance =
293 StakeRegistry::StakeRegistryInstance::new(stake_registry_address, provider.clone());
294 let call_builder = instance.getStakeHistory(operator_id, quorum_number);
295 let response = call_builder.call().await?;
296 Ok(response)
297 }
298
299 pub async fn get_operator_stake_update_at_index(
305 &self,
306 quorum_number: u8,
307 operator_id: alloy_primitives::FixedBytes<32>,
308 index: alloy_primitives::U256,
309 ) -> Result<IStakeRegistry::StakeUpdate> {
310 let contract_addresses = self.config.protocol_settings.eigenlayer()?;
311 let provider = self.get_provider_http();
312 let registry_coordinator = RegistryCoordinator::new(
313 contract_addresses.registry_coordinator_address,
314 provider.clone(),
315 );
316 let stake_registry_address = registry_coordinator.stakeRegistry().call().await?;
317 let instance =
318 StakeRegistry::StakeRegistryInstance::new(stake_registry_address, provider.clone());
319 let call_builder = instance.getStakeUpdateAtIndex(quorum_number, operator_id, index);
320 let response = call_builder.call().await?;
321 Ok(response)
322 }
323
324 pub async fn get_operator_stake_at_block_number(
330 &self,
331 operator_id: alloy_primitives::FixedBytes<32>,
332 quorum_number: u8,
333 block_number: u32,
334 ) -> Result<alloy_primitives::Uint<96, 2>> {
335 let contract_addresses = self.config.protocol_settings.eigenlayer()?;
336 let provider = self.get_provider_http();
337 let registry_coordinator = RegistryCoordinator::new(
338 contract_addresses.registry_coordinator_address,
339 provider.clone(),
340 );
341 let stake_registry_address = registry_coordinator.stakeRegistry().call().await?;
342 let instance =
343 StakeRegistry::StakeRegistryInstance::new(stake_registry_address, provider.clone());
344 let call_builder = instance.getStakeAtBlockNumber(operator_id, quorum_number, block_number);
345 let response = call_builder.call().await?;
346 Ok(response)
347 }
348
349 pub async fn get_latest_stake_update(
374 &self,
375 operator_id: alloy_primitives::FixedBytes<32>,
376 quorum_number: u8,
377 ) -> Result<IStakeRegistry::StakeUpdate> {
378 let contract_addresses = self.config.protocol_settings.eigenlayer()?;
379 let provider = self.get_provider_http();
380 let registry_coordinator = RegistryCoordinator::new(
381 contract_addresses.registry_coordinator_address,
382 provider.clone(),
383 );
384 let stake_registry_address = registry_coordinator.stakeRegistry().call().await?;
385 let instance =
386 StakeRegistry::StakeRegistryInstance::new(stake_registry_address, provider.clone());
387 let call_builder = instance.getLatestStakeUpdate(operator_id, quorum_number);
388 let response = call_builder.call().await?;
389 Ok(response)
390 }
391
392 pub async fn get_operator_id(
402 &self,
403 operator_addr: Address,
404 ) -> Result<alloy_primitives::FixedBytes<32>> {
405 self.avs_registry_reader()
406 .await?
407 .get_operator_id(operator_addr)
408 .await
409 .map_err(Into::into)
410 }
411
412 pub async fn get_total_stake_at_block_number_from_index(
418 &self,
419 quorum_number: u8,
420 block_number: u32,
421 index: alloy_primitives::U256,
422 ) -> Result<alloy_primitives::Uint<96, 2>> {
423 let contract_addresses = self.config.protocol_settings.eigenlayer()?;
424 let provider = self.get_provider_http();
425 let registry_coordinator = RegistryCoordinator::new(
426 contract_addresses.registry_coordinator_address,
427 provider.clone(),
428 );
429 let stake_registry_address = registry_coordinator.stakeRegistry().call().await?;
430 let instance =
431 StakeRegistry::StakeRegistryInstance::new(stake_registry_address, provider.clone());
432 let call_builder =
433 instance.getTotalStakeAtBlockNumberFromIndex(quorum_number, block_number, index);
434 let response = call_builder.call().await?;
435 Ok(response)
436 }
437
438 pub async fn get_total_stake_history_length(
444 &self,
445 quorum_number: u8,
446 ) -> Result<alloy_primitives::U256> {
447 let contract_addresses = self.config.protocol_settings.eigenlayer()?;
448 let provider = self.get_provider_http();
449 let registry_coordinator = RegistryCoordinator::new(
450 contract_addresses.registry_coordinator_address,
451 provider.clone(),
452 );
453 let stake_registry_address = registry_coordinator.stakeRegistry().call().await?;
454 let instance =
455 StakeRegistry::StakeRegistryInstance::new(stake_registry_address, provider.clone());
456 let call_builder = instance.getTotalStakeHistoryLength(quorum_number);
457 let response = call_builder.call().await?;
458 Ok(response)
459 }
460
461 pub async fn query_existing_registered_operator_pub_keys(
470 &self,
471 start_block: u64,
472 to_block: u64,
473 ) -> Result<(
474 Vec<Address>,
475 Vec<eigensdk::types::operator::OperatorPubKeys>,
476 )> {
477 self.avs_registry_reader()
478 .await?
479 .query_existing_registered_operator_pub_keys(
480 start_block,
481 to_block,
482 self.config.ws_rpc_endpoint.to_string(),
483 )
484 .await
485 .map_err(Into::into)
486 }
487
488 pub async fn get_strategies_in_operator_set(
503 &self,
504 avs_address: Address,
505 operator_set_id: u8,
506 ) -> Result<Vec<Address>> {
507 let contract_addresses = self.config.protocol_settings.eigenlayer()?;
508 let provider = self.get_provider_http();
509
510 let allocation_manager = AllocationManager::AllocationManagerInstance::new(
511 contract_addresses.allocation_manager_address,
512 provider,
513 );
514
515 let operator_set = AllocationManager::OperatorSet {
516 avs: avs_address,
517 id: u32::from(operator_set_id), };
519
520 let result = allocation_manager
521 .getStrategiesInOperatorSet(operator_set)
522 .call()
523 .await?;
524
525 Ok(result)
526 }
527
528 pub async fn get_strategy_allocations(
545 &self,
546 operator_address: Address,
547 strategy_address: Address,
548 ) -> Result<(
549 Vec<AllocationManager::OperatorSet>,
550 Vec<IAllocationManagerTypes::Allocation>,
551 )> {
552 let contract_addresses = self.config.protocol_settings.eigenlayer()?;
553 let provider = self.get_provider_http();
554
555 let allocation_manager = AllocationManager::AllocationManagerInstance::new(
556 contract_addresses.allocation_manager_address,
557 provider,
558 );
559
560 let result = allocation_manager
561 .getStrategyAllocations(operator_address, strategy_address)
562 .call()
563 .await?;
564
565 Ok((result._0, result._1))
566 }
567
568 pub async fn get_max_magnitude(
583 &self,
584 operator_address: Address,
585 strategy_address: Address,
586 ) -> Result<u64> {
587 let contract_addresses = self.config.protocol_settings.eigenlayer()?;
588 let provider = self.get_provider_http();
589
590 let allocation_manager = AllocationManager::AllocationManagerInstance::new(
591 contract_addresses.allocation_manager_address,
592 provider,
593 );
594
595 let result = allocation_manager
596 .getMaxMagnitude(operator_address, strategy_address)
597 .call()
598 .await?;
599
600 Ok(result)
601 }
602
603 pub async fn get_slashable_shares_in_queue(
618 &self,
619 operator_address: Address,
620 strategy_address: Address,
621 ) -> Result<U256> {
622 let contract_addresses = self.config.protocol_settings.eigenlayer()?;
623 let provider = self.get_provider_http();
624
625 let delegation_manager = DelegationManager::DelegationManagerInstance::new(
626 contract_addresses.delegation_manager_address,
627 provider,
628 );
629
630 let result = delegation_manager
631 .getSlashableSharesInQueue(operator_address, strategy_address)
632 .call()
633 .await?;
634
635 Ok(result)
636 }
637
638 pub async fn get_operators_for_service(
654 &self,
655 _avs_address: Address,
656 block_number: u32,
657 quorum_numbers: Vec<u8>,
658 ) -> Result<Vec<Vec<OperatorStateRetriever::Operator>>> {
659 let quorum_bytes = Bytes::from(quorum_numbers);
660 self.get_operator_stake_in_quorums_at_block(block_number, quorum_bytes)
661 .await
662 }
663
664 pub async fn get_slashable_assets_for_avs(
684 &self,
685 avs_address: Address,
686 block_number: u32,
687 quorum_numbers: Vec<u8>,
688 ) -> Result<HashMap<Address, HashMap<Address, U256>>> {
689 let mut result = HashMap::new();
690
691 let all_operator_info = self
692 .get_operators_for_service(avs_address, block_number, quorum_numbers.clone())
693 .await?;
694
695 for (operators, quorum_number) in all_operator_info.iter().zip(quorum_numbers) {
696 let strategies = self
697 .get_strategies_in_operator_set(avs_address, quorum_number)
698 .await?;
699
700 for operator in operators {
701 let operator_id = operator.operatorId;
702
703 let operator_address = self.get_operator_by_id(operator_id.into()).await?;
704
705 let operator_entry = result.entry(operator_address).or_insert_with(HashMap::new);
706
707 for strategy_address in &strategies {
708 match self
709 .get_slashable_shares_in_queue(operator_address, *strategy_address)
710 .await
711 {
712 Ok(slashable_shares) => {
713 operator_entry.insert(*strategy_address, slashable_shares);
714 }
715 Err(e) => {
716 blueprint_core::error!(
718 "Error getting slashable shares for operator {operator_address}, strategy {strategy_address}: {e}",
719 );
720 }
721 }
722 }
723 }
724 }
725
726 Ok(result)
727 }
728}