anvil_core/eth/transaction/
mod.rs1use alloy_consensus::{
3 Signed, Transaction, TxEnvelope, Typed2718, crypto::RecoveryError, transaction::Recovered,
4};
5
6use alloy_eips::eip2718::Encodable2718;
7use alloy_primitives::{Address, B256, Bytes, TxHash};
8use alloy_rlp::{Decodable, Encodable};
9use alloy_rpc_types::Transaction as RpcTransaction;
10use bytes::BufMut;
11use foundry_evm::traces::CallTraceNode;
12use foundry_primitives::FoundryTxEnvelope;
13use revm::interpreter::InstructionResult;
14use serde::{Deserialize, Serialize};
15use std::ops::Deref;
16
17#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
22pub struct MaybeImpersonatedTransaction {
23 transaction: FoundryTxEnvelope,
24 impersonated_sender: Option<Address>,
25}
26
27impl Typed2718 for MaybeImpersonatedTransaction {
28 fn ty(&self) -> u8 {
29 self.transaction.ty()
30 }
31}
32
33impl MaybeImpersonatedTransaction {
34 pub fn new(transaction: FoundryTxEnvelope) -> Self {
36 Self { transaction, impersonated_sender: None }
37 }
38
39 pub fn impersonated(transaction: FoundryTxEnvelope, impersonated_sender: Address) -> Self {
41 Self { transaction, impersonated_sender: Some(impersonated_sender) }
42 }
43
44 pub fn recover(&self) -> Result<Address, RecoveryError> {
46 if let Some(sender) = self.impersonated_sender {
47 return Ok(sender);
48 }
49 self.transaction.recover()
50 }
51
52 pub fn is_impersonated(&self) -> bool {
54 self.impersonated_sender.is_some()
55 }
56
57 pub fn hash(&self) -> B256 {
59 if let Some(sender) = self.impersonated_sender {
60 return self.transaction.impersonated_hash(sender);
61 }
62 self.transaction.hash()
63 }
64
65 pub fn into_rpc_transaction(self) -> RpcTransaction {
67 let hash = self.hash();
68 let from = self.recover().unwrap_or_default();
69 let envelope = self.transaction.try_into_eth().expect("cant build deposit transactions");
70
71 let inner_envelope = match envelope {
74 TxEnvelope::Legacy(t) => {
75 let (tx, sig, _) = t.into_parts();
76 TxEnvelope::Legacy(Signed::new_unchecked(tx, sig, hash))
77 }
78 TxEnvelope::Eip2930(t) => {
79 let (tx, sig, _) = t.into_parts();
80 TxEnvelope::Eip2930(Signed::new_unchecked(tx, sig, hash))
81 }
82 TxEnvelope::Eip1559(t) => {
83 let (tx, sig, _) = t.into_parts();
84 TxEnvelope::Eip1559(Signed::new_unchecked(tx, sig, hash))
85 }
86 TxEnvelope::Eip4844(t) => {
87 let (tx, sig, _) = t.into_parts();
88 TxEnvelope::Eip4844(Signed::new_unchecked(tx, sig, hash))
89 }
90 TxEnvelope::Eip7702(t) => {
91 let (tx, sig, _) = t.into_parts();
92 TxEnvelope::Eip7702(Signed::new_unchecked(tx, sig, hash))
93 }
94 };
95
96 RpcTransaction {
97 block_hash: None,
98 block_number: None,
99 transaction_index: None,
100 effective_gas_price: None,
101 inner: Recovered::new_unchecked(inner_envelope, from),
102 }
103 }
104}
105
106impl Encodable2718 for MaybeImpersonatedTransaction {
107 fn encode_2718_len(&self) -> usize {
108 self.transaction.encode_2718_len()
109 }
110
111 fn encode_2718(&self, out: &mut dyn BufMut) {
112 self.transaction.encode_2718(out)
113 }
114}
115
116impl Encodable for MaybeImpersonatedTransaction {
117 fn encode(&self, out: &mut dyn bytes::BufMut) {
118 self.transaction.encode(out)
119 }
120}
121
122impl From<MaybeImpersonatedTransaction> for FoundryTxEnvelope {
123 fn from(value: MaybeImpersonatedTransaction) -> Self {
124 value.transaction
125 }
126}
127
128impl From<FoundryTxEnvelope> for MaybeImpersonatedTransaction {
129 fn from(value: FoundryTxEnvelope) -> Self {
130 Self::new(value)
131 }
132}
133
134impl Decodable for MaybeImpersonatedTransaction {
135 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
136 FoundryTxEnvelope::decode(buf).map(Self::new)
137 }
138}
139
140impl AsRef<FoundryTxEnvelope> for MaybeImpersonatedTransaction {
141 fn as_ref(&self) -> &FoundryTxEnvelope {
142 &self.transaction
143 }
144}
145
146impl Deref for MaybeImpersonatedTransaction {
147 type Target = FoundryTxEnvelope;
148
149 fn deref(&self) -> &Self::Target {
150 &self.transaction
151 }
152}
153
154impl From<MaybeImpersonatedTransaction> for RpcTransaction {
155 fn from(value: MaybeImpersonatedTransaction) -> Self {
156 value.into_rpc_transaction()
157 }
158}
159
160#[derive(Clone, Debug, PartialEq, Eq)]
162pub struct PendingTransaction {
163 pub transaction: MaybeImpersonatedTransaction,
165 sender: Address,
167 hash: TxHash,
169}
170
171impl PendingTransaction {
172 pub fn new(transaction: FoundryTxEnvelope) -> Result<Self, RecoveryError> {
173 let sender = transaction.recover()?;
174 let hash = transaction.hash();
175 Ok(Self { transaction: MaybeImpersonatedTransaction::new(transaction), sender, hash })
176 }
177
178 pub fn with_impersonated(transaction: FoundryTxEnvelope, sender: Address) -> Self {
179 let hash = transaction.impersonated_hash(sender);
180 Self {
181 transaction: MaybeImpersonatedTransaction::impersonated(transaction, sender),
182 sender,
183 hash,
184 }
185 }
186
187 pub fn from_maybe_impersonated(
189 transaction: MaybeImpersonatedTransaction,
190 ) -> Result<Self, RecoveryError> {
191 if let Some(impersonated) = transaction.impersonated_sender {
192 Ok(Self::with_impersonated(transaction.transaction, impersonated))
193 } else {
194 Self::new(transaction.transaction)
195 }
196 }
197
198 pub fn nonce(&self) -> u64 {
199 self.transaction.nonce()
200 }
201
202 pub fn hash(&self) -> &TxHash {
203 &self.hash
204 }
205
206 pub fn sender(&self) -> &Address {
207 &self.sender
208 }
209}
210
211#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
213pub struct TransactionInfo {
214 pub transaction_hash: B256,
215 pub transaction_index: u64,
216 pub from: Address,
217 pub to: Option<Address>,
218 pub contract_address: Option<Address>,
219 pub traces: Vec<CallTraceNode>,
220 pub exit: InstructionResult,
221 pub out: Option<Bytes>,
222 pub nonce: u64,
223 pub gas_used: u64,
224}