Introduction to native Account Abstraction in zkSync

Author: Written by ChiHaoLu, imToken Labs

This article mainly introduces the development and related content of abstract accounts (AA, abstract accounts) in the Layer 2 solution of zkSync. The focus will be on three parts:

  • Account contract: account type, important entry point and related key points of account contract
  • Transaction: verification method, execution method and process of AA transaction
  • Handling fee: transaction fee, Paymaster

wXO9pTRzhIvPDKhIdGhNYWqOdj85rH6eYhNdyZun.png

Table of contents

  • preface
  • Brief overview of zkSync AA contract
  • Fee model and Paymaster in the era of zkSync
  • Summary and comparison
  • Conclusion

background

  • Familiar with smart contract wallet and its common features
  • Get an overview of how Ethereum transactions work
  • A general understanding of the operation mode of EIP-4337
  • A general understanding of the operation mode of ZK (validity) Rollup
  • Quick Look at the zkSync

Here, for the convenience of reading, it is not necessary to understand zkSync in depth, but briefly review the basic information of zkSync. There are two main versions of zkSync, version 1.0 (zkSync Lite) and version 2.0 (zkSync Era).

zkSync version 1.0 only supports EOA (external account) and does not support smart contracts (only supports token transfer and exchange), while zkSync 2.0, namely zkSync Era, belongs to native AA (abstract account) (all account types are contracts, no EOA , which is the difference between EOA and contract account in Ethereum), and is compatible with EVM (Ethereum Virtual Machine), and supports the development of smart contracts using Rust, Yul, Vyper, Solidity, etc.

The zkSync mentioned below refers to zkSync 2.0, namely zkSync Era, unless otherwise specified.

In zkSync Era, there are multiple contracts, which can be understood as they implement some important operating system functions of zkSync in smart contracts. These contracts are pre-compiled contracts that have never been deployed (run directly in the node), but they all have a formal address.

When implementing the AA protocol, zkSync will perform logical operations and judgments through some contracts. For example, when verifying nonce, it is judged by NonceHolder, while the implementation of abstract account mechanism and collection of fees are judged by bootloader. The following will introduce them one by one.

Recap Account Abstraction

The core concept of account abstraction can be summarized into two key points: signature abstraction and payment abstraction.

The goal of the signature abstraction is to enable various account contracts to use different verification schemes. This means that users are not limited to a digital signature algorithm that can only use a specific curve, but can choose any verification mechanism they like.

The payment abstraction aims to provide users with a variety of transaction payment options. For example, payments could be made using ERC-20 tokens instead of native tokens, or transactions could be sponsored by a third party, or even other more ad hoc payment models.

Accounts in zkSync 2.0 can initiate transactions like EOA, but can also use its programmability to implement arbitrary logic, such as contract accounts. This is what we call Account Abstraction, which combines the advantages of the two types of accounts in Ethereum to make the experience of using AA accounts more flexible, thus achieving the above two goals: signature abstraction and payment abstraction.

AA Mechanism in zkSync Era

In zkSync Era, the most important role of zkSync AA is the bootloader, which is a Contract, mainly used to process transactions and execute the AA mechanism, corresponding to the EntryPoint Contract of EIP-4337. The bootloader cannot be called by the user (it can only be triggered by the Operator), and it has never been deployed (runs directly on the node), but it has an official address (can be used to receive payments).

Operator is an important role in ZK Rollup. It is a centralized Off-Chain Server. Similar to the Sequencer you may have seen, it is responsible for triggering contracts such as bootloader from the outside.

Native account abstraction protocols (such as StarkNet, zkSync) are basically designed with reference to EIP-4337. In the implementation of zkSync, the user will send the transaction to the Operator, and the Operator will send the transaction to the bootloader and start a series of processing.

From a block point of view:

When the bootloader receives input from the Operator, the bootloader will first define some environment variables for the block (such as gas price, block number, block timestamp, etc.). Then, the bootloader will sequentially read the transaction list, first query whether the account contract agrees with the transaction (that is, call the validate function in the AA mechanism), and then put them into the block.

After each transaction is verified, the Operator verifies whether the block is large enough to be sent to the verifier (or whether it times out). If it is large enough or times out, the Operator closes the block, stops adding new transactions to the bootloader, and completes the transaction execution.

