On Abstract, all accounts are smart contracts that implement the IAccount interface. As outlined in the transaction flow, the bootloader calls the functions of the smart contract account deployed at the tx.from address for each transaction that it processes.

Abstract maintains compatibility with popular EOA wallets from the Ethereum ecosystem (e.g. MetaMask) by converting them to the DefaultAccount system contract during the transaction flow. This contract acts as you would expect an EOA to act, with the added benefit of supporting paymasters.

Get Started with Smart Contract Wallets

Use our example repositories to quickly get started building smart contract wallets.

Or follow our video tutorial for a step-by-step guide to building a smart contract wallet.

YouTube Video: Build a Smart Contract Wallet on Abstract

IAccount Interface

The IAccount interface defines the mandatory functions that a smart contract account must implement to be compatible with Abstract. View source code ↗.

First, install the system contracts library:

Ensure you have the isSystem flag set to true in your config: HardhatFoundry

Then, import and implement the IAccount interface in your smart contract:

import {IAccount} from "@matterlabs/zksync-contracts/l2/system-contracts/interfaces/IAccount.sol";

contract SmartAccount is IAccount {
  // Implement the interface (see docs below)
    // validateTransaction
    // executeTransaction
    // executeTransactionFromOutside
    // payForTransaction
    // prepareForPaymaster
}

See the DefaultAccount contract for an example implementation.

Using system contracts

Learn more about how to use system contracts in Solidity.

validateTransaction

This function is called to determine whether or not the transaction should be executed (i.e. it validates the transaction). Typically, you would perform some kind of check in this step to restrict who can use the account. This function must:

  1. Increment the nonce for the account. See handling nonces for more information.
  2. Return magic = ACCOUNT_VALIDATION_SUCCESS_MAGIC if the transaction is valid and should be executed.
  3. Should only be called by the bootloader contract (e.g. using an onlyBootloader modifier).
function validateTransaction(
    bytes32 _txHash,
    bytes32 _suggestedSignedHash,
    Transaction calldata _transaction
) external payable returns (bytes4 magic);

executeTransaction

This function is called if the validation step returned the ACCOUNT_VALIDATION_SUCCESS_MAGIC value. Consider:

  1. Using the EfficientCall library for executing transactions efficiently using zkEVM-specific features.
  2. Consider that the transaction may involve a contract deployment, in which case you should use the ContractDeployer system contract with the isSystemCall flag set to true.
  3. Should only be called by the bootloader contract (e.g. using an onlyBootloader modifier).
function executeTransaction(
    bytes32 _txHash,
    bytes32 _suggestedSignedHash,
    Transaction calldata _transaction
) external payable;

executeTransactionFromOutside

This function should be used to initiate a transaction from the smart contract wallet by an external call. Accounts can implement this method to initiate a transaction on behalf of the account via L1 -> L2 communication.

function executeTransactionFromOutside(
    Transaction calldata _transaction
) external payable;

payForTransaction

This function is called to pay the bootloader for the gas fee of the transaction. It should only be called by the bootloader contract (e.g. using an onlyBootloader modifier).

For convenience, there is a _transaction.payToTheBootloader() function that can be used to pay the bootloader for the gas fee.

function payForTransaction(
    bytes32 _txHash,
    bytes32 _suggestedSignedHash,
    Transaction calldata _transaction
) external payable;

prepareForPaymaster

Alternatively to payForTransaction, if the transaction has a paymaster set, you can use prepareForPaymaster to ask the paymaster to sponsor the gas fee for the transaction. It should only be called by the bootloader contract (e.g. using an onlyBootloader modifier).

For convenience, there is a _transaction.processPaymasterInput() function that can be used to prepare the transaction for the paymaster.

function prepareForPaymaster(
    bytes32 _txHash,
    bytes32 _possibleSignedHash,
    Transaction calldata _transaction
) external payable;

Deploying a Smart Contract Wallet

The ContractDeployer system contract has separate functions for deploying smart contract wallets: createAccount and create2Account.

Differentiate deploying an account contract from deploying a regular contract by providing either of these function names when initializing a contract factory.

Sending Transactions from a Smart Contract Wallet

Use EIP-712 formatted transactions to submit transactions from a smart contract wallet. You must specify:

  1. The from field as the address of the deployed smart contract wallet.
  2. Provide a customData object containing a customSignature that is not an empty string.

DefaultAccount Contract

The DefaultAccount contract is a system contract that mimicks the behaviour of an EOA. The bytecode of the contract is set by default for all addresses for which no other bytecodes are deployed.

DefaultAccount system contract

Learn more about the DefaultAccount system contract and how it works.

Smart Contract References