diff --git a/Cargo.toml b/Cargo.toml index a063a71..a80c7d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,10 @@ repository = "https://github.com/init4tech/bin-base" [dependencies] init4-from-env-derive = "0.1.0" + +# Signet +signet-constants = { git = "https://github.com/init4tech/signet-sdk.git", branch = "main" } + # Tracing tracing = "0.1.40" tracing-core = "0.1.33" @@ -45,7 +49,6 @@ alloy = { version = "0.12.6", optional = true, default-features = false, feature serde = { version = "1", features = ["derive"] } async-trait = { version = "0.1.80", optional = true } - # AWS aws-config = { version = "1.1.7", optional = true } aws-sdk-kms = { version = "1.15.0", optional = true } diff --git a/src/lib.rs b/src/lib.rs index a2bd866..c85a41b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,10 @@ pub mod utils { /// [`FromEnvVar`]: from_env::FromEnvVar pub mod from_env; + #[cfg(feature = "alloy")] + /// SignetConstants variables FromEnv + pub mod constants_from_env; + /// Prometheus metrics utilities. pub mod metrics; diff --git a/src/utils/constants_from_env.rs b/src/utils/constants_from_env.rs new file mode 100644 index 0000000..04389e3 --- /dev/null +++ b/src/utils/constants_from_env.rs @@ -0,0 +1,344 @@ +use crate::utils::from_env::{EnvItemInfo, FromEnv, FromEnvErr, FromEnvVar}; +use alloy::primitives::{hex::FromHexError, Address}; +use signet_constants::{ + HostConstants, ParseChainError, PredeployTokens, RollupConstants, SignetConstants, + SignetEnvironmentConstants, SignetSystemConstants, +}; +use std::{borrow::Cow, num::ParseIntError}; + +/// EnvItemInfo for .env variable holding chain name +/// Used to implement FromEnv for SignetConstants type structs +/// that can be instantiated from a single chain name +const CHAIN_NAME: EnvItemInfo = EnvItemInfo { + var: "CHAIN_NAME", + description: "The name of the chain, e.g. `pecorino`. If CHAIN_NAME is present, the known, hard-coded constants for the chain will be loaded from the SDK. If CHAIN_NAME is not present, each constant will be loaded from environment variables.", + optional: true, +}; + +// --- RollupConstants --- +const ROLLUP_CHAIN_ID: &str = "ROLLUP_CHAIN_ID"; +const ROLLUP_BASE_FEE_RECIPIENT: &str = "ROLLUP_BASE_FEE_RECIPIENT"; +const ROLLUP_ORDERS: &str = "ROLLUP_ORDERS"; +const ROLLUP_PASSAGE: &str = "ROLLUP_PASSAGE"; +const ROLLUP_USDC: &str = "ROLLUP_USDC"; +const ROLLUP_USDT: &str = "ROLLUP_USDT"; +const ROLLUP_WBTC: &str = "ROLLUP_WBTC"; +// --- HostConstants --- +const HOST_CHAIN_ID: &str = "HOST_CHAIN_ID"; +const HOST_DEPLOY_HEIGHT: &str = "HOST_DEPLOY_HEIGHT"; +const HOST_ZENITH: &str = "HOST_ZENITH"; +const HOST_ORDERS: &str = "HOST_ORDERS"; +const HOST_PASSAGE: &str = "HOST_PASSAGE"; +const HOST_TRANSACTOR: &str = "HOST_TRANSACTOR"; +const HOST_USDC: &str = "HOST_USDC"; +const HOST_USDT: &str = "HOST_USDT"; +const HOST_WBTC: &str = "HOST_WBTC"; +// --- SignetEnvironmentConstants --- +const SIGNET_HOST_NAME: &str = "SIGNET_HOST_NAME"; +const SIGNET_ROLLUP_NAME: &str = "SIGNET_ROLLUP_NAME"; +const SIGNET_TRANSACTION_CACHE: &str = "SIGNET_TRANSACTION_CACHE"; + +/// Error type for parsing SignetConstants from environment variables. +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +pub enum ConstantsFromEnvError { + /// Error parsing a chain name. + #[error(transparent)] + ParseChainError(#[from] ParseChainError), + /// Error parsing a u64. + #[error(transparent)] + ParseIntError(#[from] ParseIntError), + /// Error parsing a hex string. + #[error(transparent)] + FromHexError(#[from] FromHexError), +} + +impl From> for FromEnvErr { + fn from(e: FromEnvErr) -> Self { + match e { + FromEnvErr::ParseError(i) => FromEnvErr::ParseError(i.into()), + FromEnvErr::Empty(i) => FromEnvErr::Empty(i), + FromEnvErr::EnvError(var, err) => FromEnvErr::EnvError(var, err), + } + } +} + +impl From> for FromEnvErr { + fn from(e: FromEnvErr) -> Self { + match e { + FromEnvErr::ParseError(i) => FromEnvErr::ParseError(i.into()), + FromEnvErr::Empty(i) => FromEnvErr::Empty(i), + FromEnvErr::EnvError(var, err) => FromEnvErr::EnvError(var, err), + } + } +} + +impl From> for FromEnvErr { + fn from(e: FromEnvErr) -> Self { + match e { + FromEnvErr::ParseError(i) => FromEnvErr::ParseError(i.into()), + FromEnvErr::Empty(i) => FromEnvErr::Empty(i), + FromEnvErr::EnvError(var, err) => FromEnvErr::EnvError(var, err), + } + } +} + +impl FromEnv for RollupConstants { + type Error = ConstantsFromEnvError; + + fn inventory() -> Vec<&'static EnvItemInfo> { + vec![ + &CHAIN_NAME, + &EnvItemInfo { + var: ROLLUP_CHAIN_ID, + description: "Rollup chain ID.", + optional: false, + }, + &EnvItemInfo { + var: ROLLUP_BASE_FEE_RECIPIENT, + description: "Rollup address of the base fee recipient.", + optional: false, + }, + &EnvItemInfo { + var: ROLLUP_ORDERS, + description: "Rollup address of the orders contract.", + optional: false, + }, + &EnvItemInfo { + var: ROLLUP_PASSAGE, + description: "Rollup address of the passage contract.", + optional: false, + }, + &EnvItemInfo { + var: ROLLUP_USDC, + description: "Rollup address of usdc token.", + optional: false, + }, + &EnvItemInfo { + var: ROLLUP_USDT, + description: "Rollup address of usdt token.", + optional: false, + }, + &EnvItemInfo { + var: ROLLUP_WBTC, + description: "Rollup address of wbtc token.", + optional: false, + }, + ] + } + + fn from_env() -> Result> { + match Self::from_env_var(CHAIN_NAME.var) { + Ok(c) => Ok(c), + Err(e) => { + match e { + // if chain name is present but malformed, propagate the error + FromEnvErr::ParseError(_) => Err(e.into()), + // if the chain name is empty or missing, + // instantiate each prop from env vars + FromEnvErr::EnvError(_, _) | FromEnvErr::Empty(_) => Ok(RollupConstants::new( + u64::from_env_var(ROLLUP_CHAIN_ID)?, + Address::from_env_var(ROLLUP_ORDERS)?, + Address::from_env_var(ROLLUP_PASSAGE)?, + Address::from_env_var(ROLLUP_BASE_FEE_RECIPIENT)?, + PredeployTokens::new( + Address::from_env_var(ROLLUP_USDC)?, + Address::from_env_var(ROLLUP_USDT)?, + Address::from_env_var(ROLLUP_WBTC)?, + ), + )), + } + } + } + } +} + +impl FromEnv for HostConstants { + type Error = ConstantsFromEnvError; + + fn inventory() -> Vec<&'static EnvItemInfo> { + vec![ + &CHAIN_NAME, + &EnvItemInfo { + var: HOST_CHAIN_ID, + description: "Host chain ID.", + optional: false, + }, + &EnvItemInfo { + var: HOST_DEPLOY_HEIGHT, + description: "Height at which the host chain deployed the rollup contracts.", + optional: false, + }, + &EnvItemInfo { + var: HOST_ZENITH, + description: "Host address for the zenith contract", + optional: false, + }, + &EnvItemInfo { + var: HOST_ORDERS, + description: "Host address for the orders contract", + optional: false, + }, + &EnvItemInfo { + var: HOST_PASSAGE, + description: "Host address for the passage contract", + optional: false, + }, + &EnvItemInfo { + var: HOST_TRANSACTOR, + description: "Host address for the transactor contract", + optional: false, + }, + &EnvItemInfo { + var: HOST_USDC, + description: "Host address for the USDC token", + optional: false, + }, + &EnvItemInfo { + var: HOST_USDT, + description: "Host address for the USDT token", + optional: false, + }, + &EnvItemInfo { + var: HOST_WBTC, + description: "Host address for the WBTC token", + optional: false, + }, + ] + } + + fn from_env() -> Result> { + match Self::from_env_var(CHAIN_NAME.var) { + Ok(c) => Ok(c), + Err(e) => { + match e { + // if chain name is present but malformed, propagate the error + FromEnvErr::ParseError(_) => Err(e.into()), + // if the chain name is empty or missing, + // instantiate each prop from env vars + FromEnvErr::EnvError(_, _) | FromEnvErr::Empty(_) => Ok(HostConstants::new( + u64::from_env_var(HOST_CHAIN_ID)?, + u64::from_env_var(HOST_DEPLOY_HEIGHT)?, + Address::from_env_var(HOST_ZENITH)?, + Address::from_env_var(HOST_ORDERS)?, + Address::from_env_var(HOST_PASSAGE)?, + Address::from_env_var(HOST_TRANSACTOR)?, + PredeployTokens::new( + Address::from_env_var(HOST_USDC)?, + Address::from_env_var(HOST_USDT)?, + Address::from_env_var(HOST_WBTC)?, + ), + )), + } + } + } + } +} + +impl FromEnv for SignetEnvironmentConstants { + type Error = ConstantsFromEnvError; + + fn inventory() -> Vec<&'static EnvItemInfo> { + vec![ + &CHAIN_NAME, + &EnvItemInfo { + var: SIGNET_HOST_NAME, + description: "Name of the host chain.", + optional: false, + }, + &EnvItemInfo { + var: SIGNET_ROLLUP_NAME, + description: "Name of the rollup.", + optional: false, + }, + &EnvItemInfo { + var: SIGNET_TRANSACTION_CACHE, + description: "URL of the Transaction Cache", + optional: false, + }, + ] + } + + fn from_env() -> Result> { + match Self::from_env_var(CHAIN_NAME.var) { + Ok(c) => Ok(c), + Err(e) => { + match e { + // if chain name is present but malformed, propagate the error + FromEnvErr::ParseError(_) => Err(e.into()), + // if the chain name is empty or missing, + // instantiate each prop from env vars + FromEnvErr::EnvError(_, _) | FromEnvErr::Empty(_) => { + Ok(SignetEnvironmentConstants::new( + Cow::from_env_var(SIGNET_HOST_NAME) + .map_err(|e| e.infallible_into::())?, + Cow::from_env_var(SIGNET_ROLLUP_NAME) + .map_err(|e| e.infallible_into::())?, + Cow::from_env_var(SIGNET_TRANSACTION_CACHE) + .map_err(|e| e.infallible_into::())?, + )) + } + } + } + } + } +} + +impl FromEnv for SignetSystemConstants { + type Error = ConstantsFromEnvError; + + fn inventory() -> Vec<&'static EnvItemInfo> { + let mut inventory = Vec::new(); + inventory.extend_from_slice(&HostConstants::inventory()); + inventory.extend_from_slice(&RollupConstants::inventory()[1..]); + inventory + } + + fn from_env() -> Result> { + match Self::from_env_var(CHAIN_NAME.var) { + Ok(c) => Ok(c), + Err(e) => { + match e { + // if chain name is present but malformed, propagate the error + FromEnvErr::ParseError(_) => Err(e.into()), + // if the chain name is empty or missing, + // instantiate each prop from env vars + FromEnvErr::EnvError(_, _) | FromEnvErr::Empty(_) => { + Ok(SignetSystemConstants::new( + HostConstants::from_env()?, + RollupConstants::from_env()?, + )) + } + } + } + } + } +} + +impl FromEnv for SignetConstants { + type Error = ConstantsFromEnvError; + + fn inventory() -> Vec<&'static EnvItemInfo> { + let mut inventory = Vec::new(); + inventory.extend_from_slice(&SignetSystemConstants::inventory()); + inventory.extend_from_slice(&SignetEnvironmentConstants::inventory()[1..]); + inventory + } + + fn from_env() -> Result> { + match Self::from_env_var(CHAIN_NAME.var) { + Ok(c) => Ok(c), + Err(e) => { + match e { + // if chain name is present but malformed, propagate the error + FromEnvErr::ParseError(_) => Err(e.into()), + // if the chain name is empty or missing, + // instantiate each prop from env vars + FromEnvErr::EnvError(_, _) | FromEnvErr::Empty(_) => Ok(SignetConstants::new( + SignetSystemConstants::from_env()?, + SignetEnvironmentConstants::from_env()?, + )), + } + } + } + } +} diff --git a/src/utils/from_env.rs b/src/utils/from_env.rs index f60578c..df26f79 100644 --- a/src/utils/from_env.rs +++ b/src/utils/from_env.rs @@ -1,3 +1,7 @@ +use signet_constants::{ + HostConstants, RollupConstants, SignetConstants, SignetEnvironmentConstants, + SignetSystemConstants, +}; use std::{convert::Infallible, env::VarError, num::ParseIntError, str::FromStr}; /// The `derive(FromEnv)` macro. @@ -609,7 +613,12 @@ impl_for_parseable!( i128, isize, url::Url, - tracing::Level + tracing::Level, + SignetConstants, + SignetEnvironmentConstants, + SignetSystemConstants, + HostConstants, + RollupConstants ); #[cfg(feature = "alloy")]