-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
Component
Forge
Describe the feature you would like
Foundry zkSync Support
To enable Foundry compatibility with zkSync, we created the fork https://github.com/matter-labs/foundry-zksync. This fork introduces changes to support smart contract compilation, deployment, testing, and interaction on zkSync Era.
This feature request is a proposal to integrate zkSync support into Foundry. It details an approach designed to be minimally invasive, complemented by a subsequent request for zksolc
support in foundry-compilers. For more info related to zksolc
please refer to here, and here.
Objective
The goal is to integrate zkSync support into Foundry, facilitating community and maintainer dialogue on how best to have these changes accepted.
Below is a brief outline on the differences that are accounted for in the proposed approach.
Differences
zkSync VM
The zkSync VM is fundamentally different from the EVM, in it, it uses an entirely different register-based architecture.
Storage
The Storage is as well different from the EVM's account based trie. In the zkSync VM there's a single global trie. Account balances, nonces, etc. are stored under the hashes slots for special system contracts.
Bytecode
Owing to a different VM, the corresponding bytecode is as well different than what is compiled with solc. In comparison, the zkSync VM is compiled with zksolc
, which supports all opcodes, with a few exceptions.
Proposed Approach
1. Compilation
The foundry-identified files are passed through zksolc
to generate zkSync VM bytecode. This was then matched using the contract's name with its respective EVM/solc counterpart - finally giving us a DualCompiledContract
containing both evm and zk bytecodes and their respective hashes (which also differ in how they are computed). This "registry" of compiled contracts was propagated down to the Executors
and to the tracers to perform specialized operations in the context of interoperability between the two scopes, namely EVM and ZK.
We're currently in the process of migrating the compilation logic to foundry-compilers
, but the concept of compiling the same contract for both environments and passing it down to the internals would probably remain.
2. forge
The proposed approach leverages the CheatcodeTracer
and an early implementation can be reviewed on the dev
branch of foundry-zksync.
Consider the following contract:
contract Counter {
uint256 value;
function setValue(uint256 _value) {
value = _value;
}
function getValue() view returns (uint256) {
return value;
}
}
contract CounterTest is Test {
Counter counter;
function setUp() {
counter = new Counter();
}
function testSetValue() {
counter.setValue(10);
assertEq(10, counter.getValue());
}
}
We assume the test contract is being tested for deployment on zkSync.
- Foundry compiles the test suite with
solc
and thenzksolc
. Both bytecodes are bundled asDualCompiledContract
and passed down to theExecutor
, till theCheatcodeTracer
. - All interactions from the default
CALLER
account to the deployed test contract (includingensure_success
calls) are handled as normal EVM calls. Except,address.balance
, which are intercepted as opcodes and retrieve the data from ZK-storageblock.timestamp
,block.number
, which are updated with the ZK-specific context on theEnv
directly.
- Any
CALL
orCREATE
is intercepted in the tracer (withcall
andcreate
hooks) and translated into a ZK transactions. This includes- Fetching ZK-equivalent bytecode
- Fetching correct account nonce, etc.
- Marking the callee as EOA (in ZK terms) to bypass EIP-3607 restriction
- The transaction is then sent to ZK-VM where it returns the statediff, which is applied on the
journaled_state
, and the result returned back. - Any
console.log()
in the ZK-VM execution is translated back for foundry to pick up
This forms the basic premise of our implementation that foundry gets to do foundry-specific operations, and only at the necessary stage with invoke the zkSync VM when passing the --zksync
flag.
Challenges
- The above use-case assumes
forge test
case. A similar strategy would be required inforge script
. - An executor must be context-aware to
set_balance
andset_nonce
on the correct form of storage - EVM or ZK - Certain cheatcodes like
warp
,deal
, etc. must be context aware to set correct storage - EVM or ZK - Certain functionalities like
expectCall
must be supported by the ZK tracer to return the appropriate data back to foundry'sCheatcodeTracer
to evaluate their success.
Plan
Feature Flag
It is proposed to put all zkSync related features behind a zksync
feature flag. Rust nightly would be required to compile with this flag, as the zksync libraries currently depend on nightly rust.
Foundry zkSync
A single foundry-zksync
module will contain all zkSync specific logic, and translations. Other parts of foundry source will simply use this module.
Foundry Compilers
zksolc
compilation would initially be added to foundry-compilers
that would later be used in foundry.
Forge Commands
Forge commands like test
, create
, build
, script
would be incrementally supported as pull requests.
Conclusion
This is obviously the result of work and testing over several months, and is offered as the best case from our current perspective to integrate foundry into the zkSync ecosystem, while maintaining the same level of foundry's user experience. On that front, we'd like to get the thoughts of foundry developers on anything we may have missed, misunderstood, or vaguely underestimated, in our proposed implementation.
Contributors
@aon, @Deniallugo, @dutterbutter, @HermanObst, @Jrigada, @Karrq, @nbaztec
Additional context
No response