From the perspective of transactions, when the Operator triggers the bootloader, the bootloader will process each transaction sequentially:

  1. Confirm whether the nonce corresponding to the contract address of the user account is legal
  2. Call the validate function on the user account contract for verification
  3. After the verification is passed, the account contract will remit the gas fee to the address of the bootloader (or through Paymaster, which will be introduced later), and the bootloader will check whether it has received enough money.
  4. Call the ute function on the user account contract to execute the transaction.

The first three steps above correspond to the verification loop (Verification Loop) of EIP-4337, and the fourth step corresponds to the execution loop (ution Loop) of EIP-4337.

Here is mainly an overview introduction, and the details and roles of each step will be elaborated one by one in the following detailed description.

Quick overview of zkSync abstract account contract

Nonce

The account nonce of zkSync is recorded in a system contract named NonceHolder, which remembers whether each (account_address, nonce) pair is used by mapping (mapping) to judge whether the nonce is legal.

According to the above, the first step after the Operator triggers the bootloader is to check the nonce. Therefore, before each transaction starts, NonceHolder will be used to confirm whether the currently used set of nonces is legal (currently only checks whether they have been used). If the nonce is legal, it will enter the verification phase (Verification Phase), at which time the nonce will be marked as used; if it is not legal, the transaction (verification) will fail.

Important points about zkSync's current nonce:

Although currently users can send multiple transactions with different nonces to the account for execution at the same time, since zkSync does not support parallel processing, transactions with different nonces will still be processed sequentially.

