newton-core 0.4.16

newton protocol core sdk
//! config
use std::path::PathBuf;

use crate::{
    common::chain,
    config::{
        contracts::{get_deployment_env, ContractsConfig},
        ipfs::IpfsConfig,
        rpc::ChainRpcProviderConfig,
    },
};
/// Contracts Config
pub mod contracts;
/// Data Provider Config
pub mod data_provider;
pub use data_provider::DataProviderConfig;
/// Encryption Config
pub mod encryption;
pub use encryption::EncryptionConfig;
/// Environment variables
pub mod dotenv;
/// Config Error
pub mod error;
/// IPFS Config
pub mod ipfs;
/// Key Config
pub mod key;
/// Config Loader Trait
pub mod loader;
pub use loader::ConfigLoader;
use tracing::info;
/// Log Config
pub mod log;
/// Macros
pub mod macros;
/// Multi-chain Config
pub mod multichain;
pub use multichain::MultiChainConfig;
/// RPC Config
pub mod rpc;

/// Root configuration for the Newton AVS service.
///
/// This struct holds configuration for both source chains (where EigenLayer is deployed)
/// and destination chains (L2s where policies are executed). The `rpc` field contains
/// RPC configurations for all supported chains, allowing automatic derivation of
/// source chain RPC from destination chain configuration.
#[derive(Debug, Clone)]
pub struct NewtonAvsConfig<T: PartialEq + Eq + Clone + ConfigLoader> {
    /// Deployment environment name (e.g. "local", "stage", "prod").
    pub env: String,
    /// Primary chain identifier for this configuration (destination chain in multichain mode).
    pub chain_id: u64,
    /// Source chain ID where the AVS is registered (EigenLayer chain).
    /// Set when operating on a destination chain; None for source chain operation.
    pub source_chain_id: Option<u64>,
    /// RPC provider configurations for all supported chains.
    /// Use `rpc.get(chain_id)` to retrieve configuration for a specific chain.
    pub rpc: ChainRpcProviderConfig,
    /// IPFS configuration for content addressing and retrieval.
    pub ipfs: IpfsConfig,
    /// On-chain contract addresses used by the AVS.
    pub contracts: ContractsConfig,
    /// Service-specific configuration loaded via `ConfigLoader`.
    pub service: T,
    /// Data provider configuration for WASM execution and caching.
    /// Loaded separately from service config to allow shared configuration across services.
    pub data_provider: DataProviderConfig,
}

/// Builder for constructing a `NewtonAvsConfig` with optional overrides.
#[derive(Debug)]
pub struct NewtonAvsConfigBuilder {
    /// EVM chain identifier used for this configuration.
    chain_id: u64,
    /// Optional path to a service-specific configuration file.
    service_config_path: Option<PathBuf>,
    /// Optional path to a data provider configuration file.
    data_provider: Option<PathBuf>,
    /// When true, skip loading contract addresses from deployment JSONs
    /// and use `Default::default()` (all `Address::ZERO`). Useful for
    /// tools like newton-cli that talk to the gateway via RPC and never
    /// call contracts directly.
    skip_contracts: bool,
}

impl NewtonAvsConfigBuilder {
    /// Create a new builder for the given `chain_id`.
    pub fn new(chain_id: u64) -> Self {
        Self {
            chain_id,
            service_config_path: None,
            data_provider: None,
            skip_contracts: false,
        }
    }

    /// Set the path to the service-specific configuration file.
    pub fn with_service_path(mut self, path: PathBuf) -> Self {
        self.service_config_path = Some(path);
        self
    }

    /// Set the path to the data provider configuration file.
    ///
    /// If not provided, DataProviderConfig will be loaded from the default
    /// `data-provider.toml` file or use built-in defaults.
    pub fn with_data_provider_path(mut self, path: PathBuf) -> Self {
        self.data_provider = Some(path);
        self
    }

    /// Skip loading contract addresses from deployment JSONs.
    ///
    /// All contract addresses will be `Address::ZERO`. Use this for tools
    /// (like newton-cli) that talk to the gateway via RPC and never call
    /// contracts directly.
    pub fn skip_contracts(mut self) -> Self {
        self.skip_contracts = true;
        self
    }

    /// Build a fully loaded `NewtonAvsConfig` for the generic service type `T`.
    pub fn build<T: PartialEq + Eq + Clone + ConfigLoader>(self) -> Result<NewtonAvsConfig<T>, std::io::Error> {
        let service = T::load_config(self.service_config_path).map_err(|e| std::io::Error::other(e.to_string()))?;
        let data_provider =
            DataProviderConfig::load_config(self.data_provider).map_err(|e| std::io::Error::other(e.to_string()))?;
        let config = NewtonAvsConfig::load(self.chain_id, service, data_provider, self.skip_contracts)?;
        Ok(config)
    }
}

impl<T: PartialEq + Eq + Clone + ConfigLoader> NewtonAvsConfig<T> {
    /// Load the full AVS configuration for the provided `chain_id`, service config, and data provider config.
    fn load(
        chain_id: u64,
        service: T,
        data_provider: DataProviderConfig,
        skip_contracts: bool,
    ) -> Result<Self, std::io::Error> {
        info!("Loading config for chain_id: {}", chain_id);
        crate::config::dotenv::init().ok();

        let env = get_deployment_env();
        let rpc = ChainRpcProviderConfig::load();

        let contracts = if skip_contracts {
            ContractsConfig::default()
        } else if chain::is_destination_chain(chain_id) {
            let src_id = chain::get_source_chain_id(chain_id).ok_or_else(|| {
                std::io::Error::other(format!("no source chain mapping for destination chain {}", chain_id))
            })?;
            ContractsConfig::load(src_id, env.clone())?.with_destination_chain_id(chain_id, &env)?
        } else {
            ContractsConfig::load(chain_id, env.clone())?
        };

        Ok(Self {
            chain_id,
            source_chain_id: chain::get_source_chain_id(chain_id),
            env,
            rpc,
            ipfs: IpfsConfig::default(),
            contracts,
            service,
            data_provider,
        })
    }

    /// Returns true if this chain is a destination chain in the multichain setup.
    ///
    /// Chain type is determined purely from the chain ID, mirroring ChainLib.sol logic.
    /// This ensures consistent chain classification across Rust and Solidity code.
    ///
    /// Destination chains use EIP712 structured signing with the certificate verifier,
    /// while source chains use simple keccak256 hash signing.
    pub fn is_destination_chain(&self) -> bool {
        chain::is_destination_chain(self.chain_id)
    }

    /// Returns true if this chain is a source chain (where EigenLayer is deployed).
    pub fn is_source_chain(&self) -> bool {
        chain::is_source_chain(self.chain_id)
    }

    /// Returns the source chain ID for this deployment.
    ///
    /// For source chains, returns the chain's own ID.
    /// For destination chains, returns the mapped source chain ID.
    pub fn get_source_chain_id(&self) -> u64 {
        chain::get_source_chain_id(self.chain_id).unwrap_or(self.chain_id)
    }
}