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