0/5
## Understanding Account Abstraction Transactions in zkSync zkSync introduces a refined approach to transaction processing, particularly with its native support for account abstraction. A transaction under this model typically undergoes a two-phase lifecycle: Validation and Execution. 1. **Phase 1: Validation:** This initial phase is crucial for verifying the transaction's legitimacy before it consumes significant resources or impacts the state. 2. **Phase 2: Execution:** Once validated, the transaction proceeds to the execution phase, where its intended operations are carried out. A practical illustration of this two-phase process can be seen in the `ZkMinimalAccount.sol` contract, which serves as a foundational example for account abstraction implementations on zkSync. ```solidity // src/zksync/ZkMinimalAccount.sol // // Phase 1 Validation // // Phase 2 Execution contract ZkMinimalAccount is IAccount { function validateTransaction(bytes32 _txHash, bytes32 _suggestedSignedHash, Transaction memory _transaction) external payable returns (bytes4 magic) { // Logic for validating the transaction, e.g., signature verification, nonce check // ... } function executeTransaction(bytes32 _txHash, bytes32 _suggestedSignedHash, Transaction memory _transaction) external payable { // Logic for executing the transaction's operations // ... } // ... other functions like executeTransactionFromOutside, payForTransaction, prepareForPaymaster } ``` The `validateTransaction` function handles the first phase, ensuring prerequisites are met, while `executeTransaction` manages the actual state changes in the second phase. ## Deep Dive: The Transaction Validation Process The validation phase (Phase 1) in zkSync's account abstraction model involves several key steps to ensure a transaction's integrity before it's processed further. Let's break down the initial interactions: 1. **Transaction Submission:** The user initiates the process by sending their transaction to what can be described as a "zkSync API client." This client acts somewhat like a light node, serving as the entry point for transactions into the zkSync network. ``` /** * Phase 1 Validation * 1. The user sends the transaction to the "zkSync API client" (sort of a "light node") * ... */ ``` 2. **Nonce Uniqueness Verification:** A critical step in preventing replay attacks and ensuring orderly transaction processing is verifying the uniqueness of the transaction's nonce. The zkSync API client achieves this by querying a specialized system contract known as the **`NonceHolder`**. ``` /** * Phase 1 Validation * ... * 2. The zkSync API client checks to see the nonce is unique by querying the * NonceHolder system contract * ... */ ``` This interaction with the `NonceHolder` system contract is a distinctive feature of zkSync. ## The Role of System Contracts in zkSync One of the most significant architectural distinctions between Ethereum and zkSync lies in zkSync's extensive utilization of **System Contracts**. These are pre-deployed smart contracts on the zkSync network that manage fundamental protocol-level functionalities. Unlike Ethereum, where many core operations are handled by the client software or hard-coded into the protocol, zkSync delegates these to on-chain smart contracts. The **`NonceHolder.sol`** contract is a prime example. Its responsibility is to manage nonces for all accounts on zkSync, ensuring that each transaction from a specific sender has a unique nonce. This mechanism is vital for maintaining transaction order and preventing double-spending. A partial view of the `NonceHolder.sol` contract reveals its purpose: ```solidity // lib/foundry-era-contracts/src/system-contracts/contracts/NonceHolder.sol (partial view) /** * @author Matter Labs * @custom:security-contact security@matterlabs.dev * @notice A contract used for managing nonces for accounts. Together with bootloader, * this contract ensures that the pair (sender, nonce) is always unique, ensuring * unique transaction hashes. * @dev The account allows for both ascending growth in nonces and mapping nonces to specific * stored values in them. * The users can either marked a range of nonces by increasing the `minNonce`. This way all th * less than `minNonce` will become used. The other way to mark a certain 256-bit key as nonce * some value under it in this contract. * @dev Apart from transaction nonces, this contract also stores the deployment nonce for acco * will be used for address derivation using CREATE. For the economy of space, this nonce is s * packed with the `minNonce`. * @dev The behavior of some of the methods depends on the nonce ordering of the account. Nonc * here serve more as a help to users to prevent from doing mistakes, rather than any invarian */ contract NonceHolder is INonceHolder, ISystemContract { uint256 private constant DEPLOY_NONCE_MULTIPLIER = 2 ** 128; // ... mapping(uint256 account => uint256 packedMinAndDeploymentNonce) internal rawNonces; // ... } ``` Other system contracts on zkSync handle tasks like managing account code storage (`AccountCodeStorage`), system-wide context (`SystemContext`), and, crucially, contract deployment. Understanding these system contracts is essential for developers building applications that deeply integrate with zkSync's capabilities. You can find a comprehensive list and descriptions of these contracts in the official zkSync Era documentation under "System Contracts." ## Contract Deployment: A Tale of Two Networks (Ethereum vs. zkSync) The process of deploying smart contracts differs notably between Ethereum and zkSync, primarily due to zkSync's reliance on system contracts for core operations. **Ethereum's Deployment Mechanism:** On Ethereum, deploying a smart contract involves sending a transaction where the `to` field (recipient address) is left null or set to the zero address. The transaction's data payload contains the compiled bytecode of the contract. Ethereum nodes recognize this specific transaction format as a contract creation request and execute the deployment. This method is well-documented in Ethereum's developer resources. **zkSync's Deployment Mechanism:** zkSync takes a different path. Instead of the null-recipient pattern, contract deployment is facilitated by interacting with a dedicated system contract: the **`ContractDeployer`**. This contract resides at a well-known, predefined address on the zkSync network (for instance, `0x0000000000000000000000000000000000008006` on zkSync Era Mainnet, verifiable via the zkSync block explorer). To deploy a contract on zkSync, you call specific functions on this `ContractDeployer` system contract. These functions include: * `create` * `create2` * `createAccount` * `create2Account` * `forceDeployOnAddress` And others, each serving slightly different deployment scenarios or offering varied control over the deployment process. For example, `createAccount` is specifically designed for deploying account abstraction-compatible smart contract accounts. Developers can inspect the `ContractDeployer.sol` source code and its available functions directly on the zkSync block explorer by navigating to its address and viewing the "Write Contract" tab. This system contract-based approach centralizes and standardizes contract deployment logic within the zkSync protocol. ## Navigating zkSync Deployment with Developer Tools: The Foundry Case The divergence in contract deployment mechanisms between Ethereum and zkSync has direct implications for developer tooling. Standard Ethereum development tools, often designed around Ethereum's deployment conventions, may require specific configurations or flags to operate correctly with zkSync. Foundry, a popular smart contract development toolkit, provides a good example. The standard Foundry command for deploying a contract, `forge create`, is tailored for the Ethereum deployment model (sending bytecode to a null address). When targeting zkSync, this command, in its vanilla form, won't work as expected because zkSync requires interaction with the `ContractDeployer` system contract. To accommodate zkSync's deployment flow, `foundry-zksync` (the zkSync-compatible version of Foundry) introduces specific flags. For instance, you might use a command like: ```bash # Terminal command forge create --zksync --legacy ``` The `--zksync` flag tells Foundry to prepare the transaction for the zkSync network. The `--legacy` flag, in this context, instructs `foundry-zksync` to use an older deployment method that specifically interacts with the `ContractDeployer` system contract's `create` function. This ensures that the deployment adheres to zkSync's native process rather than attempting an Ethereum-style deployment. Without such flags or zkSync-aware configurations, developers might find that their usual deployment commands fail or behave unexpectedly on zkSync. This highlights the importance of understanding how system contracts influence tooling interactions. ## Key Takeaways: Embracing zkSync's Unique Architecture zkSync's architecture, particularly its extensive use of system contracts like `NonceHolder` for nonce management and `ContractDeployer` for smart contract deployment, represents a notable departure from Ethereum's operational model. These system contracts are not just peripheral components; they are integral to zkSync's core functionality and define how developers interact with the protocol at a low level. While interacting with system contracts might initially seem like an added layer of complexity, it also offers standardization and dedicated functionalities (e.g., `createAccount` for streamlined AA wallet deployment). For developers building on zkSync, a thorough understanding of these system contracts and their impact on transaction flows and tooling is crucial for successful and efficient development. This knowledge allows for better utilization of zkSync's unique features and a smoother transition from Ethereum-centric development practices.
An essential primer to Unpacking zkSync: How Transactions and System Contracts Work - Delve into zkSync's two-phase transaction model (validation and execution) and the key functions of system contracts such as NonceHolder and ContractDeployer. Understand how this architecture handles nonces and deploys contracts differently from Ethereum, affecting tools like Foundry.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
Advanced smart contract development
How to develop a stablecoin
How to develop a DeFi protocol
How to develop a DAO
Advanced smart contracts testing
Fuzz testing
Manual verification
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Security researcher
$49,999 - $120,000 (avg. salary)
Guest lecturers:
Juliette Chevalier
Lead Developer relations at Aragon
Nader Dabit
Director of developer relations at EigenLayer
Ally Haire
Developer relations at Protocol Labs
Harrison
Founder at GasliteGG
Last updated on May 12, 2025
Solidity Developer
Advanced FoundryDuration: 36min
Duration: 3h 06min
Duration: 5h 02min
Duration: 6h 02min
Duration: 2h 47min
Duration: 1h 23min
Duration: 4h 28min
Duration: 1h 19min
Duration: 1h 10min
Course Overview
About the course
Advanced smart contract development
How to develop a stablecoin
How to develop a DeFi protocol
How to develop a DAO
Advanced smart contracts testing
Fuzz testing
Manual verification
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Security researcher
$49,999 - $120,000 (avg. salary)
Guest lecturers:
Juliette Chevalier
Lead Developer relations at Aragon
Nader Dabit
Director of developer relations at EigenLayer
Ally Haire
Developer relations at Protocol Labs
Harrison
Founder at GasliteGG
Last updated on May 12, 2025