Important

This repo is for demo purposes only.

Account Abstraction

What is Account Abstraction?

EoAs are now smart contracts. That's all account abstraction is.

But what does that mean?

Right now, every single transaction in web3 stems from a single private key.

account abstraction means that not only the execution of a transaction can be arbitrarily complex computation logic as specified by the EVM, but also the authorization logic.

What's this repo show?

  1. A minimal EVM "Smart Wallet" using alt-mempool AA
    1. We even send a transactoin to the EntryPoint.sol
  2. A minimal zkSync "Smart Wallet" using native AA
    1. zkSync uses native AA, which is slightly different than ERC-4337
    2. We do send our zkSync transaction to the alt-mempool

What does this repo not show?

  1. Sending your userop to the alt-mempool
    1. You can learn how to do this via the alchemy docs

Getting Started

Requirements

  • git
    • You'll know you did it right if you can run git --version and you see a response like git version x.x.x
  • foundry
    • You'll know you did it right if you can run forge --version and you see a response like forge 0.2.0 (816e00b 2023-03-16T00:05:26.396218Z)
  • foundry-zksync
    • You'll know you did it right if you can run forge-zksync --help and you see zksync somewhere in the output

Installation

git clone https://github.com/PatrickAlphaC/minimal-account-abstraction
cd minimal-account-abstraction
make

Quickstart

Vanilla Foundry

foundryup
make test

Deploy - Arbitrum

make deployEth

User operation - Arbitrum

make sendUserOp

zkSync Foundry

foundryup-zksync
make zkbuild
make zktest

Deploy - zkSync local network

Additional Requirements

  • npx & npm
    • You'll know you did it right if you can run npm --version and you see a response like 7.24.0 and npx --version and you see a response like 8.1.0.
  • yarn
    • You'll know you did it right if you can run yarn --version and you see a response like 1.22.17.
  • docker
    • You'll know you did it right if you can run docker --version and you see a response like Docker version 20.10.7, build f0df350.
    • Then, you'll want the daemon running, you'll know it's running if you can run docker --info and in the output you'll see something like the following to know it's running:
Client:
 Context:    default
 Debug Mode: false

Install dependencies:

yarn

Setup - local node

# Select `in memory node` and nothing else
npx zksync-cli dev start

Deploy - local node

Important

Never have a private key associated with real funds in plaintext.

# Setup your .env file, see the .env.example for an example
make zkdeploy

Note: Sending an account abstraction transaction doesn't work on the local network, because we don't have the system contracts setup on the local network.

Deploy - zkSync Sepolia or Mainnet

Make sure your wallet has at least 0.01 zkSync ETH in it.

  1. Encrypt your key

Add your PRIVATE_KEY and PRIVATE_KEY_PASSWORD to your .env file, then run:

make encryptKey

Important

NOW DELETE YOUR PRIVATE KEY AND PASSWORD FROM YOUR .env FILE!!! Don't push your .encryptedKey.json up to GitHub either!

  1. Un-Comment the Sepolia or Mainnet section (depending on which you'd like to use) of DeployZkMinimal.ts and SendAATx.ts:
// // Sepolia - uncomment to use
  1. Deploy the contract
make zkdeploy

You'll get an output like:

zkMinimalAccount deployed to: 0x4768d649Da9927a8b3842108117eC0ca7Bc6953f
With transaction hash: 0x103f6d894c20620dc632896799960d06ca37e722d20682ca824d428579ba157c

Grab the address of the zkMinimalAccount and add it to the ZK_MINIMAL_ADDRESS of SendAATx.ts.

  1. Fund your account

Send it 0.005 zkSync sepolia ETH.

  1. Send an AA transaction
make sendTx

You'll get an out put like this:

Let's do this!
Setting up contract details...
The owner of this minimal account is:  0x643315C9Be056cDEA171F4e7b2222a4ddaB9F88D
Populating transaction...
Signing transaction...
The minimal account nonce before the first tx is 0
Transaction sent from minimal account with hash 0xec7800e3a01d5ba5e472396127b656f7058cdcc5a1bd292b2b49f76aa19548c8
The account's nonce after the first tx is 1

Example Deployments

zkSync (Sepolia)

Ethereum (Arbitrum)

Account Abstraction zkSync Contract Deployment Flow

First time

  1. Calls createAccount or create2Account on the CONTRACT_DEPLOYER system contract
    1. This will deploy the contract to the L1.
    2. Mark the contract hash in the KnownCodesStorage contract
    3. Mark it as an AA contract
    4. Example
      1. Notice 6 logs emitted?

Subsequent times

  1. Calls createAccount or create2Account on the CONTRACT_DEPLOYER system contract
    1. The CONTRACT_DEPLOYER will check and see it's deployed this hash before
    2. It will put in another system contract that this address is associated with the first has
    3. Example
      1. Only 3 logs emitted!

FAQ

What if I don't add the contract hash to factory deps?

The transaction will revert. The ContractDeployer checks to see if it knows the hash, and if not, it will revert! The ContractDeployer calls the KnownCodesStorage contract, which keeps track of every single contract hash deployed on the zkSync chain. Crazy right!

Why can't we do these deployments with foundry or cast?

Foundry and cast don't have support for the factoryDeps transaction field, or support for type 113 transactions.

Why can I use forge create --legacy to deploy a regular contract?

foundry-zksync is smart enough to see a legacy deployment (when you send a transaction to the 0 address with data) and transform it into a contract call to the deployer. It's only smart enough for legacy deployments as of today, not the new EIP-1559 type 2 transactions or account creation.

Acknowledgements

Disclaimer

This codebase is for educational purposes only and has not undergone a security review.