kona_protocol/batch/tx_data/
eip7702.rs

1//! This module contains the eip7702 transaction data type for a span batch.
2
3use crate::SpanBatchError;
4use alloc::vec::Vec;
5use alloy_consensus::{SignableTransaction, Signed, TxEip7702};
6use alloy_eips::{eip2930::AccessList, eip7702::SignedAuthorization};
7use alloy_primitives::{Address, PrimitiveSignature as Signature, U256};
8use alloy_rlp::{Bytes, RlpDecodable, RlpEncodable};
9
10/// The transaction data for an EIP-7702 transaction within a span batch.
11#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
12pub struct SpanBatchEip7702TransactionData {
13    /// The ETH value of the transaction.
14    pub value: U256,
15    /// Maximum priority fee per gas.
16    pub max_priority_fee_per_gas: U256,
17    /// Maximum fee per gas.
18    pub max_fee_per_gas: U256,
19    /// Transaction calldata.
20    pub data: Bytes,
21    /// Access list, used to pre-warm storage slots through static declaration.
22    pub access_list: AccessList,
23    /// Authorization list, used to allow a signer to delegate code to a contract
24    pub authorization_list: Vec<SignedAuthorization>,
25}
26
27impl SpanBatchEip7702TransactionData {
28    /// Converts [SpanBatchEip7702TransactionData] into a signed [`TxEip7702`].
29    pub fn to_signed_tx(
30        &self,
31        nonce: u64,
32        gas: u64,
33        to: Address,
34        chain_id: u64,
35        signature: Signature,
36    ) -> Result<Signed<TxEip7702>, SpanBatchError> {
37        // SAFETY: A U256 as be bytes is always 32 bytes long.
38        let mut max_fee_per_gas = [0u8; 16];
39        max_fee_per_gas.copy_from_slice(&self.max_fee_per_gas.to_be_bytes::<32>()[16..]);
40        let max_fee_per_gas = u128::from_be_bytes(max_fee_per_gas);
41
42        // SAFETY: A U256 as be bytes is always 32 bytes long.
43        let mut max_priority_fee_per_gas = [0u8; 16];
44        max_priority_fee_per_gas
45            .copy_from_slice(&self.max_priority_fee_per_gas.to_be_bytes::<32>()[16..]);
46        let max_priority_fee_per_gas = u128::from_be_bytes(max_priority_fee_per_gas);
47
48        let eip7702_tx = TxEip7702 {
49            chain_id,
50            nonce,
51            max_fee_per_gas,
52            max_priority_fee_per_gas,
53            gas_limit: gas,
54            to,
55            value: self.value,
56            input: self.data.clone().into(),
57            access_list: self.access_list.clone(),
58            authorization_list: self.authorization_list.clone(),
59        };
60        let signature_hash = eip7702_tx.signature_hash();
61        Ok(Signed::new_unchecked(eip7702_tx, signature, signature_hash))
62    }
63}
64
65#[cfg(test)]
66mod test {
67    use super::*;
68    use crate::SpanBatchTransactionData;
69    use alloc::{vec, vec::Vec};
70    use alloy_rlp::{Decodable, Encodable};
71    use revm::primitives::Authorization;
72
73    #[test]
74    fn test_eip7702_to_signed_tx() {
75        let authorization = Authorization {
76            chain_id: U256::from(0x01),
77            address: Address::left_padding_from(&[0x01, 0x02, 0x03]),
78            nonce: 2,
79        };
80        let signature = Signature::test_signature();
81        let arb_authorization: SignedAuthorization = authorization.into_signed(signature);
82
83        let variable_fee_tx = SpanBatchEip7702TransactionData {
84            value: U256::from(0xFF),
85            max_fee_per_gas: U256::from(0xEE),
86            max_priority_fee_per_gas: U256::from(0xDD),
87            data: Bytes::from(alloc::vec![0x01, 0x02, 0x03]),
88            access_list: AccessList::default(),
89            authorization_list: vec![arb_authorization],
90        };
91
92        let signed_tx = variable_fee_tx
93            .to_signed_tx(0, 0, Address::ZERO, 0, Signature::test_signature())
94            .unwrap();
95
96        assert_eq!(*signed_tx.signature(), Signature::test_signature());
97    }
98
99    #[test]
100    fn encode_eip7702_tx_data_roundtrip() {
101        let authorization = Authorization {
102            chain_id: U256::from(0x01),
103            address: Address::left_padding_from(&[0x01, 0x02, 0x03]),
104            nonce: 2,
105        };
106        let signature = Signature::test_signature();
107        let arb_authorization: SignedAuthorization = authorization.into_signed(signature);
108
109        let variable_fee_tx = SpanBatchEip7702TransactionData {
110            value: U256::from(0xFF),
111            max_fee_per_gas: U256::from(0xEE),
112            max_priority_fee_per_gas: U256::from(0xDD),
113            data: Bytes::from(alloc::vec![0x01, 0x02, 0x03]),
114            access_list: AccessList::default(),
115            authorization_list: vec![arb_authorization],
116        };
117
118        let mut encoded_buf = Vec::new();
119        SpanBatchTransactionData::Eip7702(variable_fee_tx.clone()).encode(&mut encoded_buf);
120
121        let decoded = SpanBatchTransactionData::decode(&mut encoded_buf.as_slice()).unwrap();
122        let SpanBatchTransactionData::Eip7702(variable_fee_decoded) = decoded else {
123            panic!("Expected SpanBatchEip7702TransactionData, got {:?}", decoded);
124        };
125
126        assert_eq!(variable_fee_tx, variable_fee_decoded);
127    }
128}