From e9bea5b45725b8b37e8e60e5e62bcb0b2e862619 Mon Sep 17 00:00:00 2001 From: Nisheeth Barthwal Date: Tue, 17 Sep 2024 15:24:46 +0200 Subject: [PATCH] add trace and event sections, hide unsupported sections (#12) * add trace and event sections, hide unsupported sections * commit missing files * update trace and compilation section --- src/SUMMARY.md | 36 ++-- src/anvil/README.md | 28 +--- src/chisel/README.md | 19 +-- src/forge/cheatcodes.md | 5 + src/forge/debugger.md | 5 + src/forge/deploying.md | 8 +- src/forge/fork-testing.md | 2 + src/forge/gas-reports.md | 4 + src/forge/gas-snapshots.md | 4 + src/forge/gas-tracking.md | 4 + src/forge/tests.md | 2 +- src/forge/traces.md | 11 ++ src/projects/creating-a-new-project.md | 6 +- .../forking-mainnet-with-cast-anvil.md | 2 +- .../limitations/compilation.md | 47 +++++- src/zksync-specifics/limitations/events.md | 154 ++++++++++++++++++ src/zksync-specifics/limitations/traces.md | 126 ++++++++++++++ 17 files changed, 392 insertions(+), 71 deletions(-) create mode 100644 src/zksync-specifics/limitations/events.md create mode 100644 src/zksync-specifics/limitations/traces.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 35b8d188e..8e0f1bf4a 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -25,6 +25,8 @@ - [General](./zksync-specifics/limitations/general.md) - [Compilation](./zksync-specifics/limitations/compilation.md) - [Broadcast](./zksync-specifics/limitations/broadcast.md) + - [Events](./zksync-specifics/limitations/events.md) + - [Traces](./zksync-specifics/limitations/traces.md) - [Cheatcodes](./zksync-specifics/limitations/cheatcodes.md) - [Additional Cheatcodes](./zksync-specifics/cheatcodes/README.md) - [zkVm](./zksync-specifics/cheatcodes/zkvm.md) @@ -32,7 +34,6 @@ # Supported Commands Overview - [Command List](./supported-commands/README.md) - + + + + + + + # Appendix @@ -382,8 +380,8 @@ - [cast wallet verify](./reference/cast/cast-wallet-verify.md) - [cast wallet import](./reference/cast/cast-wallet-import.md) - [cast wallet list](./reference/cast/cast-wallet-list.md) - - [`anvil` Reference](./reference/anvil/README.md) - - [`chisel` Reference](./reference/chisel/README.md) + + - [Config Reference](./reference/config/README.md) - [Overview](./reference/config/overview.md) - [Project](./reference/config/project.md) @@ -493,6 +491,8 @@ - [Snapshots](./cheatcodes/snapshots.md) - [RPC](./cheatcodes/rpc.md) - [Files](./cheatcodes/fs.md) + - [ZKsync](./zksync-specifics/cheatcodes/README.md) + - [zkVm](./zksync-specifics/cheatcodes/zkvm.md) - [Forge Standard Library Reference](./reference/forge-std/README.md) - [Std Logs](./reference/forge-std/std-logs.md) - [Std Assertions](./reference/forge-std/std-assertions.md) diff --git a/src/anvil/README.md b/src/anvil/README.md index 2c9f8b251..8d49322c9 100644 --- a/src/anvil/README.md +++ b/src/anvil/README.md @@ -2,30 +2,6 @@ Anvil is a local testnet node shipped with Foundry. You can use it for testing your contracts from frontends or for interacting over RPC. -Anvil is part of the Foundry suite and is installed alongside `forge`, `cast`, and `chisel`. If you haven't installed Foundry yet, see [Foundry installation](../getting-started/installation.md). - -> Note: If you have an older version of Foundry installed, you'll need to re-install `foundryup` in order for Anvil to be downloaded. - -### How to use Anvil - -To use Anvil, simply type `anvil`. You should see a list of accounts and private keys available for use, as well as the address and port that the node is listening on. - -Anvil is highly configurable. You can run `anvil -h` to see all the configuration options. - -Some basic options are: - -```bash -# Number of dev accounts to generate and configure. [default: 10] -anvil -a, --accounts - -# The EVM hardfork to use. [default: latest] -anvil --hardfork - -# Port number to listen on. [default: 8545] -anvil -p, --port -``` - -> 📚 **Reference** -> -> See the [`anvil` Reference](../reference/anvil/) for in depth information on Anvil and its capabilities. +Officially we do not support anvil for ZKsync related operations. +An alternative to anvil is current recommended in [ZKsync Era In-Memory Node](https://github.com/matter-labs/era-test-node) \ No newline at end of file diff --git a/src/chisel/README.md b/src/chisel/README.md index af6e175be..af02f59c8 100644 --- a/src/chisel/README.md +++ b/src/chisel/README.md @@ -3,21 +3,4 @@ Chisel is an advanced Solidity REPL shipped with Foundry. It can be used to quickly test the behavior of Solidity snippets on a local or forked network. -Chisel is part of the Foundry suite and is installed alongside `forge`, `cast`, and `anvil`. If you haven't installed Foundry -yet, see [Foundry installation](../getting-started/installation.md). - -> Note: If you have an older version of Foundry installed, you'll need to re-install `foundryup` in order for Chisel to be downloaded. - -### How to use Chisel - -To use Chisel, simply type `chisel`. From there, start writing Solidity code! Chisel will offer verbose feedback on each input. - -Chisel can be used both within and outside of a foundry project. If the binary is executed in a Foundry project root, Chisel will -inherit the project's configuration options. - -To see available commands, type `!help` within the REPL. - -> 📚 **Reference** -> -> See the [`chisel` Reference](../reference/chisel/) for in depth information on Chisel and its capabilities. - +Officially we do not support chisel for ZKsync related operations. \ No newline at end of file diff --git a/src/forge/cheatcodes.md b/src/forge/cheatcodes.md index bf2b5d136..281fe603c 100644 --- a/src/forge/cheatcodes.md +++ b/src/forge/cheatcodes.md @@ -1,5 +1,10 @@ ## Cheatcodes + +> 🚨 **Important** +> +> See [Cheatcode Limitations](../zksync-specifics/limitations/cheatcodes.md) when using cheatcodes in ZKsync context. + Most of the time, simply testing your smart contracts outputs isn't enough. To manipulate the state of the blockchain, as well as test for specific reverts and events, Foundry is shipped with a set of cheatcodes. Cheatcodes allow you to change the block number, your identity, and more. They are invoked by calling specific functions on a specially designated address: `0x7109709ECfa91a80626fF3989D68f67F5b1DD12D`. diff --git a/src/forge/debugger.md b/src/forge/debugger.md index 8f6ee34c9..42b7250b0 100644 --- a/src/forge/debugger.md +++ b/src/forge/debugger.md @@ -1,5 +1,10 @@ ## Debugger +> 🚨 **Important** +> +> Debugger in not officially supported for ZKsync. +> + Forge ships with an interactive debugger. The debugger is accessible on [`forge debug`](../reference/forge/forge-debug.md) and on [`forge test`](../reference/forge/forge-test.md). diff --git a/src/forge/deploying.md b/src/forge/deploying.md index 27eb901b4..89a546515 100644 --- a/src/forge/deploying.md +++ b/src/forge/deploying.md @@ -11,7 +11,7 @@ To deploy a contract, you must provide a RPC URL (env: `ETH_RPC_URL`) and the pr To deploy `MyContract` to a network: ```sh -$ forge create --rpc-url --private-key src/MyContract.sol:MyContract +$ forge create --zksync --rpc-url --private-key src/MyContract.sol:MyContract compiling... success. Deployer: 0xa735b3c25f... @@ -44,7 +44,8 @@ contract MyToken is ERC20 { Additionally, we can tell Forge to verify our contract on Etherscan, Sourcify or Blockscout, if the network is supported, by passing `--verify`. ```sh -$ forge create --rpc-url \ +$ forge create --zksync \ + --rpc-url \ --constructor-args "ForgeUSD" "FUSD" 18 1000000000000000000000 \ --private-key \ --etherscan-api-key \ @@ -78,6 +79,7 @@ Here's how to verify it: ```bash forge verify-contract \ + --zksync \ --chain-id 11155111 \ --num-of-optimizations 1000000 \ --watch \ @@ -100,7 +102,7 @@ If the `--watch` flag was not supplied, you can check the verification status with the [`forge verify-check`](../reference/forge/forge-verify-check.md) command: ```bash -$ forge verify-check --chain-id 11155111 +$ forge verify-check --zksync --chain-id 11155111 Contract successfully verified. ``` diff --git a/src/forge/fork-testing.md b/src/forge/fork-testing.md index 33b98b802..c55241fb9 100644 --- a/src/forge/fork-testing.md +++ b/src/forge/fork-testing.md @@ -7,6 +7,8 @@ Forge supports testing in a forked environment with two different approaches: Which approach to use? Forking mode affords running an entire test suite against a specific forked environment, while forking cheatcodes provide more flexibility and expressiveness to work with multiple forks in your tests. Your particular use case and testing strategy will help inform which approach to use. +Note that ZKsync context will be set accordingly based on the fork url, so the `--zksync` flag need not be passed. + ### Forking Mode To run all tests in a forked environment, such as a forked Ethereum mainnet, pass an RPC URL via the `--fork-url` flag: diff --git a/src/forge/gas-reports.md b/src/forge/gas-reports.md index 43e32bc0d..6c5a322b0 100644 --- a/src/forge/gas-reports.md +++ b/src/forge/gas-reports.md @@ -1,5 +1,9 @@ ## Gas Reports +> 🚨 **Important** +> +> Gas reports may not be entirely accurate in the ZKsync context. This is mostly due to the additional overhead to executing each `CREATE` or `CALL` in its own zkEVM which has additional bootloader gas costs. + Forge can produce gas reports for your contracts. You can configure which contracts output gas reports via the `gas_reports` field in `foundry.toml`. To produce reports for specific contracts: diff --git a/src/forge/gas-snapshots.md b/src/forge/gas-snapshots.md index b155e95f2..720a2e69c 100644 --- a/src/forge/gas-snapshots.md +++ b/src/forge/gas-snapshots.md @@ -1,5 +1,9 @@ ## Gas Snapshots +> 🚨 **Important** +> +> Gas snapshots may not be entirely accurate in the ZKsync context. This is mostly due to the > additional overhead to executing each `CREATE` or `CALL` in its own zkEVM which has additional bootloader gas costs. + Forge can generate gas snapshots for all your test functions. This can be useful to get a general feel for how much gas your contract will consume, or to compare gas usage before and after various optimizations. diff --git a/src/forge/gas-tracking.md b/src/forge/gas-tracking.md index d18bc7f70..32f933923 100644 --- a/src/forge/gas-tracking.md +++ b/src/forge/gas-tracking.md @@ -1,5 +1,9 @@ ## Gas Tracking +> 🚨 **Important** +> +> Gas tracking may not be entirely accurate in the ZKsync context. This is mostly due to the additional overhead to executing each `CREATE` or `CALL` in its own zkEVM which has additional bootloader gas costs. + Forge can help you estimate how much gas your contract will consume. Currently, Forge ships with two different tools for this job, but they may be merged in the future: diff --git a/src/forge/tests.md b/src/forge/tests.md index d90aa9725..5ddbd8182 100644 --- a/src/forge/tests.md +++ b/src/forge/tests.md @@ -40,4 +40,4 @@ The default behavior for `forge test` is to only display a summary of passing an Forge can re-run your tests when you make changes to your files using `forge test --watch`. -By default, only changed test files are re-run. If you want to re-run all tests on a change, you can use `forge test --watch --run-all`. +By default, only changed test files are re-run. If you want to re-run all tests on a change, you can use `forge test --zksync --watch --run-all`. diff --git a/src/forge/traces.md b/src/forge/traces.md index 99c9f57a1..2cb412cbf 100644 --- a/src/forge/traces.md +++ b/src/forge/traces.md @@ -36,6 +36,8 @@ The gas usage (marked in square brackets) is for the entirety of the function ca The gas unaccounted for is due to some extra operations happening between calls, such as arithmetic and store reads/writes. + + Forge will try to decode as many signatures and values as possible, but sometimes this is not possible. In these cases, the traces will appear like so: ```ignore @@ -51,3 +53,12 @@ Some traces might be harder to grasp at first glance. These include: - `InvalidFEOpcode` means that an undefined bytecode value has been encountered during execution. The EVM catches the unknown bytecode and returns the `INVALID` opcode instead, of value `0xFE`. You can find out more [here](https://www.evm.codes/#fe). For a deeper insight into the various traces, you can explore the [revm source code](https://github.com/bluealloy/revm/blob/main/crates/interpreter/src/instruction_result.rs). + +### ZKsync Limitations + +In addition to the above anomalies of incorrect gas and un-decodable traces, there are additional caveats within the ZKsync context: + +* The events emitted from within the zkEVM will not show on traces. See [events](../zksync-specifics/limitations/events.md) in zkEVM. +* The system call traces from within the zkEVM's bootloader are currently ignored in order to simplify the trace output. +* Executing each `CREATE` or `CALL` in its own zkEVM has additional bootloader gas costs, which may sometimes not be accounted in the traces. The ignored bootloader system calls, have a heuristic in-place to sum up their gas usage to the nearest non-system parent call, but this may also not add up accurately. + diff --git a/src/projects/creating-a-new-project.md b/src/projects/creating-a-new-project.md index 428d0eade..23e367f38 100644 --- a/src/projects/creating-a-new-project.md +++ b/src/projects/creating-a-new-project.md @@ -1,6 +1,6 @@ ## Creating a New Project -To start a new project with Foundry, use [`forge init`](../reference/forge/forge-init.md): +To start a new project with Foundry-ZKsync, use [`forge init`](../reference/forge/forge-init.md): ```sh {{#include ../output/hello_foundry/forge-init:command}} @@ -35,6 +35,6 @@ And run the tests: {{#include ../output/hello_foundry/forge-test:all}} ``` -You'll notice that two new directories have popped up: `out` and `cache`. +You'll notice that two new directories have popped up: `out`, `zkout` and `cache`. -The `out` directory contains your contract artifact, such as the ABI, while the `cache` is used by `forge` to only recompile what is necessary. +The `out` directory contains your EVM contract artifact, such as the ABI, the `zkout` directory contains the zkEVM contract artifacts, while the `cache` is used by `forge` to only recompile what is necessary. diff --git a/src/tutorials/forking-mainnet-with-cast-anvil.md b/src/tutorials/forking-mainnet-with-cast-anvil.md index f2edda4ab..add52d3b4 100644 --- a/src/tutorials/forking-mainnet-with-cast-anvil.md +++ b/src/tutorials/forking-mainnet-with-cast-anvil.md @@ -1,4 +1,4 @@ -## Forking Mainnet with Cast and Anvil +## Forking Mainnet with Cast and Era Test Node ### Introduction diff --git a/src/zksync-specifics/limitations/compilation.md b/src/zksync-specifics/limitations/compilation.md index f47bffa1a..49493a375 100644 --- a/src/zksync-specifics/limitations/compilation.md +++ b/src/zksync-specifics/limitations/compilation.md @@ -32,4 +32,49 @@ Error: assembly-to-bytecode conversion: assembly parse error Label DEFAULT_UNWIN for either PC or constant at offset 65947 that is more than 65535 addressable space ``` -As an attempt the same contract can be compiled with `--zk-force-evmla=true`, but if it doesn't help then the contract must be split into smaller units. \ No newline at end of file +#### Solution + +There are three possible solutions to address this issue: + +1. **Compilation with `--zk-force-evmla=true`:** + + You can attempt to compile the contract using ZKsync's EVM legacy architecture by adding the `--zk-force-evmla=true` flag. This can sometimes bypass the contract size limit by compiling in a different mode. + + Example command: + ```bash + forge build --zk-force-evmla=true --zksync + ``` + +1. **Compilation with `--zk-fallback-oz=true`:** + + If the contract size still exceeds the limit, try compiling with optimization level `-Oz` by using the `--zk-fallback-oz=true` flag. This tells the compiler to fall back to `-Oz` optimization when the bytecode is too large, potentially reducing the contract size further. + + Example command: + ```bash + forge build --zk-fallback-oz=true --zksync + ``` + +1. **Split the Contract into Smaller Units** + + If neither of the above flags resolves the issue, the contract must be refactored into smaller, modular contracts. This involves separating your logic into different contracts and using contract inheritance or external contract calls to maintain functionality. + + **Before (single large contract):** + ```solidity + contract LargeContract { + function largeFunction1() public { /* complex logic */ } + function largeFunction2() public { /* complex logic */ } + // Additional large functions and state variables... + } + ``` + **After (multiple smaller contracts):** + ```solidity + contract ContractPart1 { + function part1Function() public { /* logic from largeFunction1 */ } + } + contract ContractPart2 { + function part2Function() public { /* logic from largeFunction2 */ } + } + contract MainContract is ContractPart1, ContractPart2 { + // Logic to combine the functionalities of both parts + } + ``` \ No newline at end of file diff --git a/src/zksync-specifics/limitations/events.md b/src/zksync-specifics/limitations/events.md new file mode 100644 index 000000000..a88a07e03 --- /dev/null +++ b/src/zksync-specifics/limitations/events.md @@ -0,0 +1,154 @@ +## Emitted Events + +zkEVM in addition to user events, emits its [own system events](https://github.com/search?q=repo%3Amatter-labs%2Fera-contracts+%2Fevent+%5BA-Za-z%5D%2B%5C%28%2F&type=code), like `Transfer`, `Withdrawl`, `ContractCreated`, etc. These events are not printed as part of traces, as currently it's not trivial to match emitted events with zkEVM traces. + +These system events can be observed via setting the `RUST_LOG` env variable: +```bash +RUST_LOG=foundry_zksync_core::vm::inspect=info,era_test_node::formatter=info forge test --zksync +``` + +```ignore +==== 2 events +EthToken System Contract + Topics: + 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef + 0x0000000000000000000000001804c8ab1f12e6bbf3894d4083f33e07309d1f38 + 0x0000000000000000000000000000000000000000000000000000000000008001 + Data (Hex): 0x00000000000000000000000000000000000000000000000003dfd24000000000 + + ... +``` + +### Issues with `expectEmit` +This can often come as a surprise to users, who have the following test structure in place: + +```solidity +contract Number { + uint256 accesses; + function one() public returns (uint8) { + accesses++; + return 1; + } + function two() public returns (uint8) { + accesses++; + return 2; + } + function three() public returns (uint8) { + accesses++; + return 3; + } +} + +contract Calculator { + event Added(uint8 indexed sum); + function add(uint8 a, uint8 b) public returns (uint8) { + uint8 sum = a + b; + emit Added(sum); + return sum; + } +} + +contract FooTest is Test { + event Added(uint8 indexed sum); + + function testFoo() public { + Number num = new Number(); + + // We emit the event we expect to see. + vm.expectEmit(); + emit Added(num.three()); // num.three() will emit zkEVM events + + Calculator calc = new Calculator(); + calc.add(num.one(), num.two()); + } +} +``` + +This test would currently fail as the non-static call to `num.three()` when setting up `vm.expectEmit()`. + +If run with `RUST_LOG` enabled as specified above, the following output will be observed: + +```ignore +┌──────────────────────────┐ +│ VM EXECUTION RESULTS │ +└──────────────────────────┘ +Cycles Used: 6703 +Computation Gas Used: 106816 +Contracts Used: 26 +════════════════════════════ +=== Console Logs: +=== Calls: +Call(Normal) Account Code Storage 4de2e468 4227857424 + Call(Normal) System context 02fa5779 4227853014 + ... + Call(Normal) Bootloader utilities ebe4a3d7 4227834933 + ... + Call(Normal) 0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38 202bcce7 78705333 + Call(Normal) Nonce Holder e1239cd8 77474754 + ... + Call(Normal) EthToken System Contract 9cc7f708 77468328 + Call(Normal) Keccak 00000000 76257342 + Call(Normal) Nonce Holder 6ee1dc20 78694182 + Call(Normal) Keccak 00000000 77464044 + Call(Normal) EthToken System Contract 9cc7f708 78692796 + Call(Normal) Keccak 00000000 77462658 + Call(Normal) 0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38 e2f318e3 78691095 + Call(Normal) Msg Value System Contract 0x 77460741 + Call(Normal) EthToken System Contract 579952fc 76249719 + ... + Call(Normal) Event writer 00000000 75052593 + Call(Mimic) bootloader 0x 76243293 + Call(Normal) EthToken System Contract 9cc7f708 78682086 + Call(Normal) Keccak 00000000 77452137 + Call(Normal) EthToken System Contract 579952fc 78680889 + ... + Call(Normal) Event writer 00000000 77449239 + Call(Normal) Known code storage e516761e 78656571 + Call(Normal) Account Code Storage 4de2e468 78654114 + Call(Normal) System context a851ae78 78653421 + Call(Normal) 0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38 df9c1589 78652161 + Call(Normal) 0xf9e9ba9ed9b96ab918c74b21dd0f1d5f2ac38a30 45caa117 77422023 + Call(Normal) System context a851ae78 4227757947 + ... +==== 3 events +EthToken System Contract + Topics: + 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef + 0x0000000000000000000000001804c8ab1f12e6bbf3894d4083f33e07309d1f38 + 0x0000000000000000000000000000000000000000000000000000000000008001 + Data (Hex): 0x00000000000000000000000000000000000000000000000003dfd24000000000 + +EthToken System Contract + Topics: + 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef + 0x0000000000000000000000000000000000000000000000000000000000008001 + 0x0000000000000000000000001804c8ab1f12e6bbf3894d4083f33e07309d1f38 + Data (Hex): 0x00000000000000000000000000000000000000000000000003dfd24000000000 + +EthToken System Contract + Topics: + 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef + 0x0000000000000000000000000000000000000000000000000000000000008001 + 0x0000000000000000000000001804c8ab1f12e6bbf3894d4083f33e07309d1f38 + Data (String): + +zk vm decoded result 0000000000000000000000000000000000000000000000000000000000000003 +``` + +Here we observe that 3 events were emitted when we called `num.three()` in zkEVM. These correspond to the `Transfer(address indexed from, address indexed to, uint256 value)` event, which denotes change of L2 ETH. As a result the `vm.expectEmit` will register the first event emitted, and try to match the next two events, which will fail and so will the test with: + +```ignore +[FAIL. Reason: log != expected log] testFoo() (gas: 35515) +Traces: + [35515] 0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496::testFoo() + ├─ [0] → new @0xF9E9ba9Ed9B96AB918c74B21dD0f1D5f2ac38a30 + │ └─ ← [Return] 32 bytes of code + ├─ [0] VM::expectEmit() + │ └─ ← [Return] + ├─ [0] 0xF9E9ba9Ed9B96AB918c74B21dD0f1D5f2ac38a30::three() + │ └─ ← [Return] 3 + └─ ← [Revert] log != expected log +``` + + +To avoid such a scenario, it's recommended to explicitly emit the events for `expectEmit`, with hard-coded values. \ No newline at end of file diff --git a/src/zksync-specifics/limitations/traces.md b/src/zksync-specifics/limitations/traces.md new file mode 100644 index 000000000..6dcf12cc9 --- /dev/null +++ b/src/zksync-specifics/limitations/traces.md @@ -0,0 +1,126 @@ +## Trace Limitations + +zkEVM traces are attached to the EVM traces that are printed with `-vvvv`. + + +* The events emitted from within the zkEVM will not show on traces. See [events](../zksync-specifics/limitations/events.md) in zkEVM. +* The system call traces from within the zkEVM's bootloader are currently ignored in order to simplify the trace output. +* Executing each `CREATE` or `CALL` in its own zkEVM has additional bootloader gas costs, which may sometimes not be accounted in the traces. The ignored bootloader system calls, have a heuristic in-place to sum up their gas usage to the nearest non-system parent call, but this may also not add up accurately. + + +These system traces can be observed via setting the `RUST_LOG` env variable: +```bash +RUST_LOG=foundry_zksync_core::vm::inspect=info,era_test_node::formatter=info forge test --zksync +``` + +```ignore +┌──────────────────────────┐ +│ VM EXECUTION RESULTS │ +└──────────────────────────┘ +Cycles Used: 6703 +Computation Gas Used: 106816 +Contracts Used: 26 +════════════════════════════ +=== Console Logs: +=== Calls: +Call(Normal) Account Code Storage 4de2e468 4227857424 + Call(Normal) System context 02fa5779 4227853014 + ... + Call(Normal) Bootloader utilities ebe4a3d7 4227834933 + ... + Call(Normal) 0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38 202bcce7 78705333 + Call(Normal) Nonce Holder e1239cd8 77474754 + ... + Call(Normal) EthToken System Contract 9cc7f708 77468328 + Call(Normal) Keccak 00000000 76257342 + Call(Normal) Nonce Holder 6ee1dc20 78694182 + Call(Normal) Keccak 00000000 77464044 + Call(Normal) EthToken System Contract 9cc7f708 78692796 + Call(Normal) Keccak 00000000 77462658 + Call(Normal) 0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38 e2f318e3 78691095 + Call(Normal) Msg Value System Contract 0x 77460741 + Call(Normal) EthToken System Contract 579952fc 76249719 + ... + Call(Normal) Event writer 00000000 75052593 + Call(Mimic) bootloader 0x 76243293 + Call(Normal) EthToken System Contract 9cc7f708 78682086 + Call(Normal) Keccak 00000000 77452137 + Call(Normal) EthToken System Contract 579952fc 78680889 + ... + Call(Normal) Event writer 00000000 77449239 + Call(Normal) Known code storage e516761e 78656571 + Call(Normal) Account Code Storage 4de2e468 78654114 + Call(Normal) System context a851ae78 78653421 + Call(Normal) 0x1804c8ab1f12e6bbf3894d4083f33e07309d1f38 df9c1589 78652161 + Call(Normal) 0xf9e9ba9ed9b96ab918c74b21dd0f1d5f2ac38a30 45caa117 77422023 + Call(Normal) System context a851ae78 4227757947 + ... +``` + + +### Combined Traces +Foundry ZKsync will combine the traces from within the zkEVM into the EVM traces, that foundry displays. Running the following test with `forge test --zksync -vvvv`, yields the displayed trace: + +```solidity +contract InnerNumber { + event Value(uint8); + + function innerFive() public returns (uint8) { + emit Value(5); + return 5; + } +} + +contract Number { + function five() public returns (uint8) { + InnerNumber num = new InnerNumber(); + return num.innerFive(); + } +} + +contract Adder { + function add() public returns (uint8) { + Number num = new Number(); + return num.five() + num.five(); + } +} + +contract FooTest is Test { + function testFoo() public { + Adder adder = new Adder(); + uint8 value = adder.add(); + assert(value == 10); + console.log(value); + } +} +``` + +```ignore +[PASS] testFoo() (gas: 35807) +Logs: + 10 + +Traces: + [35807] ZkTraceTest::testZkTraceOutputDuringCall() + ├─ [0] → new Adder@0xF9E9ba9Ed9B96AB918c74B21dD0f1D5f2ac38a30 + │ └─ ← [Return] 2976 bytes of code + ├─ [0] Adder::add() + │ ├─ [127] → new Number@0xf232f12E115391c535FD519B00efADf042fc8Be5 + │ │ └─ ← [Return] 2272 bytes of code + │ ├─ [91190] Number::five() + │ │ ├─ [91] → new InnerNumber@0xEd570f3F91621894E001DF0fB70BfbD123D3c8AD + │ │ │ └─ ← [Return] 736 bytes of code + │ │ ├─ [889] InnerNumber::innerFive() + │ │ │ └─ ← [Return] 5 + │ │ └─ ← [Return] 5 + │ ├─ [74776] Number::five() + │ │ ├─ [91] → new InnerNumber@0xAbceAEaC3d3a2ac3Dcffd7A60Ca00A3fAC9490cA + │ │ │ └─ ← [Return] 736 bytes of code + │ │ ├─ [889] InnerNumber::innerFive() + │ │ │ └─ ← [Return] 5 + │ │ └─ ← [Return] 5 + │ └─ ← [Return] 10 + ├─ [0] console::log(10) [staticcall] + │ └─ ← [Stop] + └─ ← [Stop] +``` \ No newline at end of file