Account

This directory includes contracts to build accounts for ERC-4337. These include:

  • AccountCore: An ERC-4337 smart account implementation that includes the core logic to process user operations.

  • Account: An extension of AccountCore that implements the recommended features for ERC-4337 smart accounts.

  • {AccountSignerERC7702}: An account implementation with low-level signature validation performed by an EOA.

  • ERC7821: Minimal batch executor implementation contracts. Useful to enable easy batch execution for smart contracts.

Core

AccountCore

import "@openzeppelin/community-contracts/account/AccountCore.sol";

A simple ERC4337 account implementation. This base implementation only includes the minimal logic to process user operations.

Developers must implement the AccountCore._signableUserOpHash and AbstractSigner._rawSignatureValidation functions to define the account’s validation logic.

This core account doesn’t include any mechanism for performing arbitrary external calls. This is an essential feature that all Account should have. We leave it up to the developers to implement the mechanism of their choice. Common choices include ERC-6900, ERC-7579 and ERC-7821 (among others).
Implementing a mechanism to validate signatures is a security-sensitive operation as it may allow an attacker to bypass the account’s security measures. Check out SignerECDSA, SignerP256, or SignerRSA for digital signature validation implementations.
Modifiers
  • onlyEntryPointOrSelf()

  • onlyEntryPoint()

Functions
  • entryPoint()

  • getNonce()

  • getNonce(key)

  • validateUserOp(userOp, userOpHash, missingAccountFunds)

  • _validateUserOp(userOp, userOpHash)

  • _signableUserOpHash(userOp, userOpHash)

  • _payPrefund(missingAccountFunds)

  • _checkEntryPoint()

  • _checkEntryPointOrSelf()

  • receive()

AbstractSigner
  • _rawSignatureValidation(hash, signature)

Errors
  • AccountUnauthorized(sender)

onlyEntryPointOrSelf() modifier

Revert if the caller is not the entry point or the account itself.

onlyEntryPoint() modifier

Revert if the caller is not the entry point.

entryPoint() → contract IEntryPoint public

Canonical entry point for the account that forwards and validates user operations.

getNonce() → uint256 public

Return the account nonce for the canonical sequence.

getNonce(uint192 key) → uint256 public

Return the account nonce for a given sequence (key).

validateUserOp(struct PackedUserOperation userOp, bytes32 userOpHash, uint256 missingAccountFunds) → uint256 public

Validates a user operation.

  • MUST validate the caller is a trusted EntryPoint

  • MUST validate that the signature is a valid signature of the userOpHash, and SHOULD return SIG_VALIDATION_FAILED (and not revert) on signature mismatch. Any other error MUST revert.

  • MUST pay the entryPoint (caller) at least the “missingAccountFunds” (which might be zero, in case the current account’s deposit is high enough)

Returns an encoded packed validation data that is composed of the following elements:

  • authorizer (address): 0 for success, 1 for failure, otherwise the address of an authorizer contract

  • validUntil (uint48): The UserOp is valid only up to this time. Zero for “infinite”.

  • validAfter (uint48): The UserOp is valid only after this time.

_validateUserOp(struct PackedUserOperation userOp, bytes32 userOpHash) → uint256 internal

Returns the validationData for a given user operation. By default, this checks the signature of the signable hash (produced by _signableUserOpHash) using the abstract signer ({_rawSignatureValidation}).

The userOpHash is assumed to be correct. Calling this function with a userOpHash that does not match the userOp will result in undefined behavior.

_signableUserOpHash(struct PackedUserOperation userOp, bytes32 userOpHash) → bytes32 internal

Virtual function that returns the signable hash for a user operations. Some implementation may return userOpHash while other may prefer a signer-friendly value such as an EIP-712 hash describing the userOp details.

_payPrefund(uint256 missingAccountFunds) internal

Sends the missing funds for executing the user operation to the entryPoint. The missingAccountFunds must be defined by the entrypoint when calling validateUserOp.

_checkEntryPoint() internal

Ensures the caller is the entryPoint.

_checkEntryPointOrSelf() internal

Ensures the caller is the entryPoint or the account itself.

receive() external

Receive Ether.

AccountUnauthorized(address sender) error

Unauthorized call to the account.

Account

import "@openzeppelin/community-contracts/account/Account.sol";

Extension of AccountCore with recommended feature that most account abstraction implementation will want:

  • {ERC721Holder} and {ERC1155Holder} to accept ERC-712 and ERC-1155 token transfers transfers.

  • ERC7739 for ERC-1271 signature support with ERC-7739 replay protection

  • ERC7821 for performing external calls in batches.

Use ERC7821 to enable external calls in batches.
To use this contract, the {ERC7739-_rawSignatureValidation} function must be implemented using a specific signature verification algorithm. See SignerECDSA, SignerP256 or SignerRSA.
Functions
  • _signableUserOpHash(userOp, )

ERC7739
  • isValidSignature(hash, signature)

ERC1155Holder
  • supportsInterface(interfaceId)

  • onERC1155Received(, , , , )

  • onERC1155BatchReceived(, , , , )

ERC721Holder
  • onERC721Received(, , , )

EIP712
  • _domainSeparatorV4()

  • _hashTypedDataV4(structHash)

  • eip712Domain()

  • _EIP712Name()

  • _EIP712Version()

AccountCore
  • entryPoint()

  • getNonce()

  • getNonce(key)

  • validateUserOp(userOp, userOpHash, missingAccountFunds)

  • _validateUserOp(userOp, userOpHash)

  • _payPrefund(missingAccountFunds)

  • _checkEntryPoint()

  • _checkEntryPointOrSelf()

  • receive()

AbstractSigner
  • _rawSignatureValidation(hash, signature)

Events
IERC5267
  • EIP712DomainChanged()

Errors
AccountCore
  • AccountUnauthorized(sender)

Internal Variables
  • bytes32 constant _PACKED_USER_OPERATION

_signableUserOpHash(struct PackedUserOperation userOp, bytes32) → bytes32 internal

Specialization of AccountCore._signableUserOpHash that returns a typehash following EIP-712 typed data hashing for readability. This assumes the underlying signature scheme implements signTypedData, which will be the case when combined with SignerECDSA or SignerERC7702.

bytes32 _PACKED_USER_OPERATION internal constant

Extensions

ERC7821

import "@openzeppelin/community-contracts/account/extensions/ERC7821.sol";

Minimal batch executor following ERC-7821. Only supports basic mode (no optional "opData").

Functions
  • execute(mode, executionData)

  • supportsExecutionMode(mode)

  • _erc7821AuthorizedExecutor(caller, , )

Errors
  • UnsupportedExecutionMode()

execute(bytes32 mode, bytes executionData) public

Executes the calls in executionData with no optional opData support.

Access to this function is controlled by _erc7821AuthorizedExecutor. Changing access permissions, for example to approve calls by the ERC-4337 entrypoint, should be implement by overriding it.

Reverts and bubbles up error if any call fails.

supportsExecutionMode(bytes32 mode) → bool result public

This function is provided for frontends to detect support. Only returns true for: - bytes32(0x01000000000000000000…​): does not support optional opData. - bytes32(0x01000000000078210001…​): supports optional opData.

_erc7821AuthorizedExecutor(address caller, bytes32, bytes) → bool internal

Access control mechanism for the execute function. By default, only the contract itself is allowed to execute.

Override this function to implement custom access control, for example to allow the ERC-4337 entrypoint to execute.

function _erc7821AuthorizedExecutor(
  address caller,
  bytes32 mode,
  bytes calldata executionData
) internal view virtual override returns (bool) {
  return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);
}

UnsupportedExecutionMode() error