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
104impl From<MaybeImpersonatedTransaction<Self>> for FoundryTxEnvelope {
105 fn from(value: MaybeImpersonatedTransaction<Self>) -> Self {
106 value.transaction
107 }
108}
109
110impl<T> From<T> for MaybeImpersonatedTransaction<T> {
111 fn from(value: T) -> Self {
112 Self::new(value)
113 }
114}
115
116impl<T: Decodable> Decodable for MaybeImpersonatedTransaction<T> {
117 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
118 T::decode(buf).map(Self::new)
119 }
120}
121
122impl<T> AsRef<T> for MaybeImpersonatedTransaction<T> {
123 fn as_ref(&self) -> &T {
124 &self.transaction
125 }
126}
127
128impl<T> Deref for MaybeImpersonatedTransaction<T> {
129 type Target = T;
130
131 fn deref(&self) -> &Self::Target {
132 &self.transaction
133 }
134}
135
136#[derive(Clone, Debug, PartialEq, Eq)]
138pub struct PendingTransaction<T> {
139 pub transaction: MaybeImpersonatedTransaction<T>,
141 sender: Address,
143 hash: TxHash,
145}
146
147impl<T> PendingTransaction<T> {
148 pub const fn hash(&self) -> &TxHash {
149 &self.hash
150 }
151
152 pub const fn sender(&self) -> &Address {
153 &self.sender
154 }
155}
156
157impl<T: SignerRecoverable + TxHashRef + Encodable> PendingTransaction<T> {
158 pub fn new(transaction: T) -> Result<Self, RecoveryError> {
159 let transaction = MaybeImpersonatedTransaction::new(transaction);
160 let sender = transaction.recover()?;
161 let hash = transaction.hash();
162 Ok(Self { transaction, sender, hash })
163 }
164
165 pub fn with_impersonated(transaction: T, sender: Address) -> Self {
166 let transaction = MaybeImpersonatedTransaction::impersonated(transaction, sender);
167 let hash = transaction.hash();
168 Self { transaction, sender, hash }
169 }
170
171 pub fn from_maybe_impersonated(
173 transaction: MaybeImpersonatedTransaction<T>,
174 ) -> Result<Self, RecoveryError> {
175 if let Some(impersonated) = transaction.impersonated_sender {
176 Ok(Self::with_impersonated(transaction.transaction, impersonated))
177 } else {
178 Self::new(transaction.transaction)
179 }
180 }
181}
182
183impl<T: Transaction> PendingTransaction<T> {
184 pub fn nonce(&self) -> u64 {
185 self.transaction.nonce()
186 }
187}
188
189#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
191pub struct TransactionInfo {
192 pub transaction_hash: B256,
193 pub transaction_index: u64,
194 pub from: Address,
195 pub to: Option<Address>,
196 pub contract_address: Option<Address>,
197 pub traces: Vec<CallTraceNode>,
198 pub exit: InstructionResult,
199 pub out: Option<Bytes>,
200 pub nonce: u64,
201 pub gas_used: u64,
202}