Now that we know how to bridge tokens back and forth, let's talk about running things on zkSync.
We have a bunch of great tutorials (like this one https://era.zksync.io/docs/api/hardhat/getting-started.html) that you can follow to get the exact code & command line calls to create the contracts - so in this article, let's focus on how things differ between zkSync and Ethereum.
Note Before reading this article, I'd recommend doing the hardhat tutorial above.
In case of Ethereum, you start by writing a contract code in solidity, then you compile it with solc
, and you get the
EVM bytecode, deployment bytecode (which is a function that should return the bytecode itself) and ABI (interface).
Afterwards, you send the deployment bytecode to the 0x000 address on Ethereum, which does some magic (executes the deployment bytecode, that should contain the constructor etc) and puts the contract under the address that is generated based on your account id and a nonce.
From this moment on, you can send the transactions to this new address (and most of the tools would ask you to provide the ABI, so that they can set the proper function arguments).
All the bytecode will be run on the EVM (Ethereum Virtual Machine) - that has a stack, access to memory and storage, and a bunch of opcodes.
The main part (and the main cost) of the zkSync is the proving system. In order to make proof as fast as possible, we're running a little bit different virtual machine (zkEVM) - that has a slightly different set of opcodes, and also contains a bunch of registers. More details on this will be written in the future articles.
Having a different VM means that we must have a separate compiler zk-solc - as the bytecode that is produced by this compiler has to use the zkEVM specific opcodes.
While having a separte compiler introduces a bunch of challenges (for example, we need a custom hardhat plugins ), it brings a bunch of benefits too: for example it allows us to move some of the VM logic (like new contract deployment) into System contracts - which allows faster & cheaper modifications and increased flexibility.
Small note on system contracts: as mentioned above, we moved some of the VM logic into system contracts, which allows us to keep VM simpler (and with this - keep the proving system simpler).
You can see the full list (and codes) of the system contracts here: https://github.com/matter-labs/system-contracts.
While some of them are not really visible to the contract developer (like the fact that we're running a special
Bootleader
to package a bunch of transactions together - more info in a future article) - some others are very
visible - like our ContractDeployer
Deploying a new contract differs on Ethereum and zkSync.
While on Ethereum - you send the transaction to 0x00 address - on zkSync you have to call the special ContractDeployer
system contract.
If you look on your hardhat example, you'll notice that your deploy.ts
is actually using a Deployer
class from the
hardhat-zksync-deploy
plugin.
Which inside uses the zkSync's web3.js, that calls the contract deployer here
override getDeployTransaction(..) {
...
txRequest.to = CONTRACT_DEPLOYER_ADDRESS;
...
}
Also ContractDeployer
adding a special prefix for all the new contract addresses. This means that contract addesses
WILL be different on zkSync
and Ethereum (and also leaves us the possibility of adding Ethereum addresses in the
future if needed).
You can look for CREATE2_PREFIX
and CREATE_PREFIX
in the code.
Another part, where zkSync differs from Ethereum is gas cost. The best example for this are storage slots.
If you have two transactions that are updating the same storage slot - and they are in the same 'batch' - only the first one would be charged (as when we write the final storage to ethereum, we just write the final diff of what slots have changed - so updating the same slot multiple times doesn't increase the amount of data that we have to write to L1).
As zkSync
has a built-in AccountAbstration (more on this in a separate article) - you shouldn't depend on some of the
solidity functions (like ecrecover
- that checks the keys, or tx.origin
) - in all the cases, the compiler will try
to warn you.
In this article, we looked at how contract development & deployment differs on Ethereum and zkSync (looking at differences in VMs, compilers and system contracts).