zksync_types/
lib.rs

1//! ZKsync types: essential type definitions for ZKsync network.
2//!
3//! `zksync_types` is a crate containing essential ZKsync network types, such as transactions, operations and
4//! blockchain primitives.
5
6#![allow(clippy::upper_case_acronyms, clippy::derive_partial_eq_without_eq)]
7
8use std::{fmt, fmt::Debug};
9
10use anyhow::Context as _;
11pub use event::{VmEvent, VmEventGroupKey};
12use fee::encoding_len;
13pub use l1::L1TxCommonData;
14pub use l2::L2TxCommonData;
15pub use protocol_upgrade::{ProtocolUpgrade, ProtocolVersion};
16use serde::{Deserialize, Serialize};
17pub use storage::*;
18pub use tx::Execute;
19pub use zksync_basic_types::{protocol_version::ProtocolVersionId, vm_version::VmVersion, *};
20pub use zksync_crypto_primitives::*;
21use zksync_utils::{
22    address_to_u256, bytecode::hash_bytecode, h256_to_u256, u256_to_account_address,
23};
24
25use crate::{
26    l2::{L2Tx, TransactionType},
27    protocol_upgrade::ProtocolUpgradeTxCommonData,
28};
29pub use crate::{Nonce, H256, U256, U64};
30
31pub type SerialId = u64;
32
33pub mod abi;
34pub mod aggregated_operations;
35pub mod blob;
36pub mod block;
37pub mod circuit;
38pub mod commitment;
39pub mod contract_verification_api;
40pub mod debug_flat_call;
41pub mod event;
42pub mod fee;
43pub mod fee_model;
44pub mod l1;
45pub mod l2;
46pub mod l2_to_l1_log;
47pub mod priority_op_onchain_data;
48pub mod protocol_upgrade;
49pub mod pubdata_da;
50pub mod snapshots;
51pub mod storage;
52pub mod storage_writes_deduplicator;
53pub mod system_contracts;
54pub mod tokens;
55pub mod tx;
56pub mod vm_trace;
57pub mod zk_evm_types;
58
59pub mod api;
60pub mod base_token_ratio;
61pub mod eth_sender;
62pub mod helpers;
63pub mod proto;
64pub mod transaction_request;
65pub mod utils;
66
67/// Denotes the first byte of the special ZKsync's EIP-712-signed transaction.
68pub const EIP_712_TX_TYPE: u8 = 0x71;
69
70/// Denotes the first byte of the `EIP-1559` transaction.
71pub const EIP_1559_TX_TYPE: u8 = 0x02;
72
73/// Denotes the first byte of the `EIP-4844` transaction.
74pub const EIP_4844_TX_TYPE: u8 = 0x03;
75
76/// Denotes the first byte of the `EIP-2930` transaction.
77pub const EIP_2930_TX_TYPE: u8 = 0x01;
78
79/// Denotes the first byte of some legacy transaction, which type is unknown to the server.
80pub const LEGACY_TX_TYPE: u8 = 0x0;
81
82/// Denotes the first byte of the priority transaction.
83pub const PRIORITY_OPERATION_L2_TX_TYPE: u8 = 0xff;
84
85/// Denotes the first byte of the protocol upgrade transaction.
86pub const PROTOCOL_UPGRADE_TX_TYPE: u8 = 0xfe;
87
88#[derive(Clone, Serialize, Deserialize)]
89pub struct Transaction {
90    pub common_data: ExecuteTransactionCommon,
91    pub execute: Execute,
92    pub received_timestamp_ms: u64,
93    pub raw_bytes: Option<web3::Bytes>,
94}
95
96impl std::fmt::Debug for Transaction {
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        f.debug_tuple("Transaction").field(&self.hash()).finish()
99    }
100}
101
102impl PartialEq for Transaction {
103    fn eq(&self, other: &Transaction) -> bool {
104        self.hash() == other.hash()
105    }
106}
107
108impl Eq for Transaction {}
109
110impl Transaction {
111    /// Returns recipient account of the transaction.
112    pub fn recipient_account(&self) -> Address {
113        self.execute.contract_address
114    }
115
116    pub fn nonce(&self) -> Option<Nonce> {
117        match &self.common_data {
118            ExecuteTransactionCommon::L1(_) => None,
119            ExecuteTransactionCommon::L2(tx) => Some(tx.nonce),
120            ExecuteTransactionCommon::ProtocolUpgrade(_) => None,
121        }
122    }
123
124    pub fn is_l1(&self) -> bool {
125        matches!(self.common_data, ExecuteTransactionCommon::L1(_))
126    }
127
128    pub fn tx_format(&self) -> TransactionType {
129        match &self.common_data {
130            ExecuteTransactionCommon::L1(tx) => tx.tx_format(),
131            ExecuteTransactionCommon::L2(tx) => tx.transaction_type,
132            ExecuteTransactionCommon::ProtocolUpgrade(tx) => tx.tx_format(),
133        }
134    }
135
136    pub fn hash(&self) -> H256 {
137        match &self.common_data {
138            ExecuteTransactionCommon::L1(data) => data.hash(),
139            ExecuteTransactionCommon::L2(data) => data.hash(),
140            ExecuteTransactionCommon::ProtocolUpgrade(data) => data.hash(),
141        }
142    }
143
144    /// Returns the account that initiated this transaction.
145    pub fn initiator_account(&self) -> Address {
146        match &self.common_data {
147            ExecuteTransactionCommon::L1(data) => data.sender,
148            ExecuteTransactionCommon::L2(data) => data.initiator_address,
149            ExecuteTransactionCommon::ProtocolUpgrade(data) => data.sender,
150        }
151    }
152
153    /// Returns the payer for L2 transaction and 0 for L1 transactions
154    pub fn payer(&self) -> Address {
155        match &self.common_data {
156            ExecuteTransactionCommon::L1(data) => data.sender,
157            ExecuteTransactionCommon::L2(data) => {
158                let paymaster = data.paymaster_params.paymaster;
159                if paymaster == Address::default() {
160                    data.initiator_address
161                } else {
162                    paymaster
163                }
164            }
165            ExecuteTransactionCommon::ProtocolUpgrade(data) => data.sender,
166        }
167    }
168
169    pub fn gas_limit(&self) -> U256 {
170        match &self.common_data {
171            ExecuteTransactionCommon::L1(data) => data.gas_limit,
172            ExecuteTransactionCommon::L2(data) => data.fee.gas_limit,
173            ExecuteTransactionCommon::ProtocolUpgrade(data) => data.gas_limit,
174        }
175    }
176
177    pub fn max_fee_per_gas(&self) -> U256 {
178        match &self.common_data {
179            ExecuteTransactionCommon::L1(data) => data.max_fee_per_gas,
180            ExecuteTransactionCommon::L2(data) => data.fee.max_fee_per_gas,
181            ExecuteTransactionCommon::ProtocolUpgrade(data) => data.max_fee_per_gas,
182        }
183    }
184
185    pub fn gas_per_pubdata_byte_limit(&self) -> U256 {
186        match &self.common_data {
187            ExecuteTransactionCommon::L1(data) => data.gas_per_pubdata_limit,
188            ExecuteTransactionCommon::L2(data) => data.fee.gas_per_pubdata_limit,
189            ExecuteTransactionCommon::ProtocolUpgrade(data) => data.gas_per_pubdata_limit,
190        }
191    }
192
193    // Returns how many slots it takes to encode the transaction
194    pub fn encoding_len(&self) -> usize {
195        let data_len = self.execute.calldata.len();
196        let factory_deps_len = self.execute.factory_deps.len();
197        let (signature_len, paymaster_input_len) = match &self.common_data {
198            ExecuteTransactionCommon::L1(_) => (0, 0),
199            ExecuteTransactionCommon::L2(l2_common_data) => (
200                l2_common_data.signature.len(),
201                l2_common_data.paymaster_params.paymaster_input.len(),
202            ),
203            ExecuteTransactionCommon::ProtocolUpgrade(_) => (0, 0),
204        };
205
206        encoding_len(
207            data_len as u64,
208            signature_len as u64,
209            factory_deps_len as u64,
210            paymaster_input_len as u64,
211            0,
212        )
213    }
214}
215
216/// Optional input `Ethereum`-like encoded transaction if submitted via Web3 API.
217/// If exists, its hash will be used to identify transaction.
218/// Note, that for EIP712-type transactions, `hash` is not equal to the hash
219/// of the `data`, but rather calculated by special formula.
220#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
221pub struct InputData {
222    pub hash: H256,
223    pub data: Vec<u8>,
224}
225
226#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
227pub enum ExecuteTransactionCommon {
228    L1(L1TxCommonData),
229    L2(L2TxCommonData),
230    ProtocolUpgrade(ProtocolUpgradeTxCommonData),
231}
232
233impl fmt::Display for ExecuteTransactionCommon {
234    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
235        match self {
236            ExecuteTransactionCommon::L1(data) => write!(f, "L1TxCommonData: {:?}", data),
237            ExecuteTransactionCommon::L2(data) => write!(f, "L2TxCommonData: {:?}", data),
238            ExecuteTransactionCommon::ProtocolUpgrade(data) => {
239                write!(f, "ProtocolUpgradeTxCommonData: {:?}", data)
240            }
241        }
242    }
243}
244
245impl TryFrom<Transaction> for abi::Transaction {
246    type Error = anyhow::Error;
247
248    fn try_from(tx: Transaction) -> anyhow::Result<Self> {
249        use ExecuteTransactionCommon as E;
250        let factory_deps = tx.execute.factory_deps;
251        Ok(match tx.common_data {
252            E::L2(data) => Self::L2(
253                data.input
254                    .context("input is required for L2 transactions")?
255                    .data,
256            ),
257            E::L1(data) => Self::L1 {
258                tx: abi::L2CanonicalTransaction {
259                    tx_type: PRIORITY_OPERATION_L2_TX_TYPE.into(),
260                    from: address_to_u256(&data.sender),
261                    to: address_to_u256(&tx.execute.contract_address),
262                    gas_limit: data.gas_limit,
263                    gas_per_pubdata_byte_limit: data.gas_per_pubdata_limit,
264                    max_fee_per_gas: data.max_fee_per_gas,
265                    max_priority_fee_per_gas: 0.into(),
266                    paymaster: 0.into(),
267                    nonce: data.serial_id.0.into(),
268                    value: tx.execute.value,
269                    reserved: [
270                        data.to_mint,
271                        address_to_u256(&data.refund_recipient),
272                        0.into(),
273                        0.into(),
274                    ],
275                    data: tx.execute.calldata,
276                    signature: vec![],
277                    factory_deps: factory_deps
278                        .iter()
279                        .map(|b| h256_to_u256(hash_bytecode(b)))
280                        .collect(),
281                    paymaster_input: vec![],
282                    reserved_dynamic: vec![],
283                }
284                .into(),
285                factory_deps,
286                eth_block: data.eth_block,
287            },
288            E::ProtocolUpgrade(data) => Self::L1 {
289                tx: abi::L2CanonicalTransaction {
290                    tx_type: PROTOCOL_UPGRADE_TX_TYPE.into(),
291                    from: address_to_u256(&data.sender),
292                    to: address_to_u256(&tx.execute.contract_address),
293                    gas_limit: data.gas_limit,
294                    gas_per_pubdata_byte_limit: data.gas_per_pubdata_limit,
295                    max_fee_per_gas: data.max_fee_per_gas,
296                    max_priority_fee_per_gas: 0.into(),
297                    paymaster: 0.into(),
298                    nonce: (data.upgrade_id as u16).into(),
299                    value: tx.execute.value,
300                    reserved: [
301                        data.to_mint,
302                        address_to_u256(&data.refund_recipient),
303                        0.into(),
304                        0.into(),
305                    ],
306                    data: tx.execute.calldata,
307                    signature: vec![],
308                    factory_deps: factory_deps
309                        .iter()
310                        .map(|b| h256_to_u256(hash_bytecode(b)))
311                        .collect(),
312                    paymaster_input: vec![],
313                    reserved_dynamic: vec![],
314                }
315                .into(),
316                factory_deps,
317                eth_block: data.eth_block,
318            },
319        })
320    }
321}
322
323impl TryFrom<abi::Transaction> for Transaction {
324    type Error = anyhow::Error;
325    fn try_from(tx: abi::Transaction) -> anyhow::Result<Self> {
326        Ok(match tx {
327            abi::Transaction::L1 {
328                tx,
329                factory_deps,
330                eth_block,
331            } => {
332                let factory_deps_hashes: Vec<_> = factory_deps
333                    .iter()
334                    .map(|b| h256_to_u256(hash_bytecode(b)))
335                    .collect();
336                anyhow::ensure!(tx.factory_deps == factory_deps_hashes);
337                for item in &tx.reserved[2..] {
338                    anyhow::ensure!(item == &U256::zero());
339                }
340                assert_eq!(tx.max_priority_fee_per_gas, U256::zero());
341                assert_eq!(tx.paymaster, U256::zero());
342                assert!(tx.signature.is_empty());
343                assert!(tx.paymaster_input.is_empty());
344                assert!(tx.reserved_dynamic.is_empty());
345                let hash = tx.hash();
346                Transaction {
347                    common_data: match tx.tx_type {
348                        t if t == PRIORITY_OPERATION_L2_TX_TYPE.into() => {
349                            ExecuteTransactionCommon::L1(L1TxCommonData {
350                                serial_id: PriorityOpId(
351                                    tx.nonce
352                                        .try_into()
353                                        .map_err(|err| anyhow::format_err!("{err}"))?,
354                                ),
355                                canonical_tx_hash: hash,
356                                sender: u256_to_account_address(&tx.from),
357                                layer_2_tip_fee: U256::zero(),
358                                to_mint: tx.reserved[0],
359                                refund_recipient: u256_to_account_address(&tx.reserved[1]),
360                                full_fee: U256::zero(),
361                                gas_limit: tx.gas_limit,
362                                max_fee_per_gas: tx.max_fee_per_gas,
363                                gas_per_pubdata_limit: tx.gas_per_pubdata_byte_limit,
364                                op_processing_type: l1::OpProcessingType::Common,
365                                priority_queue_type: l1::PriorityQueueType::Deque,
366                                eth_block,
367                            })
368                        }
369                        t if t == PROTOCOL_UPGRADE_TX_TYPE.into() => {
370                            ExecuteTransactionCommon::ProtocolUpgrade(ProtocolUpgradeTxCommonData {
371                                upgrade_id: tx.nonce.try_into().unwrap(),
372                                canonical_tx_hash: hash,
373                                sender: u256_to_account_address(&tx.from),
374                                to_mint: tx.reserved[0],
375                                refund_recipient: u256_to_account_address(&tx.reserved[1]),
376                                gas_limit: tx.gas_limit,
377                                max_fee_per_gas: tx.max_fee_per_gas,
378                                gas_per_pubdata_limit: tx.gas_per_pubdata_byte_limit,
379                                eth_block,
380                            })
381                        }
382                        unknown_type => anyhow::bail!("unknown tx type {unknown_type}"),
383                    },
384                    execute: Execute {
385                        contract_address: u256_to_account_address(&tx.to),
386                        calldata: tx.data,
387                        factory_deps,
388                        value: tx.value,
389                    },
390                    raw_bytes: None,
391                    received_timestamp_ms: helpers::unix_timestamp_ms(),
392                }
393            }
394            abi::Transaction::L2(raw) => {
395                let (req, hash) =
396                    transaction_request::TransactionRequest::from_bytes_unverified(&raw)?;
397                let mut tx = L2Tx::from_request_unverified(req)?;
398                tx.set_input(raw, hash);
399                tx.into()
400            }
401        })
402    }
403}