1mod builder;
2mod either;
3
4pub mod error;
5
6use alloy_consensus::{
7 Signed, TxEip1559, TxEip2930, TxEip4844Variant, TxEip7702, TxEnvelope, TxLegacy,
8};
9use alloy_eips::{eip7702::SignedAuthorization, Typed2718};
10use alloy_primitives::{Bytes, ChainId, TxKind, B256, U256};
11pub use either::{AnyTxEnvelope, AnyTypedTransaction};
12use std::error::Error;
13
14mod unknowns;
15pub use unknowns::{AnyTxType, UnknownTxEnvelope, UnknownTypedTransaction};
16
17pub use alloy_consensus_any::{AnyHeader, AnyReceiptEnvelope};
18
19use crate::{any::error::AnyConversionError, Network};
20use alloy_consensus::{
21 error::ValueError,
22 transaction::{Either, Recovered},
23};
24use alloy_network_primitives::{BlockResponse, TransactionResponse};
25pub use alloy_rpc_types_any::{AnyRpcHeader, AnyTransactionReceipt};
26use alloy_rpc_types_eth::{AccessList, Block, BlockTransactions, Transaction, TransactionRequest};
27use alloy_serde::WithOtherFields;
28use derive_more::From;
29use serde::{Deserialize, Serialize};
30use std::ops::{Deref, DerefMut};
31
32#[derive(Clone, Copy, Debug)]
61pub struct AnyNetwork {
62 _private: (),
63}
64
65impl Network for AnyNetwork {
66 type TxType = AnyTxType;
67
68 type TxEnvelope = AnyTxEnvelope;
69
70 type UnsignedTx = AnyTypedTransaction;
71
72 type ReceiptEnvelope = AnyReceiptEnvelope;
73
74 type Header = AnyHeader;
75
76 type TransactionRequest = WithOtherFields<TransactionRequest>;
77
78 type TransactionResponse = AnyRpcTransaction;
79
80 type ReceiptResponse = AnyTransactionReceipt;
81
82 type HeaderResponse = AnyRpcHeader;
83
84 type BlockResponse = AnyRpcBlock;
85}
86
87#[derive(Clone, Debug, From, PartialEq, Eq, Deserialize, Serialize)]
93pub struct AnyRpcBlock(pub WithOtherFields<Block<AnyRpcTransaction, AnyRpcHeader>>);
94
95impl AnyRpcBlock {
96 pub const fn new(inner: WithOtherFields<Block<AnyRpcTransaction, AnyRpcHeader>>) -> Self {
98 Self(inner)
99 }
100
101 pub fn into_inner(self) -> Block<AnyRpcTransaction, AnyRpcHeader> {
103 self.0.into_inner()
104 }
105
106 pub fn try_into_consensus<T, H>(
110 self,
111 ) -> Result<alloy_consensus::Block<T, H>, AnyConversionError>
112 where
113 T: TryFrom<AnyRpcTransaction, Error: Error + Send + Sync + 'static>,
114 H: TryFrom<AnyHeader, Error: Error + Send + Sync + 'static>,
115 {
116 self.into_inner()
117 .map_header(|h| h.into_consensus())
118 .try_convert_header()
119 .map_err(AnyConversionError::new)?
120 .try_convert_transactions()
121 .map_err(AnyConversionError::new)
122 .map(Block::into_consensus_block)
123 }
124
125 pub fn try_into_transactions(
129 self,
130 ) -> Result<Vec<AnyRpcTransaction>, ValueError<BlockTransactions<AnyRpcTransaction>>> {
131 self.0.inner.try_into_transactions()
132 }
133
134 pub fn into_transactions_iter(self) -> impl Iterator<Item = AnyRpcTransaction> {
136 self.into_inner().transactions.into_transactions()
137 }
138}
139
140impl BlockResponse for AnyRpcBlock {
141 type Header = AnyRpcHeader;
142 type Transaction = AnyRpcTransaction;
143
144 fn header(&self) -> &Self::Header {
145 &self.0.inner.header
146 }
147
148 fn transactions(&self) -> &BlockTransactions<Self::Transaction> {
149 &self.0.inner.transactions
150 }
151
152 fn transactions_mut(&mut self) -> &mut BlockTransactions<Self::Transaction> {
153 &mut self.0.inner.transactions
154 }
155
156 fn other_fields(&self) -> Option<&alloy_serde::OtherFields> {
157 self.0.other_fields()
158 }
159}
160
161impl AsRef<WithOtherFields<Block<AnyRpcTransaction, AnyRpcHeader>>> for AnyRpcBlock {
162 fn as_ref(&self) -> &WithOtherFields<Block<AnyRpcTransaction, AnyRpcHeader>> {
163 &self.0
164 }
165}
166
167impl Deref for AnyRpcBlock {
168 type Target = WithOtherFields<Block<AnyRpcTransaction, AnyRpcHeader>>;
169
170 fn deref(&self) -> &Self::Target {
171 &self.0
172 }
173}
174
175impl DerefMut for AnyRpcBlock {
176 fn deref_mut(&mut self) -> &mut Self::Target {
177 &mut self.0
178 }
179}
180
181impl From<Block> for AnyRpcBlock {
182 fn from(value: Block) -> Self {
183 let block = value.map_header(|h| h.map(|h| h.into())).map_transactions(|tx| {
184 AnyRpcTransaction::new(WithOtherFields::new(tx.map(AnyTxEnvelope::Ethereum)))
185 });
186
187 Self(WithOtherFields::new(block))
188 }
189}
190
191impl From<AnyRpcBlock> for Block<AnyRpcTransaction, AnyRpcHeader> {
192 fn from(value: AnyRpcBlock) -> Self {
193 value.into_inner()
194 }
195}
196impl From<AnyRpcBlock> for WithOtherFields<Block<AnyRpcTransaction, AnyRpcHeader>> {
197 fn from(value: AnyRpcBlock) -> Self {
198 value.0
199 }
200}
201
202impl<T, H> TryFrom<AnyRpcBlock> for alloy_consensus::Block<T, H>
203where
204 T: TryFrom<AnyRpcTransaction, Error: Error + Send + Sync + 'static>,
205 H: TryFrom<AnyHeader, Error: Error + Send + Sync + 'static>,
206{
207 type Error = AnyConversionError;
208
209 fn try_from(value: AnyRpcBlock) -> Result<Self, Self::Error> {
210 value.try_into_consensus()
211 }
212}
213
214#[derive(Clone, Debug, From, PartialEq, Eq, Deserialize, Serialize)]
216pub struct AnyRpcTransaction(pub WithOtherFields<Transaction<AnyTxEnvelope>>);
217
218impl AnyRpcTransaction {
219 pub const fn new(inner: WithOtherFields<Transaction<AnyTxEnvelope>>) -> Self {
221 Self(inner)
222 }
223
224 pub fn into_parts(self) -> (Transaction<AnyTxEnvelope>, alloy_serde::OtherFields) {
226 let WithOtherFields { inner, other } = self.0;
227 (inner, other)
228 }
229
230 pub fn into_inner(self) -> Transaction<AnyTxEnvelope> {
232 self.0.into_inner()
233 }
234
235 pub fn as_envelope(&self) -> Option<&TxEnvelope> {
238 self.inner.inner.as_envelope()
239 }
240
241 pub fn try_into_envelope(self) -> Result<TxEnvelope, ValueError<AnyTxEnvelope>> {
244 self.0.inner.inner.into_inner().try_into_envelope()
245 }
246
247 pub fn as_legacy(&self) -> Option<&Signed<TxLegacy>> {
249 self.0.inner().inner.as_legacy()
250 }
251
252 pub fn as_eip2930(&self) -> Option<&Signed<TxEip2930>> {
254 self.0.inner().inner.as_eip2930()
255 }
256
257 pub fn as_eip1559(&self) -> Option<&Signed<TxEip1559>> {
259 self.0.inner().inner.as_eip1559()
260 }
261
262 pub fn as_eip4844(&self) -> Option<&Signed<TxEip4844Variant>> {
264 self.0.inner().inner.as_eip4844()
265 }
266
267 pub fn as_eip7702(&self) -> Option<&Signed<TxEip7702>> {
269 self.0.inner().inner.as_eip7702()
270 }
271
272 #[inline]
274 pub fn is_legacy(&self) -> bool {
275 self.0.inner().inner.is_legacy()
276 }
277
278 #[inline]
280 pub fn is_eip2930(&self) -> bool {
281 self.0.inner().inner.is_eip2930()
282 }
283
284 #[inline]
286 pub fn is_eip1559(&self) -> bool {
287 self.0.inner().inner.is_eip1559()
288 }
289
290 #[inline]
292 pub fn is_eip4844(&self) -> bool {
293 self.0.inner().inner.is_eip4844()
294 }
295
296 #[inline]
298 pub fn is_eip7702(&self) -> bool {
299 self.0.inner().inner.is_eip7702()
300 }
301
302 pub fn try_into_either<T>(self) -> Result<Either<TxEnvelope, T>, T::Error>
308 where
309 T: TryFrom<Self>,
310 {
311 if self.0.inner.inner.inner().is_ethereum() {
312 Ok(Either::Left(self.0.inner.inner.into_inner().try_into_envelope().unwrap()))
313 } else {
314 T::try_from(self).map(Either::Right)
315 }
316 }
317
318 pub fn try_unknown_into_either<T>(self) -> Result<Either<TxEnvelope, T>, T::Error>
324 where
325 T: TryFrom<UnknownTxEnvelope>,
326 {
327 self.0.inner.inner.into_inner().try_into_either()
328 }
329
330 pub fn map<Tx>(self, f: impl FnOnce(AnyTxEnvelope) -> Tx) -> Transaction<Tx> {
335 self.into_inner().map(f)
336 }
337
338 pub fn try_map<Tx, E>(
342 self,
343 f: impl FnOnce(AnyTxEnvelope) -> Result<Tx, E>,
344 ) -> Result<Transaction<Tx>, E> {
345 self.into_inner().try_map(f)
346 }
347
348 pub fn convert<U>(self) -> Transaction<U>
352 where
353 U: From<AnyTxEnvelope>,
354 {
355 self.into_inner().map(U::from)
356 }
357
358 pub fn try_convert<U>(self) -> Result<Transaction<U>, U::Error>
364 where
365 U: TryFrom<AnyTxEnvelope>,
366 {
367 self.into_inner().try_map(U::try_from)
368 }
369}
370
371impl AsRef<AnyTxEnvelope> for AnyRpcTransaction {
372 fn as_ref(&self) -> &AnyTxEnvelope {
373 &self.0.inner.inner
374 }
375}
376
377impl Deref for AnyRpcTransaction {
378 type Target = WithOtherFields<Transaction<AnyTxEnvelope>>;
379
380 fn deref(&self) -> &Self::Target {
381 &self.0
382 }
383}
384
385impl DerefMut for AnyRpcTransaction {
386 fn deref_mut(&mut self) -> &mut Self::Target {
387 &mut self.0
388 }
389}
390
391impl From<Transaction<TxEnvelope>> for AnyRpcTransaction {
392 fn from(tx: Transaction<TxEnvelope>) -> Self {
393 let tx = tx.map(AnyTxEnvelope::Ethereum);
394 Self(WithOtherFields::new(tx))
395 }
396}
397
398impl From<AnyRpcTransaction> for AnyTxEnvelope {
399 fn from(tx: AnyRpcTransaction) -> Self {
400 tx.0.inner.into_inner()
401 }
402}
403
404impl From<AnyRpcTransaction> for Transaction<AnyTxEnvelope> {
405 fn from(tx: AnyRpcTransaction) -> Self {
406 tx.0.inner
407 }
408}
409
410impl From<AnyRpcTransaction> for WithOtherFields<Transaction<AnyTxEnvelope>> {
411 fn from(tx: AnyRpcTransaction) -> Self {
412 tx.0
413 }
414}
415
416impl From<AnyRpcTransaction> for Recovered<AnyTxEnvelope> {
417 fn from(tx: AnyRpcTransaction) -> Self {
418 tx.0.inner.inner
419 }
420}
421
422impl TryFrom<AnyRpcTransaction> for TxEnvelope {
423 type Error = ValueError<AnyTxEnvelope>;
424
425 fn try_from(value: AnyRpcTransaction) -> Result<Self, Self::Error> {
426 value.try_into_envelope()
427 }
428}
429
430impl alloy_consensus::Transaction for AnyRpcTransaction {
431 fn chain_id(&self) -> Option<ChainId> {
432 self.inner.chain_id()
433 }
434
435 fn nonce(&self) -> u64 {
436 self.inner.nonce()
437 }
438
439 fn gas_limit(&self) -> u64 {
440 self.inner.gas_limit()
441 }
442
443 fn gas_price(&self) -> Option<u128> {
444 alloy_consensus::Transaction::gas_price(&self.0.inner)
445 }
446
447 fn max_fee_per_gas(&self) -> u128 {
448 alloy_consensus::Transaction::max_fee_per_gas(&self.inner)
449 }
450
451 fn max_priority_fee_per_gas(&self) -> Option<u128> {
452 self.inner.max_priority_fee_per_gas()
453 }
454
455 fn max_fee_per_blob_gas(&self) -> Option<u128> {
456 self.inner.max_fee_per_blob_gas()
457 }
458
459 fn priority_fee_or_price(&self) -> u128 {
460 self.inner.priority_fee_or_price()
461 }
462
463 fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
464 self.inner.effective_gas_price(base_fee)
465 }
466
467 fn is_dynamic_fee(&self) -> bool {
468 self.inner.is_dynamic_fee()
469 }
470
471 fn kind(&self) -> TxKind {
472 self.inner.kind()
473 }
474
475 fn is_create(&self) -> bool {
476 self.inner.is_create()
477 }
478
479 fn value(&self) -> U256 {
480 self.inner.value()
481 }
482
483 fn input(&self) -> &Bytes {
484 self.inner.input()
485 }
486
487 fn access_list(&self) -> Option<&AccessList> {
488 self.inner.access_list()
489 }
490
491 fn blob_versioned_hashes(&self) -> Option<&[B256]> {
492 self.inner.blob_versioned_hashes()
493 }
494
495 fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
496 self.inner.authorization_list()
497 }
498}
499
500impl TransactionResponse for AnyRpcTransaction {
501 fn tx_hash(&self) -> alloy_primitives::TxHash {
502 self.inner.tx_hash()
503 }
504
505 fn block_hash(&self) -> Option<alloy_primitives::BlockHash> {
506 self.0.inner.block_hash
507 }
508
509 fn block_number(&self) -> Option<u64> {
510 self.inner.block_number
511 }
512
513 fn transaction_index(&self) -> Option<u64> {
514 self.inner.transaction_index
515 }
516
517 fn from(&self) -> alloy_primitives::Address {
518 self.inner.from()
519 }
520
521 fn gas_price(&self) -> Option<u128> {
522 self.inner.effective_gas_price
523 }
524}
525
526impl Typed2718 for AnyRpcTransaction {
527 fn ty(&self) -> u8 {
528 self.inner.ty()
529 }
530}
531
532#[cfg(test)]
533mod tests {
534 use super::*;
535 use alloy_primitives::B64;
536
537 #[test]
538 fn convert_any_block() {
539 let block = AnyRpcBlock::new(
540 Block::new(
541 AnyRpcHeader::from_sealed(
542 AnyHeader {
543 nonce: Some(B64::ZERO),
544 mix_hash: Some(B256::ZERO),
545 ..Default::default()
546 }
547 .seal(B256::ZERO),
548 ),
549 BlockTransactions::Full(vec![]),
550 )
551 .into(),
552 );
553
554 let _block: alloy_consensus::Block<TxEnvelope, alloy_consensus::Header> =
555 block.try_into().unwrap();
556 }
557}