🎉 [Gate 30 Million Milestone] Share Your Gate Moment & Win Exclusive Gifts!
Gate has surpassed 30M users worldwide — not just a number, but a journey we've built together.
Remember the thrill of opening your first account, or the Gate merch that’s been part of your daily life?
📸 Join the #MyGateMoment# campaign!
Share your story on Gate Square, and embrace the next 30 million together!
✅ How to Participate:
1️⃣ Post a photo or video with Gate elements
2️⃣ Add #MyGateMoment# and share your story, wishes, or thoughts
3️⃣ Share your post on Twitter (X) — top 10 views will get extra rewards!
👉
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:
Table of contents
background
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:
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:
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.
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:
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:
The user uses the SDK or wallet locally to construct transaction objects (for example: from, to, data, value, etc.).
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.
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.
Bootloader will check whether the Nonce is legal, and use NonceHolder to check.
The Bootloader will call the validateTransaction function on the user account contract to confirm that the transaction has been authorized by the account owner.
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.
"Call payForTransaction to contract fee with Account" or "call prepareForPaymaster and validateAndPayForPaymasterTransaction to contract fee with Paymaster"
Check whether the bootloader has received at least tx.gasprice * tx.gaslimit transaction fees.
Bootloader will call the uteTransaction function on the user account contract to execute the transaction.
(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:
Compared
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.