anvil_zksync_core/node/
zks.rs

1use crate::node::InMemoryNode;
2use anyhow::Context;
3use std::collections::HashMap;
4use zksync_error::anvil_zksync::node::AnvilNodeResult;
5use zksync_mini_merkle_tree::MiniMerkleTree;
6use zksync_types::api;
7use zksync_types::fee::Fee;
8use zksync_types::hasher::keccak::KeccakHasher;
9use zksync_types::hasher::Hasher;
10use zksync_types::l2_to_l1_log::{
11    l2_to_l1_logs_tree_size, L2ToL1Log, LOG_PROOF_SUPPORTED_METADATA_VERSION,
12};
13use zksync_types::transaction_request::CallRequest;
14use zksync_types::utils::storage_key_for_standard_token_balance;
15use zksync_types::{h256_to_u256, L1BatchNumber};
16use zksync_types::{
17    AccountTreeId, Address, L2BlockNumber, Transaction, H160, H256, L2_BASE_TOKEN_ADDRESS, U256,
18};
19use zksync_web3_decl::error::Web3Error;
20
21impl InMemoryNode {
22    pub async fn estimate_fee_impl(&self, req: CallRequest) -> AnvilNodeResult<Fee> {
23        self.inner.read().await.estimate_gas_impl(req).await
24    }
25
26    pub async fn estimate_gas_l1_to_l2(&self, req: CallRequest) -> AnvilNodeResult<U256> {
27        self.inner
28            .read()
29            .await
30            .estimate_l1_to_l2_gas_impl(req)
31            .await
32    }
33
34    pub async fn get_raw_block_transactions_impl(
35        &self,
36        block_number: L2BlockNumber,
37    ) -> Result<Vec<Transaction>, Web3Error> {
38        let tx_hashes = self
39            .blockchain
40            .get_block_tx_hashes_by_number(block_number)
41            .await;
42        if let Some(tx_hashes) = tx_hashes {
43            let mut transactions = Vec::with_capacity(tx_hashes.len());
44            for tx_hash in tx_hashes {
45                let transaction = self
46                    .blockchain
47                    .get_zksync_tx(&tx_hash)
48                    .await
49                    .with_context(|| anyhow::anyhow!("Unexpectedly transaction (hash={tx_hash}) belongs to a block but could not be found"))?;
50                transactions.push(transaction);
51            }
52            Ok(transactions)
53        } else {
54            Ok(self.fork.get_raw_block_transactions(block_number).await?)
55        }
56    }
57
58    pub async fn get_bridge_contracts_impl(&self) -> Result<api::BridgeAddresses, Web3Error> {
59        Ok(self
60            .fork
61            .get_bridge_contracts()
62            .await?
63            .unwrap_or(api::BridgeAddresses {
64                l1_shared_default_bridge: Default::default(),
65                l2_shared_default_bridge: Default::default(),
66                l1_erc20_default_bridge: Default::default(),
67                l2_erc20_default_bridge: Default::default(),
68                l1_weth_bridge: Default::default(),
69                l2_weth_bridge: Default::default(),
70                l2_legacy_shared_bridge: Default::default(),
71            }))
72    }
73
74    pub async fn get_confirmed_tokens_impl(
75        &self,
76        from: u32,
77        limit: u8,
78    ) -> anyhow::Result<Vec<zksync_web3_decl::types::Token>> {
79        Ok(self
80            .fork
81            .get_confirmed_tokens(from, limit)
82            .await?
83            .unwrap_or(vec![zksync_web3_decl::types::Token {
84                l1_address: Address::zero(),
85                l2_address: L2_BASE_TOKEN_ADDRESS,
86                name: "Ether".to_string(),
87                symbol: "ETH".to_string(),
88                decimals: 18,
89            }]))
90    }
91
92    pub async fn get_all_account_balances_impl(
93        &self,
94        address: Address,
95    ) -> Result<HashMap<Address, U256>, Web3Error> {
96        let tokens = self.get_confirmed_tokens_impl(0, 100).await?;
97
98        let balances = {
99            let mut balances = HashMap::new();
100            for token in tokens {
101                // TODO: Use StorageKeyLayout once boojumos can lookup other tokens
102                let balance_key = storage_key_for_standard_token_balance(
103                    AccountTreeId::new(token.l2_address),
104                    &address,
105                );
106                let balance = self.storage.read_value_alt(&balance_key).await?;
107                if !balance.is_zero() {
108                    balances.insert(token.l2_address, h256_to_u256(balance));
109                }
110            }
111            balances
112        };
113
114        Ok(balances)
115    }
116
117    pub async fn get_block_details_impl(
118        &self,
119        block_number: L2BlockNumber,
120    ) -> anyhow::Result<Option<api::BlockDetails>> {
121        let base_system_contracts_hashes = self.system_contracts.base_system_contracts_hashes();
122        let reader = self.inner.read().await;
123        let l2_fair_gas_price = reader.fee_input_provider.gas_price();
124        let fair_pubdata_price = Some(reader.fee_input_provider.fair_pubdata_price());
125        drop(reader);
126
127        let block_details = self
128            .blockchain
129            .get_block_details_by_number(
130                block_number,
131                l2_fair_gas_price,
132                fair_pubdata_price,
133                base_system_contracts_hashes,
134            )
135            .await;
136
137        match block_details {
138            Some(block_details) => Ok(Some(block_details)),
139            None => self.fork.get_block_details(block_number).await,
140        }
141    }
142
143    pub async fn get_transaction_details_impl(
144        &self,
145        hash: H256,
146    ) -> anyhow::Result<Option<api::TransactionDetails>> {
147        match self.blockchain.get_tx_details(&hash).await {
148            Some(tx_details) => Ok(Some(tx_details)),
149            None => self.fork.get_transaction_details(hash).await,
150        }
151    }
152
153    pub async fn get_bytecode_by_hash_impl(&self, hash: H256) -> anyhow::Result<Option<Vec<u8>>> {
154        if let Some(bytecode) = self.storage.load_factory_dep_alt(hash).await? {
155            return Ok(Some(bytecode));
156        }
157
158        self.fork.get_bytecode_by_hash(hash).await
159    }
160
161    pub async fn get_base_token_l1_address_impl(&self) -> anyhow::Result<Address> {
162        Ok(H160::from_low_u64_be(1))
163    }
164
165    pub async fn get_l2_to_l1_log_proof_impl(
166        &self,
167        tx_hash: H256,
168        index: Option<usize>,
169    ) -> anyhow::Result<Option<api::L2ToL1LogProof>> {
170        let Some(tx_receipt) = self.blockchain.get_tx_receipt(&tx_hash).await else {
171            return Ok(None);
172        };
173        let l1_batch_number = L1BatchNumber(tx_receipt.l1_batch_number.expect("").as_u32());
174        let Some(l1_batch) = self.blockchain.get_batch_header(l1_batch_number).await else {
175            return Ok(None);
176        };
177        let all_l1_logs_in_batch = l1_batch
178            .l2_to_l1_logs
179            .into_iter()
180            .map(|log| log.0)
181            .collect::<Vec<_>>();
182        let l1_batch_tx_index = tx_receipt.l1_batch_tx_index.expect("").as_u32() as u16;
183        let log_filter = |log: &L2ToL1Log| log.tx_number_in_block == l1_batch_tx_index;
184        let index_in_filtered_logs = index.unwrap_or(0);
185
186        // Copied from zksync-era
187        let Some((l1_log_index, _)) = all_l1_logs_in_batch
188            .iter()
189            .enumerate()
190            .filter(|(_, log)| log_filter(log))
191            .nth(index_in_filtered_logs)
192        else {
193            return Ok(None);
194        };
195        let merkle_tree_leaves = all_l1_logs_in_batch.iter().map(L2ToL1Log::to_bytes);
196        let tree_size = l2_to_l1_logs_tree_size(self.blockchain.protocol_version());
197
198        let (local_root, proof) = MiniMerkleTree::new(merkle_tree_leaves, Some(tree_size))
199            .merkle_root_and_path(l1_log_index);
200        let Some(aggregated_root) = self
201            .blockchain
202            .get_batch_aggregation_root(l1_batch_number)
203            .await
204        else {
205            return Ok(None);
206        };
207        let root = KeccakHasher.compress(&local_root, &aggregated_root);
208
209        let mut log_leaf_proof = proof;
210        log_leaf_proof.push(aggregated_root);
211
212        let (batch_proof_len, batch_chain_proof, is_final_node) = (0, Vec::<H256>::new(), true);
213
214        let proof = {
215            let mut metadata = [0u8; 32];
216            metadata[0] = LOG_PROOF_SUPPORTED_METADATA_VERSION;
217            metadata[1] = log_leaf_proof.len() as u8;
218            metadata[2] = batch_proof_len as u8;
219            metadata[3] = if is_final_node { 1 } else { 0 };
220
221            let mut result = vec![H256(metadata)];
222
223            result.extend(log_leaf_proof);
224            result.extend(batch_chain_proof);
225
226            result
227        };
228
229        Ok(Some(api::L2ToL1LogProof {
230            proof,
231            root,
232            id: l1_log_index as u32,
233        }))
234    }
235}
236
237#[cfg(test)]
238mod tests {
239    use std::str::FromStr;
240
241    use zksync_types::{
242        api, transaction_request::CallRequest, Address, ProtocolVersionId, H160, H256,
243    };
244    use zksync_types::{u256_to_h256, L1BatchNumber};
245
246    use super::*;
247    use crate::node::fork::{ForkClient, ForkConfig};
248    use crate::node::TransactionResult;
249    use crate::{
250        node::InMemoryNode,
251        testing,
252        testing::{ForkBlockConfig, MockServer},
253    };
254
255    #[tokio::test]
256    async fn test_estimate_fee() {
257        let node = InMemoryNode::test(None);
258
259        let mock_request = CallRequest {
260            from: Some(
261                "0xa61464658afeaf65cccaafd3a512b69a83b77618"
262                    .parse()
263                    .unwrap(),
264            ),
265            to: Some(
266                "0x36615cf349d7f6344891b1e7ca7c72883f5dc049"
267                    .parse()
268                    .unwrap(),
269            ),
270            gas: Some(U256::from(0)),
271            gas_price: Some(U256::from(0)),
272            max_fee_per_gas: None,
273            max_priority_fee_per_gas: None,
274            value: Some(U256::from(0)),
275            data: Some(vec![0, 0].into()),
276            nonce: Some(U256::from(0)),
277            transaction_type: None,
278            access_list: None,
279            eip712_meta: None,
280            input: None,
281        };
282
283        let result = node.estimate_fee_impl(mock_request).await.unwrap();
284
285        assert_eq!(result.gas_limit, U256::from(153968));
286        assert_eq!(result.max_fee_per_gas, U256::from(45250000));
287        assert_eq!(result.max_priority_fee_per_gas, U256::from(0));
288        assert_eq!(result.gas_per_pubdata_limit, U256::from(168));
289    }
290
291    #[tokio::test]
292    async fn test_get_transaction_details_local() {
293        // Arrange
294        let node = InMemoryNode::test(None);
295        {
296            let mut writer = node.inner.write().await;
297            writer
298                .insert_tx_result(
299                    H256::repeat_byte(0x1),
300                    TransactionResult {
301                        info: testing::default_tx_execution_info(),
302                        new_bytecodes: vec![],
303                        receipt: api::TransactionReceipt {
304                            logs: vec![],
305                            gas_used: Some(U256::from(10_000)),
306                            effective_gas_price: Some(U256::from(1_000_000_000)),
307                            ..Default::default()
308                        },
309                        debug: testing::default_tx_debug_info(),
310                    },
311                )
312                .await;
313        }
314        let result = node
315            .get_transaction_details_impl(H256::repeat_byte(0x1))
316            .await
317            .expect("get transaction details")
318            .expect("transaction details");
319
320        // Assert
321        assert!(matches!(result.status, api::TransactionStatus::Included));
322        assert_eq!(result.fee, U256::from(10_000_000_000_000u64));
323    }
324
325    #[tokio::test]
326    async fn test_get_transaction_details_fork() {
327        let mock_server = MockServer::run_with_config(ForkBlockConfig {
328            number: 10,
329            transaction_count: 0,
330            hash: H256::repeat_byte(0xab),
331        });
332        let input_tx_hash = H256::repeat_byte(0x02);
333        mock_server.expect(
334            "zks_getTransactionDetails",
335            Some(serde_json::json!([format!("{:#x}", input_tx_hash),])),
336            serde_json::json!({
337                "isL1Originated": false,
338                "status": "included",
339                "fee": "0x74293f087500",
340                "gasPerPubdata": "0x4e20",
341                "initiatorAddress": "0x63ab285cd87a189f345fed7dd4e33780393e01f0",
342                "receivedAt": "2023-10-12T15:45:53.094Z",
343                "ethCommitTxHash": null,
344                "ethProveTxHash": null,
345                "ethExecuteTxHash": null
346            }),
347        );
348
349        let node = InMemoryNode::test(Some(
350            ForkClient::at_block_number(ForkConfig::unknown(mock_server.url()), None)
351                .await
352                .unwrap(),
353        ));
354
355        let result = node
356            .get_transaction_details_impl(input_tx_hash)
357            .await
358            .expect("get transaction details")
359            .expect("transaction details");
360
361        assert!(matches!(result.status, api::TransactionStatus::Included));
362        assert_eq!(result.fee, U256::from(127_720_500_000_000u64));
363    }
364
365    #[tokio::test]
366    async fn test_get_block_details_local() {
367        // Arrange
368        let node = InMemoryNode::test(None);
369        {
370            let mut writer = node.inner.write().await;
371            let block = api::Block::<api::TransactionVariant>::default();
372            writer.insert_block(H256::repeat_byte(0x1), block).await;
373            writer
374                .insert_block_hash(L2BlockNumber(0), H256::repeat_byte(0x1))
375                .await;
376        }
377        let result = node
378            .get_block_details_impl(L2BlockNumber(0))
379            .await
380            .expect("get block details")
381            .expect("block details");
382
383        // Assert
384        assert!(matches!(result.number, L2BlockNumber(0)));
385        assert_eq!(result.l1_batch_number, L1BatchNumber(0));
386        assert_eq!(result.base.timestamp, 0);
387    }
388
389    #[tokio::test]
390    async fn test_get_block_details_fork() {
391        let mock_server = MockServer::run_with_config(ForkBlockConfig {
392            number: 10,
393            transaction_count: 0,
394            hash: H256::repeat_byte(0xab),
395        });
396        let miniblock = L2BlockNumber::from(16474138);
397        mock_server.expect(
398            "zks_getBlockDetails",
399            Some(serde_json::json!([miniblock.0])),
400            serde_json::json!({
401                  "number": 16474138,
402                  "l1BatchNumber": 270435,
403                  "timestamp": 1697405098,
404                  "l1TxCount": 0,
405                  "l2TxCount": 1,
406                  "rootHash": "0xd9e60f9a684fd7fc16e87ae923341a6e4af24f286e76612efdfc2d55f3f4d064",
407                  "status": "sealed",
408                  "commitTxHash": null,
409                  "committedAt": null,
410                  "proveTxHash": null,
411                  "provenAt": null,
412                  "executeTxHash": null,
413                  "executedAt": null,
414                  "l1GasPrice": 6156252068u64,
415                  "l2FairGasPrice": 50000000u64,
416                  "fairPubdataPrice": 100u64,
417                  "baseSystemContractsHashes": {
418                    "bootloader": "0x0100089b8a2f2e6a20ba28f02c9e0ed0c13d702932364561a0ea61621f65f0a8",
419                    "default_aa": "0x0100067d16a5485875b4249040bf421f53e869337fe118ec747cf40a4c777e5f"
420                  },
421                  "operatorAddress": "0xa9232040bf0e0aea2578a5b2243f2916dbfc0a69",
422                  "protocolVersion": ProtocolVersionId::Version26,
423              }),
424        );
425
426        let node = InMemoryNode::test(Some(
427            ForkClient::at_block_number(ForkConfig::unknown(mock_server.url()), None)
428                .await
429                .unwrap(),
430        ));
431
432        let result = node
433            .get_block_details_impl(miniblock)
434            .await
435            .expect("get block details")
436            .expect("block details");
437
438        assert!(matches!(result.number, L2BlockNumber(16474138)));
439        assert_eq!(result.l1_batch_number, L1BatchNumber(270435));
440        assert_eq!(result.base.timestamp, 1697405098);
441        assert_eq!(result.base.fair_pubdata_price, Some(100));
442    }
443
444    #[tokio::test]
445    async fn test_get_bridge_contracts_uses_default_values_if_local() {
446        // Arrange
447        let node = InMemoryNode::test(None);
448        let expected_bridge_addresses = api::BridgeAddresses {
449            l1_shared_default_bridge: Default::default(),
450            l2_shared_default_bridge: Default::default(),
451            l1_erc20_default_bridge: Default::default(),
452            l2_erc20_default_bridge: Default::default(),
453            l1_weth_bridge: Default::default(),
454            l2_weth_bridge: Default::default(),
455            l2_legacy_shared_bridge: Default::default(),
456        };
457
458        let actual_bridge_addresses = node
459            .get_bridge_contracts_impl()
460            .await
461            .expect("get bridge addresses");
462
463        // Assert
464        testing::assert_bridge_addresses_eq(&expected_bridge_addresses, &actual_bridge_addresses)
465    }
466
467    #[tokio::test]
468    async fn test_get_bridge_contracts_uses_fork() {
469        // Arrange
470        let mock_server = MockServer::run_with_config(ForkBlockConfig {
471            number: 10,
472            transaction_count: 0,
473            hash: H256::repeat_byte(0xab),
474        });
475        let input_bridge_addresses = api::BridgeAddresses {
476            l1_shared_default_bridge: Some(H160::repeat_byte(0x1)),
477            l2_shared_default_bridge: Some(H160::repeat_byte(0x2)),
478            l1_erc20_default_bridge: Some(H160::repeat_byte(0x1)),
479            l2_erc20_default_bridge: Some(H160::repeat_byte(0x2)),
480            l1_weth_bridge: Some(H160::repeat_byte(0x3)),
481            l2_weth_bridge: Some(H160::repeat_byte(0x4)),
482            l2_legacy_shared_bridge: Some(H160::repeat_byte(0x6)),
483        };
484        mock_server.expect(
485            "zks_getBridgeContracts",
486            None,
487            serde_json::json!({
488                "l1Erc20SharedBridge": format!("{:#x}", input_bridge_addresses.l1_shared_default_bridge.unwrap()),
489                "l2Erc20SharedBridge": format!("{:#x}", input_bridge_addresses.l2_shared_default_bridge.unwrap()),
490                "l1Erc20DefaultBridge": format!("{:#x}", input_bridge_addresses.l1_erc20_default_bridge.unwrap()),
491                "l2Erc20DefaultBridge": format!("{:#x}", input_bridge_addresses.l2_erc20_default_bridge.unwrap()),
492                "l1WethBridge": format!("{:#x}", input_bridge_addresses.l1_weth_bridge.unwrap()),
493                "l2WethBridge": format!("{:#x}", input_bridge_addresses.l2_weth_bridge.unwrap())
494            }),
495        );
496
497        let node = InMemoryNode::test(Some(
498            ForkClient::at_block_number(ForkConfig::unknown(mock_server.url()), None)
499                .await
500                .unwrap(),
501        ));
502
503        let actual_bridge_addresses = node
504            .get_bridge_contracts_impl()
505            .await
506            .expect("get bridge addresses");
507
508        // Assert
509        testing::assert_bridge_addresses_eq(&input_bridge_addresses, &actual_bridge_addresses)
510    }
511
512    #[tokio::test]
513    async fn test_get_bytecode_by_hash_returns_local_value_if_available() {
514        // Arrange
515        let node = InMemoryNode::test(None);
516        let input_hash = H256::repeat_byte(0x1);
517        let input_bytecode = vec![0x1];
518        node.inner
519            .write()
520            .await
521            .fork_storage
522            .store_factory_dep(input_hash, input_bytecode.clone());
523
524        let actual = node
525            .get_bytecode_by_hash_impl(input_hash)
526            .await
527            .expect("failed fetching bytecode")
528            .expect("no bytecode was found");
529
530        // Assert
531        assert_eq!(input_bytecode, actual);
532    }
533
534    // FIXME: Multi-threaded flavor is needed because of the `block_on` mess inside `ForkStorage`.
535    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
536    async fn test_get_bytecode_by_hash_uses_fork_if_value_unavailable() {
537        // Arrange
538        let mock_server = MockServer::run_with_config(ForkBlockConfig {
539            number: 10,
540            transaction_count: 0,
541            hash: H256::repeat_byte(0xab),
542        });
543        let input_hash = H256::repeat_byte(0x1);
544        let input_bytecode = vec![0x1];
545        mock_server.expect(
546            "zks_getBytecodeByHash",
547            Some(serde_json::json!([format!("{:#x}", input_hash)])),
548            serde_json::json!(input_bytecode),
549        );
550
551        let node = InMemoryNode::test(Some(
552            ForkClient::at_block_number(ForkConfig::unknown(mock_server.url()), None)
553                .await
554                .unwrap(),
555        ));
556
557        let actual = node
558            .get_bytecode_by_hash_impl(input_hash)
559            .await
560            .expect("failed fetching bytecode")
561            .expect("no bytecode was found");
562
563        // Assert
564        assert_eq!(input_bytecode, actual);
565    }
566
567    #[tokio::test]
568    async fn test_get_raw_block_transactions_local() {
569        // Arrange
570        let node = InMemoryNode::test(None);
571        {
572            let mut writer = node.inner.write().await;
573            let mut block = api::Block::<api::TransactionVariant>::default();
574            let txn = api::Transaction::default();
575            writer
576                .insert_tx_result(
577                    txn.hash,
578                    TransactionResult {
579                        info: testing::default_tx_execution_info(),
580                        new_bytecodes: vec![],
581                        receipt: api::TransactionReceipt {
582                            logs: vec![],
583                            gas_used: Some(U256::from(10_000)),
584                            effective_gas_price: Some(U256::from(1_000_000_000)),
585                            ..Default::default()
586                        },
587                        debug: testing::default_tx_debug_info(),
588                    },
589                )
590                .await;
591            block.transactions.push(api::TransactionVariant::Full(txn));
592            writer.insert_block(H256::repeat_byte(0x1), block).await;
593            writer
594                .insert_block_hash(L2BlockNumber(0), H256::repeat_byte(0x1))
595                .await;
596        }
597
598        let txns = node
599            .get_raw_block_transactions_impl(L2BlockNumber(0))
600            .await
601            .expect("get transaction details");
602
603        // Assert
604        assert_eq!(txns.len(), 1);
605    }
606
607    #[tokio::test]
608    async fn test_get_raw_block_transactions_fork() {
609        let mock_server = MockServer::run_with_config(ForkBlockConfig {
610            number: 10,
611            transaction_count: 0,
612            hash: H256::repeat_byte(0xab),
613        });
614        let miniblock = L2BlockNumber::from(16474138);
615        mock_server.expect(
616            "zks_getRawBlockTransactions",
617            Some(serde_json::json!([miniblock.0])),
618            serde_json::json!([
619              {
620                "common_data": {
621                  "L2": {
622                    "nonce": 86,
623                    "fee": {
624                      "gas_limit": "0xcc626",
625                      "max_fee_per_gas": "0x141dd760",
626                      "max_priority_fee_per_gas": "0x0",
627                      "gas_per_pubdata_limit": "0x4e20"
628                    },
629                    "initiatorAddress": "0x840bd73f903ba7dbb501be8326fe521dadcae1a5",
630                    "signature": [
631                      135,
632                      163,
633                      2,
634                      78,
635                      118,
636                      14,
637                      209
638                    ],
639                    "transactionType": "EIP1559Transaction",
640                    "input": {
641                      "hash": "0xc1f625f55d186ad0b439054adfe3317ae703c5f588f4fa1896215e8810a141e0",
642                      "data": [
643                        2,
644                        249,
645                        1,
646                        110,
647                        130
648                      ]
649                    },
650                    "paymasterParams": {
651                      "paymaster": "0x0000000000000000000000000000000000000000",
652                      "paymasterInput": []
653                    }
654                  }
655                },
656                "execute": {
657                  "contractAddress": "0xbe7d1fd1f6748bbdefc4fbacafbb11c6fc506d1d",
658                  "calldata": "0x38ed173900000000000000000000000000000000000000000000000000000000002c34cc00000000000000000000000000000000000000000000000000000000002c9a2500000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000840bd73f903ba7dbb501be8326fe521dadcae1a500000000000000000000000000000000000000000000000000000000652c5d1900000000000000000000000000000000000000000000000000000000000000020000000000000000000000008e86e46278518efc1c5ced245cba2c7e3ef115570000000000000000000000003355df6d4c9c3035724fd0e3914de96a5a83aaf4",
659                  "value": "0x0",
660                  "factoryDeps": null
661                },
662                "received_timestamp_ms": 1697405097873u64,
663                "raw_bytes": "0x02f9016e820144568084141dd760830cc62694be7d1fd1f6748bbdefc4fbacafbb11c6fc506d1d80b9010438ed173900000000000000000000000000000000000000000000000000000000002c34cc00000000000000000000000000000000000000000000000000000000002c9a2500000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000840bd73f903ba7dbb501be8326fe521dadcae1a500000000000000000000000000000000000000000000000000000000652c5d1900000000000000000000000000000000000000000000000000000000000000020000000000000000000000008e86e46278518efc1c5ced245cba2c7e3ef115570000000000000000000000003355df6d4c9c3035724fd0e3914de96a5a83aaf4c080a087a3024e760ed14134ef541608bf308e083c899a89dba3c02bf3040f07c8b91b9fc3a7eeb6b3b8b36bb03ea4352415e7815dda4954f4898d255bd7660736285e"
664              }
665            ]),
666        );
667
668        let node = InMemoryNode::test(Some(
669            ForkClient::at_block_number(ForkConfig::unknown(mock_server.url()), None)
670                .await
671                .unwrap(),
672        ));
673
674        let txns = node
675            .get_raw_block_transactions_impl(miniblock)
676            .await
677            .expect("get transaction details");
678        assert_eq!(txns.len(), 1);
679    }
680
681    #[tokio::test]
682    async fn test_get_all_account_balances_empty() {
683        let node = InMemoryNode::test(None);
684        let balances = node
685            .get_all_account_balances_impl(Address::zero())
686            .await
687            .expect("get balances");
688        assert!(balances.is_empty());
689    }
690
691    #[tokio::test]
692    async fn test_get_confirmed_tokens_eth() {
693        let node = InMemoryNode::test(None);
694        let balances = node
695            .get_confirmed_tokens_impl(0, 100)
696            .await
697            .expect("get balances");
698        assert_eq!(balances.len(), 1);
699        assert_eq!(&balances[0].name, "Ether");
700    }
701
702    #[tokio::test]
703    async fn test_get_all_account_balances_forked() {
704        let cbeth_address = Address::from_str("0x75af292c1c9a37b3ea2e6041168b4e48875b9ed5")
705            .expect("failed to parse address");
706        let mock_server = testing::MockServer::run();
707        mock_server.expect("eth_chainId", None, serde_json::json!("0x104"));
708
709        mock_server.expect(
710            "zks_getBlockDetails",
711            Some(serde_json::json!([1])),
712            serde_json::json!({
713                "baseSystemContractsHashes": {
714                    "bootloader": "0x010008a5c30072f79f8e04f90b31f34e554279957e7e2bf85d3e9c7c1e0f834d",
715                    "default_aa": "0x01000663d7941c097ba2631096508cf9ec7769ddd40e081fd81b0d04dc07ea0e"
716                },
717                "commitTxHash": null,
718                "committedAt": null,
719                "executeTxHash": null,
720                "executedAt": null,
721                "l1BatchNumber": 0,
722                "l1GasPrice": 0,
723                "l1TxCount": 1,
724                "l2FairGasPrice": 50000000,
725                "l2TxCount": 0,
726                "number": 0,
727                "operatorAddress": "0x0000000000000000000000000000000000000000",
728                "protocolVersion": ProtocolVersionId::Version26,
729                "proveTxHash": null,
730                "provenAt": null,
731                "rootHash": "0xdaa77426c30c02a43d9fba4e841a6556c524d47030762eb14dc4af897e605d9b",
732                "status": "verified",
733                "timestamp": 1000
734            }),
735        );
736        mock_server.expect(
737            "eth_getBlockByHash",
738            Some(serde_json::json!(["0xdaa77426c30c02a43d9fba4e841a6556c524d47030762eb14dc4af897e605d9b", true])),
739            serde_json::json!({
740                "baseFeePerGas": "0x0",
741                "difficulty": "0x0",
742                "extraData": "0x",
743                "gasLimit": "0xffffffff",
744                "gasUsed": "0x0",
745                "hash": "0xdaa77426c30c02a43d9fba4e841a6556c524d47030762eb14dc4af897e605d9b",
746                "l1BatchNumber": "0x0",
747                "l1BatchTimestamp": null,
748                "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
749                "miner": "0x0000000000000000000000000000000000000000",
750                "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
751                "nonce": "0x0000000000000000",
752                "number": "0x0",
753                "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
754                "receiptsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
755                "sealFields": [],
756                "sha3Uncles": "0x0000000000000000000000000000000000000000000000000000000000000000",
757                "size": "0x0",
758                "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
759                "timestamp": "0x3e8",
760                "totalDifficulty": "0x0",
761                "transactions": [],
762                "transactionsRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
763                "uncles": []
764            }),
765        );
766        mock_server.expect(
767            "zks_getConfirmedTokens",
768            Some(serde_json::json!([0, 100])),
769            serde_json::json!([
770              {
771                "decimals": 18,
772                "l1Address": "0xbe9895146f7af43049ca1c1ae358b0541ea49704",
773                "l2Address": "0x75af292c1c9a37b3ea2e6041168b4e48875b9ed5",
774                "name": "Coinbase Wrapped Staked ETH",
775                "symbol": "cbETH"
776              }
777            ]),
778        );
779        mock_server.expect(
780            "zks_getFeeParams",
781            None,
782            serde_json::json!({
783              "V2": {
784                "config": {
785                  "minimal_l2_gas_price": 25000000,
786                  "compute_overhead_part": 0,
787                  "pubdata_overhead_part": 1,
788                  "batch_overhead_l1_gas": 800000,
789                  "max_gas_per_batch": 200000000,
790                  "max_pubdata_per_batch": 240000
791                },
792                "l1_gas_price": 46226388803u64,
793                "l1_pubdata_price": 100780475095u64,
794                "conversion_ratio": {
795                  "numerator": 1,
796                  "denominator": 1
797                }
798              }
799            }),
800        );
801
802        let node = InMemoryNode::test(Some(
803            ForkClient::at_block_number(ForkConfig::unknown(mock_server.url()), Some(1.into()))
804                .await
805                .unwrap(),
806        ));
807
808        {
809            let writer = node.inner.write().await;
810            let mut fork = writer.fork_storage.inner.write().unwrap();
811            fork.raw_storage.set_value(
812                storage_key_for_standard_token_balance(
813                    AccountTreeId::new(cbeth_address),
814                    &Address::repeat_byte(0x1),
815                ),
816                u256_to_h256(U256::from(1337)),
817            );
818        }
819
820        let balances = node
821            .get_all_account_balances_impl(Address::repeat_byte(0x1))
822            .await
823            .expect("get balances");
824        assert_eq!(balances.get(&cbeth_address).unwrap(), &U256::from(1337));
825    }
826
827    #[tokio::test]
828    async fn test_get_base_token_l1_address() {
829        let node = InMemoryNode::test(None);
830        let token_address = node
831            .get_base_token_l1_address_impl()
832            .await
833            .expect("get base token l1 address");
834        assert_eq!(
835            "0x0000000000000000000000000000000000000001",
836            format!("{:?}", token_address)
837        );
838    }
839}