Skip to main content

ethrex_l2_sdk/
privileged_data.rs

1use bytes::Bytes;
2use ethrex_common::{
3    Address, H160, H256, U256,
4    types::{PrivilegedL2Transaction, TxType},
5};
6use ethrex_rpc::{
7    EthClient,
8    clients::{EthClientError, Overrides},
9    types::receipt::RpcLogInfo,
10};
11
12use crate::build_generic_tx;
13
14pub struct PrivilegedTransactionData {
15    pub value: U256,
16    pub to_address: H160,
17    pub transaction_id: U256,
18    pub from: H160,
19    pub gas_limit: U256,
20    pub calldata: Vec<u8>,
21}
22
23impl PrivilegedTransactionData {
24    pub fn from_log(log: RpcLogInfo) -> Result<PrivilegedTransactionData, String> {
25        /*
26            event PrivilegedTxSent (
27                address indexed L1from, => part of topics, not data
28                address from, => 0..32
29                address to, => 32..64
30                uint256 transactionId, => 64..96
31                uint256 value, => 96..128
32                uint256 gasLimit, => 128..160
33                bytes data
34                    => offset_data => 160..192
35                    => length_data => 192..224
36                    => data => 224..
37            );
38            Any value that is not 32 bytes is padded with zeros.
39        */
40
41        let from = H256::from_slice(log.data.get(0..32).ok_or(
42            "Failed to parse gas_limit from log: log.data[0..32] out of bounds".to_owned(),
43        )?);
44        let from_address = hash_to_address(from);
45
46        let to = H256::from_slice(log.data.get(32..64).ok_or(
47            "Failed to parse gas_limit from log: log.data[32..64] out of bounds".to_owned(),
48        )?);
49        let to_address = hash_to_address(to);
50
51        let transaction_id = U256::from_big_endian(log.data.get(64..96).ok_or(
52            "Failed to parse gas_limit from log: log.data[64..96] out of bounds".to_owned(),
53        )?);
54
55        let value = U256::from_big_endian(log.data.get(96..128).ok_or(
56            "Failed to parse gas_limit from log: log.data[96..128] out of bounds".to_owned(),
57        )?);
58
59        let gas_limit = U256::from_big_endian(log.data.get(128..160).ok_or(
60            "Failed to parse gas_limit from log: log.data[128..160] out of bounds".to_owned(),
61        )?);
62
63        // 160..192 is taken by offset_data, which we do not need
64
65        let calldata_len = U256::from_big_endian(log.data.get(192..224).ok_or(
66            "Failed to parse calldata_len from log: log.data[192..224] out of bounds".to_owned(),
67        )?);
68
69        let calldata_len = usize::try_from(calldata_len).map_err(|_| {
70            "Failed to parse calldata_len from log: value overflows usize".to_owned()
71        })?;
72        let calldata = log.data.get(224..224 + calldata_len).ok_or(
73            "Failed to parse calldata from log: log.data[224..224 + calldata_len] out of bounds"
74                .to_owned(),
75        )?;
76
77        Ok(Self {
78            value,
79            to_address,
80            transaction_id,
81            from: from_address,
82            gas_limit,
83            calldata: calldata.to_vec(),
84        })
85    }
86    pub async fn into_tx(
87        &self,
88        eth_client: &EthClient,
89        chain_id: u64,
90        gas_price: u64,
91    ) -> Result<PrivilegedL2Transaction, EthClientError> {
92        let generic_tx =
93            build_generic_tx(
94                eth_client,
95                TxType::Privileged,
96                self.to_address,
97                self.from,
98                Bytes::copy_from_slice(&self.calldata),
99                Overrides {
100                    chain_id: Some(chain_id),
101                    // Using the transaction_id as nonce.
102                    // If we make a transaction on the L2 with this address, we may break the
103                    // privileged transaction workflow.
104                    nonce: Some(u64::try_from(self.transaction_id).map_err(|_| {
105                        EthClientError::Custom("transaction_id overflows u64".to_owned())
106                    })?),
107                    value: Some(self.value),
108                    gas_limit: Some(u64::try_from(self.gas_limit).map_err(|_| {
109                        EthClientError::Custom("gas_limit overflows u64".to_owned())
110                    })?),
111                    // TODO(CHECK): Seems that when we start the L2, we need to set the gas.
112                    // Otherwise, the transaction is not included in the mempool.
113                    // We should override the blockchain to always include the transaction.
114                    max_fee_per_gas: Some(gas_price),
115                    max_priority_fee_per_gas: Some(gas_price),
116                    ..Default::default()
117                },
118            )
119            .await?;
120        Ok(generic_tx.try_into()?)
121    }
122}
123
124pub fn hash_to_address(hash: H256) -> Address {
125    Address::from_slice(&hash.as_fixed_bytes()[12..])
126}