11//! Transaction related types
22use alloy_consensus:: {
3- Receipt , ReceiptEnvelope , ReceiptWithBloom , Signed , Transaction , TxEip1559 , TxEip2930 ,
4- TxEnvelope , TxLegacy , TxReceipt , Typed2718 ,
3+ Receipt , ReceiptEnvelope , ReceiptWithBloom , Signed , Transaction , TransactionEnvelope ,
4+ TxEip1559 , TxEip2930 , TxEnvelope , TxLegacy , TxReceipt , Typed2718 ,
55 constants:: {
66 EIP1559_TX_TYPE_ID , EIP2930_TX_TYPE_ID , EIP4844_TX_TYPE_ID , EIP7702_TX_TYPE_ID ,
77 LEGACY_TX_TYPE_ID ,
@@ -14,7 +14,7 @@ use alloy_consensus::{
1414
1515use alloy_eips:: eip2718:: { Decodable2718 , Eip2718Error , Encodable2718 } ;
1616use alloy_network:: { AnyReceiptEnvelope , AnyRpcTransaction , AnyTransactionReceipt , AnyTxEnvelope } ;
17- use alloy_primitives:: { Address , B256 , Bloom , Bytes , Signature , TxHash , TxKind , U64 , U256 } ;
17+ use alloy_primitives:: { Address , B256 , Bloom , Bytes , TxHash , TxKind , U64 , U256 } ;
1818use alloy_rlp:: { Decodable , Encodable , Header } ;
1919use alloy_rpc_types:: {
2020 AccessList , ConversionError , Transaction as RpcTransaction , TransactionReceipt ,
@@ -581,19 +581,30 @@ impl PendingTransaction {
581581}
582582
583583/// Container type for signed, typed transactions.
584- #[ derive( Clone , Debug , PartialEq , Eq , Serialize , Deserialize ) ]
584+ #[ derive( Clone , Debug , TransactionEnvelope ) ]
585+ #[ envelope(
586+ alloy_consensus = alloy_consensus,
587+ tx_type_name = TxType ,
588+ serde_cfg( all( ) ) ,
589+ ) ]
585590pub enum TypedTransaction {
586591 /// Legacy transaction type
592+ #[ envelope( ty = 0 ) ]
587593 Legacy ( Signed < TxLegacy > ) ,
588594 /// EIP-2930 transaction
595+ #[ envelope( ty = 1 ) ]
589596 EIP2930 ( Signed < TxEip2930 > ) ,
590597 /// EIP-1559 transaction
598+ #[ envelope( ty = 2 ) ]
591599 EIP1559 ( Signed < TxEip1559 > ) ,
592600 /// EIP-4844 transaction
601+ #[ envelope( ty = 3 ) ]
593602 EIP4844 ( Signed < TxEip4844Variant > ) ,
594603 /// EIP-7702 transaction
604+ #[ envelope( ty = 4 ) ]
595605 EIP7702 ( Signed < TxEip7702 > ) ,
596606 /// op-stack deposit transaction
607+ #[ envelope( ty = 126 ) ]
597608 Deposit ( TxDeposit ) ,
598609}
599610
@@ -647,73 +658,12 @@ impl TypedTransaction {
647658 }
648659 }
649660
650- /// Returns true if the transaction uses dynamic fees: EIP1559, EIP4844 or EIP7702
651- pub fn is_dynamic_fee ( & self ) -> bool {
652- matches ! ( self , Self :: EIP1559 ( _) | Self :: EIP4844 ( _) | Self :: EIP7702 ( _) )
653- }
654-
655- pub fn gas_price ( & self ) -> u128 {
656- match self {
657- Self :: Legacy ( tx) => tx. tx ( ) . gas_price ,
658- Self :: EIP2930 ( tx) => tx. tx ( ) . gas_price ,
659- Self :: EIP1559 ( tx) => tx. tx ( ) . max_fee_per_gas ,
660- Self :: EIP4844 ( tx) => tx. tx ( ) . tx ( ) . max_fee_per_gas ,
661- Self :: EIP7702 ( tx) => tx. tx ( ) . max_fee_per_gas ,
662- Self :: Deposit ( _) => 0 ,
663- }
664- }
665-
666- pub fn gas_limit ( & self ) -> u64 {
667- match self {
668- Self :: Legacy ( tx) => tx. tx ( ) . gas_limit ,
669- Self :: EIP2930 ( tx) => tx. tx ( ) . gas_limit ,
670- Self :: EIP1559 ( tx) => tx. tx ( ) . gas_limit ,
671- Self :: EIP4844 ( tx) => tx. tx ( ) . tx ( ) . gas_limit ,
672- Self :: EIP7702 ( tx) => tx. tx ( ) . gas_limit ,
673- Self :: Deposit ( tx) => tx. gas_limit ,
674- }
675- }
676-
677- pub fn value ( & self ) -> U256 {
678- U256 :: from ( match self {
679- Self :: Legacy ( tx) => tx. tx ( ) . value ,
680- Self :: EIP2930 ( tx) => tx. tx ( ) . value ,
681- Self :: EIP1559 ( tx) => tx. tx ( ) . value ,
682- Self :: EIP4844 ( tx) => tx. tx ( ) . tx ( ) . value ,
683- Self :: EIP7702 ( tx) => tx. tx ( ) . value ,
684- Self :: Deposit ( tx) => tx. value ,
685- } )
686- }
687-
688- pub fn data ( & self ) -> & Bytes {
689- match self {
690- Self :: Legacy ( tx) => & tx. tx ( ) . input ,
691- Self :: EIP2930 ( tx) => & tx. tx ( ) . input ,
692- Self :: EIP1559 ( tx) => & tx. tx ( ) . input ,
693- Self :: EIP4844 ( tx) => & tx. tx ( ) . tx ( ) . input ,
694- Self :: EIP7702 ( tx) => & tx. tx ( ) . input ,
695- Self :: Deposit ( tx) => & tx. input ,
696- }
697- }
698-
699- /// Returns the transaction type
700- pub fn r#type ( & self ) -> Option < u8 > {
701- match self {
702- Self :: Legacy ( _) => None ,
703- Self :: EIP2930 ( _) => Some ( 1 ) ,
704- Self :: EIP1559 ( _) => Some ( 2 ) ,
705- Self :: EIP4844 ( _) => Some ( 3 ) ,
706- Self :: EIP7702 ( _) => Some ( 4 ) ,
707- Self :: Deposit ( _) => Some ( 0x7E ) ,
708- }
709- }
710-
711661 /// Max cost of the transaction
712662 /// It is the gas limit multiplied by the gas price,
713663 /// and if the transaction is EIP-4844, the result of (total blob gas cost * max fee per blob
714664 /// gas) is also added
715665 pub fn max_cost ( & self ) -> u128 {
716- let mut max_cost = ( self . gas_limit ( ) as u128 ) . saturating_mul ( self . gas_price ( ) ) ;
666+ let mut max_cost = ( self . gas_limit ( ) as u128 ) . saturating_mul ( self . gas_price ( ) . unwrap_or ( 0 ) ) ;
717667
718668 if self . is_eip4844 ( ) {
719669 max_cost = max_cost. saturating_add (
@@ -744,13 +694,6 @@ impl TypedTransaction {
744694 }
745695 }
746696
747- pub fn max_fee_per_blob_gas ( & self ) -> Option < u128 > {
748- match self {
749- Self :: EIP4844 ( tx) => Some ( tx. tx ( ) . tx ( ) . max_fee_per_blob_gas ) ,
750- _ => None ,
751- }
752- }
753-
754697 /// Returns a helper type that contains commonly used values as fields
755698 pub fn essentials ( & self ) -> TransactionEssentials {
756699 match self {
@@ -841,60 +784,13 @@ impl TypedTransaction {
841784 }
842785 }
843786
844- pub fn nonce ( & self ) -> u64 {
845- match self {
846- Self :: Legacy ( t) => t. tx ( ) . nonce ,
847- Self :: EIP2930 ( t) => t. tx ( ) . nonce ,
848- Self :: EIP1559 ( t) => t. tx ( ) . nonce ,
849- Self :: EIP4844 ( t) => t. tx ( ) . tx ( ) . nonce ,
850- Self :: EIP7702 ( t) => t. tx ( ) . nonce ,
851- Self :: Deposit ( _t) => 0 ,
852- }
853- }
854-
855- pub fn chain_id ( & self ) -> Option < u64 > {
856- match self {
857- Self :: Legacy ( t) => t. tx ( ) . chain_id ,
858- Self :: EIP2930 ( t) => Some ( t. tx ( ) . chain_id ) ,
859- Self :: EIP1559 ( t) => Some ( t. tx ( ) . chain_id ) ,
860- Self :: EIP4844 ( t) => Some ( t. tx ( ) . tx ( ) . chain_id ) ,
861- Self :: EIP7702 ( t) => Some ( t. tx ( ) . chain_id ) ,
862- Self :: Deposit ( t) => t. chain_id ( ) ,
863- }
864- }
865-
866787 pub fn as_legacy ( & self ) -> Option < & Signed < TxLegacy > > {
867788 match self {
868789 Self :: Legacy ( tx) => Some ( tx) ,
869790 _ => None ,
870791 }
871792 }
872793
873- /// Returns true whether this tx is a legacy transaction
874- pub fn is_legacy ( & self ) -> bool {
875- matches ! ( self , Self :: Legacy ( _) )
876- }
877-
878- /// Returns true whether this tx is a EIP1559 transaction
879- pub fn is_eip1559 ( & self ) -> bool {
880- matches ! ( self , Self :: EIP1559 ( _) )
881- }
882-
883- /// Returns true whether this tx is a EIP2930 transaction
884- pub fn is_eip2930 ( & self ) -> bool {
885- matches ! ( self , Self :: EIP2930 ( _) )
886- }
887-
888- /// Returns true whether this tx is a EIP4844 transaction
889- pub fn is_eip4844 ( & self ) -> bool {
890- matches ! ( self , Self :: EIP4844 ( _) )
891- }
892-
893- /// Returns true whether this tx is a EIP7702 transaction
894- pub fn is_eip7702 ( & self ) -> bool {
895- matches ! ( self , Self :: EIP7702 ( _) )
896- }
897-
898794 /// Returns the hash of the transaction.
899795 ///
900796 /// Note: If this transaction has the Impersonated signature then this returns a modified unique
@@ -931,136 +827,6 @@ impl TypedTransaction {
931827 Self :: Deposit ( tx) => Ok ( tx. from ) ,
932828 }
933829 }
934-
935- /// Returns what kind of transaction this is
936- pub fn kind ( & self ) -> TxKind {
937- match self {
938- Self :: Legacy ( tx) => tx. tx ( ) . to ,
939- Self :: EIP2930 ( tx) => tx. tx ( ) . to ,
940- Self :: EIP1559 ( tx) => tx. tx ( ) . to ,
941- Self :: EIP4844 ( tx) => TxKind :: Call ( tx. tx ( ) . tx ( ) . to ) ,
942- Self :: EIP7702 ( tx) => TxKind :: Call ( tx. tx ( ) . to ) ,
943- Self :: Deposit ( tx) => tx. to ,
944- }
945- }
946-
947- /// Returns the callee if this transaction is a call
948- pub fn to ( & self ) -> Option < Address > {
949- self . kind ( ) . to ( ) . copied ( )
950- }
951-
952- /// Returns the Signature of the transaction
953- pub fn signature ( & self ) -> Signature {
954- match self {
955- Self :: Legacy ( tx) => * tx. signature ( ) ,
956- Self :: EIP2930 ( tx) => * tx. signature ( ) ,
957- Self :: EIP1559 ( tx) => * tx. signature ( ) ,
958- Self :: EIP4844 ( tx) => * tx. signature ( ) ,
959- Self :: EIP7702 ( tx) => * tx. signature ( ) ,
960- Self :: Deposit ( _) => Signature :: from_scalars_and_parity (
961- B256 :: with_last_byte ( 1 ) ,
962- B256 :: with_last_byte ( 1 ) ,
963- false ,
964- ) ,
965- }
966- }
967- }
968-
969- impl Encodable for TypedTransaction {
970- fn encode ( & self , out : & mut dyn bytes:: BufMut ) {
971- if !self . is_legacy ( ) {
972- Header { list : false , payload_length : self . encode_2718_len ( ) } . encode ( out) ;
973- }
974-
975- self . encode_2718 ( out) ;
976- }
977- }
978-
979- impl Decodable for TypedTransaction {
980- fn decode ( buf : & mut & [ u8 ] ) -> alloy_rlp:: Result < Self > {
981- let mut h_decode_copy = * buf;
982- let header = alloy_rlp:: Header :: decode ( & mut h_decode_copy) ?;
983-
984- // Legacy TX
985- if header. list {
986- return Ok ( TxEnvelope :: decode ( buf) ?. into ( ) ) ;
987- }
988-
989- // Check byte after header
990- let ty = * h_decode_copy. first ( ) . ok_or ( alloy_rlp:: Error :: Custom ( "empty slice" ) ) ?;
991-
992- if ty != 0x7E {
993- Ok ( TxEnvelope :: decode ( buf) ?. into ( ) )
994- } else {
995- Ok ( Self :: Deposit ( TxDeposit :: decode_2718 ( buf) ?) )
996- }
997- }
998- }
999-
1000- impl Typed2718 for TypedTransaction {
1001- fn ty ( & self ) -> u8 {
1002- self . r#type ( ) . unwrap_or ( 0 )
1003- }
1004- }
1005-
1006- impl Encodable2718 for TypedTransaction {
1007- fn encode_2718_len ( & self ) -> usize {
1008- match self {
1009- Self :: Legacy ( tx) => TxEnvelope :: from ( tx. clone ( ) ) . encode_2718_len ( ) ,
1010- Self :: EIP2930 ( tx) => TxEnvelope :: from ( tx. clone ( ) ) . encode_2718_len ( ) ,
1011- Self :: EIP1559 ( tx) => TxEnvelope :: from ( tx. clone ( ) ) . encode_2718_len ( ) ,
1012- Self :: EIP4844 ( tx) => TxEnvelope :: from ( tx. clone ( ) ) . encode_2718_len ( ) ,
1013- Self :: EIP7702 ( tx) => TxEnvelope :: from ( tx. clone ( ) ) . encode_2718_len ( ) ,
1014- Self :: Deposit ( tx) => 1 + tx. length ( ) ,
1015- }
1016- }
1017-
1018- fn encode_2718 ( & self , out : & mut dyn BufMut ) {
1019- match self {
1020- Self :: Legacy ( tx) => TxEnvelope :: from ( tx. clone ( ) ) . encode_2718 ( out) ,
1021- Self :: EIP2930 ( tx) => TxEnvelope :: from ( tx. clone ( ) ) . encode_2718 ( out) ,
1022- Self :: EIP1559 ( tx) => TxEnvelope :: from ( tx. clone ( ) ) . encode_2718 ( out) ,
1023- Self :: EIP4844 ( tx) => TxEnvelope :: from ( tx. clone ( ) ) . encode_2718 ( out) ,
1024- Self :: EIP7702 ( tx) => TxEnvelope :: from ( tx. clone ( ) ) . encode_2718 ( out) ,
1025- Self :: Deposit ( tx) => {
1026- tx. encode_2718 ( out) ;
1027- }
1028- }
1029- }
1030- }
1031-
1032- impl Decodable2718 for TypedTransaction {
1033- fn typed_decode ( ty : u8 , buf : & mut & [ u8 ] ) -> Result < Self , Eip2718Error > {
1034- if ty == 0x7E {
1035- return Ok ( Self :: Deposit ( TxDeposit :: decode ( buf) ?) ) ;
1036- }
1037- match TxEnvelope :: typed_decode ( ty, buf) ? {
1038- TxEnvelope :: Eip2930 ( tx) => Ok ( Self :: EIP2930 ( tx) ) ,
1039- TxEnvelope :: Eip1559 ( tx) => Ok ( Self :: EIP1559 ( tx) ) ,
1040- TxEnvelope :: Eip4844 ( tx) => Ok ( Self :: EIP4844 ( tx) ) ,
1041- TxEnvelope :: Eip7702 ( tx) => Ok ( Self :: EIP7702 ( tx) ) ,
1042- _ => Err ( Eip2718Error :: RlpError ( alloy_rlp:: Error :: Custom ( "unexpected tx type" ) ) ) ,
1043- }
1044- }
1045-
1046- fn fallback_decode ( buf : & mut & [ u8 ] ) -> Result < Self , Eip2718Error > {
1047- match TxEnvelope :: fallback_decode ( buf) ? {
1048- TxEnvelope :: Legacy ( tx) => Ok ( Self :: Legacy ( tx) ) ,
1049- _ => Err ( Eip2718Error :: RlpError ( alloy_rlp:: Error :: Custom ( "unexpected tx type" ) ) ) ,
1050- }
1051- }
1052- }
1053-
1054- impl From < TxEnvelope > for TypedTransaction {
1055- fn from ( value : TxEnvelope ) -> Self {
1056- match value {
1057- TxEnvelope :: Legacy ( tx) => Self :: Legacy ( tx) ,
1058- TxEnvelope :: Eip2930 ( tx) => Self :: EIP2930 ( tx) ,
1059- TxEnvelope :: Eip1559 ( tx) => Self :: EIP1559 ( tx) ,
1060- TxEnvelope :: Eip4844 ( tx) => Self :: EIP4844 ( tx) ,
1061- TxEnvelope :: Eip7702 ( tx) => Self :: EIP7702 ( tx) ,
1062- }
1063- }
1064830}
1065831
1066832#[ derive( Clone , Debug , PartialEq , Eq ) ]
@@ -1556,7 +1322,7 @@ pub fn convert_to_anvil_receipt(receipt: AnyTransactionReceipt) -> Option<Receip
15561322#[ cfg( test) ]
15571323mod tests {
15581324 use super :: * ;
1559- use alloy_primitives:: { Log , LogData , b256, hex} ;
1325+ use alloy_primitives:: { Log , LogData , Signature , b256, hex} ;
15601326 use std:: str:: FromStr ;
15611327
15621328 // <https://github.com/foundry-rs/foundry/issues/10852>
@@ -1634,7 +1400,7 @@ mod tests {
16341400 // https://sepolia.etherscan.io/getRawTx?tx=0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0
16351401 let raw_tx = alloy_primitives:: hex:: decode ( "0x03f9011d83aa36a7820fa28477359400852e90edd0008252089411e9ca82a3a762b4b5bd264d4173a242e7a770648080c08504a817c800f8a5a0012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921aa00152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4a0013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7a001148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1a0011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e654901a0c8de4cced43169f9aa3d36506363b2d2c44f6c49fc1fd91ea114c86f3757077ea01e11fdd0d1934eda0492606ee0bb80a7bf8f35cc5f86ec60fe5031ba48bfd544" ) . unwrap ( ) ;
16361402 let res = TypedTransaction :: decode ( & mut raw_tx. as_slice ( ) ) . unwrap ( ) ;
1637- assert_eq ! ( res. r#type ( ) , Some ( 3 ) ) ;
1403+ assert ! ( res. is_type ( 3 ) ) ;
16381404
16391405 let tx = match res {
16401406 TypedTransaction :: EIP4844 ( tx) => tx,
0 commit comments