anvil_core/eth/transaction/
mod.rs1use 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#[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 pub const fn new(transaction: T) -> Self {
37 Self { transaction, impersonated_sender: None }
38 }
39
40 pub const fn impersonated(transaction: T, impersonated_sender: Address) -> Self {
42 Self { transaction, impersonated_sender: Some(impersonated_sender) }
43 }
44
45 pub const fn is_impersonated(&self) -> bool {
47 self.impersonated_sender.is_some()
48 }
49
50 pub fn into_inner(self) -> T {
52 self.transaction
53 }
54
55 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> MaybeImpersonatedTransaction<U> {
57 MaybeImpersonatedTransaction {
58 transaction: f(self.transaction),
59 impersonated_sender: self.impersonated_sender,
60 }
61 }
62}
63
64impl<T: SignerRecoverable + TxHashRef + Encodable> MaybeImpersonatedTransaction<T> {
65 pub fn recover(&self) -> Result<Address, RecoveryError> {
67 if let Some(sender) = self.impersonated_sender {
68 return Ok(sender);
69 }
70 self.transaction.recover_signer()
71 }
72
73 pub fn hash(&self) -> B256 {
78 if let Some(sender) = self.impersonated_sender {
79 let mut buffer = Vec::new();
80 self.transaction.encode(&mut buffer);
81 buffer.extend_from_slice(sender.as_ref());
82 return B256::from_slice(alloy_primitives::utils::keccak256(&buffer).as_slice());
83 }
84 *self.transaction.tx_hash()
85 }
86}
87
88impl<T: Encodable2718> Encodable2718 for MaybeImpersonatedTransaction<T> {
89 fn encode_2718_len(&self) -> usize {
90 self.transaction.encode_2718_len()
91 }
92
93 fn encode_2718(&self, out: &mut dyn BufMut) {
94 self.transaction.encode_2718(out)
95 }
96}
97
98impl<T: Encodable> Encodable for MaybeImpersonatedTransaction<T> {
99 fn encode(&self, out: &mut dyn bytes::BufMut) {
100 self.transaction.encode(out)
101 }
102
103 fn length(&self) -> usize {
104 self.transaction.length()
105 }
106}
107
108impl From<MaybeImpersonatedTransaction<Self>> for FoundryTxEnvelope {
109 fn from(value: MaybeImpersonatedTransaction<Self>) -> Self {
110 value.transaction
111 }
112}
113
114impl<T> From<T> for MaybeImpersonatedTransaction<T> {
115 fn from(value: T) -> Self {
116 Self::new(value)
117 }
118}
119
120impl<T: Decodable> Decodable for MaybeImpersonatedTransaction<T> {
121 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
122 T::decode(buf).map(Self::new)
123 }
124}
125
126impl<T> AsRef<T> for MaybeImpersonatedTransaction<T> {
127 fn as_ref(&self) -> &T {
128 &self.transaction
129 }
130}
131
132impl<T> Deref for MaybeImpersonatedTransaction<T> {
133 type Target = T;
134
135 fn deref(&self) -> &Self::Target {
136 &self.transaction
137 }
138}
139
140#[derive(Clone, Debug, PartialEq, Eq)]
142pub struct PendingTransaction<T> {
143 pub transaction: MaybeImpersonatedTransaction<T>,
145 sender: Address,
147 hash: TxHash,
149}
150
151impl<T> PendingTransaction<T> {
152 pub const fn hash(&self) -> &TxHash {
153 &self.hash
154 }
155
156 pub const fn sender(&self) -> &Address {
157 &self.sender
158 }
159}
160
161impl<T: SignerRecoverable + TxHashRef + Encodable> PendingTransaction<T> {
162 pub fn new(transaction: T) -> Result<Self, RecoveryError> {
163 let transaction = MaybeImpersonatedTransaction::new(transaction);
164 let sender = transaction.recover()?;
165 let hash = transaction.hash();
166 Ok(Self { transaction, sender, hash })
167 }
168
169 pub fn with_impersonated(transaction: T, sender: Address) -> Self {
170 let transaction = MaybeImpersonatedTransaction::impersonated(transaction, sender);
171 let hash = transaction.hash();
172 Self { transaction, sender, hash }
173 }
174
175 pub fn with_sender(transaction: MaybeImpersonatedTransaction<T>, sender: Address) -> Self {
177 let hash = transaction.hash();
178 Self { transaction, sender, hash }
179 }
180
181 pub fn from_maybe_impersonated(
183 transaction: MaybeImpersonatedTransaction<T>,
184 ) -> Result<Self, RecoveryError> {
185 if let Some(impersonated) = transaction.impersonated_sender {
186 Ok(Self::with_impersonated(transaction.transaction, impersonated))
187 } else {
188 Self::new(transaction.transaction)
189 }
190 }
191}
192
193impl<T: Transaction> PendingTransaction<T> {
194 pub fn nonce(&self) -> u64 {
195 self.transaction.nonce()
196 }
197}
198
199#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
201pub struct TransactionInfo {
202 pub transaction_hash: B256,
203 pub transaction_index: u64,
204 pub from: Address,
205 pub to: Option<Address>,
206 pub contract_address: Option<Address>,
207 pub traces: Vec<CallTraceNode>,
208 pub exit: InstructionResult,
209 pub out: Option<Bytes>,
210 pub nonce: u64,
211 pub gas_used: u64,
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 struct EncodableLength;
219
220 impl Encodable for EncodableLength {
221 fn encode(&self, _: &mut dyn BufMut) {
222 panic!("length should not encode")
223 }
224
225 fn length(&self) -> usize {
226 42
227 }
228 }
229
230 #[test]
231 fn rlp_length_delegates_to_inner_transaction() {
232 assert_eq!(MaybeImpersonatedTransaction::new(EncodableLength).length(), 42);
233 }
234}