In theory, users can use any 256-bit non-zero integer as nonce, but zkSync still recommends using incrementNonceIfEquals as a way to manage nonce to ensure that it is incremented in order (currently zkSync's AA mechanism only confirms unused nonce, But the official document indicates that the sequential increment may be required in the future).

Account Contract

The account contract in zkSync has the following four necessary entry points (Entry Point), which are:

  • validateTransaction: Called during the verification phase to confirm whether the operation is authorized by the account owner, where users can customize their own verification logic (such as various signature algorithms, multi-signature, etc.).
  • payForTransaction: When the transaction fee is paid by this account (instead of using paymaster), the operator will call this function to pay at least tx.gasprice * tx.gaslimit of ETH to the bootloader address.
  • prepareForPaymaster: When the transaction fee will be paid by Paymaster, the operator will call this function to complete the preparation work before interacting with paymaster. The example provided by zkSync is an ERC-20 token approved by Paymaster.
  • uteTransaction: After the verification phase is successfully passed and the transaction fee is successfully charged, this function will be used to perform the operation the user wants to achieve (such as interacting with the contract, remittance, etc.).

About Paymaster, the amount of handling fee (tx.gasprice * tx.gaslimit), etc. will be explained in subsequent chapters.

There is also a non-essential insurance function uteTransactionFromOutside in the account of zkSync. Funds can be withdrawn to L1 using the "escape mechanism" when operations cannot be performed (such as when the sequence generator is unresponsive or zkSync is found to be a regulatory risk). This part has little to do with the AA protocol, so it will not be described in detail here. Those who are interested can check the official documents and the specification of zkSync.

Key Points and Limitations of Validation Functions

In the validateTransaction function, various custom logics can be implemented. For example, if the account has implemented the EIP-1271 standard, the verification logic in EIP-1271 can be directly applied to the validateTransaction, or refer to the multi-signature account contract implementation in the zkSync official document .

At the same time, in order to avoid DoS threats in the Verification Phase of EIP-4337, there are some restrictions (cannot involve external opcodes and limited depth, etc.), and there are similar restrictions in zkSync, for example:

1. The contract logic can only touch its own slot (if the address of the account contract is A):

  • slot belonging to address A

  • slot A at any other address

  • The slot keccak256(A||X) of any other address, which can directly use the address as the key of the mapping (such as mapping (address=>value)), is also equivalent to allowing access to the slot keccak256(A||X), to achieve expansion. For example token balances on ERC-20.

2. Contract logic must not use global variables, such as block.number

Key Points and Limitations of Executing Functions

What needs to be noted in the uteTransaction function is that if you want to perform a system call (Call), you need to ensure that it has the is flag. Because these system contracts have a great impact on the account system. For example, the only way to increase nonce is to interact with NonceHolder. To deploy a contract, you must interact with ContractDeployer. Using the is flag can ensure that account developers consciously interact with system contracts.

However, it is recommended that you use the ContractsCaller library provided by zkSync to avoid handling the is flag yourself, and use the CallWithPropagatedRevert to complete the system call.

HcJ4N9RyPiqxu3WmnCsZrAWDg6ahOaTvdLjzuowI.png

The code sample above involves interacting with DEPLOYER__CONTRACT. The most common system contract situation that account developers encounter is that we want to use an account to deploy a contract. At this time, we must interact with the ContractDeployer system contract. In this case, the account developer needs to communicate with the ContractDeployer contract to ensure that the contract is deployed successfully and performs the required operations.

Fee model and Paymaster in the era of zkSync

Fees and Gas Limit

The fee model of zkSync is very similar to Ethereum, the fee token is still ETH. However, like other Layer 2 solutions (such as Arbitrum, Optimism), zkSync also needs to consider the additional cost of publishing to L1 (security fee) in addition to the basic calculation and write slot costs. Since the price of gas that publishes data to L1 is very unstable, the Operator of zkSync defines the following dynamic parameters when each block is opened (begins to record transactions):

  • gasPrice: gas price in gwei, that is, tx.gasprice in the transaction object mentioned above

  • gasPerPubdata: The amount of gas required to publish a byte of data on Ethereum

In addition, unlike EIP-4337, zkSync does not need to define three gas limits: verificationGas, utionGas, and preVerificationGas, but only requires a gasLimit to cover all the above costs, so users need to ensure that the gasLimit is sufficient to cover the Verification stage, ution stage and upload data All expenses such as security fees to L1. This fee cost is included in the tx.gaslimit in the transaction object mentioned above.

Multiply the two (tx.gasprice * tx.gaslimit) to get the transaction fee paid to the bootloader.

Paymaster

Paymaster mainly pays ETH to the bootloader instead of the user's account contract at the stage of user transaction payment fee. Users can choose different Paymaster and payment modes to pay the handling fee, such as (but not limited to):

  • Payment of ERC-20 tokens to Paymaster before the transaction is initiated or after the transaction is executed

  • Top up the Paymaster contract with a credit card

  • Paymaster will continue to pay part or all of the fees for users for free

The way users interact with Paymaster depends on different protocols, it can be centralized or decentralized; it can be before or after the transaction; it can use ERC-20 tokens or legal tender, or even free.

The Paymaster contract of zkSync mainly consists of two functions, namely validateAndPayForPaymasterTransaction (required) and postTransaction (optional), both of which can only be called by the bootloader:

  • validateAndPayForPaymasterTransaction is the only function that must be implemented in the entire Paymaster contract. When the operator receives a transaction with a Paymaster parameter, it means that the handling fee is not paid by the user's account contract, but by Paymaster. At this point, the operator will call validateAndPayForPaymasterTransaction to determine whether the Paymaster is willing to pay the transaction fee. If Paymaster agrees, this function will send at least tx.gasprice * tx.gaslimit ETH to the bootloader.

  • postTransaction is an optional function, usually used for refund (return unused gas to sender). However, current zkSync does not support this operation yet.

The Paymaster in zkSync will execute postTransaction after postTransaction is implemented, which is different from EIP-4337. EIP-4337 will not call postOp when validatePaymasterUserOp does not return the context, and vice versa.

Based on the above, for example, the user now wants to send a transaction whose handling fee is paid by Paymaster, the process is as follows:

  1. Use NonceHolder to confirm whether the nonce is legal
  2. Call validateTransaction on the user's Account Contract to verify that the transaction is authorized by the account owner
  3. Call prepareForPaymaster on the user's Account Contract, which may execute, for example, approve a certain number of ERC-20 Tokens to Paymaster or do nothing
  4. Call validateAndPayForPaymasterTransaction on the Paymaster Contract to confirm that Paymaster is willing to pay and charge the handling fee, and at the same time, Paymaster will charge the user a certain amount of ERC-20 (approved earlier)
  5. Confirm that the bootloader receives the correct amount (at least tx.gasprice * tx.gaslimit) of ETH fees
  6. Call uteTransaction on the user's Account Contract to execute the transaction the user wants
  7. If the Paymaster Contract implements postTransaction and the gas is still sufficient (no out of gas error), then execute postTransaction

In the last step, even if postTransaction cannot be executed due to out of gas error, this AA transaction is considered successful, but the action of calling postTransaction is omitted.

If you delve deeper into zkSync's Paymaster, you will find that its Verification Rules are slightly different from 4337 (zkSync Paymaster can step on any other contract slot), and there are also various types (such as Approval-based). This part is due to the comparison of details. Those who are interested can refer to official documents or my previous implementation.

Summary & Comparison

Through the previous explanations, we have learned what important entry points the account contract has, as well as their functions and related restrictions. At the same time, we also learned about the functions of the system contract. Next, let's summarize the process of an automated operation (AA) transaction in zkSync from construction to completion, and I will also provide more detailed references for those who want to learn more:

  1. The user uses the SDK or wallet locally to construct transaction objects (for example: from, to, data, value, etc.).

  2. The user signs the transaction. The signature here is not necessarily the traditional EIP-712 format and ECDSA curve signature. zkSync also supports EIP-2718 and EIP-1559. The key to choosing a signature method and a verification method is to verify through the verification function in the account contract.

  3. Send the signed transaction to the operator through the RPC API. At this point the transaction enters the pending state. The operator passes the transaction to the bootloader (calls the processL2Tx function on the bootloader contract), and starts a series of AA protocol processes.

  4. Bootloader will check whether the Nonce is legal, and use NonceHolder to check.

  5. The Bootloader will call the validateTransaction function on the user account contract to confirm that the transaction has been authorized by the account owner.

  6. There are two ways for Bootloader to charge fees, and the specific charging method depends on the transaction parameters (whether the paymaster parameter is attached when constructing the transaction object):

a. Call the payForTransaction function and account contract to charge the transaction fee;

b. Call the prepareForPaymaster and validateAndPayForPaymasterTransaction functions to collect the transaction fee with the Paymaster contract.

  1. "Call payForTransaction to contract fee with Account" or "call prepareForPaymaster and validateAndPayForPaymasterTransaction to contract fee with Paymaster"

  2. Check whether the bootloader has received at least tx.gasprice * tx.gaslimit transaction fees.

  3. Bootloader will call the uteTransaction function on the user account contract to execute the transaction.

  4. (Optional) If using Paymaster to pay the transaction fee, the bootloader will call the postTransaction function. If Paymaster does not implement postTransaction, or gas is exhausted, this step will be skipped.

The above 4.~7. steps are the verification phase (defined in the bootloader's l2TxValidation), and the 8.~9. step execution phase (defined in the bootloader's l2Txution).

Comparison of EIP-4337, StarkNet and zkSync Era

Basically, the AA mechanism processes of the three are similar, all of which are verification stage→handling fee mechanism (paid by account contract or Paymaster)→execution stage. The main differences are:

  • The role of implementing the AA mechanism is: the difference between opening in the zkSync era and the other two AA is that the Operator needs to cooperate with the bootloader (system contract), for example, the bootloader will open a new block and define the relevant parameters of the block, and receive the operation The trader sent by the member and verified. In 4337, this part is coordinated by Bundler and EntryPoint, while in StarkNet, this part is all in charge of Sequencer.
  • Does Gas Cost need to consider the L1 security fee: L2 AA needs to consider the cost of uploading data to L1, not just the ZK (Validity) Rollups Native AA mentioned in the push, but also needs to be included in L1 when Optimistic Rollups implement 4337 Security fee (calculated in preVerificationGas, see Alchemy related documents for details).
  • Whether it is possible to send transactions before the account contract is deployed: In the era of StarkNet and zkSync, there is no EntryPoint like 4337 that has the initCode field to allow the user to deploy the account contract, so it does not send transactions before the account can be configured.

Compared

VOBhvEvzk06iHKk5Z1pMseUlue6yBpVw7CHkxXwR.png

Since StarkNet has not yet realized the Paymaster mechanism, and zkSync has not yet completed the design of the gas refund mechanism, some more detailed comparisons are not listed here.

In addition, we have completed the P2P mempool for the current 4337 bundler, and the Sequencer and Operator of zkRollups are also the only official servers, so there are certain centralized components.

In the development process, since zkSync does not have the problem of connecting with various bundlers (only needs to interact with the Operator API), it is easy to use 4337, and the experience of developing account contracts (SDK) is also better; at the same time, zkSync can use Solidity as a contract Development language, so there is no need to cross the threshold of Cairo in StarkNet development.

Conclusion

Since both StarkNet and zkSync belong to the category of local AA (Native AA), you can also refer to my previous introduction to StarkNet AA, entitled "Introduction of StarkNet Account Abstraction" (Introduction of StarkNet Account Abstraction). Also, you can read other articles related to EIP-4337 for more information.

View Original
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
  • Reward
  • Comment
  • Share
Comment
0/400
No comments
Trade Crypto Anywhere Anytime
qrCode
Scan to download Gate app
Community
English
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)