parent
ec87181f32
commit
6536efb2a0
@ -1,155 +0,0 @@ |
||||
use config::{Config, ConfigError, Environment, File}; |
||||
use std::{collections::HashMap, convert::TryFrom, env}; |
||||
|
||||
use ethers_core::types::Address; |
||||
use ethers_providers::{Http, Provider, Ws}; |
||||
|
||||
use optics_core::traits::{Home, Replica}; |
||||
|
||||
/// Ethereum connection configuration
|
||||
#[derive(Debug, serde::Deserialize)] |
||||
#[serde(tag = "type")] |
||||
pub enum EthereumConf { |
||||
Http { url: String }, |
||||
Ws { url: String }, |
||||
} |
||||
|
||||
impl EthereumConf { |
||||
/// Try to convert this into a home contract
|
||||
async fn try_into_home(&self, slip44: u32, address: Address) -> Result<Box<dyn Home>, String> { |
||||
let b: Box<dyn Home> = match self { |
||||
Self::Http { url } => { |
||||
let provider = Provider::<Http>::try_from(url.as_ref()).map_err(|_| "!url")?; |
||||
Box::new(crate::abis::HomeContract::at( |
||||
slip44, |
||||
address, |
||||
provider.into(), |
||||
)) |
||||
} |
||||
Self::Ws { url } => { |
||||
let ws = Ws::connect(url).await.map_err(|_| "!ws connect")?; |
||||
let provider = Provider::new(ws); |
||||
Box::new(crate::abis::HomeContract::at( |
||||
slip44, |
||||
address, |
||||
provider.into(), |
||||
)) |
||||
} |
||||
}; |
||||
Ok(b) |
||||
} |
||||
|
||||
/// Try to convert this into a replica contract
|
||||
pub async fn try_into_replica( |
||||
&self, |
||||
slip44: u32, |
||||
address: Address, |
||||
) -> Result<Box<dyn Replica>, String> { |
||||
let b: Box<dyn Replica> = match self { |
||||
Self::Http { url } => { |
||||
let provider = Provider::<Http>::try_from(url.as_ref()).map_err(|_| "!url")?; |
||||
Box::new(crate::abis::ReplicaContract::at( |
||||
slip44, |
||||
address, |
||||
provider.into(), |
||||
)) |
||||
} |
||||
Self::Ws { url } => { |
||||
let ws = Ws::connect(url).await.map_err(|_| "!ws connect")?; |
||||
let provider = Provider::new(ws); |
||||
Box::new(crate::abis::ReplicaContract::at( |
||||
slip44, |
||||
address, |
||||
provider.into(), |
||||
)) |
||||
} |
||||
}; |
||||
Ok(b) |
||||
} |
||||
} |
||||
|
||||
/// A connection to _some_ blockchain.
|
||||
///
|
||||
/// Specify the chain name (enum variant) in toml under the `chain` key
|
||||
/// Specify the connection details as a toml object under the `connection` key.
|
||||
#[derive(Debug, serde::Deserialize)] |
||||
#[serde(tag = "chain", content = "connection")] |
||||
pub enum ChainConnection { |
||||
Ethereum(EthereumConf), |
||||
} |
||||
|
||||
/// A chain setup is a slip44 ID, an address on that chain (where the home or
|
||||
/// replica is deployed) and details for connecting to the chain API.
|
||||
#[derive(Debug, serde::Deserialize)] |
||||
pub struct ChainSetup { |
||||
slip44: u32, |
||||
address: String, |
||||
#[serde(flatten)] |
||||
connection: ChainConnection, |
||||
} |
||||
|
||||
impl ChainSetup { |
||||
pub async fn try_into_home(&self) -> Result<Box<dyn Home>, String> { |
||||
match &self.connection { |
||||
ChainConnection::Ethereum(conf) => { |
||||
conf.try_into_home(self.slip44, self.address.parse().map_err(|_| "!address")?) |
||||
.await |
||||
} |
||||
} |
||||
} |
||||
|
||||
pub async fn try_into_replica(&self) -> Result<Box<dyn Replica>, String> { |
||||
match &self.connection { |
||||
ChainConnection::Ethereum(conf) => { |
||||
conf.try_into_replica(self.slip44, self.address.parse().map_err(|_| "!address")?) |
||||
.await |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Settings. Usually this should be treated as a base config and used as
|
||||
/// follows:
|
||||
///
|
||||
/// ```
|
||||
/// use optics_base::settings::*;
|
||||
///
|
||||
/// pub struct OtherSettings { /* anything */ };
|
||||
///
|
||||
/// #[derive(Debug, serde::Deseirialize)]
|
||||
/// pub struct MySettings {
|
||||
/// #[serde(flatten)]
|
||||
/// base_settings: Settings,
|
||||
/// #[serde(flatten)]
|
||||
/// other_settings: (),
|
||||
/// }
|
||||
///
|
||||
/// // Make sure to define MySettings::new()
|
||||
/// impl MySettings {
|
||||
/// fn new() -> Self {
|
||||
/// unimplemented!()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, serde::Deserialize)] |
||||
pub struct Settings { |
||||
pub home: ChainSetup, |
||||
pub replicas: HashMap<String, ChainSetup>, |
||||
} |
||||
|
||||
impl Settings { |
||||
pub fn new() -> Result<Self, ConfigError> { |
||||
let mut s = Config::new(); |
||||
|
||||
s.merge(File::with_name("config/default"))?; |
||||
|
||||
let env = env::var("RUN_MODE").unwrap_or_else(|_| "development".into()); |
||||
s.merge(File::with_name(&format!("config/{}", env)).required(false))?; |
||||
|
||||
// Add in settings from the environment (with a prefix of OPTRELAY)
|
||||
// Eg.. `OPTRELAY_DEBUG=1 would set the `debug` key
|
||||
s.merge(Environment::with_prefix("OPTRELAY"))?; |
||||
|
||||
s.try_into() |
||||
} |
||||
} |
@ -0,0 +1,109 @@ |
||||
use std::convert::TryFrom; |
||||
|
||||
use ethers_core::types::Address; |
||||
|
||||
use optics_core::traits::{Home, Replica}; |
||||
|
||||
/// Ethereum connection configuration
|
||||
#[derive(Debug, serde::Deserialize)] |
||||
#[serde(tag = "type")] |
||||
pub enum EthereumConnection { |
||||
/// HTTP connection details
|
||||
Http { |
||||
/// Fully qualified string to connect to
|
||||
url: String, |
||||
}, |
||||
/// Websocket connection details
|
||||
Ws { |
||||
/// Fully qualified string to connect to
|
||||
url: String, |
||||
}, |
||||
} |
||||
|
||||
// Construct boxed contracts in a big "if-else" chain to handle multiple
|
||||
// combinations of middleware.
|
||||
macro_rules! construct_box_contract { |
||||
($contract:ident, $slip44:expr, $address:expr, $provider:expr, $signer:expr) => {{ |
||||
if let Some(signer) = $signer { |
||||
let provider = ethers_middleware::SignerMiddleware::new($provider, signer); |
||||
Box::new(crate::abis::$contract::at( |
||||
$slip44, |
||||
$address, |
||||
provider.into(), |
||||
)) |
||||
} else { |
||||
Box::new(crate::abis::$contract::at( |
||||
$slip44, |
||||
$address, |
||||
$provider.into(), |
||||
)) |
||||
} |
||||
}}; |
||||
} |
||||
|
||||
macro_rules! construct_ws_box_contract { |
||||
($contract:ident, $slip44:expr, $address:expr, $url:expr, $signer:expr) => {{ |
||||
let ws = ethers_providers::Ws::connect($url) |
||||
.await |
||||
.map_err(|_| "!ws connect")?; |
||||
let provider = ethers_providers::Provider::new(ws); |
||||
construct_box_contract!($contract, $slip44, $address, provider, $signer) |
||||
}}; |
||||
} |
||||
|
||||
macro_rules! construct_http_box_contract { |
||||
($contract:ident, $slip44:expr, $address:expr, $url:expr, $signer:expr) => {{ |
||||
let provider = |
||||
ethers_providers::Provider::<ethers_providers::Http>::try_from($url.as_ref()) |
||||
.map_err(|_| "!url")?; |
||||
|
||||
construct_box_contract!($contract, $slip44, $address, provider, $signer) |
||||
}}; |
||||
} |
||||
|
||||
/// Ethereum configuration
|
||||
#[derive(Debug, serde::Deserialize)] |
||||
pub struct EthereumConf { |
||||
connection: EthereumConnection, |
||||
signer: Option<String>, |
||||
} |
||||
|
||||
impl EthereumConf { |
||||
fn signer(&self) -> Option<ethers_signers::LocalWallet> { |
||||
self.signer.clone().map(|s| s.parse().expect("!valid key")) |
||||
} |
||||
|
||||
/// Try to convert this into a home contract
|
||||
pub async fn try_into_home( |
||||
&self, |
||||
slip44: u32, |
||||
address: Address, |
||||
) -> Result<Box<dyn Home>, String> { |
||||
let b: Box<dyn Home> = match &self.connection { |
||||
EthereumConnection::Http { url } => { |
||||
construct_http_box_contract!(HomeContract, slip44, address, url, self.signer()) |
||||
} |
||||
EthereumConnection::Ws { url } => { |
||||
construct_ws_box_contract!(HomeContract, slip44, address, url, self.signer()) |
||||
} |
||||
}; |
||||
Ok(b) |
||||
} |
||||
|
||||
/// Try to convert this into a replica contract
|
||||
pub async fn try_into_replica( |
||||
&self, |
||||
slip44: u32, |
||||
address: Address, |
||||
) -> Result<Box<dyn Replica>, String> { |
||||
let b: Box<dyn Replica> = match &self.connection { |
||||
EthereumConnection::Http { url } => { |
||||
construct_http_box_contract!(ReplicaContract, slip44, address, url, self.signer()) |
||||
} |
||||
EthereumConnection::Ws { url } => { |
||||
construct_ws_box_contract!(ReplicaContract, slip44, address, url, self.signer()) |
||||
} |
||||
}; |
||||
Ok(b) |
||||
} |
||||
} |
@ -0,0 +1,101 @@ |
||||
use config::{Config, ConfigError, Environment, File}; |
||||
use std::{collections::HashMap, env}; |
||||
|
||||
use optics_core::traits::{Home, Replica}; |
||||
|
||||
/// Ethereum configuration
|
||||
pub mod ethereum; |
||||
|
||||
use ethereum::EthereumConf; |
||||
|
||||
/// A connection to _some_ blockchain.
|
||||
///
|
||||
/// Specify the chain name (enum variant) in toml under the `chain` key
|
||||
/// Specify the connection details as a toml object under the `connection` key.
|
||||
#[derive(Debug, serde::Deserialize)] |
||||
#[serde(tag = "chain", content = "config")] |
||||
pub enum ChainConf { |
||||
/// Ethereum configuration
|
||||
Ethereum(EthereumConf), |
||||
} |
||||
|
||||
/// A chain setup is a slip44 ID, an address on that chain (where the home or
|
||||
/// replica is deployed) and details for connecting to the chain API.
|
||||
#[derive(Debug, serde::Deserialize)] |
||||
pub struct ChainSetup { |
||||
slip44: u32, |
||||
address: String, |
||||
#[serde(flatten)] |
||||
chain: ChainConf, |
||||
} |
||||
|
||||
impl ChainSetup { |
||||
/// Try to convert the chain setting into a Home contract
|
||||
pub async fn try_into_home(&self) -> Result<Box<dyn Home>, String> { |
||||
match &self.chain { |
||||
ChainConf::Ethereum(conf) => { |
||||
conf.try_into_home(self.slip44, self.address.parse().map_err(|_| "!address")?) |
||||
.await |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Try to convert the chain setting into a replica contract
|
||||
pub async fn try_into_replica(&self) -> Result<Box<dyn Replica>, String> { |
||||
match &self.chain { |
||||
ChainConf::Ethereum(conf) => { |
||||
conf.try_into_replica(self.slip44, self.address.parse().map_err(|_| "!address")?) |
||||
.await |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// Settings. Usually this should be treated as a base config and used as
|
||||
/// follows:
|
||||
///
|
||||
/// ```
|
||||
/// use optics_base::settings::*;
|
||||
///
|
||||
/// pub struct OtherSettings { /* anything */ };
|
||||
///
|
||||
/// #[derive(Debug, serde::Deseirialize)]
|
||||
/// pub struct MySettings {
|
||||
/// #[serde(flatten)]
|
||||
/// base_settings: Settings,
|
||||
/// #[serde(flatten)]
|
||||
/// other_settings: (),
|
||||
/// }
|
||||
///
|
||||
/// // Make sure to define MySettings::new()
|
||||
/// impl MySettings {
|
||||
/// fn new() -> Self {
|
||||
/// unimplemented!()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, serde::Deserialize)] |
||||
pub struct Settings { |
||||
/// The home configuration
|
||||
pub home: ChainSetup, |
||||
/// The replica configurations
|
||||
pub replicas: HashMap<String, ChainSetup>, |
||||
} |
||||
|
||||
impl Settings { |
||||
/// Read settings from the config file
|
||||
pub fn new() -> Result<Self, ConfigError> { |
||||
let mut s = Config::new(); |
||||
|
||||
s.merge(File::with_name("config/default"))?; |
||||
|
||||
let env = env::var("RUN_MODE").unwrap_or_else(|_| "development".into()); |
||||
s.merge(File::with_name(&format!("config/{}", env)).required(false))?; |
||||
|
||||
// Add in settings from the environment (with a prefix of OPTRELAY)
|
||||
// Eg.. `OPTRELAY_DEBUG=1 would set the `debug` key
|
||||
s.merge(Environment::with_prefix("OPTRELAY"))?; |
||||
|
||||
s.try_into() |
||||
} |
||||
} |
Loading…
Reference in new issue