kona_protocol/batch/tx_data/
eip7702.rs1use 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#[derive(Debug, Clone, PartialEq, Eq, RlpEncodable, RlpDecodable)]
12pub struct SpanBatchEip7702TransactionData {
13 pub value: U256,
15 pub max_priority_fee_per_gas: U256,
17 pub max_fee_per_gas: U256,
19 pub data: Bytes,
21 pub access_list: AccessList,
23 pub authorization_list: Vec<SignedAuthorization>,
25}
26
27impl SpanBatchEip7702TransactionData {
28 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 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 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}