use alloy_contract::Error as ContractError;
use alloy_primitives::{Address, U256};
use alloy_provider::network::AnyNetwork;
use alloy_provider::{DynProvider, PendingTransactionError, Provider};
use alloy_signer::Signer;
use async_trait::async_trait;
use crate::chain_handler::{
ChainHandler, ChainHandlerBuildError, ChainHandlerBuilder, ChainHandlerError,
};
use crate::conversions::ProtoConversionError;
use crate::deposit::DepositContext;
use crate::proto::v1;
use crate::spend_attestation::SpendAttestationChainContext;
use crate::{Domain, TridentClientBuilder};
use super::signing;
#[derive(Debug, thiserror::Error)]
pub enum EvmChainError {
#[error("Contract call failed: {0}")]
Contract(#[from] ContractError),
#[error("Transaction pending error: {0}")]
PendingTransaction(#[from] PendingTransactionError),
#[error("Failed to sign spend intent: {0}")]
Signer(alloy_signer::Error),
#[error("Invalid proto conversion: {0}")]
ProtoConversion(#[from] ProtoConversionError),
}
pub(crate) struct EvmChainHandler {
provider: DynProvider<AnyNetwork>,
signer: Box<dyn Signer + Send + Sync>,
deposit_contract: Address,
delivery_contract: Address,
}
impl EvmChainHandler {
pub(crate) fn new(
provider: DynProvider<AnyNetwork>,
signer: Box<dyn Signer + Send + Sync>,
deposit_contract: Address,
delivery_contract: Address,
) -> Self {
Self {
provider,
signer,
deposit_contract,
delivery_contract,
}
}
}
#[async_trait]
impl ChainHandler for EvmChainHandler {
fn delivery_contract(&self) -> v1::MultiChainAddress {
self.delivery_contract.into()
}
fn deposit_contract(&self) -> v1::MultiChainAddress {
self.deposit_contract.into()
}
fn signer(&self) -> v1::MultiChainAddress {
self.signer.address().into()
}
async fn sign_spend_intent(
&self,
intent: v1::SpendIntent,
) -> Result<Vec<u8>, ChainHandlerError> {
signing::sign_spend_intent(self.signer.as_ref(), intent)
.await
.map_err(Into::into)
}
fn prepare_deposit(
&self,
token: v1::MultiChainAddress,
amount: U256,
) -> Result<DepositContext, ChainHandlerError> {
let token_address = Address::try_from(token).map_err(EvmChainError::from)?;
Ok(DepositContext::Evm {
provider: self.provider.clone(),
signer_address: self.signer.address(),
deposit_contract: self.deposit_contract,
token: token_address,
amount,
})
}
fn prepare_spend_attestation(&self) -> SpendAttestationChainContext {
SpendAttestationChainContext::Evm {
provider: self.provider.clone(),
delivery_contract: self.delivery_contract,
signer_address: self.signer.address(),
}
}
}
pub(crate) struct EvmChainHandlerBuilder {
provider: DynProvider<AnyNetwork>,
signer: Box<dyn Signer + Send + Sync>,
}
#[async_trait]
impl ChainHandlerBuilder for EvmChainHandlerBuilder {
async fn build(
self: Box<Self>,
delivery_contract: v1::MultiChainAddress,
deposit_contract: v1::MultiChainAddress,
) -> Result<Box<dyn ChainHandler>, ChainHandlerBuildError> {
let delivery_contract = Address::try_from(delivery_contract)?;
let deposit_contract = Address::try_from(deposit_contract)?;
Ok(Box::new(EvmChainHandler::new(
self.provider,
self.signer,
deposit_contract,
delivery_contract,
)))
}
}
impl TridentClientBuilder {
pub fn with_evm_chain(
self,
domain: Domain,
provider: impl Provider<AnyNetwork> + 'static,
signer: impl Signer + Send + Sync + 'static,
) -> Self {
self.with_chain(
domain,
EvmChainHandlerBuilder {
provider: provider.erased(),
signer: Box::new(signer),
},
)
}
}