1#![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
67pub const EIP_712_TX_TYPE: u8 = 0x71;
69
70pub const EIP_1559_TX_TYPE: u8 = 0x02;
72
73pub const EIP_4844_TX_TYPE: u8 = 0x03;
75
76pub const EIP_2930_TX_TYPE: u8 = 0x01;
78
79pub const LEGACY_TX_TYPE: u8 = 0x0;
81
82pub const PRIORITY_OPERATION_L2_TX_TYPE: u8 = 0xff;
84
85pub 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 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 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 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 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#[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}