arb_rpc/
transaction.rs

1//! Arbitrum transaction request and conversion types.
2
3use alloy_consensus::{error::ValueError, SignableTransaction};
4use alloy_evm::rpc::TryIntoTxEnv;
5use alloy_primitives::Signature;
6use alloy_rpc_types_eth::request::TransactionRequest;
7use arb_primitives::ArbTransactionSigned;
8use reth_rpc_convert::{SignTxRequestError, SignableTxRequest, TryIntoSimTx};
9use serde::{Deserialize, Serialize};
10
11/// Arbitrum transaction request wrapping the standard Ethereum transaction request.
12///
13/// This newtype allows implementing Arbitrum-specific RPC traits while
14/// delegating serialization and most behavior to the inner type.
15#[derive(Clone, Debug, Default, Serialize, Deserialize)]
16#[serde(transparent)]
17pub struct ArbTransactionRequest(pub TransactionRequest);
18
19impl AsRef<TransactionRequest> for ArbTransactionRequest {
20    fn as_ref(&self) -> &TransactionRequest {
21        &self.0
22    }
23}
24
25impl AsMut<TransactionRequest> for ArbTransactionRequest {
26    fn as_mut(&mut self) -> &mut TransactionRequest {
27        &mut self.0
28    }
29}
30
31impl From<TransactionRequest> for ArbTransactionRequest {
32    fn from(req: TransactionRequest) -> Self {
33        Self(req)
34    }
35}
36
37impl SignableTxRequest<ArbTransactionSigned> for ArbTransactionRequest {
38    async fn try_build_and_sign(
39        self,
40        signer: impl alloy_network::TxSigner<Signature> + Send,
41    ) -> Result<ArbTransactionSigned, SignTxRequestError> {
42        // Build a standard typed transaction, sign it, then wrap as Arbitrum.
43        let mut tx = self
44            .0
45            .build_typed_tx()
46            .map_err(|_| SignTxRequestError::InvalidTransactionRequest)?;
47        let signature = signer.sign_transaction(&mut tx).await?;
48        let signed = tx.into_signed(signature);
49        Ok(ArbTransactionSigned::from_envelope(signed.into()))
50    }
51}
52
53impl TryIntoSimTx<ArbTransactionSigned> for ArbTransactionRequest {
54    fn try_into_sim_tx(self) -> Result<ArbTransactionSigned, ValueError<Self>> {
55        // Build the typed simulation tx via alloy's reference impl (fills in
56        // defaults for missing fields and wraps with a placeholder signature),
57        // then wrap into the Arbitrum envelope.
58        match TransactionRequest::build_typed_simulate_transaction(self.0.clone()) {
59            Ok(envelope) => Ok(ArbTransactionSigned::from_envelope(envelope)),
60            Err(err) => Err(ValueError::new(self, err.to_string())),
61        }
62    }
63}
64
65impl<Block: alloy_evm::env::BlockEnvironment> TryIntoTxEnv<arb_evm::ArbTransaction, Block>
66    for ArbTransactionRequest
67{
68    type Err = alloy_evm::rpc::EthTxEnvError;
69
70    fn try_into_tx_env<Spec>(
71        self,
72        evm_env: &alloy_evm::EvmEnv<Spec, Block>,
73    ) -> Result<arb_evm::ArbTransaction, Self::Err> {
74        let tx_env = self.0.try_into_tx_env(evm_env)?;
75        Ok(arb_evm::ArbTransaction(tx_env))
76    }
77}