use alloy::primitives::Address;
use crate::onchain::{NetworkConfig, OnchainError, Result};
use super::client::OnchainClient;
use super::provider::OnchainProvider;
use super::signer::OnchainSigner;
#[derive(Debug)]
pub struct OnchainClientBuilder {
network: NetworkConfig,
signer: Option<OnchainSigner>,
}
impl OnchainClientBuilder {
pub fn new() -> Self {
Self {
network: NetworkConfig::polygon_mainnet(),
signer: None,
}
}
pub fn network(mut self, network: NetworkConfig) -> Self {
self.network = network;
self
}
pub fn mainnet(mut self) -> Self {
self.network = NetworkConfig::polygon_mainnet();
self
}
pub fn testnet(mut self) -> Self {
self.network = NetworkConfig::polygon_amoy();
self
}
pub fn private_key(mut self, private_key: impl AsRef<str>) -> Result<Self> {
self.signer = Some(OnchainSigner::from_private_key(private_key)?);
Ok(self)
}
pub fn proxy_wallet(mut self, proxy_address: Address) -> Result<Self> {
let private_key_signer = self
.signer
.as_ref()
.ok_or_else(|| {
OnchainError::ConfigurationError(
"Must call .private_key() before .proxy_wallet()".to_string(),
)
})?
.private_key_signer()
.clone();
self.signer = Some(OnchainSigner::Proxy {
signer: private_key_signer,
proxy_address,
});
Ok(self)
}
pub fn safe_wallet(
mut self,
safe_address: Address,
threshold: Option<u64>,
) -> Result<Self> {
let private_key_signer = self
.signer
.as_ref()
.ok_or_else(|| {
OnchainError::ConfigurationError(
"Must call .private_key() before .safe_wallet()".to_string(),
)
})?
.private_key_signer()
.clone();
self.signer = Some(OnchainSigner::Safe {
signer: private_key_signer,
safe_address,
threshold: threshold.unwrap_or(1),
});
Ok(self)
}
pub fn signer(mut self, signer: OnchainSigner) -> Self {
self.signer = Some(signer);
self
}
pub fn build(self) -> Result<OnchainClient> {
let provider = OnchainProvider::new(&self.network)?;
let signer = self.signer.ok_or_else(|| {
OnchainError::ConfigurationError("Must specify a signer".to_string())
})?;
Ok(OnchainClient::new(provider, signer))
}
}
impl Default for OnchainClientBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
const TEST_PRIVATE_KEY: &str =
"0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
#[test]
fn test_builder_new() {
let builder = OnchainClientBuilder::new();
assert_eq!(builder.network.chain_id, 137); assert!(builder.signer.is_none());
}
#[test]
fn test_builder_mainnet() {
let builder = OnchainClientBuilder::new().mainnet();
assert_eq!(builder.network.chain_id, 137);
}
#[test]
fn test_builder_testnet() {
let builder = OnchainClientBuilder::new().testnet();
assert_eq!(builder.network.chain_id, 80002);
}
#[test]
fn test_builder_private_key() {
let builder = OnchainClientBuilder::new()
.private_key(TEST_PRIVATE_KEY)
.unwrap();
assert!(builder.signer.is_some());
}
#[test]
fn test_builder_proxy_wallet() {
let proxy_address = Address::repeat_byte(0x42);
let builder = OnchainClientBuilder::new()
.private_key(TEST_PRIVATE_KEY)
.unwrap()
.proxy_wallet(proxy_address)
.unwrap();
assert!(builder.signer.is_some());
if let Some(signer) = &builder.signer {
assert!(signer.is_smart_wallet());
assert_eq!(signer.proxy_address(), Some(proxy_address));
}
}
#[test]
fn test_builder_safe_wallet() {
let safe_address = Address::repeat_byte(0x99);
let builder = OnchainClientBuilder::new()
.private_key(TEST_PRIVATE_KEY)
.unwrap()
.safe_wallet(safe_address, Some(2))
.unwrap();
assert!(builder.signer.is_some());
if let Some(signer) = &builder.signer {
assert!(signer.is_smart_wallet());
assert_eq!(signer.safe_address(), Some(safe_address));
assert_eq!(signer.safe_threshold(), Some(2));
}
}
#[test]
fn test_builder_proxy_without_private_key() {
let proxy_address = Address::repeat_byte(0x42);
let result = OnchainClientBuilder::new().proxy_wallet(proxy_address);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Must call .private_key() before .proxy_wallet()"));
}
#[test]
fn test_builder_safe_without_private_key() {
let safe_address = Address::repeat_byte(0x99);
let result = OnchainClientBuilder::new().safe_wallet(safe_address, Some(2));
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Must call .private_key() before .safe_wallet()"));
}
#[test]
fn test_builder_missing_signer() {
let result = OnchainClientBuilder::new().mainnet().build();
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("Must specify a signer"));
}
}