The contract must be deployed specifying the initial ("genesis") state root hash, appointing the network governor (see the "Governance" section), and linking the exit queue (see the "Cenosorship resistance" section).
Governance of the network will be excerised from a separate contract registered in the ZKSync contract as
networkGovernor
. It has the power to:
- Change the set of validators.
- Add new tokens (tokens can not be removed after being added).
- Initiate migration to a new contract (see the "Migration" section).
To enforece censorship-resistance and enable guaranteed retrievability of the funds, ZKSync employs the mechanisms of Priority queue (soft enforcement) and Exodus mode (hard enforcement).
To make deposit, a user can:
- Either send ETH to smart contract (will be handled by the default function),
- or call
depositERC20()
function to perform transferFrom for a registered ERC20 token. Note: the user must have previously called approve() on the ERC20 token contract in order to authorize ZKSync contract to perform this operation.
This deposit creates deposit priority request that is placed in corresponding priority requests mapping and also emits NewPriorityRequest(opType, pubData, expirationBlock) event to notify validators that they must include this request to upcoming blocks. Complete PriorityQueue logic that handles priority requests is described in Priority Requests section.
When a validator commits a block which contains circuit operations deposit
, deposit onchain operation for this
deposit is created to verify compliance with priority queue requests. If it succeeds than their count will be added to
priority requests count for this block. If the block is verified, deposit onchain operations and deposit
priority request are simply discarded.
If the block is reverted deposit onchain operations are siply discarded.
If ZKSync contract has entered Exodus mode and the block is unverified, the funds held by this blocks' Deposit priority requests are accrued to the owners' root-chain balances to make them possible to withdraw. This withdraw onchain operations and full exit priority requests are simply discarded.
It is a standard withdrawal operation. When a block with partial_exit
circuit operation is committed, withdraw
onchain operation for this withdrawal is created. If the block is verified, funds from the withdrawal onchain
operation are acrued to the users' root-chain balances.
If the block is reverted, this withdraw onchain operations are simply discarded.
A user can withdraw funds from the root-chain balance at any time by calling a withdrawETH()
or withdrawERC20()
function.
User can request this expensive operation to withdraw funds if he thinks that his transactions are censored by validators.
The user must send a transaction to ZKSync contract function registerFullExit()
. This function creates full exit
priority request that is placed in corresponding priority requests mapping and also emits
NewPriorityRequest(serialId, opType, pubData, expirationBlock) event to notify validators that they must include
this request to upcoming blocks. Complete PriorityQueue logic that handles priority requests is described in
Priority Requests section.
When a validator commits a block which contains a circuit operation full_exit
, the corresponding withdraw
onchain operation for this withdrawal is created to verify compliance with priority queue requests. If it succeeds
than their count will be added to priority requests count for this block. If the block is verified, funds from the
withdrawal onchain operation are accrued to the users' root-chain balances and withdraw onchain operations
and full exit priority requests are simply discarded.
If the block is reverted, this withdraw onchain operations are simply discarded.
If ZKSync contract has entered Exodus mode and the block is unverified, this withdraw onchain operations and full exit priority requests are simply discarded.
Only a sender from the validator set can commit a block.
The number of committed but unverified blocks is limited by MAX_UNVERIFIED_BLOCKS
constant in order to make sure that
gas is always sufficient to revert all committed blocks if the verification has not happened in time.
Anybody can perform verification for the committed block.
If the first committed block was not verified within EXPECT_VERIFICATION_IN
ETH blocks, all unverified blocks will be
reverted and the funds held by onchain operations and priority requests will be released and stored on
root-chain balances..
This queue will be implemented in separate contract to ensure that priority operations like deposit
and full_exit
will be processed in a timely manner and will be included in one of ZKSync's blocks (a situation that leads to the need
to reverse blocks will not happen), and also, in the case of full_exit
transactions, the user can always withdraw
funds (censorship-resistance). Its' functionality is divided into 2 parts: Requests Queue and Exodus Mode.
NewPriorityRequest event is emitted when a user send according transaction to ZKSync contract. Also some info about it will be stored in the mapping (operation type and expiration block) strictly in the order of arrival. NewPriorityRequest event structure:
serialId
- serial id of this priority requestopType
- operation typepubData
- request dataexpirationBlock
- the number of Ethereum block when request becomes expiredexpirationBlock
is calculated as follows:expirationBlock = block.number + 250
- about 1 hour for the transaction to expire,block.number
- current Ethereum block number.
When corresponding transactions are found in the commited block, their count must be recorded. If the block is verified, this count of the satisfied priority requests is removed from mapping.
If the block is reverted via Exodus Mode, the funds held by Deposit priority requests from this block are accrued to the owners' root-chain balances to make them possible to withdraw. And this Deposit priority requests will be removed from mapping.
In order to send priority request, the user MUST pay some extra fee. That fee will be subtracted from the amount of
Ether that the user sent to Deposit or Full Exit funcitons. That fee will be the payment for the validator’s work to
include these transactions in the block. One transaction fee is calculated as follows:
fee = FEE_COEFF * (BASE_GAS + gasleft) * gasprice
, where
FEE_COEFF
- fee coefficient for priority request transactionBASE_GAS
- base gas cost for transaction (usually 21000)gasleft
- remaining gas for transaction code executiongasprice
- gas price of the transaction
If the user sends more Ether than necessary, the difference will be returned to him.
Validators MUST subscribe for NewPriorityRequest
events in the RIGHT order to include priority transactions in
some upcoming blocks. The need for Validators to include these transactions in blocks as soon as possible is dedicated
by the increasing probability of entering the Exodus Mode (described below).
If the Requests Queue is being processed too slow, it will trigger the Exodus mode in ZKSync contract. This
moment is determined by the first (oldest) priority request with oldest expirationBlock
value . If
current ethereum block number >= oldest expiration block number
the Exodus Mode will be entered.
In the Exodus mode, the contract freezes all block processing, and all users must exit. All existing block commitments will be reverted.
Every user will be able to submit a SNARK proof that she owns funds in the latest verified state of ZKSync by calling
exit()
function. If the proof verification succeeds, the entire user amount will be accrued to her onchain
balance, and a flag will be set in order to prevent double exits of the same token balance.
(to be implemented later)
ZKSync shall always have a strict opt-in policy: we guarantee that user funds are retrievable forever under the conditions a user has opted in when depositing funds, no matter what. A migration to a newer version of the contract shall be easy and cheap, but MUST require a separate opt-in or allow the user to exit.
The update mechanism shall follow this workflow:
- The network governor can schedule an update, specifying a target contract and an ETH block deadline.
- A scheduled update can not be cancelled (to proceed with migration even if exodus mode is activated while waiting for the migration; otherwise we would need to recover funds scheduled for migration with a separate procedure).
- Users can opt-in via a separate ZKSync operation: move specific token balance into a subtree on a special migration account. This subtree must also maintain and update counters for total balances per token.
- The migration account MUST have a dedicated hardcoded account_id (to specify).
- When the scheduled ETH block is reached, anybody MUST be able to seal the migration.
- After the migration is sealed, anybody MUST be able to transfer total balances for each token by providing a SNARK proof of the amounts from the migration account subtree.
- When the migration is sealed, the contract enters exodus mode: whoever has not opted in can now exit. Thus, the root state will remain frozen.
- The new contract will read the latest state root directly from the old one (this is safe, because the root state is frozen and can not be changed).
- introduce key hash?
- deploy keys separately?
- unit conversion: different by token?
- describe authorization for ERC20 transferFrom
- enforce max deposits/exits per block in the circuit