1- use alloy_consensus:: { SignableTransaction , Signed , Transaction , TxType } ;
1+ use alloy_consensus:: { SignableTransaction , Signed , Transaction } ;
22use alloy_primitives:: { keccak256, Address , Bytes , ChainId , Signature , TxKind , B256 , U256 } ;
33use alloy_rlp:: {
44 length_of_length, Decodable , Encodable , Error as DecodeError , Header as RlpHeader ,
55} ;
6+ use bytes:: BufMut ;
67use serde:: { Deserialize , Serialize } ;
78use std:: mem;
89
10+ pub const DEPOSIT_TX_TYPE_ID : u8 = 0x7E ;
11+
912#[ derive( Clone , Debug , PartialEq , Eq ) ]
1013pub struct DepositTransactionRequest {
1114 pub source_hash : B256 ,
@@ -20,13 +23,17 @@ pub struct DepositTransactionRequest {
2023
2124impl DepositTransactionRequest {
2225 pub fn hash ( & self ) -> B256 {
23- B256 :: from_slice ( alloy_primitives:: keccak256 ( alloy_rlp:: encode ( self ) ) . as_slice ( ) )
26+ let mut encoded = Vec :: new ( ) ;
27+ encoded. put_u8 ( DEPOSIT_TX_TYPE_ID ) ;
28+ self . encode ( & mut encoded) ;
29+
30+ B256 :: from_slice ( alloy_primitives:: keccak256 ( encoded) . as_slice ( ) )
2431 }
2532
2633 /// Encodes only the transaction's fields into the desired buffer, without a RLP header.
2734 pub ( crate ) fn encode_fields ( & self , out : & mut dyn alloy_rlp:: BufMut ) {
28- self . from . encode ( out) ;
2935 self . source_hash . encode ( out) ;
36+ self . from . encode ( out) ;
3037 self . kind . encode ( out) ;
3138 self . mint . encode ( out) ;
3239 self . value . encode ( out) ;
@@ -103,8 +110,8 @@ impl DepositTransactionRequest {
103110 }
104111
105112 /// Get transaction type
106- pub ( crate ) const fn tx_type ( & self ) -> TxType {
107- TxType :: Eip1559
113+ pub ( crate ) const fn tx_type ( & self ) -> u8 {
114+ DEPOSIT_TX_TYPE_ID
108115 }
109116
110117 /// Calculates a heuristic for the in-memory size of the [DepositTransaction] transaction.
@@ -121,7 +128,7 @@ impl DepositTransactionRequest {
121128
122129 /// Encodes the legacy transaction in RLP for signing.
123130 pub ( crate ) fn encode_for_signing ( & self , out : & mut dyn alloy_rlp:: BufMut ) {
124- out. put_u8 ( self . tx_type ( ) as u8 ) ;
131+ out. put_u8 ( self . tx_type ( ) ) ;
125132 alloy_rlp:: Header { list : true , payload_length : self . fields_len ( ) } . encode ( out) ;
126133 self . encode_fields ( out) ;
127134 }
@@ -247,7 +254,9 @@ impl DepositTransaction {
247254 }
248255
249256 pub fn hash ( & self ) -> B256 {
250- B256 :: from_slice ( alloy_primitives:: keccak256 ( alloy_rlp:: encode ( self ) ) . as_slice ( ) )
257+ let mut encoded = Vec :: new ( ) ;
258+ self . encode_2718 ( & mut encoded) ;
259+ B256 :: from_slice ( alloy_primitives:: keccak256 ( encoded) . as_slice ( ) )
251260 }
252261
253262 // /// Recovers the Ethereum address which was used to sign the transaction.
@@ -259,9 +268,13 @@ impl DepositTransaction {
259268 None
260269 }
261270
271+ pub ( crate ) fn encode_2718 ( & self , out : & mut dyn alloy_rlp:: BufMut ) {
272+ out. put_u8 ( DEPOSIT_TX_TYPE_ID ) ;
273+ self . encode ( out) ;
274+ }
275+
262276 /// Encodes only the transaction's fields into the desired buffer, without a RLP header.
263277 pub ( crate ) fn encode_fields ( & self , out : & mut dyn alloy_rlp:: BufMut ) {
264- self . nonce . encode ( out) ;
265278 self . source_hash . encode ( out) ;
266279 self . from . encode ( out) ;
267280 self . kind . encode ( out) ;
@@ -286,6 +299,20 @@ impl DepositTransaction {
286299 len
287300 }
288301
302+ pub fn decode_2718 ( buf : & mut & [ u8 ] ) -> Result < Self , DecodeError > {
303+ use bytes:: Buf ;
304+
305+ let tx_type = * buf. first ( ) . ok_or ( alloy_rlp:: Error :: Custom ( "empty slice" ) ) ?;
306+
307+ if tx_type != DEPOSIT_TX_TYPE_ID {
308+ return Err ( alloy_rlp:: Error :: Custom ( "invalid tx type: expected deposit tx type" ) ) ;
309+ }
310+
311+ // Skip the tx type byte
312+ buf. advance ( 1 ) ;
313+ Self :: decode ( buf)
314+ }
315+
289316 /// Decodes the inner fields from RLP bytes
290317 ///
291318 /// NOTE: This assumes a RLP header has already been decoded, and _just_ decodes the following
@@ -325,11 +352,76 @@ impl Decodable for DepositTransaction {
325352 fn decode ( buf : & mut & [ u8 ] ) -> alloy_rlp:: Result < Self > {
326353 let header = RlpHeader :: decode ( buf) ?;
327354 let remaining_len = buf. len ( ) ;
328-
329355 if header. payload_length > remaining_len {
330356 return Err ( alloy_rlp:: Error :: InputTooShort ) ;
331357 }
332358
333359 Self :: decode_inner ( buf)
334360 }
335361}
362+
363+ #[ cfg( test) ]
364+ mod tests {
365+ use super :: * ;
366+
367+ #[ test]
368+ fn test_encode_decode ( ) {
369+ let tx = DepositTransaction {
370+ nonce : 0 ,
371+ source_hash : B256 :: default ( ) ,
372+ from : Address :: default ( ) ,
373+ kind : TxKind :: Call ( Address :: default ( ) ) ,
374+ mint : U256 :: from ( 100 ) ,
375+ value : U256 :: from ( 100 ) ,
376+ gas_limit : 50000 ,
377+ is_system_tx : false ,
378+ input : Bytes :: default ( ) ,
379+ } ;
380+
381+ let encoded_tx: Vec < u8 > = alloy_rlp:: encode ( & tx) ;
382+
383+ let decoded_tx = DepositTransaction :: decode ( & mut encoded_tx. as_slice ( ) ) . unwrap ( ) ;
384+
385+ assert_eq ! ( tx, decoded_tx) ;
386+ }
387+ #[ test]
388+ fn test_encode_decode_2718 ( ) {
389+ let tx = DepositTransaction {
390+ nonce : 0 ,
391+ source_hash : B256 :: default ( ) ,
392+ from : Address :: default ( ) ,
393+ kind : TxKind :: Call ( Address :: default ( ) ) ,
394+ mint : U256 :: from ( 100 ) ,
395+ value : U256 :: from ( 100 ) ,
396+ gas_limit : 50000 ,
397+ is_system_tx : false ,
398+ input : Bytes :: default ( ) ,
399+ } ;
400+
401+ let mut encoded_tx: Vec < u8 > = Vec :: new ( ) ;
402+ tx. encode_2718 ( & mut encoded_tx) ;
403+
404+ let decoded_tx = DepositTransaction :: decode_2718 ( & mut encoded_tx. as_slice ( ) ) . unwrap ( ) ;
405+
406+ assert_eq ! ( tx, decoded_tx) ;
407+ }
408+
409+ #[ test]
410+ fn test_tx_request_hash_equals_tx_hash ( ) {
411+ let tx = DepositTransaction {
412+ nonce : 0 ,
413+ source_hash : B256 :: default ( ) ,
414+ from : Address :: default ( ) ,
415+ kind : TxKind :: Call ( Address :: default ( ) ) ,
416+ mint : U256 :: from ( 100 ) ,
417+ value : U256 :: from ( 100 ) ,
418+ gas_limit : 50000 ,
419+ is_system_tx : false ,
420+ input : Bytes :: default ( ) ,
421+ } ;
422+
423+ let tx_request = DepositTransactionRequest :: from ( tx. clone ( ) ) ;
424+
425+ assert_eq ! ( tx. hash( ) , tx_request. hash( ) ) ;
426+ }
427+ }
0 commit comments