use crate::BlueprintConfig;
use crate::config::BlueprintEnvironment;
use crate::eigenlayer::error::EigenlayerError;
use crate::error::RunnerError;
use alloy_primitives::{Address, FixedBytes, U256, hex};
use alloy_signer::Signer;
use alloy_signer_local::PrivateKeySigner;
use blueprint_evm_extra::util::get_provider_http;
use blueprint_keystore::backends::Backend;
use blueprint_keystore::backends::eigenlayer::EigenlayerBackend;
use blueprint_keystore::crypto::k256::K256Ecdsa;
use eigensdk::client_elcontracts::{reader::ELChainReader, writer::ELChainWriter};
use eigensdk::types::operator::Operator;
use eigensdk::utils::rewardsv2::middleware::ecdsa_stake_registry::ECDSAStakeRegistry;
use eigensdk::utils::rewardsv2::middleware::ecdsa_stake_registry::ISignatureUtils::SignatureWithSaltAndExpiry;
use std::str::FromStr;
#[derive(Clone, Copy)]
pub struct EigenlayerECDSAConfig {
delegation_approver_address: Address,
}
impl EigenlayerECDSAConfig {
#[must_use]
pub fn new(delegation_approver_address: Address) -> Self {
Self {
delegation_approver_address,
}
}
}
impl BlueprintConfig for EigenlayerECDSAConfig {
async fn register(&self, env: &BlueprintEnvironment) -> Result<(), RunnerError> {
register_ecdsa_impl(env, self.delegation_approver_address).await
}
async fn requires_registration(&self, env: &BlueprintEnvironment) -> Result<bool, RunnerError> {
requires_registration_ecdsa_impl(env).await
}
}
async fn requires_registration_ecdsa_impl(env: &BlueprintEnvironment) -> Result<bool, RunnerError> {
let provider = get_provider_http(env.http_rpc_endpoint.clone());
let contract_addresses = env.protocol_settings.eigenlayer()?;
let ecdsa_public = env.keystore().first_local::<K256Ecdsa>()?;
let ecdsa_secret = env
.keystore()
.expose_ecdsa_secret(&ecdsa_public)?
.ok_or_else(|| EigenlayerError::Other("No ECDSA secret found".into()))?;
let operator_address = ecdsa_secret
.alloy_address()
.map_err(|e| EigenlayerError::Crypto(e.into()))?;
let stake_registry_address = contract_addresses.stake_registry_address;
let ecdsa_stake_registry = ECDSAStakeRegistry::new(stake_registry_address, provider.clone());
match ecdsa_stake_registry
.operatorRegistered(operator_address)
.call()
.await
.map_err(EigenlayerError::Contract)
{
Ok(is_registered) => Ok(!is_registered),
Err(e) => Err(e.into()),
}
}
async fn register_ecdsa_impl(
env: &BlueprintEnvironment,
delegation_approver_address: Address,
) -> Result<(), RunnerError> {
let contract_addresses = env.protocol_settings.eigenlayer()?;
let registry_coordinator_address = contract_addresses.registry_coordinator_address;
let allocation_manager_address = contract_addresses.allocation_manager_address;
let delegation_manager_address = contract_addresses.delegation_manager_address;
let strategy_manager_address = contract_addresses.strategy_manager_address;
let avs_directory_address = contract_addresses.avs_directory_address;
let service_manager_address = contract_addresses.service_manager_address;
let stake_registry_address = contract_addresses.stake_registry_address;
let rewards_coordinator_address = contract_addresses.rewards_coordinator_address;
let permission_controller_address = contract_addresses.permission_controller_address;
let ecdsa_public = env.keystore().first_local::<K256Ecdsa>()?;
let ecdsa_secret = env
.keystore()
.expose_ecdsa_secret(&ecdsa_public)?
.ok_or_else(|| EigenlayerError::Other("No ECDSA secret found".into()))?;
let operator_address = ecdsa_secret
.alloy_address()
.map_err(|e| EigenlayerError::Crypto(e.into()))?;
let operator_private_key = hex::encode(ecdsa_secret.0.to_bytes());
blueprint_core::info!("Operator private key: {}", operator_private_key);
let wallet = PrivateKeySigner::from_str(&operator_private_key)
.map_err(|_| EigenlayerError::Other("Invalid private key".into()))?;
let provider = get_provider_http(env.http_rpc_endpoint.clone());
let el_chain_reader = ELChainReader::new(
Some(allocation_manager_address),
delegation_manager_address,
rewards_coordinator_address,
avs_directory_address,
Some(permission_controller_address),
env.http_rpc_endpoint.to_string(),
);
let el_writer = ELChainWriter::new(
strategy_manager_address,
rewards_coordinator_address,
Some(permission_controller_address),
Some(allocation_manager_address),
registry_coordinator_address,
el_chain_reader.clone(),
env.http_rpc_endpoint.to_string(),
operator_private_key.clone(),
);
let eigenlayer_settings = env
.protocol_settings
.eigenlayer()
.map_err(|e| EigenlayerError::Other(e.to_string().into()))?;
let operator_details = Operator {
address: operator_address,
delegation_approver_address,
metadata_url: eigenlayer_settings.metadata_url.clone(),
allocation_delay: Some(eigenlayer_settings.allocation_delay),
_deprecated_earnings_receiver_address: None, staker_opt_out_window_blocks: Some(eigenlayer_settings.staker_opt_out_window_blocks),
};
let tx_hash = el_writer
.register_as_operator(operator_details)
.await
.map_err(EigenlayerError::ElContracts)?;
blueprint_core::info!("Registered as operator for Eigenlayer {:?}", tx_hash);
let digest_hash_salt: FixedBytes<32> = FixedBytes::from([0x02; 32]);
let now = std::time::SystemTime::now();
let sig_expiry = now.duration_since(std::time::UNIX_EPOCH).map_or_else(
|_| U256::from(0),
|duration| U256::from(duration.as_secs()) + U256::from(3600),
);
blueprint_core::info!(
"Registration parameters: operator={:?}, manager={:?}, salt={:?}, expiry={:?}",
operator_address,
service_manager_address,
digest_hash_salt,
sig_expiry
);
let msg_to_sign = el_chain_reader
.calculate_operator_avs_registration_digest_hash(
operator_address,
service_manager_address,
digest_hash_salt,
sig_expiry,
)
.await
.map_err(|e| EigenlayerError::Other(e.into()))?;
let operator_signature = wallet
.sign_hash(&msg_to_sign)
.await
.map_err(EigenlayerError::SignatureError)?;
let signing_key_address = wallet.address();
let operator_signature_with_salt_and_expiry = SignatureWithSaltAndExpiry {
signature: operator_signature.as_bytes().into(),
salt: digest_hash_salt,
expiry: sig_expiry,
};
let ecdsa_stake_registry = ECDSAStakeRegistry::new(stake_registry_address, provider.clone());
let _register_response = ecdsa_stake_registry
.registerOperatorWithSignature(
operator_signature_with_salt_and_expiry.clone(),
signing_key_address,
)
.send()
.await
.unwrap()
.get_receipt()
.await
.unwrap();
let is_registered = ecdsa_stake_registry
.operatorRegistered(operator_address)
.call()
.await
.map_err(|e| {
EigenlayerError::Registration(format!("Failed to check registration: {}", e))
})?;
blueprint_core::info!("Operator Registration Status {:?}", is_registered);
Ok(())
}