Skip to main content

trident_sdk/evm/
chain_handler.rs

1use alloy_contract::Error as ContractError;
2use alloy_primitives::{Address, U256};
3use alloy_provider::network::AnyNetwork;
4use alloy_provider::{DynProvider, PendingTransactionError, Provider};
5use alloy_signer::Signer;
6use async_trait::async_trait;
7
8use crate::chain_handler::{
9    ChainHandler, ChainHandlerBuildError, ChainHandlerBuilder, ChainHandlerError,
10};
11use crate::conversions::ProtoConversionError;
12use crate::deposit::DepositContext;
13use crate::proto::v1;
14use crate::spend_attestation::SpendAttestationChainContext;
15use crate::{Domain, TridentClientBuilder};
16
17use super::signing;
18
19/// Errors produced by EVM-specific SDK operations.
20///
21/// This error type is returned by on-chain EVM helpers such as
22/// [`super::signing::sign_spend_intent`].
23#[derive(Debug, thiserror::Error)]
24pub enum EvmChainError {
25    /// The underlying contract call or transaction submission failed.
26    #[error("Contract call failed: {0}")]
27    Contract(#[from] ContractError),
28
29    /// Waiting on a pending transaction failed.
30    #[error("Transaction pending error: {0}")]
31    PendingTransaction(#[from] PendingTransactionError),
32
33    /// Producing the spend-intent signature failed.
34    #[error("Failed to sign spend intent: {0}")]
35    Signer(alloy_signer::Error),
36
37    /// Converting between protobuf and EVM-native types failed.
38    #[error("Invalid proto conversion: {0}")]
39    ProtoConversion(#[from] ProtoConversionError),
40}
41
42pub(crate) struct EvmChainHandler {
43    provider: DynProvider<AnyNetwork>,
44    signer: Box<dyn Signer + Send + Sync>,
45    deposit_contract: Address,
46    delivery_contract: Address,
47}
48
49impl EvmChainHandler {
50    pub(crate) fn new(
51        provider: DynProvider<AnyNetwork>,
52        signer: Box<dyn Signer + Send + Sync>,
53        deposit_contract: Address,
54        delivery_contract: Address,
55    ) -> Self {
56        Self {
57            provider,
58            signer,
59            deposit_contract,
60            delivery_contract,
61        }
62    }
63}
64
65#[async_trait]
66impl ChainHandler for EvmChainHandler {
67    fn delivery_contract(&self) -> v1::MultiChainAddress {
68        self.delivery_contract.into()
69    }
70
71    fn deposit_contract(&self) -> v1::MultiChainAddress {
72        self.deposit_contract.into()
73    }
74
75    fn signer(&self) -> v1::MultiChainAddress {
76        self.signer.address().into()
77    }
78
79    async fn sign_spend_intent(
80        &self,
81        intent: v1::SpendIntent,
82    ) -> Result<Vec<u8>, ChainHandlerError> {
83        signing::sign_spend_intent(self.signer.as_ref(), intent)
84            .await
85            .map_err(Into::into)
86    }
87
88    fn prepare_deposit(
89        &self,
90        token: v1::MultiChainAddress,
91        amount: U256,
92    ) -> Result<DepositContext, ChainHandlerError> {
93        let token_address = Address::try_from(token).map_err(EvmChainError::from)?;
94        Ok(DepositContext::Evm {
95            provider: self.provider.clone(),
96            signer_address: self.signer.address(),
97            deposit_contract: self.deposit_contract,
98            token: token_address,
99            amount,
100        })
101    }
102
103    fn prepare_spend_attestation(&self) -> SpendAttestationChainContext {
104        SpendAttestationChainContext::Evm {
105            provider: self.provider.clone(),
106            delivery_contract: self.delivery_contract,
107            signer_address: self.signer.address(),
108        }
109    }
110}
111
112/// Pending EVM chain registration. Materializes into an [`EvmChainHandler`]
113/// once the delivery service has reported the deployment's deposit and
114/// delivery contract addresses.
115pub(crate) struct EvmChainHandlerBuilder {
116    provider: DynProvider<AnyNetwork>,
117    signer: Box<dyn Signer + Send + Sync>,
118}
119
120#[async_trait]
121impl ChainHandlerBuilder for EvmChainHandlerBuilder {
122    async fn build(
123        self: Box<Self>,
124        delivery_contract: v1::MultiChainAddress,
125        deposit_contract: v1::MultiChainAddress,
126    ) -> Result<Box<dyn ChainHandler>, ChainHandlerBuildError> {
127        let delivery_contract = Address::try_from(delivery_contract)?;
128        let deposit_contract = Address::try_from(deposit_contract)?;
129        Ok(Box::new(EvmChainHandler::new(
130            self.provider,
131            self.signer,
132            deposit_contract,
133            delivery_contract,
134        )))
135    }
136}
137
138impl TridentClientBuilder {
139    /// Registers an EVM chain `domain` with its provider and signer.
140    ///
141    /// The signer's address is used as `src_spender` for spends originating
142    /// from this domain and as the sender for
143    /// [`crate::TridentClient::deposit`].
144    pub fn with_evm_chain(
145        self,
146        domain: Domain,
147        provider: impl Provider<AnyNetwork> + 'static,
148        signer: impl Signer + Send + Sync + 'static,
149    ) -> Self {
150        self.with_chain(
151            domain,
152            EvmChainHandlerBuilder {
153                provider: provider.erased(),
154                signer: Box::new(signer),
155            },
156        )
157    }
158}