kona_protocol/batch/tx_data/
wrapper.rs

1//! This module contains the top level span batch transaction data type.
2
3use alloy_consensus::{Transaction, TxEnvelope, TxType};
4use alloy_primitives::{Address, PrimitiveSignature as Signature, U256};
5use alloy_rlp::{Bytes, Decodable, Encodable};
6
7use crate::{
8    SpanBatchEip1559TransactionData, SpanBatchEip2930TransactionData,
9    SpanBatchEip7702TransactionData, SpanBatchError, SpanBatchLegacyTransactionData,
10    SpanDecodingError,
11};
12
13/// The typed transaction data for a transaction within a span batch.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum SpanBatchTransactionData {
16    /// Legacy transaction data.
17    Legacy(SpanBatchLegacyTransactionData),
18    /// EIP-2930 transaction data.
19    Eip2930(SpanBatchEip2930TransactionData),
20    /// EIP-1559 transaction data.
21    Eip1559(SpanBatchEip1559TransactionData),
22    /// EIP-7702 transaction data.
23    Eip7702(SpanBatchEip7702TransactionData),
24}
25
26impl Encodable for SpanBatchTransactionData {
27    fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
28        match self {
29            Self::Legacy(data) => {
30                data.encode(out);
31            }
32            Self::Eip2930(data) => {
33                out.put_u8(TxType::Eip2930 as u8);
34                data.encode(out);
35            }
36            Self::Eip1559(data) => {
37                out.put_u8(TxType::Eip1559 as u8);
38                data.encode(out);
39            }
40            Self::Eip7702(data) => {
41                out.put_u8(TxType::Eip7702 as u8);
42                data.encode(out);
43            }
44        }
45    }
46}
47
48impl Decodable for SpanBatchTransactionData {
49    fn decode(r: &mut &[u8]) -> Result<Self, alloy_rlp::Error> {
50        if !r.is_empty() && r[0] > 0x7F {
51            // Legacy transaction
52            return Ok(Self::Legacy(SpanBatchLegacyTransactionData::decode(r)?));
53        }
54        // Non-legacy transaction (EIP-2718 envelope encoding)
55        Self::decode_typed(r)
56    }
57}
58
59impl TryFrom<&TxEnvelope> for SpanBatchTransactionData {
60    type Error = SpanBatchError;
61
62    fn try_from(tx_envelope: &TxEnvelope) -> Result<Self, Self::Error> {
63        match tx_envelope {
64            TxEnvelope::Legacy(s) => {
65                let s = s.tx();
66                Ok(Self::Legacy(SpanBatchLegacyTransactionData {
67                    value: s.value,
68                    gas_price: U256::from(s.gas_price),
69                    data: Bytes::from(s.input().to_vec()),
70                }))
71            }
72            TxEnvelope::Eip2930(s) => {
73                let s = s.tx();
74                Ok(Self::Eip2930(SpanBatchEip2930TransactionData {
75                    value: s.value,
76                    gas_price: U256::from(s.gas_price),
77                    data: Bytes::from(s.input().to_vec()),
78                    access_list: s.access_list.clone(),
79                }))
80            }
81            TxEnvelope::Eip1559(s) => {
82                let s = s.tx();
83                Ok(Self::Eip1559(SpanBatchEip1559TransactionData {
84                    value: s.value,
85                    max_fee_per_gas: U256::from(s.max_fee_per_gas),
86                    max_priority_fee_per_gas: U256::from(s.max_priority_fee_per_gas),
87                    data: Bytes::from(s.input().to_vec()),
88                    access_list: s.access_list.clone(),
89                }))
90            }
91            TxEnvelope::Eip7702(s) => {
92                let s = s.tx();
93                Ok(Self::Eip7702(SpanBatchEip7702TransactionData {
94                    value: s.value,
95                    max_fee_per_gas: U256::from(s.max_fee_per_gas),
96                    max_priority_fee_per_gas: U256::from(s.max_priority_fee_per_gas),
97                    data: Bytes::from(s.input().to_vec()),
98                    access_list: s.access_list.clone(),
99                    authorization_list: s.authorization_list.clone(),
100                }))
101            }
102            _ => Err(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionType)),
103        }
104    }
105}
106
107impl SpanBatchTransactionData {
108    /// Returns the transaction type of the [SpanBatchTransactionData].
109    pub const fn tx_type(&self) -> TxType {
110        match self {
111            Self::Legacy(_) => TxType::Legacy,
112            Self::Eip2930(_) => TxType::Eip2930,
113            Self::Eip1559(_) => TxType::Eip1559,
114            Self::Eip7702(_) => TxType::Eip7702,
115        }
116    }
117
118    /// Decodes a typed transaction into a [SpanBatchTransactionData] from a byte slice.
119    pub fn decode_typed(b: &[u8]) -> Result<Self, alloy_rlp::Error> {
120        if b.len() <= 1 {
121            return Err(alloy_rlp::Error::Custom("Invalid transaction data"));
122        }
123
124        match b[0].try_into().map_err(|_| alloy_rlp::Error::Custom("Invalid tx type"))? {
125            TxType::Eip2930 => {
126                Ok(Self::Eip2930(SpanBatchEip2930TransactionData::decode(&mut &b[1..])?))
127            }
128            TxType::Eip1559 => {
129                Ok(Self::Eip1559(SpanBatchEip1559TransactionData::decode(&mut &b[1..])?))
130            }
131            TxType::Eip7702 => {
132                Ok(Self::Eip7702(SpanBatchEip7702TransactionData::decode(&mut &b[1..])?))
133            }
134            _ => Err(alloy_rlp::Error::Custom("Invalid transaction type")),
135        }
136    }
137
138    /// Converts the [SpanBatchTransactionData] into a singed transaction as [`TxEnvelope`].
139    pub fn to_signed_tx(
140        &self,
141        nonce: u64,
142        gas: u64,
143        to: Option<Address>,
144        chain_id: u64,
145        signature: Signature,
146        is_protected: bool,
147    ) -> Result<TxEnvelope, SpanBatchError> {
148        Ok(match self {
149            Self::Legacy(data) => TxEnvelope::Legacy(data.to_signed_tx(
150                nonce,
151                gas,
152                to,
153                chain_id,
154                signature,
155                is_protected,
156            )?),
157            Self::Eip2930(data) => {
158                TxEnvelope::Eip2930(data.to_signed_tx(nonce, gas, to, chain_id, signature)?)
159            }
160            Self::Eip1559(data) => {
161                TxEnvelope::Eip1559(data.to_signed_tx(nonce, gas, to, chain_id, signature)?)
162            }
163            Self::Eip7702(data) => {
164                let Some(addr) = to else {
165                    return Err(SpanBatchError::Decoding(SpanDecodingError::InvalidTransactionData));
166                };
167                TxEnvelope::Eip7702(data.to_signed_tx(nonce, gas, addr, chain_id, signature)?)
168            }
169        })
170    }
171}