diff --git a/crates/scroll/alloy/consensus/src/transaction/l1_message.rs b/crates/scroll/alloy/consensus/src/transaction/l1_message.rs index 0c200904fbc..ec743db4f51 100644 --- a/crates/scroll/alloy/consensus/src/transaction/l1_message.rs +++ b/crates/scroll/alloy/consensus/src/transaction/l1_message.rs @@ -29,17 +29,18 @@ pub const L1_MESSAGE_TRANSACTION_TYPE: u8 = 126; /// contain optionally serializable fields, no `bincode` compatible bridge implementation is /// required. #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] -#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(any(test, feature = "serde"), serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr( + any(test, feature = "serde"), + serde(from = "msg_serde::L1MsgSerdeHelper", into = "msg_serde::L1MsgSerdeHelper") +)] #[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] #[cfg_attr(any(test, feature = "reth-codec"), derive(Compact))] #[cfg_attr(any(test, feature = "reth-codec"), add_arbitrary_tests(compact, rlp))] pub struct TxL1Message { /// The queue index of the message in the L1 contract queue. - #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity"))] pub queue_index: u64, /// The gas limit for the transaction. Gas is paid for when message is sent from the L1. - #[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity", rename = "gas"))] pub gas_limit: u64, /// The destination for the transaction. `Address` is used in place of `TxKind` since contract /// creations aren't allowed via L1 message transactions. @@ -300,6 +301,55 @@ impl Sealable for TxL1Message { } } +#[cfg(any(test, feature = "serde"))] +mod msg_serde { + use super::*; + use serde::{Deserialize, Serialize}; + + /// Helper struct to serialize/deserialize the `TxL1Message` with a `nonce` field. + #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct L1MsgSerdeHelper { + #[serde(with = "alloy_serde::quantity")] + queue_index: u64, + #[serde(with = "alloy_serde::quantity", rename = "gas")] + gas_limit: u64, + to: Address, + value: U256, + sender: Address, + input: Bytes, + #[serde(default, with = "alloy_serde::quantity")] + nonce: u64, + } + + impl From for TxL1Message { + fn from(helper: L1MsgSerdeHelper) -> Self { + Self { + queue_index: helper.queue_index, + gas_limit: helper.gas_limit, + to: helper.to, + value: helper.value, + sender: helper.sender, + input: helper.input, + } + } + } + + impl From for L1MsgSerdeHelper { + fn from(helper: TxL1Message) -> Self { + Self { + queue_index: helper.queue_index, + gas_limit: helper.gas_limit, + to: helper.to, + value: helper.value, + sender: helper.sender, + input: helper.input, + nonce: 0, + } + } + } +} + /// Scroll specific transaction fields #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] #[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))] @@ -347,6 +397,24 @@ mod tests { use rand::Rng; use reth_codecs::{test_utils::UnusedBits, validate_bitflag_backwards_compat}; + #[test] + fn test_serde_roundtrip() { + let original = TxL1Message { + queue_index: 100, + gas_limit: 1234, + to: Address::random(), + value: U256::random(), + sender: Address::random(), + input: bytes!("deadbeef"), + }; + let json = serde_json::to_value(&original).expect("Failed to serialize"); + assert_eq!(json.get("nonce"), Some(&serde_json::Value::String("0x0".to_string()))); + + let deserialized: TxL1Message = + serde_json::from_value(json).expect("Failed to deserialize"); + assert_eq!(original, deserialized); + } + #[test] fn test_rlp_roundtrip() { // diff --git a/crates/scroll/alloy/rpc-types/src/transaction.rs b/crates/scroll/alloy/rpc-types/src/transaction.rs index d4c482d3cd4..1aef1bf0591 100644 --- a/crates/scroll/alloy/rpc-types/src/transaction.rs +++ b/crates/scroll/alloy/rpc-types/src/transaction.rs @@ -296,10 +296,10 @@ mod tests { use alloy_primitives::address; #[test] - fn can_deserialize_deposit() { + fn can_deserialize_l1_messages() { // cast rpc eth_getTransactionByHash // 0x5c1c3785c8bf5d7f1cb714abd1d22e32642887215602c3a14a5e9ee105bad6aa --rpc-url https://rpc.scroll.io - let rpc_tx = r#"{"blockHash":"0x018ed80ea8340984a1f4841490284d6e51d71f9e9411feeca41e007a89fbfdff","blockNumber":"0xb81121","from":"0x7885bcbd5cecef1336b5300fb5186a12ddd8c478","gas":"0x1e8480","gasPrice":"0x0","hash":"0x5c1c3785c8bf5d7f1cb714abd1d22e32642887215602c3a14a5e9ee105bad6aa","input":"0x8ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7ba000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f846ffc1507cbfe98a2b0ba1f06ea7e4eb749c001f78f6cb5540daa556a0566322a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","to":"0x781e90f1c8fc4611c9b7497c3b47f99ef6969cbc","transactionIndex":"0x0","value":"0x0","type":"0x7e","v":"0x0","r":"0x0","s":"0x0","sender":"0x7885bcbd5cecef1336b5300fb5186a12ddd8c478","queueIndex":"0xe7ba0", "yParity":"0x0"}"#; + let rpc_tx = r#"{"blockHash":"0x018ed80ea8340984a1f4841490284d6e51d71f9e9411feeca41e007a89fbfdff","blockNumber":"0xb81121","from":"0x7885bcbd5cecef1336b5300fb5186a12ddd8c478","gas":"0x1e8480","gasPrice":"0x0","hash":"0x5c1c3785c8bf5d7f1cb714abd1d22e32642887215602c3a14a5e9ee105bad6aa","input":"0x8ef1332e000000000000000000000000c186fa914353c44b2e33ebe05f21846f1048beda0000000000000000000000003bad7ad0728f9917d1bf08af5782dcbd516cdd96000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e7ba000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000044493a4f846ffc1507cbfe98a2b0ba1f06ea7e4eb749c001f78f6cb5540daa556a0566322a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","to":"0x781e90f1c8fc4611c9b7497c3b47f99ef6969cbc","transactionIndex":"0x0","value":"0x0","type":"0x7e","v":"0x0","r":"0x0","s":"0x0","sender":"0x7885bcbd5cecef1336b5300fb5186a12ddd8c478","queueIndex":"0xe7ba0", "yParity":"0x0"}"#; let tx = serde_json::from_str::(rpc_tx).unwrap();