use std::str::FromStr;
use alloy_primitives::Address;
use tracing::{error, info};
include!(concat!(env!("OUT_DIR"), "/embedded_deployments.rs"));
pub fn load_deployment_json(category: &str, chain_id: u64, env: &str) -> Result<String, std::io::Error> {
if let Ok(dir) = std::env::var("DEPLOYMENT_DIR") {
let path = format!("{}/{}/{}-{}.json", dir, category, chain_id, env);
info!(path = %path, "loading deployment from DEPLOYMENT_DIR override");
return std::fs::read_to_string(&path).map_err(|e| {
error!(path = %path, err = %e, "DEPLOYMENT_DIR set but file unreadable");
std::io::Error::new(e.kind(), format!("DEPLOYMENT_DIR={dir} set but {path} unreadable: {e}"))
});
}
get_embedded_deployment(category, chain_id, env)
.map(|s| s.to_string())
.ok_or_else(|| {
error!(
category,
chain_id, env, "no embedded deployment found — set DEPLOYMENT_DIR for custom chains"
);
std::io::Error::other(format!(
"No embedded deployment found for {}/{}-{}.json. \
Set DEPLOYMENT_DIR to a directory containing deployment JSONs for custom chains.",
category, chain_id, env
))
})
}
#[derive(Debug, Clone, Default)]
pub struct ContractsConfig {
pub avs: NewtonAvsContractsConfig,
pub eigenlayer: EigenlayerContractsConfig,
pub policy: NewtonPolicyContractsConfig,
pub destination_multichain: Option<DestinationChainMultichainContracts>,
}
impl ContractsConfig {
pub fn load(chain_id: u64, env: String) -> Result<Self, std::io::Error> {
info!(
"Loading contracts config for chain ID {} and environment {}",
chain_id, env
);
Ok(Self {
avs: NewtonAvsContractsConfig::load_src(chain_id, &env)?,
eigenlayer: EigenlayerContractsConfig::load(chain_id, &env)?,
policy: NewtonPolicyContractsConfig::load(chain_id, &env)?,
destination_multichain: None,
})
}
pub fn with_destination_chain_id(self, chain_id: u64, env: &str) -> Result<Self, std::io::Error> {
let eigenlayer = self.eigenlayer;
let policy = self.policy;
let mut avs = NewtonAvsContractsConfig::load_dest(chain_id, env)?;
avs.merge_source_for_dest(&self.avs);
avs.validate_for_role(ChainRole::Destination)?;
let dest_multichain = DestinationChainMultichainContracts::load(chain_id, env)?;
Ok(Self {
avs,
eigenlayer,
policy,
destination_multichain: Some(dest_multichain),
})
}
pub fn destination_multichain(&self) -> Result<&DestinationChainMultichainContracts, std::io::Error> {
self.destination_multichain.as_ref().ok_or_else(|| {
std::io::Error::other(
"destination_multichain_contracts not configured - use with_destination_chain_id() for multichain mode",
)
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ChainRole {
Source,
Destination,
}
#[derive(Debug, Clone, Default)]
pub struct NewtonAvsContractsConfig {
pub newton_prover_service_manager: Option<Address>,
pub newton_prover_task_manager: Address,
pub challenge_verifier: Address,
pub rego_verifier: Address,
pub attestation_validator: Address,
pub operator_registry: Address,
pub operator_state_retriever: Option<Address>,
pub bls_apk_registry: Option<Address>,
pub index_registry: Option<Address>,
pub stake_registry: Option<Address>,
pub socket_registry: Address,
pub strategy: Option<Address>,
pub identity_registry: Address,
pub confidential_data_registry: Address,
pub batch_task_manager: Address,
pub epoch_registry: Option<Address>,
pub enclave_version_registry: Option<Address>,
pub state_commit_registry: Option<Address>,
pub policy_client_registry: Address,
}
impl NewtonAvsContractsConfig {
pub fn state_commit_registry_or_zero(&self) -> Address {
self.state_commit_registry.unwrap_or(Address::ZERO)
}
pub fn validate_for_role(&self, role: ChainRole) -> Result<(), std::io::Error> {
let missing = |field: &str| {
std::io::Error::other(format!(
"{:?} role requires {} but it is None — check deployment JSON or merge_source_for_dest",
role, field
))
};
if self.newton_prover_service_manager.is_none() {
return Err(missing("newton_prover_service_manager"));
}
if self.bls_apk_registry.is_none() {
return Err(missing("bls_apk_registry"));
}
if self.index_registry.is_none() {
return Err(missing("index_registry"));
}
if self.stake_registry.is_none() {
return Err(missing("stake_registry"));
}
if self.operator_state_retriever.is_none() {
return Err(missing("operator_state_retriever"));
}
if self.strategy.is_none() {
return Err(missing("strategy"));
}
Ok(())
}
fn merge_source_for_dest(&mut self, src: &Self) {
self.newton_prover_service_manager = src.newton_prover_service_manager;
self.bls_apk_registry = src.bls_apk_registry;
self.index_registry = src.index_registry;
self.stake_registry = src.stake_registry;
self.operator_state_retriever = src.operator_state_retriever;
self.strategy = src.strategy;
self.socket_registry = src.socket_registry;
}
pub fn load_src(chain_id: u64, env: &str) -> Result<Self, std::io::Error> {
let cfg = Self::load(chain_id, false, env)?;
cfg.validate_for_role(ChainRole::Source)?;
Ok(cfg)
}
pub fn load_dest(chain_id: u64, env: &str) -> Result<Self, std::io::Error> {
Self::load(chain_id, true, env)
}
fn load(chain_id: u64, is_destination_chain: bool, env: &str) -> Result<Self, std::io::Error> {
let category = if !is_destination_chain {
"newton-prover"
} else {
"newton-cross-chain"
};
info!(
"[NewtonAvsContractsConfig] Loading deployment for {}/{}-{}",
category, chain_id, env
);
let raw = load_deployment_json(category, chain_id, env)?;
let json: serde_json::Value = serde_json::from_str(&raw)?;
let addrs = &json["addresses"];
let required = |key: &str| -> Result<Address, std::io::Error> {
let s = addrs[key]
.as_str()
.ok_or_else(|| std::io::Error::other(format!("{} address not found", key)))?;
Address::from_str(s).map_err(|e| std::io::Error::other(format!("Failed to parse {} address: {}", key, e)))
};
let optional = |key: &str| -> Option<Address> {
addrs[key]
.as_str()
.and_then(|s| Address::from_str(s).ok())
.filter(|addr| *addr != Address::ZERO)
};
Ok(Self {
newton_prover_task_manager: required("newtonProverTaskManager")?,
challenge_verifier: required("challengeVerifier")?,
rego_verifier: required("regoVerifier")?,
attestation_validator: required("attestationValidator")?,
operator_registry: required("operatorRegistry")?,
socket_registry: required("socketRegistry")?,
identity_registry: required("identityRegistry")?,
confidential_data_registry: required("confidentialDataRegistry")?,
batch_task_manager: required("batchTaskManager")?,
policy_client_registry: required("policyClientRegistry")?,
newton_prover_service_manager: optional("newtonProverServiceManager"),
operator_state_retriever: optional("operatorStateRetriever"),
bls_apk_registry: optional("blsApkRegistry"),
index_registry: optional("indexRegistry"),
stake_registry: optional("stakeRegistry"),
strategy: optional("strategy"),
epoch_registry: optional("epochRegistry"),
enclave_version_registry: optional("enclaveVersionRegistry"),
state_commit_registry: optional("stateCommitRegistry"),
})
}
}
#[derive(Debug, Clone, Default)]
pub struct NewtonPolicyContractsConfig {
pub policy_factory: Address,
pub policy_data_factory: Address,
}
impl NewtonPolicyContractsConfig {
pub fn load(chain_id: u64, env: &str) -> Result<Self, std::io::Error> {
info!(
"[NewtonPolicyContractsConfig] Loading deployment for policy/{}-{}",
chain_id, env
);
let raw = load_deployment_json("policy", chain_id, env)?;
let newton_policy_deployment_data: serde_json::Value = serde_json::from_str(&raw)?;
Ok(Self {
policy_factory: Address::from_str(
newton_policy_deployment_data["addresses"]["policyFactory"]
.as_str()
.ok_or_else(|| std::io::Error::other("policyFactory address not found"))?,
)
.map_err(|e| std::io::Error::other(format!("Failed to parse policy factory address: {}", e)))?,
policy_data_factory: Address::from_str(
newton_policy_deployment_data["addresses"]["policyDataFactory"]
.as_str()
.ok_or_else(|| std::io::Error::other("policyDataFactory address not found"))?,
)
.map_err(|e| std::io::Error::other(format!("Failed to parse policy data factory address: {}", e)))?,
})
}
}
#[derive(Debug, Clone, Default)]
pub struct EigenlayerContractsConfig {
pub delegation_manager: Address,
pub avs_directory: Address,
pub strategy_manager: Address,
pub allocation_manager: Address,
pub rewards_coordinator: Address,
pub strategy_factory: Address,
pub permission_controller: Address,
pub bn254_certificate_verifier: Address,
pub key_registrar: Address,
}
impl EigenlayerContractsConfig {
pub fn load(chain_id: u64, env: &str) -> Result<Self, std::io::Error> {
info!(
"[EigenlayerContractsConfig] Loading deployment for core/{}-{}",
chain_id, env
);
let raw = load_deployment_json("core", chain_id, env)?;
let eigenlayer_deployment_data: serde_json::Value = serde_json::from_str(&raw)?;
Ok(Self {
delegation_manager: Address::from_str(
eigenlayer_deployment_data["addresses"]["delegation"]
.as_str()
.unwrap_or_default(),
)
.unwrap_or_default(),
avs_directory: Address::from_str(
eigenlayer_deployment_data["addresses"]["avsDirectory"]
.as_str()
.unwrap_or_default(),
)
.unwrap_or_default(),
strategy_manager: Address::from_str(
eigenlayer_deployment_data["addresses"]["strategyManager"]
.as_str()
.unwrap_or_default(),
)
.unwrap_or_default(),
allocation_manager: Address::from_str(
eigenlayer_deployment_data["addresses"]["allocationManager"]
.as_str()
.unwrap_or_default(),
)
.unwrap_or_default(),
rewards_coordinator: Address::from_str(
eigenlayer_deployment_data["addresses"]["rewardsCoordinator"]
.as_str()
.unwrap_or_default(),
)
.unwrap_or_default(),
strategy_factory: Address::from_str(
eigenlayer_deployment_data["addresses"]["strategyFactory"]
.as_str()
.unwrap_or_default(),
)
.unwrap_or_default(),
permission_controller: Address::from_str(
eigenlayer_deployment_data["addresses"]["permissionController"]
.as_str()
.unwrap_or_default(),
)
.unwrap_or_default(),
bn254_certificate_verifier: Address::from_str(
eigenlayer_deployment_data["addresses"]["bn254CertificateVerifier"]
.as_str()
.unwrap_or_default(),
)
.unwrap_or_default(),
key_registrar: Address::from_str(
eigenlayer_deployment_data["addresses"]["keyRegistrar"]
.as_str()
.unwrap_or_default(),
)
.unwrap_or_default(),
})
}
}
pub fn get_deployment_env() -> String {
std::env::var("DEPLOYMENT_ENV").unwrap_or_else(|_| "stagef".to_string())
}
#[derive(Debug, Clone)]
pub struct DestinationChainMultichainContracts {
pub operator_table_updater: Address,
pub bn254_certificate_verifier: Address,
pub ecdsa_certificate_verifier: Address,
}
impl DestinationChainMultichainContracts {
pub fn load(chain_id: u64, env: &str) -> Result<Self, std::io::Error> {
info!(
"[DestinationChainMultichainContracts] Loading deployment for newton-cross-chain/{}-{}",
chain_id, env
);
let raw = load_deployment_json("newton-cross-chain", chain_id, env)?;
let deployment_data: serde_json::Value = serde_json::from_str(&raw)?;
Ok(Self {
operator_table_updater: Address::from_str(
deployment_data["addresses"]["operatorTableUpdater"]
.as_str()
.ok_or_else(|| std::io::Error::other("operatorTableUpdater address not found"))?,
)
.map_err(|e| std::io::Error::other(format!("Failed to parse operator table updater address: {}", e)))?,
bn254_certificate_verifier: Address::from_str(
deployment_data["addresses"]["bn254CertificateVerifier"]
.as_str()
.ok_or_else(|| std::io::Error::other("bn254CertificateVerifier address not found"))?,
)
.map_err(|e| std::io::Error::other(format!("Failed to parse bn254 certificate verifier address: {}", e)))?,
ecdsa_certificate_verifier: Address::from_str(
deployment_data["addresses"]["ecdsaCertificateVerifier"]
.as_str()
.ok_or_else(|| std::io::Error::other("ecdsaCertificateVerifier address not found"))?,
)
.map_err(|e| std::io::Error::other(format!("Failed to parse ecdsa certificate verifier address: {}", e)))?,
})
}
}
#[derive(Debug, Clone)]
pub struct SourceChainMultichainContracts {
pub cross_chain_registry: Address,
pub table_calculator: Address,
}
impl SourceChainMultichainContracts {
pub fn load(chain_id: u64, env: &str) -> Result<Self, std::io::Error> {
info!(
"[SourceChainMultichainContracts] Loading deployment for newton-cross-chain/{}-{}",
chain_id, env
);
let raw = load_deployment_json("newton-cross-chain", chain_id, env)?;
let deployment_data: serde_json::Value = serde_json::from_str(&raw)?;
Ok(Self {
cross_chain_registry: Address::from_str(
deployment_data["addresses"]["crossChainRegistry"]
.as_str()
.ok_or_else(|| std::io::Error::other("crossChainRegistry address not found"))?,
)
.map_err(|e| std::io::Error::other(format!("Failed to parse cross chain registry address: {}", e)))?,
table_calculator: Address::from_str(
deployment_data["addresses"]["operatorTableCalculator"]
.as_str()
.ok_or_else(|| std::io::Error::other("operatorTableCalculator address not found"))?,
)
.map_err(|e| std::io::Error::other(format!("Failed to parse table calculator address: {}", e)))?,
})
}
pub fn try_load(chain_id: u64, env: &str) -> Option<Self> {
Self::load(chain_id, env).ok()
}
}