blueprint_eigenlayer_extra/services/
lifecycle.rs1use crate::error::{EigenlayerExtraError, Result};
2use alloy_primitives::{Address, FixedBytes};
3use blueprint_core::info;
4use blueprint_keystore::backends::Backend;
5use blueprint_keystore::backends::eigenlayer::EigenlayerBackend;
6use blueprint_keystore::crypto::k256::K256Ecdsa;
7use blueprint_runner::config::BlueprintEnvironment;
8use eigensdk::client_elcontracts::writer::ELChainWriter;
9use eigensdk::types::operator::Operator;
10
11#[derive(Clone)]
16pub struct OperatorLifecycleManager {
17 env: BlueprintEnvironment,
18}
19
20#[derive(Debug, Clone)]
22pub struct OperatorMetadata {
23 pub metadata_url: String,
25 pub delegation_approver_address: Address,
27}
28
29impl OperatorLifecycleManager {
30 pub fn new(env: BlueprintEnvironment) -> Self {
32 Self { env }
33 }
34
35 fn get_operator_credentials(&self) -> Result<(Address, String)> {
41 let ecdsa_public = self
42 .env
43 .keystore()
44 .first_local::<K256Ecdsa>()
45 .map_err(EigenlayerExtraError::Keystore)?;
46
47 let ecdsa_secret = self
48 .env
49 .keystore()
50 .expose_ecdsa_secret(&ecdsa_public)
51 .map_err(EigenlayerExtraError::Keystore)?
52 .ok_or_else(|| {
53 EigenlayerExtraError::InvalidConfiguration("No ECDSA secret found".into())
54 })?;
55
56 let operator_address = ecdsa_secret
57 .alloy_address()
58 .map_err(|e| EigenlayerExtraError::InvalidConfiguration(e.to_string()))?;
59
60 let private_key = alloy_primitives::hex::encode(ecdsa_secret.0.to_bytes());
61
62 Ok((operator_address, private_key))
63 }
64
65 pub async fn deregister_operator(&self) -> Result<FixedBytes<32>> {
76 let (operator_address, private_key) = self.get_operator_credentials()?;
77 let contract_addresses = self
78 .env
79 .protocol_settings
80 .eigenlayer()
81 .map_err(|e| EigenlayerExtraError::InvalidConfiguration(e.to_string()))?;
82
83 let el_writer = ELChainWriter::new(
84 contract_addresses.strategy_manager_address,
85 contract_addresses.rewards_coordinator_address,
86 Some(contract_addresses.permission_controller_address),
87 Some(contract_addresses.allocation_manager_address),
88 contract_addresses.registry_coordinator_address,
89 eigensdk::client_elcontracts::reader::ELChainReader::new(
90 Some(contract_addresses.allocation_manager_address),
91 contract_addresses.delegation_manager_address,
92 contract_addresses.rewards_coordinator_address,
93 contract_addresses.avs_directory_address,
94 Some(contract_addresses.permission_controller_address),
95 self.env.http_rpc_endpoint.to_string(),
96 ),
97 self.env.http_rpc_endpoint.to_string(),
98 private_key,
99 );
100
101 let tx_hash = el_writer
103 .deregister_from_operator_sets(
104 operator_address,
105 contract_addresses.service_manager_address,
106 contract_addresses.operator_sets.clone(),
107 )
108 .await
109 .map_err(|e| EigenlayerExtraError::EigenSdk(e.to_string()))?;
110
111 info!(
112 "Operator {} deregistered from AVS {} operator sets {:?}: {:?}",
113 operator_address,
114 contract_addresses.service_manager_address,
115 contract_addresses.operator_sets,
116 tx_hash
117 );
118
119 Ok(tx_hash)
120 }
121
122 pub async fn update_operator_metadata(
135 &self,
136 metadata: OperatorMetadata,
137 ) -> Result<FixedBytes<32>> {
138 let (operator_address, private_key) = self.get_operator_credentials()?;
139 let contract_addresses = self
140 .env
141 .protocol_settings
142 .eigenlayer()
143 .map_err(|e| EigenlayerExtraError::InvalidConfiguration(e.to_string()))?;
144
145 let el_writer = ELChainWriter::new(
146 contract_addresses.strategy_manager_address,
147 contract_addresses.rewards_coordinator_address,
148 Some(contract_addresses.permission_controller_address),
149 Some(contract_addresses.allocation_manager_address),
150 contract_addresses.registry_coordinator_address,
151 eigensdk::client_elcontracts::reader::ELChainReader::new(
152 Some(contract_addresses.allocation_manager_address),
153 contract_addresses.delegation_manager_address,
154 contract_addresses.rewards_coordinator_address,
155 contract_addresses.avs_directory_address,
156 Some(contract_addresses.permission_controller_address),
157 self.env.http_rpc_endpoint.to_string(),
158 ),
159 self.env.http_rpc_endpoint.to_string(),
160 private_key,
161 );
162
163 let operator_details = Operator {
165 address: operator_address,
166 delegation_approver_address: metadata.delegation_approver_address,
167 metadata_url: metadata.metadata_url,
168 allocation_delay: Some(30), _deprecated_earnings_receiver_address: None,
170 staker_opt_out_window_blocks: Some(50400),
171 };
172
173 let tx_hash = el_writer
174 .update_operator_details(operator_details)
175 .await
176 .map_err(|e| EigenlayerExtraError::EigenSdk(e.to_string()))?;
177
178 info!(
179 "Operator {} metadata updated successfully: {:?}",
180 operator_address, tx_hash
181 );
182
183 Ok(tx_hash)
184 }
185
186 pub async fn get_operator_status(&self) -> Result<bool> {
195 let (operator_address, _) = self.get_operator_credentials()?;
196 let contract_addresses = self
197 .env
198 .protocol_settings
199 .eigenlayer()
200 .map_err(|e| EigenlayerExtraError::InvalidConfiguration(e.to_string()))?;
201
202 let el_chain_reader = eigensdk::client_elcontracts::reader::ELChainReader::new(
203 Some(contract_addresses.allocation_manager_address),
204 contract_addresses.delegation_manager_address,
205 contract_addresses.rewards_coordinator_address,
206 contract_addresses.avs_directory_address,
207 Some(contract_addresses.permission_controller_address),
208 self.env.http_rpc_endpoint.to_string(),
209 );
210
211 el_chain_reader
212 .is_operator_registered(operator_address)
213 .await
214 .map_err(|e| EigenlayerExtraError::EigenSdk(e.to_string()))
215 }
216}
217
218#[cfg(test)]
219mod tests {
220
221 #[tokio::test]
222 #[ignore] async fn test_lifecycle_manager_creation() {
224 }
227}