@@ -6,7 +6,7 @@ use alloy_network::eip2718::{
66 Eip2718Error , Encodable2718 , LEGACY_TX_TYPE_ID ,
77} ;
88use alloy_primitives:: { Bloom , Log , TxHash , logs_bloom} ;
9- use alloy_rlp:: { BufMut , Decodable , Encodable , Header , bytes} ;
9+ use alloy_rlp:: { BufMut , Decodable , Encodable , bytes} ;
1010use alloy_rpc_types:: { BlockNumHash , trace:: otterscan:: OtsReceipt } ;
1111#[ cfg( feature = "optimism" ) ]
1212use op_alloy_consensus:: {
@@ -293,129 +293,17 @@ where
293293
294294impl Encodable for FoundryReceiptEnvelope {
295295 fn encode ( & self , out : & mut dyn bytes:: BufMut ) {
296- match self {
297- Self :: Legacy ( r) => r. encode ( out) ,
298- receipt => {
299- let payload_len = match receipt {
300- Self :: Eip2930 ( r) => r. length ( ) + 1 ,
301- Self :: Eip1559 ( r) => r. length ( ) + 1 ,
302- Self :: Eip4844 ( r) => r. length ( ) + 1 ,
303- Self :: Eip7702 ( r) => r. length ( ) + 1 ,
304- #[ cfg( feature = "optimism" ) ]
305- Self :: PostExec ( r) => r. length ( ) + 1 ,
306- #[ cfg( feature = "optimism" ) ]
307- Self :: Deposit ( r) => r. length ( ) + 1 ,
308- Self :: Tempo ( r) => r. length ( ) + 1 ,
309- _ => unreachable ! ( "receipt already matched" ) ,
310- } ;
296+ self . network_encode ( out) ;
297+ }
311298
312- match receipt {
313- Self :: Eip2930 ( r) => {
314- Header { list : true , payload_length : payload_len } . encode ( out) ;
315- EIP2930_TX_TYPE_ID . encode ( out) ;
316- r. encode ( out) ;
317- }
318- Self :: Eip1559 ( r) => {
319- Header { list : true , payload_length : payload_len } . encode ( out) ;
320- EIP1559_TX_TYPE_ID . encode ( out) ;
321- r. encode ( out) ;
322- }
323- Self :: Eip4844 ( r) => {
324- Header { list : true , payload_length : payload_len } . encode ( out) ;
325- EIP4844_TX_TYPE_ID . encode ( out) ;
326- r. encode ( out) ;
327- }
328- Self :: Eip7702 ( r) => {
329- Header { list : true , payload_length : payload_len } . encode ( out) ;
330- EIP7702_TX_TYPE_ID . encode ( out) ;
331- r. encode ( out) ;
332- }
333- #[ cfg( feature = "optimism" ) ]
334- Self :: PostExec ( r) => {
335- Header { list : true , payload_length : payload_len } . encode ( out) ;
336- POST_EXEC_TX_TYPE_ID . encode ( out) ;
337- r. encode ( out) ;
338- }
339- #[ cfg( feature = "optimism" ) ]
340- Self :: Deposit ( r) => {
341- Header { list : true , payload_length : payload_len } . encode ( out) ;
342- DEPOSIT_TX_TYPE_ID . encode ( out) ;
343- r. encode ( out) ;
344- }
345- Self :: Tempo ( r) => {
346- Header { list : true , payload_length : payload_len } . encode ( out) ;
347- TEMPO_TX_TYPE_ID . encode ( out) ;
348- r. encode ( out) ;
349- }
350- _ => unreachable ! ( "receipt already matched" ) ,
351- }
352- }
353- }
299+ fn length ( & self ) -> usize {
300+ self . network_len ( )
354301 }
355302}
356303
357304impl Decodable for FoundryReceiptEnvelope {
358305 fn decode ( buf : & mut & [ u8 ] ) -> alloy_rlp:: Result < Self > {
359- use bytes:: Buf ;
360- use std:: cmp:: Ordering ;
361-
362- // a receipt is either encoded as a string (non legacy) or a list (legacy).
363- // We should not consume the buffer if we are decoding a legacy receipt, so let's
364- // check if the first byte is between 0x80 and 0xbf.
365- let rlp_type = * buf
366- . first ( )
367- . ok_or ( alloy_rlp:: Error :: Custom ( "cannot decode a receipt from empty bytes" ) ) ?;
368-
369- match rlp_type. cmp ( & alloy_rlp:: EMPTY_LIST_CODE ) {
370- Ordering :: Less => {
371- // strip out the string header
372- let _header = Header :: decode ( buf) ?;
373- let receipt_type = * buf. first ( ) . ok_or ( alloy_rlp:: Error :: Custom (
374- "typed receipt cannot be decoded from an empty slice" ,
375- ) ) ?;
376- if receipt_type == EIP2930_TX_TYPE_ID {
377- buf. advance ( 1 ) ;
378- <ReceiptWithBloom as Decodable >:: decode ( buf)
379- . map ( FoundryReceiptEnvelope :: Eip2930 )
380- } else if receipt_type == EIP1559_TX_TYPE_ID {
381- buf. advance ( 1 ) ;
382- <ReceiptWithBloom as Decodable >:: decode ( buf)
383- . map ( FoundryReceiptEnvelope :: Eip1559 )
384- } else if receipt_type == EIP4844_TX_TYPE_ID {
385- buf. advance ( 1 ) ;
386- <ReceiptWithBloom as Decodable >:: decode ( buf)
387- . map ( FoundryReceiptEnvelope :: Eip4844 )
388- } else if receipt_type == EIP7702_TX_TYPE_ID {
389- buf. advance ( 1 ) ;
390- <ReceiptWithBloom as Decodable >:: decode ( buf)
391- . map ( FoundryReceiptEnvelope :: Eip7702 )
392- } else if receipt_type == TEMPO_TX_TYPE_ID {
393- buf. advance ( 1 ) ;
394- <ReceiptWithBloom as Decodable >:: decode ( buf) . map ( FoundryReceiptEnvelope :: Tempo )
395- } else {
396- #[ cfg( feature = "optimism" ) ]
397- {
398- if receipt_type == POST_EXEC_TX_TYPE_ID {
399- buf. advance ( 1 ) ;
400- return <ReceiptWithBloom as Decodable >:: decode ( buf)
401- . map ( FoundryReceiptEnvelope :: PostExec ) ;
402- }
403- if receipt_type == DEPOSIT_TX_TYPE_ID {
404- buf. advance ( 1 ) ;
405- return <OpDepositReceiptWithBloom as Decodable >:: decode ( buf)
406- . map ( FoundryReceiptEnvelope :: Deposit ) ;
407- }
408- }
409- Err ( alloy_rlp:: Error :: Custom ( "invalid receipt type" ) )
410- }
411- }
412- Ordering :: Equal => {
413- Err ( alloy_rlp:: Error :: Custom ( "an empty list is not a valid receipt encoding" ) )
414- }
415- Ordering :: Greater => {
416- <ReceiptWithBloom as Decodable >:: decode ( buf) . map ( FoundryReceiptEnvelope :: Legacy )
417- }
418- }
306+ Self :: network_decode ( buf) . map_err ( Into :: into)
419307 }
420308}
421309
@@ -532,6 +420,45 @@ mod tests {
532420 . map_logs ( |log| log. inner )
533421 }
534422
423+ #[ test]
424+ fn rlp_roundtrip ( ) {
425+ fn assert_roundtrip ( receipt : FoundryReceiptEnvelope ) {
426+ let mut encoded = Vec :: new ( ) ;
427+ receipt. encode ( & mut encoded) ;
428+ assert_eq ! ( encoded. len( ) , receipt. length( ) ) ;
429+
430+ let mut encoded = encoded. as_slice ( ) ;
431+ let decoded = FoundryReceiptEnvelope :: decode ( & mut encoded) . unwrap ( ) ;
432+ assert_eq ! ( decoded, receipt) ;
433+ assert ! ( encoded. is_empty( ) ) ;
434+ }
435+
436+ for tx_type in [
437+ FoundryTxType :: Legacy ,
438+ FoundryTxType :: Eip2930 ,
439+ FoundryTxType :: Eip1559 ,
440+ FoundryTxType :: Eip4844 ,
441+ FoundryTxType :: Eip7702 ,
442+ FoundryTxType :: Tempo ,
443+ ] {
444+ assert_roundtrip ( receipt_for ( tx_type) ) ;
445+ }
446+ #[ cfg( feature = "optimism" ) ]
447+ for tx_type in [ FoundryTxType :: PostExec , FoundryTxType :: Deposit ] {
448+ assert_roundtrip ( receipt_for ( tx_type) ) ;
449+ }
450+ }
451+
452+ #[ test]
453+ fn encode_typed_receipt_uses_rlp_string ( ) {
454+ let receipt = receipt_for ( FoundryTxType :: Eip2930 ) ;
455+ let mut encoded = Vec :: new ( ) ;
456+ receipt. encode ( & mut encoded) ;
457+
458+ // Long string containing a 266-byte EIP-2718 envelope, beginning with type 0x01.
459+ assert_eq ! ( & encoded[ ..4 ] , & [ 0xb9 , 0x01 , 0x0a , EIP2930_TX_TYPE_ID ] ) ;
460+ }
461+
535462 #[ test]
536463 fn receipt_predicates ( ) {
537464 assert ! ( receipt_for( FoundryTxType :: Legacy ) . is_legacy( ) ) ;
0 commit comments