Skip to content

Commit

Permalink
Remove ERC-7821 from Account.sol (#67)
Browse files Browse the repository at this point in the history
Co-authored-by: Hadrien Croubois <[email protected]>
  • Loading branch information
ernestognw and Amxx authored Jan 15, 2025
1 parent fed22bf commit aa642cc
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 22 deletions.
14 changes: 3 additions & 11 deletions contracts/account/Account.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
import {ERC7739} from "../utils/cryptography/ERC7739.sol";
import {ERC7821} from "./extensions/ERC7821.sol";
import {AccountCore} from "./AccountCore.sol";

/**
Expand All @@ -17,10 +16,12 @@ import {AccountCore} from "./AccountCore.sol";
* * {ERC7739} for ERC-1271 signature support with ERC-7739 replay protection
* * {ERC7821} for performing external calls in batches.
*
* TIP: Use {ERC7821} to enable external calls in batches.
*
* NOTE: To use this contract, the {ERC7739-_rawSignatureValidation} function must be
* implemented using a specific signature verification algorithm. See {SignerECDSA}, {SignerP256} or {SignerRSA}.
*/
abstract contract Account is AccountCore, EIP712, ERC721Holder, ERC1155Holder, ERC7739, ERC7821 {
abstract contract Account is AccountCore, EIP712, ERC721Holder, ERC1155Holder, ERC7739 {
bytes32 internal constant _PACKED_USER_OPERATION =
keccak256(
"PackedUserOperation(address sender,uint256 nonce,bytes initCode,bytes callData,bytes32 accountGasLimits,uint256 preVerificationGas,bytes32 gasFees,bytes paymasterAndData)"
Expand Down Expand Up @@ -52,13 +53,4 @@ abstract contract Account is AccountCore, EIP712, ERC721Holder, ERC1155Holder, E
)
);
}

/// @inheritdoc ERC7821
function _erc7821AuthorizedExecutor(
address caller,
bytes32 mode,
bytes calldata executionData
) internal view virtual override returns (bool) {
return super._erc7821AuthorizedExecutor(caller, mode, executionData) || caller == address(entryPoint());
}
}
14 changes: 14 additions & 0 deletions contracts/account/extensions/ERC7821.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@ abstract contract ERC7821 is IERC7821 {

/**
* @dev 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.
*
* ```solidity
* function _erc7821AuthorizedExecutor(
* address caller,
* bytes32 mode,
* bytes calldata executionData
* ) internal view virtual override returns (bool) {
* return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);
* }
* ```
*/
function _erc7821AuthorizedExecutor(
address caller,
Expand Down
12 changes: 11 additions & 1 deletion contracts/mocks/account/AccountECDSAMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@
pragma solidity ^0.8.20;

import {Account} from "../../account/Account.sol";
import {ERC7821} from "../../account/extensions/ERC7821.sol";
import {SignerECDSA} from "../../utils/cryptography/SignerECDSA.sol";

abstract contract AccountECDSAMock is Account, SignerECDSA {
abstract contract AccountECDSAMock is Account, SignerECDSA, ERC7821 {
constructor(address signerAddr) {
_initializeSigner(signerAddr);
}

/// @inheritdoc ERC7821
function _erc7821AuthorizedExecutor(
address caller,
bytes32 mode,
bytes calldata executionData
) internal view virtual override returns (bool) {
return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);
}
}
12 changes: 11 additions & 1 deletion contracts/mocks/account/AccountERC7702Mock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
pragma solidity ^0.8.20;

import {Account} from "../../account/Account.sol";
import {ERC7821} from "../../account/extensions/ERC7821.sol";
import {SignerERC7702} from "../../utils/cryptography/SignerERC7702.sol";

abstract contract AccountERC7702Mock is Account, SignerERC7702 {}
abstract contract AccountERC7702Mock is Account, SignerERC7702, ERC7821 {
/// @inheritdoc ERC7821
function _erc7821AuthorizedExecutor(
address caller,
bytes32 mode,
bytes calldata executionData
) internal view virtual override returns (bool) {
return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);
}
}
12 changes: 11 additions & 1 deletion contracts/mocks/account/AccountMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,25 @@
pragma solidity ^0.8.20;

import {PackedUserOperation} from "@openzeppelin/contracts/interfaces/draft-IERC4337.sol";
import {ERC7821} from "../../account/extensions/ERC7821.sol";
import {ERC4337Utils} from "@openzeppelin/contracts/account/utils/draft-ERC4337Utils.sol";
import {Account} from "../../account/Account.sol";

abstract contract AccountMock is Account {
abstract contract AccountMock is Account, ERC7821 {
/// Validates a user operation with a boolean signature.
function _rawSignatureValidation(
bytes32 /* userOpHash */,
bytes calldata signature
) internal pure override returns (bool) {
return bytes1(signature[0:1]) == bytes1(0x01);
}

/// @inheritdoc ERC7821
function _erc7821AuthorizedExecutor(
address caller,
bytes32 mode,
bytes calldata executionData
) internal view virtual override returns (bool) {
return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);
}
}
12 changes: 11 additions & 1 deletion contracts/mocks/account/AccountP256Mock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@
pragma solidity ^0.8.20;

import {Account} from "../../account/Account.sol";
import {ERC7821} from "../../account/extensions/ERC7821.sol";
import {SignerP256} from "../../utils/cryptography/SignerP256.sol";

abstract contract AccountP256Mock is Account, SignerP256 {
abstract contract AccountP256Mock is Account, SignerP256, ERC7821 {
constructor(bytes32 qx, bytes32 qy) {
_initializeSigner(qx, qy);
}

/// @inheritdoc ERC7821
function _erc7821AuthorizedExecutor(
address caller,
bytes32 mode,
bytes calldata executionData
) internal view virtual override returns (bool) {
return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);
}
}
12 changes: 11 additions & 1 deletion contracts/mocks/account/AccountRSAMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@
pragma solidity ^0.8.20;

import {Account} from "../../account/Account.sol";
import {ERC7821} from "../../account/extensions/ERC7821.sol";
import {SignerRSA} from "../../utils/cryptography/SignerRSA.sol";

abstract contract AccountRSAMock is Account, SignerRSA {
abstract contract AccountRSAMock is Account, SignerRSA, ERC7821 {
constructor(bytes memory e, bytes memory n) {
_initializeSigner(e, n);
}

/// @inheritdoc ERC7821
function _erc7821AuthorizedExecutor(
address caller,
bytes32 mode,
bytes calldata executionData
) internal view virtual override returns (bool) {
return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);
}
}
12 changes: 11 additions & 1 deletion contracts/mocks/docs/account/MyAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ pragma solidity ^0.8.20;
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import {Account} from "../../../account/Account.sol"; // or AccountCore
import {ERC7821} from "../../../account/extensions/ERC7821.sol";

contract MyAccount is Account, Initializable {
contract MyAccount is Account, ERC7821, Initializable {
/**
* NOTE: EIP-712 domain is set at construction because each account clone
* will recalculate its domain separator based on their own address.
Expand All @@ -25,4 +26,13 @@ contract MyAccount is Account, Initializable {
function initializeSigner() public initializer {
// Most accounts will require some form of signer initialization logic
}

/// @dev Allows the entry point as an authorized executor.
function _erc7821AuthorizedExecutor(
address caller,
bytes32 mode,
bytes calldata executionData
) internal view virtual override returns (bool) {
return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);
}
}
12 changes: 11 additions & 1 deletion contracts/mocks/docs/account/MyAccountECDSA.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,23 @@ pragma solidity ^0.8.20;

import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {Account} from "../../../account/Account.sol";
import {ERC7821} from "../../../account/extensions/ERC7821.sol";
import {SignerECDSA} from "../../../utils/cryptography/SignerECDSA.sol";

contract MyAccountECDSA is Account, SignerECDSA {
contract MyAccountECDSA is Account, SignerECDSA, ERC7821 {
constructor() EIP712("MyAccountECDSA", "1") {}

function initializeSigner(address signerAddr) public virtual {
// Will revert if the signer is already initialized
_initializeSigner(signerAddr);
}

/// @dev Allows the entry point as an authorized executor.
function _erc7821AuthorizedExecutor(
address caller,
bytes32 mode,
bytes calldata executionData
) internal view virtual override returns (bool) {
return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);
}
}
12 changes: 11 additions & 1 deletion contracts/mocks/docs/account/MyAccountP256.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,23 @@ pragma solidity ^0.8.20;

import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {Account} from "../../../account/Account.sol";
import {ERC7821} from "../../../account/extensions/ERC7821.sol";
import {SignerP256} from "../../../utils/cryptography/SignerP256.sol";

contract MyAccountP256 is Account, SignerP256 {
contract MyAccountP256 is Account, SignerP256, ERC7821 {
constructor() EIP712("MyAccountP256", "1") {}

function initializeSigner(bytes32 qx, bytes32 qy) public virtual {
// Will revert if the signer is already initialized
_initializeSigner(qx, qy);
}

/// @dev Allows the entry point as an authorized executor.
function _erc7821AuthorizedExecutor(
address caller,
bytes32 mode,
bytes calldata executionData
) internal view virtual override returns (bool) {
return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);
}
}
12 changes: 11 additions & 1 deletion contracts/mocks/docs/account/MyAccountRSA.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,23 @@ pragma solidity ^0.8.20;

import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {Account} from "../../../account/Account.sol";
import {ERC7821} from "../../../account/extensions/ERC7821.sol";
import {SignerRSA} from "../../../utils/cryptography/SignerRSA.sol";

contract MyAccountRSA is Account, SignerRSA {
contract MyAccountRSA is Account, SignerRSA, ERC7821 {
constructor() EIP712("MyAccountRSA", "1") {}

function initializeSigner(bytes memory e, bytes memory n) public virtual {
// Will revert if the signer is already initialized
_initializeSigner(e, n);
}

/// @dev Allows the entry point as an authorized executor.
function _erc7821AuthorizedExecutor(
address caller,
bytes32 mode,
bytes calldata executionData
) internal view virtual override returns (bool) {
return caller == address(entryPoint()) || super._erc7821AuthorizedExecutor(caller, mode, executionData);
}
}
3 changes: 2 additions & 1 deletion docs/modules/ROOT/pages/account-abstraction.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ User operations are validated using an xref:api:utils.adoc#AbstractSigner[`Abstr
A more opinionated version is the xref:api:account.adoc#Account[`Account`] contract, which also inherits from:

* xref:api:utils.adoc#ERC7739Signer[ERC7739Signer]: An implementation of the https://eips.ethereum.org/EIPS/eip-1271[ERC-1271] interface for smart contract signatures. This layer adds a defensive rehashing mechanism that prevents signatures for this account to be replayed in another account controlled by the same signer. See xref:account-abstraction.adoc#erc7739_signatures[ERC-7739 signatures].
* https://docs.openzeppelin.com/contracts/api/token/erc721#AccountERC7821[AccountERC7821]: An extension that provides the minimal logic batch multiple calls in a single execution. Useful to execute multiple operations within a single user operation.
* https://docs.openzeppelin.com/contracts/api/token/erc721#ERC721Holder[ERC721Holder], https://docs.openzeppelin.com/contracts/api/token/erc1155#ERC1155Holder[ERC1155Holder]: Allows the account to hold https://eips.ethereum.org/EIPS/eip-721[ERC-721] and https://eips.ethereum.org/EIPS/eip-1155[ERC-1155] tokens.

NOTE: The Account doesn't include an execution mechanism. Using xref:api:account.adoc#ERC7821[`ERC7821`] is a recommended solution with the minimal logic to batch multiple calls in a single execution. This is useful to execute multiple calls within a single user operation (e.g. approve and transfer).

[source,solidity]
----
include::api:example$account/MyAccount.sol[]
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit aa642cc

Please sign in to comment.