Skip to content

Commit

Permalink
starting rewrite account common as ERC7579
Browse files Browse the repository at this point in the history
  • Loading branch information
Amxx committed Jul 19, 2024
1 parent 5a4b300 commit 63f1767
Show file tree
Hide file tree
Showing 21 changed files with 813 additions and 109 deletions.
21 changes: 13 additions & 8 deletions contracts/abstraction/account/Account.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

pragma solidity ^0.8.20;

import {PackedUserOperation, IAccount, IEntryPoint} from "../../interfaces/IERC4337.sol";
import {SignatureChecker} from "../../utils/cryptography/SignatureChecker.sol";
import {PackedUserOperation, IAccount, IAccountExecute, IEntryPoint} from "../../interfaces/IERC4337.sol";
import {ERC4337Utils} from "./../utils/ERC4337Utils.sol";
import {SignatureChecker} from "../../utils/cryptography/SignatureChecker.sol";
import {Address} from "../../utils/Address.sol";

abstract contract Account is IAccount {
abstract contract Account is IAccount, IAccountExecute {
error AccountEntryPointRestricted();
error AccountInvalidBatchLength();

/****************************************************************************************************************
* Modifiers *
Expand Down Expand Up @@ -39,15 +39,15 @@ abstract contract Account is IAccount {
*
* Subclass must implement this using their own access control mechanism.
*/
function _isAuthorized(address) internal virtual returns (bool);
function _isAuthorized(address) internal view virtual returns (bool);

/**
* @dev Recover the signer for a given signature and user operation hash. This function does not need to verify
* that the recovered signer is authorized.
*
* Subclass must implement this using their own choice of cryptography.
*/
function _recoverSigner(bytes32 userOpHash, bytes calldata signature) internal virtual returns (address);
function _recoverSigner(bytes32 userOpHash, bytes calldata signature) internal view virtual returns (address);

/****************************************************************************************************************
* Public interface *
Expand All @@ -72,13 +72,18 @@ abstract contract Account is IAccount {
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) public virtual override onlyEntryPoint returns (uint256 validationData) {
) public virtual onlyEntryPoint returns (uint256 validationData) {
(bool valid, , uint48 validAfter, uint48 validUntil) = _processSignature(userOpHash, userOp.signature);
_validateNonce(userOp.nonce);
_payPrefund(missingAccountFunds);
return ERC4337Utils.packValidationData(valid, validAfter, validUntil);
}

/// @inheritdoc IAccountExecute
function executeUserOp(PackedUserOperation calldata userOp, bytes32 /*userOpHash*/) public virtual onlyEntryPoint {
Address.functionDelegateCall(address(this), userOp.callData[4:]);
}

/****************************************************************************************************************
* Internal mechanisms *
****************************************************************************************************************/
Expand All @@ -95,7 +100,7 @@ abstract contract Account is IAccount {
function _processSignature(
bytes32 userOpHash,
bytes calldata signature
) internal virtual returns (bool valid, address signer, uint48 validAfter, uint48 validUntil) {
) internal view virtual returns (bool valid, address signer, uint48 validAfter, uint48 validUntil) {
address recovered = _recoverSigner(userOpHash, signature);
return (recovered != address(0) && _isAuthorized(recovered), recovered, 0, 0);
}
Expand Down
46 changes: 0 additions & 46 deletions contracts/abstraction/account/AccountCommon.sol

This file was deleted.

133 changes: 133 additions & 0 deletions contracts/abstraction/account/ERC7579Account.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {Account} from "./Account.sol";
import {Address} from "../../utils/Address.sol";
import {ERC1155Holder} from "../../token/ERC1155/utils/ERC1155Holder.sol";
import {ERC721Holder} from "../../token/ERC721/utils/ERC721Holder.sol";
import {ERC7579Utils} from "../utils/ERC7579Utils.sol";
import {IEntryPoint} from "../../interfaces/IERC4337.sol";
import {IERC1271} from "../../interfaces/IERC1271.sol";
import {IERC165, ERC165} from "../../utils/introspection/ERC165.sol";
import {IERC7579Account, Execution} from "../../interfaces/IERC7579Account.sol";

abstract contract ERC7579Account is IERC7579Account, Account, ERC165, ERC721Holder, ERC1155Holder {
using ERC7579Utils for *;

IEntryPoint private immutable _entryPoint;

error UnsupportedCallType(bytes1);

modifier onlyExecutorModule() {
// TODO
_;
}

constructor(IEntryPoint entryPoint_) {
_entryPoint = entryPoint_;
}

receive() external payable {}

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

/// IERC165
function supportsInterface(
bytes4 interfaceId
) public view virtual override(IERC165, ERC165, ERC1155Holder) returns (bool) {
// TODO: more?
return super.supportsInterface(interfaceId);
}

/// IERC1271
function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4 magicValue) {
(bool valid, , uint48 validAfter, uint48 validUntil) = _processSignature(hash, signature);
return
(valid && validAfter < block.timestamp && (validUntil == 0 || validUntil > block.timestamp))
? IERC1271.isValidSignature.selector
: bytes4(0);
}

/// IERC7579Execution
function execute(bytes32 mode, bytes calldata executionCalldata) public virtual onlyEntryPoint {
// TODO: support execType ?
(bytes1 callType, , , ) = mode.parseMode();

if (callType == ERC7579Utils.CALLTYPE_SINGLE) {
(address target, uint256 value, bytes calldata callData) = executionCalldata.parseSingle();
_execute(target, value, callData);
} else if (callType == ERC7579Utils.CALLTYPE_BATCH) {
Execution[] calldata executionBatch = executionCalldata.parseBatch();
for (uint256 i = 0; i < executionBatch.length; ++i) {
_execute(executionBatch[i].target, executionBatch[i].value, executionBatch[i].callData);
}
} else {
revert UnsupportedCallType(callType);
}
}

function executeFromExecutor(
bytes32 mode,
bytes calldata executionCalldata
) public virtual onlyExecutorModule returns (bytes[] memory returnData) {
// TODO: support execType ?
(bytes1 callType, , , ) = mode.parseMode();

if (callType == ERC7579Utils.CALLTYPE_SINGLE) {
(address target, uint256 value, bytes calldata callData) = executionCalldata.parseSingle();
returnData = new bytes[](1);
returnData[0] = _execute(target, value, callData);
} else if (callType == ERC7579Utils.CALLTYPE_BATCH) {
Execution[] calldata executionBatch = executionCalldata.parseBatch();
returnData = new bytes[](executionBatch.length);
for (uint256 i = 0; i < executionBatch.length; ++i) {
returnData[i] = _execute(executionBatch[i].target, executionBatch[i].value, executionBatch[i].callData);
}
} else {
revert UnsupportedCallType(callType);
}
}

function _execute(address target, uint256 value, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.call{value: value}(data);
Address.verifyCallResult(success, returndata);
return returndata;
}

/// IERC7579AccountConfig
function accountId() external pure returns (/*view*/ string memory /*accountImplementationId*/) {
revert("not-implemented-yet");
}

function supportsExecutionMode(bytes32 /*encodedMode*/ /*view*/) external pure returns (bool) {
revert("not-implemented-yet");
}

function supportsModule(uint256 /*moduleTypeId*/ /*view*/) external pure returns (bool) {
revert("not-implemented-yet");
}

/// IERC7579ModuleConfig
function installModule(uint256 /*moduleTypeId*/, address /*module*/, bytes calldata /*initData*/) external pure {
revert("not-implemented-yet");
}

function uninstallModule(
uint256 /*moduleTypeId*/,
address /*module*/,
bytes calldata /*deInitData*/
) external pure {
revert("not-implemented-yet");
}

function isModuleInstalled(
uint256 /*moduleTypeId*/,
address /*module*/,
bytes calldata /*additionalContext*/ /*view*/
) external pure returns (bool) {
revert("not-implemented-yet");
}
}
2 changes: 1 addition & 1 deletion contracts/abstraction/account/modules/AccountMultisig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ abstract contract AccountMultisig is Account {
function _processSignature(
bytes32 userOpHash,
bytes calldata signatures
) internal virtual override returns (bool, address, uint48 validAfter, uint48 validUntil) {
) internal view virtual override returns (bool, address, uint48 validAfter, uint48 validUntil) {
bytes[] calldata signatureArray = _decodeBytesArray(signatures);

if (signatureArray.length < requiredSignatures()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ abstract contract AccountAllSignatures is AccountECDSA, AccountERC1271 {
function _recoverSigner(
bytes32 userOpHash,
bytes calldata signature
) internal virtual override(AccountECDSA, AccountERC1271) returns (address) {
) internal view virtual override(AccountECDSA, AccountERC1271) returns (address) {
SignatureType sigType = SignatureType(uint8(bytes1(signature)));

if (sigType == SignatureType.ECDSA) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ abstract contract AccountECDSA is Account {
function _recoverSigner(
bytes32 userOpHash,
bytes calldata signature
) internal virtual override returns (address signer) {
) internal view virtual override returns (address signer) {
bytes32 msgHash = MessageHashUtils.toEthSignedMessageHash(userOpHash);

// This implementation support both "normal" and short signature formats:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import {Account} from "../../Account.sol";
abstract contract AccountERC1271 is Account {
error P256InvalidSignatureLength(uint256 length);

function _recoverSigner(bytes32 userOpHash, bytes calldata signature) internal virtual override returns (address) {
function _recoverSigner(
bytes32 userOpHash,
bytes calldata signature
) internal view virtual override returns (address) {
bytes32 msgHash = MessageHashUtils.toEthSignedMessageHash(userOpHash);
address signer = address(bytes20(signature[0x00:0x14]));

Expand Down
11 changes: 5 additions & 6 deletions contracts/abstraction/mocks/AdvancedAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ pragma solidity ^0.8.20;

import {IEntryPoint} from "../../interfaces/IERC4337.sol";
import {AccessControl} from "../../access/AccessControl.sol";
import {ERC1155Holder} from "../../token/ERC1155/utils/ERC1155Holder.sol";
import {Account} from "../account/Account.sol";
import {AccountCommon} from "../account/AccountCommon.sol";
import {ERC7579Account} from "../account/ERC7579Account.sol";
import {AccountMultisig} from "../account/modules/AccountMultisig.sol";
import {AccountAllSignatures} from "../account/modules/recovery/AccountAllSignatures.sol";

contract AdvancedAccount is AccessControl, AccountCommon, AccountAllSignatures, AccountMultisig {
contract AdvancedAccount is AccessControl, ERC7579Account, AccountAllSignatures, AccountMultisig {
bytes32 public constant SIGNER_ROLE = keccak256("SIGNER_ROLE");
uint256 private _requiredSignatures;

Expand All @@ -19,7 +18,7 @@ contract AdvancedAccount is AccessControl, AccountCommon, AccountAllSignatures,
address admin_,
address[] memory signers_,
uint256 requiredSignatures_
) AccountCommon(entryPoint_) {
) ERC7579Account(entryPoint_) {
_grantRole(DEFAULT_ADMIN_ROLE, admin_);
for (uint256 i = 0; i < signers_.length; ++i) {
_grantRole(SIGNER_ROLE, signers_[i]);
Expand All @@ -29,7 +28,7 @@ contract AdvancedAccount is AccessControl, AccountCommon, AccountAllSignatures,

function supportsInterface(
bytes4 interfaceId
) public view virtual override(AccessControl, ERC1155Holder) returns (bool) {
) public view virtual override(ERC7579Account, AccessControl) returns (bool) {
return super.supportsInterface(interfaceId);
}

Expand All @@ -44,7 +43,7 @@ contract AdvancedAccount is AccessControl, AccountCommon, AccountAllSignatures,
function _processSignature(
bytes32 userOpHash,
bytes calldata signature
) internal virtual override(Account, AccountMultisig) returns (bool, address, uint48, uint48) {
) internal view virtual override(Account, AccountMultisig) returns (bool, address, uint48, uint48) {
return super._processSignature(userOpHash, signature);
}
}
10 changes: 5 additions & 5 deletions contracts/abstraction/mocks/SimpleAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ pragma solidity ^0.8.20;

import {IEntryPoint} from "../../interfaces/IERC4337.sol";
import {Ownable} from "../../access/Ownable.sol";
import {AccountCommon} from "../account/AccountCommon.sol";
import {ERC7579Account} from "../account/ERC7579Account.sol";
import {AccountECDSA} from "../account/modules/recovery/AccountECDSA.sol";
import {AccountERC1271} from "../account/modules/recovery/AccountERC1271.sol";

contract SimpleAccountECDSA is Ownable, AccountCommon, AccountECDSA {
constructor(IEntryPoint entryPoint_, address owner_) AccountCommon(entryPoint_) Ownable(owner_) {}
contract SimpleAccountECDSA is Ownable, ERC7579Account, AccountECDSA {
constructor(IEntryPoint entryPoint_, address owner_) ERC7579Account(entryPoint_) Ownable(owner_) {}

function _isAuthorized(address user) internal view virtual override returns (bool) {
return user == owner();
}
}

contract SimpleAccountERC1271 is Ownable, AccountCommon, AccountERC1271 {
constructor(IEntryPoint entryPoint_, address owner_) AccountCommon(entryPoint_) Ownable(owner_) {}
contract SimpleAccountERC1271 is Ownable, ERC7579Account, AccountERC1271 {
constructor(IEntryPoint entryPoint_, address owner_) ERC7579Account(entryPoint_) Ownable(owner_) {}

function _isAuthorized(address user) internal view virtual override returns (bool) {
return user == owner();
Expand Down
Loading

0 comments on commit 63f1767

Please sign in to comment.