πŸ“ƒ
Documentation
  • πŸ‘οΈβ€πŸ—¨οΈVERITAS PROTOCOL
    • πŸ“–WHITE PAPER
      • Legal and Regulatory Considerations
    • πŸ“ŠTECH PAPER
    • πŸ’™Insurance Pool
    • πŸ“ŠTOKENOMICS
  • πŸ§‘β€πŸ’»RESEARCH
  • πŸ“ƒTHESIS
    • Automated Audits
    • Smart Contract β€œKill Switch”
  • ⁉️WHY AI?
  • ℹ️GUIDES
    • A Guide on What to do After Your Funds Have Been Stolen
    • Security Audit Checklist for Account Abstraction Wallets
    • Upgrade to Public Blockchain Security Audit Guide
    • Security Guide for Securing X (Twitter) Account
    • Navigating Wallet Types and Risks
    • The Dark Forest Manual Guide
    • Understanding Known-Plaintext Attacks and How to Prevent Them
  • ECOSYSTEM
    • πŸ”Veritas Explorer (B2C)
    • πŸ›‘οΈVeritas Audit (B2B)
    • πŸ•΅οΈβ€β™‚οΈPrivate Security Audit
      • Security Audit Methodology
    • β˜‘οΈSoulBound Audit Token
    • ⛑️Add-Ons
Powered by GitBook
On this page
  • Introduction
  • Architecture
  • Checklist
  • Conclusion
  1. GUIDES

Security Audit Checklist for Account Abstraction Wallets

A baseline checklist for auditing account abstraction wallets implemented based on the EIP4337 standard

PreviousA Guide on What to do After Your Funds Have Been StolenNextUpgrade to Public Blockchain Security Audit Guide

Last updated 8 months ago

Introduction

This guide provides auditors with a fundamental checklist for reviewing account abstraction wallets based on the EIP4337 standard, along with targeted auditing guidelines. It assumes auditors are familiar with the and the We'll briefly cover the EIP4337 architecture and wallet transaction execution flow.

Architecture

Transaction Execution

In EIP4337, an EOA signs data and submits it to a separate via RPC. This mempool, distinct from Ethereum's, aggregates user-submitted UserOp data. The Bundler extracts and simulates UserOps before execution, discarding failed simulations. All UserOp executions occur through the Bundler calling the EntryPoint contract. After verification, calls the user's AA wallet to execute the user's calldata. Users pay the Bundler for on-chain execution fees or specify a to cover costs.

Execution Details

Auditors should understand the process of the Bundler calling the user's wallet via EntryPoint:

Checklist

The following checklist items ensure each 4337 wallet passes crucial security checks:

  1. Verify Compatibility with All EVM-Compatible Chains

solidityCopysolc = "0.8.19"
evm_version = "paris"
  1. Ensure Interface Implementation and Return Values Comply with EIP4337

Wallets must implement core interfaces with specific return value structures. Paymasters must also implement required interfaces. Signature validation should return appropriate values or revert as specified.

  1. Verify Trusted Wallet Callers

Example Code:

solidityCopyfunction entryPoint() public view virtual override returns (IEntryPoint) {
   return _entryPoint;
}

function execute(address dest, uint256 value, bytes calldata func) external {
    _requireFromEntryPointOrOwner();
    _call(dest, value, func);
}

function executeBatch(address[] calldata dest, uint256[] calldata value, bytes[] calldata func) external {
    _requireFromEntryPointOrOwner();
    ...
}
  1. Check Fee Payment Implementation

Wallets should implement logic to transfer missingAccountFunds to the EntryPoint contract when necessary.

Example Code:

solidityCopyfunction validateUserOp(
    PackedUserOperation calldata userOp,
    bytes32 userOpHash,
    uint256 missingAccountFunds
) external virtual override returns (uint256 validationData) {
    ...
    _payPrefund(missingAccountFunds);
}
  1. Verify Wallet Creation Method

Factories must use CREATE2 for deterministic wallet creation addresses.

Example Code:

solidityCopyfunction createAccount(address owner,uint256 salt) public returns (SimpleAccount ret) {
    address addr = getAddress(owner, salt);
    uint256 codeSize = addr.code.length;
    if (codeSize > 0) {
        return SimpleAccount(payable(addr));
    }
    ret = SimpleAccount(payable(new ERC1967Proxy{salt : bytes32(salt)}(
            address(accountImplementation),
            abi.encodeCall(SimpleAccount.initialize, (owner))
        )));
}
  1. Check Return Value for Repeated Wallet Creation

