Skip to main content

anvil_core/eth/transaction/
mod.rs

1//! Transaction related types
2use alloy_consensus::{
3    Transaction, Typed2718,
4    crypto::RecoveryError,
5    transaction::{SignerRecoverable, TxHashRef},
6};
7
8use alloy_eips::eip2718::Encodable2718;
9use alloy_primitives::{Address, B256, Bytes, TxHash};
10use alloy_rlp::{Decodable, Encodable};
11use bytes::BufMut;
12use foundry_evm::traces::CallTraceNode;
13use foundry_primitives::FoundryTxEnvelope;
14use revm::interpreter::InstructionResult;
15use serde::{Deserialize, Serialize};
16use std::ops::Deref;
17
18/// A wrapper for a transaction envelope that allows impersonating accounts.
19///
20/// This is a helper that carries the `impersonated` sender so that the right hash
21/// can be created.
22#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
23pub struct MaybeImpersonatedTransaction<T> {
24    transaction: T,
25    impersonated_sender: Option<Address>,
26}
27
28impl<T: Typed2718> Typed2718 for MaybeImpersonatedTransaction<T> {
29    fn ty(&self) -> u8 {
30        self.transaction.ty()
31    }
32}
33
34impl<T> MaybeImpersonatedTransaction<T> {
35    /// Creates a new wrapper for the given transaction
36    pub fn new(transaction: T) -> Self {
37        Self { transaction, impersonated_sender: None }
38    }
39
40    /// Creates a new impersonated transaction wrapper using the given sender
41    pub fn impersonated(transaction: T, impersonated_sender: Address) -> Self {
42        Self { transaction, impersonated_sender: Some(impersonated_sender) }
43    }
44
45    /// Returns whether the transaction is impersonated
46    pub fn is_impersonated(&self) -> bool {
47        self.impersonated_sender.is_some()
48    }
49
50    /// Returns the inner transaction.
51    pub fn into_inner(self) -> T {
52        self.transaction
53    }
54}
55
56impl<T: SignerRecoverable + TxHashRef + Encodable> MaybeImpersonatedTransaction<T> {
57    /// Recovers the Ethereum address which was used to sign the transaction.
58    pub fn recover(&self) -> Result<Address, RecoveryError> {
59        if let Some(sender) = self.impersonated_sender {
60            return Ok(sender);
61        }
62        self.transaction.recover_signer()
63    }
64
65    /// Returns the hash of the transaction.
66    ///
67    /// If the transaction is impersonated, returns a unique hash derived by appending the
68    /// impersonated sender address to the encoded transaction before hashing.
69    pub fn hash(&self) -> B256 {
70        if let Some(sender) = self.impersonated_sender {
71            let mut buffer = Vec::new();
72            self.transaction.encode(&mut buffer);
73            buffer.extend_from_slice(sender.as_ref());
74            return B256::from_slice(alloy_primitives::utils::keccak256(&buffer).as_slice());
75        }
76        *self.transaction.tx_hash()
77    }
78}
79
80impl<T: Encodable2718> Encodable2718 for MaybeImpersonatedTransaction<T> {
81    fn encode_2718_len(&self) -> usize {
82        self.transaction.encode_2718_len()
83    }
84
85    fn encode_2718(&self, out: &mut dyn BufMut) {
86        self.transaction.encode_2718(out)
87    }
88}
89
90impl<T: Encodable> Encodable for MaybeImpersonatedTransaction<T> {
91    fn encode(&self, out: &mut dyn bytes::BufMut) {
92        self.transaction.encode(out)
93    }
94}
95
96impl From<MaybeImpersonatedTransaction<Self>> for FoundryTxEnvelope {
97    fn from(value: MaybeImpersonatedTransaction<Self>) -> Self {
98        value.transaction
99    }
100}
101
102impl From<FoundryTxEnvelope> for MaybeImpersonatedTransaction<FoundryTxEnvelope> {
103    fn from(value: FoundryTxEnvelope) -> Self {
104        Self::new(value)
105    }
106}
107
108impl<T: Decodable> Decodable for MaybeImpersonatedTransaction<T> {
109    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
110        T::decode(buf).map(Self::new)
111    }
112}
113
114impl<T> AsRef<T> for MaybeImpersonatedTransaction<T> {
115    fn as_ref(&self) -> &T {
116        &self.transaction
117    }
118}
119
120impl<T> Deref for MaybeImpersonatedTransaction<T> {
121    type Target = T;
122
123    fn deref(&self) -> &Self::Target {
124        &self.transaction
125    }
126}
127
128/// Queued transaction
129#[derive(Clone, Debug, PartialEq, Eq)]
130pub struct PendingTransaction<T> {
131    /// The actual transaction
132    pub transaction: MaybeImpersonatedTransaction<T>,
133    /// the recovered sender of this transaction
134    sender: Address,
135    /// hash of `transaction`, so it can easily be reused with encoding and hashing again
136    hash: TxHash,
137}
138
139impl<T> PendingTransaction<T> {
140    pub fn hash(&self) -> &TxHash {
141        &self.hash
142    }
143
144    pub fn sender(&self) -> &Address {
145        &self.sender
146    }
147}
148
149impl<T: SignerRecoverable + TxHashRef + Encodable> PendingTransaction<T> {
150    pub fn new(transaction: T) -> Result<Self, RecoveryError> {
151        let transaction = MaybeImpersonatedTransaction::new(transaction);
152        let sender = transaction.recover()?;
153        let hash = transaction.hash();
154        Ok(Self { transaction, sender, hash })
155    }
156
157    pub fn with_impersonated(transaction: T, sender: Address) -> Self {
158        let transaction = MaybeImpersonatedTransaction::impersonated(transaction, sender);
159        let hash = transaction.hash();
160        Self { transaction, sender, hash }
161    }
162
163    /// Converts a [`MaybeImpersonatedTransaction`] into a [`PendingTransaction`].
164    pub fn from_maybe_impersonated(
165        transaction: MaybeImpersonatedTransaction<T>,
166    ) -> Result<Self, RecoveryError> {
167        if let Some(impersonated) = transaction.impersonated_sender {
168            Ok(Self::with_impersonated(transaction.transaction, impersonated))
169        } else {
170            Self::new(transaction.transaction)
171        }
172    }
173}
174
175impl<T: Transaction> PendingTransaction<T> {
176    pub fn nonce(&self) -> u64 {
177        self.transaction.nonce()
178    }
179}
180
181/// Represents all relevant information of an executed transaction
182#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
183pub struct TransactionInfo {
184    pub transaction_hash: B256,
185    pub transaction_index: u64,
186    pub from: Address,
187    pub to: Option<Address>,
188    pub contract_address: Option<Address>,
189    pub traces: Vec<CallTraceNode>,
190    pub exit: InstructionResult,
191    pub out: Option<Bytes>,
192    pub nonce: u64,
193    pub gas_used: u64,
194}