1use std::path::{Path, PathBuf};
2
3use crate::deps::system_contracts::load_builtin_contract;
4use crate::node::ImpersonationManager;
5use anvil_zksync_config::types::{BoojumConfig, SystemContractsOptions};
6use zksync_contracts::{
7 read_sys_contract_bytecode, BaseSystemContracts, BaseSystemContractsHashes, ContractLanguage,
8 SystemContractCode, SystemContractsRepo,
9};
10use zksync_multivm::interface::TxExecutionMode;
11use zksync_types::bytecode::BytecodeHash;
12use zksync_types::{Address, ProtocolVersionId};
13
14#[derive(Debug, Default)]
16pub struct SystemContractsBuilder {
17 system_contracts_options: Option<SystemContractsOptions>,
18 system_contracts_path: Option<PathBuf>,
19 protocol_version: Option<ProtocolVersionId>,
20 use_evm_interpreter: bool,
21 boojum: BoojumConfig,
22}
23
24impl SystemContractsBuilder {
25 pub fn new() -> Self {
27 Self::default()
28 }
29
30 pub fn system_contracts_options(mut self, opts: SystemContractsOptions) -> Self {
32 self.system_contracts_options = Some(opts);
33 self
34 }
35
36 pub fn system_contracts_path(mut self, path: Option<PathBuf>) -> Self {
38 self.system_contracts_path = path;
39 self
40 }
41
42 pub fn protocol_version(mut self, version: ProtocolVersionId) -> Self {
44 self.protocol_version = Some(version);
45 self
46 }
47
48 pub fn with_evm_interpreter(mut self, flag: bool) -> Self {
50 self.use_evm_interpreter = flag;
51 self
52 }
53
54 pub fn with_boojum(mut self, config: BoojumConfig) -> Self {
56 self.boojum = config;
57 self
58 }
59
60 pub fn build(self) -> SystemContracts {
65 let options = self
66 .system_contracts_options
67 .expect("SystemContractsOptions must be provided");
68 let protocol_version = self
69 .protocol_version
70 .unwrap_or_else(ProtocolVersionId::latest);
71
72 tracing::debug!(
73 %protocol_version, use_evm_interpreter = self.use_evm_interpreter, use_boojum = self.boojum.use_boojum,
74 "Building SystemContracts"
75 );
76
77 SystemContracts::from_options(
78 options,
79 self.system_contracts_path,
80 protocol_version,
81 self.use_evm_interpreter,
82 self.boojum,
83 )
84 }
85}
86
87#[derive(Debug, Clone)]
89pub struct SystemContracts {
90 pub protocol_version: ProtocolVersionId,
91 baseline_contracts: BaseSystemContracts,
92 playground_contracts: BaseSystemContracts,
93 fee_estimate_contracts: BaseSystemContracts,
94 baseline_impersonating_contracts: BaseSystemContracts,
95 fee_estimate_impersonating_contracts: BaseSystemContracts,
96 use_evm_emulator: bool,
97 pub boojum: BoojumConfig,
101}
102
103impl SystemContracts {
104 pub fn builder() -> SystemContractsBuilder {
106 SystemContractsBuilder::new()
107 }
108
109 pub fn from_options(
112 options: SystemContractsOptions,
113 system_contracts_path: Option<PathBuf>,
114 protocol_version: ProtocolVersionId,
115 use_evm_emulator: bool,
116 boojum: BoojumConfig,
117 ) -> Self {
118 tracing::info!(
119 %protocol_version,
120 use_evm_emulator,
121 boojum.use_boojum,
122 "initializing system contracts"
123 );
124 let path = system_contracts_path.unwrap_or_else(|| SystemContractsRepo::default().root);
125 Self {
126 protocol_version,
127 baseline_contracts: baseline_contracts(
128 options,
129 protocol_version,
130 use_evm_emulator,
131 &path,
132 ),
133 playground_contracts: playground(options, protocol_version, use_evm_emulator, &path),
134 fee_estimate_contracts: fee_estimate_contracts(
135 options,
136 protocol_version,
137 use_evm_emulator,
138 &path,
139 ),
140 baseline_impersonating_contracts: baseline_impersonating_contracts(
141 options,
142 protocol_version,
143 use_evm_emulator,
144 &path,
145 ),
146 fee_estimate_impersonating_contracts: fee_estimate_impersonating_contracts(
147 options,
148 protocol_version,
149 use_evm_emulator,
150 &path,
151 ),
152 use_evm_emulator,
153 boojum,
154 }
155 }
156
157 pub fn allow_no_target(&self) -> bool {
160 self.boojum.use_boojum || self.use_evm_emulator
161 }
162
163 pub fn contracts_for_l2_call(&self) -> &BaseSystemContracts {
164 self.contracts(TxExecutionMode::EthCall, false)
165 }
166
167 pub fn contracts_for_fee_estimate(&self, impersonating: bool) -> &BaseSystemContracts {
168 self.contracts(TxExecutionMode::EstimateFee, impersonating)
169 }
170
171 pub fn contracts(
172 &self,
173 execution_mode: TxExecutionMode,
174 impersonating: bool,
175 ) -> &BaseSystemContracts {
176 match (execution_mode, impersonating) {
177 (TxExecutionMode::VerifyExecute, false) => &self.baseline_contracts,
179 (TxExecutionMode::EstimateFee, false) => &self.fee_estimate_contracts,
182 (TxExecutionMode::EthCall, false) => &self.playground_contracts,
184 (TxExecutionMode::VerifyExecute, true) => &self.baseline_impersonating_contracts,
186 (TxExecutionMode::EstimateFee, true) => &self.fee_estimate_impersonating_contracts,
187 (TxExecutionMode::EthCall, true) => {
188 panic!("Account impersonating with eth_call is not supported")
189 }
190 }
191 }
192
193 pub fn base_system_contracts_hashes(&self) -> BaseSystemContractsHashes {
194 self.baseline_contracts.hashes()
195 }
196
197 pub fn system_contracts_for_initiator(
198 &self,
199 impersonation: &ImpersonationManager,
200 initiator: &Address,
201 ) -> BaseSystemContracts {
202 if impersonation.is_impersonating(initiator) {
203 tracing::info!("Executing tx from impersonated account {initiator:?}");
204 self.contracts(TxExecutionMode::VerifyExecute, true).clone()
205 } else {
206 self.contracts(TxExecutionMode::VerifyExecute, false)
207 .clone()
208 }
209 }
210}
211
212fn bsc_load_with_bootloader(
214 bootloader_bytecode: Vec<u8>,
215 options: SystemContractsOptions,
216 protocol_version: ProtocolVersionId,
217 use_evm_emulator: bool,
218 system_contracts_path: &Path,
219) -> BaseSystemContracts {
220 let repo = system_contracts_repo(system_contracts_path);
221 let hash = BytecodeHash::for_bytecode(&bootloader_bytecode);
222
223 let bootloader = SystemContractCode {
224 code: bootloader_bytecode,
225 hash: hash.value(),
226 };
227
228 let aa_bytecode = match options {
229 SystemContractsOptions::BuiltIn => {
230 load_builtin_contract(protocol_version, "DefaultAccount")
231 }
232 SystemContractsOptions::Local => {
233 repo.read_sys_contract_bytecode("", "DefaultAccount", None, ContractLanguage::Sol)
234 }
235 SystemContractsOptions::BuiltInWithoutSecurity => {
236 load_builtin_contract(protocol_version, "DefaultAccountNoSecurity")
237 }
238 };
239
240 let aa_hash = BytecodeHash::for_bytecode(&aa_bytecode);
241 let default_aa = SystemContractCode {
242 code: aa_bytecode,
243 hash: aa_hash.value(),
244 };
245
246 let evm_emulator = if use_evm_emulator {
247 let evm_emulator_bytecode = match options {
248 SystemContractsOptions::Local => {
249 read_sys_contract_bytecode("", "EvmEmulator", ContractLanguage::Yul)
250 }
251 SystemContractsOptions::BuiltIn | SystemContractsOptions::BuiltInWithoutSecurity => {
252 load_builtin_contract(protocol_version, "EvmEmulator")
253 }
254 };
255 let evm_emulator_hash = BytecodeHash::for_bytecode(&evm_emulator_bytecode);
256 Some(SystemContractCode {
257 code: evm_emulator_bytecode,
258 hash: evm_emulator_hash.value(),
259 })
260 } else {
261 None
262 };
263
264 BaseSystemContracts {
265 bootloader,
266 default_aa,
267 evm_emulator,
268 }
269}
270
271fn playground(
273 options: SystemContractsOptions,
274 protocol_version: ProtocolVersionId,
275 use_evm_emulator: bool,
276 system_contracts_path: &Path,
277) -> BaseSystemContracts {
278 let repo = system_contracts_repo(system_contracts_path);
279 let bootloader_bytecode = match options {
280 SystemContractsOptions::BuiltIn | SystemContractsOptions::BuiltInWithoutSecurity => {
281 load_builtin_contract(protocol_version, "playground_batch")
282 }
283 SystemContractsOptions::Local => repo.read_sys_contract_bytecode(
284 "bootloader",
285 "playground_batch",
286 Some("Bootloader"),
287 ContractLanguage::Yul,
288 ),
289 };
290
291 bsc_load_with_bootloader(
292 bootloader_bytecode,
293 options,
294 protocol_version,
295 use_evm_emulator,
296 system_contracts_path,
297 )
298}
299
300fn fee_estimate_contracts(
307 options: SystemContractsOptions,
308 protocol_version: ProtocolVersionId,
309 use_evm_emulator: bool,
310 system_contracts_path: &Path,
311) -> BaseSystemContracts {
312 let repo = system_contracts_repo(system_contracts_path);
313 let bootloader_bytecode = match options {
314 SystemContractsOptions::BuiltIn | SystemContractsOptions::BuiltInWithoutSecurity => {
315 load_builtin_contract(protocol_version, "fee_estimate")
316 }
317 SystemContractsOptions::Local => repo.read_sys_contract_bytecode(
318 "bootloader",
319 "fee_estimate",
320 Some("Bootloader"),
321 ContractLanguage::Yul,
322 ),
323 };
324
325 bsc_load_with_bootloader(
326 bootloader_bytecode,
327 options,
328 protocol_version,
329 use_evm_emulator,
330 system_contracts_path,
331 )
332}
333
334fn fee_estimate_impersonating_contracts(
335 options: SystemContractsOptions,
336 protocol_version: ProtocolVersionId,
337 use_evm_emulator: bool,
338 system_contracts_path: &Path,
339) -> BaseSystemContracts {
340 let repo = system_contracts_repo(system_contracts_path);
341 let bootloader_bytecode = match options {
342 SystemContractsOptions::BuiltIn | SystemContractsOptions::BuiltInWithoutSecurity => {
343 load_builtin_contract(protocol_version, "fee_estimate_impersonating")
344 }
345 SystemContractsOptions::Local => repo.read_sys_contract_bytecode(
347 "bootloader",
348 "fee_estimate",
349 Some("Bootloader"),
350 ContractLanguage::Yul,
351 ),
352 };
353
354 bsc_load_with_bootloader(
355 bootloader_bytecode,
356 options,
357 protocol_version,
358 use_evm_emulator,
359 system_contracts_path,
360 )
361}
362
363fn baseline_contracts(
364 options: SystemContractsOptions,
365 protocol_version: ProtocolVersionId,
366 use_evm_emulator: bool,
367 system_contracts_path: &Path,
368) -> BaseSystemContracts {
369 let repo = system_contracts_repo(system_contracts_path);
370 let bootloader_bytecode = match options {
371 SystemContractsOptions::BuiltIn | SystemContractsOptions::BuiltInWithoutSecurity => {
372 load_builtin_contract(protocol_version, "proved_batch")
373 }
374 SystemContractsOptions::Local => repo.read_sys_contract_bytecode(
375 "bootloader",
376 "proved_batch",
377 Some("Bootloader"),
378 ContractLanguage::Yul,
379 ),
380 };
381 bsc_load_with_bootloader(
382 bootloader_bytecode,
383 options,
384 protocol_version,
385 use_evm_emulator,
386 system_contracts_path,
387 )
388}
389
390fn baseline_impersonating_contracts(
391 options: SystemContractsOptions,
392 protocol_version: ProtocolVersionId,
393 use_evm_emulator: bool,
394 system_contracts_path: &Path,
395) -> BaseSystemContracts {
396 let repo = system_contracts_repo(system_contracts_path);
397 let bootloader_bytecode = match options {
398 SystemContractsOptions::BuiltIn | SystemContractsOptions::BuiltInWithoutSecurity => {
399 load_builtin_contract(protocol_version, "proved_batch_impersonating")
400 }
401 SystemContractsOptions::Local => repo.read_sys_contract_bytecode(
403 "bootloader",
404 "proved_batch",
405 Some("Bootloader"),
406 ContractLanguage::Yul,
407 ),
408 };
409 bsc_load_with_bootloader(
410 bootloader_bytecode,
411 options,
412 protocol_version,
413 use_evm_emulator,
414 system_contracts_path,
415 )
416}
417
418fn system_contracts_repo(root: &Path) -> SystemContractsRepo {
419 SystemContractsRepo {
420 root: root.to_path_buf(),
421 }
422}