Ensure consistent address returns for already-created wallets.

Example Code:

solidityCopyfunction createAccount(address owner,uint256 salt) public returns (SimpleAccount ret) {
    address addr = getAddress(owner, salt);
    uint256 codeSize = addr.code.length;
    if (codeSize > 0) {
        return SimpleAccount(payable(addr));
    }
    ...
}
  1. Prevent Wallet Takeover During Creation

Verify that wallet creation cannot be front-run and that ownership is correctly set.

function deployCounterFactualWallet(address _owner, address _entryPoint, address _handler, uint _index) public returns(address proxy){
    bytes32 salt = keccak256(abi.encodePacked(_owner, address(uint160(_index))));
    bytes memory deploymentData = abi.encodePacked(type(Proxy).creationCode, uint(uint160(_defaultImpl)));
    // solhint-disable-next-line no-inline-assembly
    assembly {
        proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
    }
    require(address(proxy) != address(0), "Create2 call failed");
    // EOA + Version tracking
    emit SmartAccountCreated(proxy,_defaultImpl,_owner, VERSION, _index);
    BaseSmartAccount(proxy).init(_owner, _entryPoint, _handler);
    isAccountExist[proxy] = true;
}
  1. Validate Signature Verification

Ensure rigorous signature validation in validateUserOp/validatePaymasterUserOp.

Example Code:

solidityCopyfunction validateUserOp(
    PackedUserOperation calldata userOp,
    bytes32 userOpHash,
    uint256 missingAccountFunds
) external virtual override returns (uint256 validationData) {
    _requireFromEntryPoint();
    validationData = _validateSignature(userOp, userOpHash);
    _validateNonce(userOp.nonce);
    _payPrefund(missingAccountFunds);
}
  1. Verify Correct ERC1271 Implementation

Check ERC1271 standard compliance and signature verification logic security.

Example Code:

solidityCopyfunction isValidSignature(bytes32 _dataHash, bytes calldata _signature) public view override returns (bytes4) {
    // Caller should be a Safe
    ISafe safe = ISafe(payable(msg.sender));
    bytes memory messageData = encodeMessageDataForSafe(safe, abi.encode(_dataHash));
    bytes32 messageHash = keccak256(messageData);
    if (_signature.length == 0) {
        require(safe.signedMessages(messageHash) != 0, "Hash not approved");
    } else {
        safe.checkSignatures(messageHash, _signature);
    }
    return EIP1271_MAGIC_VALUE;
}
  1. Prevent Permanent Locking of Staked Tokens

Ensure staking logic doesn't allow permanent token locking.

Example Code:

solidityCopyfunction addStake(uint32 unstakeDelaySec) external payable onlyOwner {
    entryPoint.addStake{value: msg.value}(unstakeDelaySec);
}
  1. Restrict Non-EntryPoint Transaction Execution

Verify that wallets implement proper permission checks for non-EntryPoint executions.

Example Code:

solidityCopy    function execute(address dest, uint256 value, bytes calldata func) external {
        _requireFromEntryPointOrOwner();
        _call(dest, value, func);
    }
  1. Limit Wallet Storage Access

Ensure wallets only access storage fields associated with the sender.

  1. Verify Paymaster's Failure Handling Logic

Check that Paymasters correctly handle fees in case of execution failures.

  1. Ensure Secure Implementation of Modular Wallets

Verify safe management of wallet modules and secure data storage when using DELEGATECALL.

Conclusion

This checklist provides a foundation for auditing account abstraction wallets based on the current EIP4337 standard. Given the early stages of EIP4337 implementation and varying wallet designs, auditors should conduct thorough checks based on specific wallet implementations.

Detailed flow chart:

AA wallets may deploy on various chains. Post-Shanghai Ethereum mainnet introduced PUSH0 bytecode, affecting Solidity versions . Auditors should check the Solidity version or compiled files for PUSH0 bytecode. For multi-chain deployment, use a compiler version below 0.8.20 or specify the paris compilation version.

interfaces should only allow trusted EntryPoint calls to prevent unauthorized wallet use.

(entryPoint not involved in address calculation, can be takeover and modified to a malicious entryPoint)

ℹ️
4337 Execution Details
0.8.20+
EIP4337
Example of Incorrect Code:
EIP4337 Account Abstraction Standard
EIP7562 Account Abstraction Validation Scope Rules Standard.
UserOperation
Alt Mempool
locally
EntryPoint
Paymaster