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