Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ERC-4337 Account and ERC-7579 modules #5182

Closed
wants to merge 80 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
24e6c8c
Account implementation and identities
ernestognw Sep 3, 2024
33774a6
Remove unnecessary change
ernestognw Sep 3, 2024
d2ef51d
Merge branch 'master' into account
ernestognw Sep 4, 2024
2d761cd
Remove unnecessary changes to Clones
ernestognw Sep 4, 2024
57021d6
Merge branch 'account-abstraction' into account
ernestognw Sep 18, 2024
481ffb0
Change Account implementations
ernestognw Sep 19, 2024
d816744
Lint
ernestognw Sep 19, 2024
aa4a7f7
Remove Memory utils
ernestognw Sep 19, 2024
074423d
Reorder
ernestognw Sep 19, 2024
558e16d
Reorder
ernestognw Sep 19, 2024
5803227
First docs
ernestognw Sep 19, 2024
54fc32b
More docs
ernestognw Sep 20, 2024
3b79b88
Fix readable signer
ernestognw Sep 20, 2024
ce2399b
Codespell
ernestognw Sep 20, 2024
f8ddac4
Add AccountEIP7702
ernestognw Sep 20, 2024
a3813d7
Fix compilation
ernestognw Sep 20, 2024
8a8de51
Add ERC7579
ernestognw Sep 20, 2024
0f5604a
Moar docs
ernestognw Sep 20, 2024
3fbd873
up
ernestognw Sep 21, 2024
e4d5b93
up
ernestognw Sep 21, 2024
1b70879
More docs
ernestognw Sep 21, 2024
d1738e0
Add ERC7579MultiSigner module
ernestognw Sep 21, 2024
ba8251c
up
ernestognw Sep 21, 2024
d8fff66
up
ernestognw Sep 21, 2024
b5dc98e
up
ernestognw Sep 22, 2024
79e11bb
up
ernestognw Sep 22, 2024
270ff94
up
ernestognw Sep 22, 2024
65f45ae
up
ernestognw Sep 23, 2024
5efd040
up
ernestognw Sep 23, 2024
41db378
up
ernestognw Sep 23, 2024
8b46711
up
ernestognw Sep 23, 2024
62d8164
up
ernestognw Sep 23, 2024
9f25076
up
ernestognw Sep 24, 2024
96a4800
up
ernestognw Sep 24, 2024
582accb
up
ernestognw Sep 25, 2024
dc7584f
codespell
ernestognw Sep 25, 2024
4844229
up
ernestognw Sep 25, 2024
7d3f069
Add notes
ernestognw Sep 25, 2024
2b14d66
Set test structure
ernestognw Sep 25, 2024
b80af5f
Fix codespell
ernestognw Sep 25, 2024
d4071d9
Reorder inheritance
ernestognw Sep 25, 2024
cfc8a9f
up
ernestognw Sep 25, 2024
eb8ee50
Update
ernestognw Sep 28, 2024
af35c87
Update
ernestognw Sep 28, 2024
0f11896
Update
ernestognw Sep 28, 2024
85171ef
Update
ernestognw Sep 28, 2024
4013a56
Update
ernestognw Sep 28, 2024
2d939e2
Fix stack too deep
ernestognw Sep 28, 2024
2d007e3
Update docs
ernestognw Sep 28, 2024
5ac6585
Update
ernestognw Sep 30, 2024
ed8678b
Make AccountERC7579Hooked abstract
ernestognw Oct 1, 2024
c3502c6
Revert clones test changes
ernestognw Oct 1, 2024
8c10ffa
Checkpoint MessageEnvelope tests
ernestognw Oct 1, 2024
90d6170
Fix inheritance order
ernestognw Oct 1, 2024
b63b53b
Exclude erc4337 entrypoint from transpilation
ernestognw Oct 1, 2024
a20b241
Remove console
ernestognw Oct 1, 2024
e2cae7d
Codespell
ernestognw Oct 1, 2024
ccd3e88
Exclude erc4337 entrypoint from transpilation
ernestognw Oct 1, 2024
185a280
Finish MessageEnvelopeUtils
ernestognw Oct 2, 2024
30eda8e
update transpiler
ernestognw Oct 2, 2024
278c5ad
WIP: Add ERC1271TypedSigner testrs
ernestognw Oct 2, 2024
66ff4ac
Lint
ernestognw Oct 2, 2024
6282757
Remove SHA256 wrapper
ernestognw Oct 2, 2024
46278ec
Make ERC1271TypedSigner tests work
ernestognw Oct 2, 2024
8212c68
Merge branch 'account-abstraction' into account
ernestognw Oct 2, 2024
46a3cfc
Fix ERC7579 Hook
ernestognw Oct 2, 2024
592339d
Apply suggestions
ernestognw Oct 2, 2024
b804925
Lint
ernestognw Oct 2, 2024
b03295a
Split ERC1155Holder into a lean version
ernestognw Oct 2, 2024
fd635dd
Add signer helpers
ernestognw Oct 2, 2024
872d148
Reorder Accounts inheritance
ernestognw Oct 2, 2024
253da58
Simplify and fix signers
ernestognw Oct 2, 2024
b93096a
WIP: AccountTests
ernestognw Oct 3, 2024
44586c3
Complete validateUserOp tests
ernestognw Oct 5, 2024
1e9efb0
Complete signer account tests
ernestognw Oct 7, 2024
e3e0fd0
Validators tests
ernestognw Oct 7, 2024
f6d6145
Lint
ernestognw Oct 7, 2024
6bd0a9f
Document validators
ernestognw Oct 7, 2024
99dbbe9
Test accounts fallback
ernestognw Oct 7, 2024
f791820
Add missing modules docs
ernestognw Oct 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 0 additions & 115 deletions contracts/abstraction/utils/ERC7579Utils.sol

This file was deleted.

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

pragma solidity ^0.8.20;

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

/**
* @dev A simple ERC4337 account implementation.
*
* This base implementation only includes the minimal logic to process user operations.
* Developers must implement the {_validateUserOp} function to define the account's validation logic.
*/
abstract contract AccountBase is IAccount, IAccountExecute {
/**
* @dev Unauthorized call to the account.
*/
error AccountUnauthorized(address sender);

/**
* @dev Revert if the caller is not the entry point or the account itself.
*/
modifier onlyEntryPointOrSelf() {
_checkEntryPointOrSelf();
_;
}

/**
* @dev Revert if the caller is not the entry point.
*/
modifier onlyEntryPoint() {
_checkEntryPoint();
_;
}

/**
* @dev Canonical entry point for the account that forwards and validates user operations.
*/
function entryPoint() public view virtual returns (IEntryPoint) {
return IEntryPoint(0x0000000071727De22E5E9d8BAf0edAc6f37da032);
}

/**
* @dev Return the account nonce for the canonical sequence.
*/
function getNonce() public view virtual returns (uint256) {
return getNonce(0);
}

/**
* @dev Return the account nonce for a given sequence (key).
*/
function getNonce(uint192 key) public view virtual returns (uint256) {
return entryPoint().getNonce(address(this), key);
}

/**
* @inheritdoc IAccount
*/
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash,
uint256 missingAccountFunds
) public virtual onlyEntryPoint returns (uint256) {
uint256 validationData = _validateUserOp(userOp, userOpHash);
_payPrefund(missingAccountFunds);
return validationData;
}

/**
* @inheritdoc IAccountExecute
*/
function executeUserOp(
PackedUserOperation calldata userOp,
bytes32 /*userOpHash*/
) public virtual onlyEntryPointOrSelf {
(address target, uint256 value, bytes memory data) = abi.decode(userOp.callData[4:], (address, uint256, bytes));
Address.functionCallWithValue(target, data, value);
}

/**
* @dev Validation logic for {validateUserOp}.
*
* IMPORTANT: Implementing a mechanism to validate user operations is a security-sensitive operation
* as it may allow an attacker to bypass the account's security measures. Check out {AccountECDSA},
* {AccountP256}, or {AccountRSA} for digital signature validation implementations.
*/
function _validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) internal virtual returns (uint256 validationData);

/**
* @dev Sends the missing funds for executing the user operation to the {entrypoint}.
* The `missingAccountFunds` must be defined by the entrypoint when calling {validateUserOp}.
*/
function _payPrefund(uint256 missingAccountFunds) internal virtual {
if (missingAccountFunds > 0) {
(bool success, ) = payable(msg.sender).call{value: missingAccountFunds}("");
success; // Silence warning. The entrypoint should validate the result.
}
}

/**
* @dev Ensures the caller is the {entrypoint}.
*/
function _checkEntryPoint() internal view virtual {
if (msg.sender != address(entryPoint())) {
revert AccountUnauthorized(msg.sender);
}
}

/**
* @dev Ensures the caller is the {entrypoint} or the account itself.
*/
function _checkEntryPointOrSelf() internal view virtual {
if (msg.sender != address(this) && msg.sender != address(entryPoint())) {
revert AccountUnauthorized(msg.sender);
}
}

/**
* @dev Receive Ether.
*/
receive() external payable virtual {}
}
60 changes: 60 additions & 0 deletions contracts/account/AccountECDSA.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {PackedUserOperation} from "../interfaces/IERC4337.sol";
import {AccountBase} from "./AccountBase.sol";
import {ERC1271TypedSigner} from "../utils/cryptography/ERC1271TypedSigner.sol";
import {ECDSA} from "../utils/cryptography/ECDSA.sol";
import {ERC4337Utils} from "./utils/ERC4337Utils.sol";
import {ERC721Holder} from "../token/ERC721/utils/ERC721Holder.sol";
import {ERC1155HolderLean, IERC1155Receiver} from "../token/ERC1155/utils/ERC1155HolderLean.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
import {IERC165} from "../utils/introspection/IERC165.sol";

/**
* @dev Account implementation using {ECDSA} signatures and {ERC1271TypedSigner} for replay protection.
*/
abstract contract AccountECDSA is ERC165, ERC1271TypedSigner, ERC721Holder, ERC1155HolderLean, AccountBase {
address private immutable _signer;

/**
* @dev Initializes the account with the address of the native signer.
*/
constructor(address signerAddr) {
_signer = signerAddr;
}

/**
* @dev Return the account's signer address.
*/
function signer() public view virtual returns (address) {
return _signer;
}

/**
* @dev Internal version of {validateUserOp} that relies on {_isValidSignature}.
*/
function _validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) internal virtual override returns (uint256) {
return
_isValidSignature(userOpHash, userOp.signature)
? ERC4337Utils.SIG_VALIDATION_SUCCESS
: ERC4337Utils.SIG_VALIDATION_FAILED;
}

/**
* @dev Validates the signature using the account's signer.S
*/
function _validateSignature(bytes32 hash, bytes calldata signature) internal view virtual override returns (bool) {
(address recovered, ECDSA.RecoverError err, ) = ECDSA.tryRecover(hash, signature);
return signer() == recovered && err == ECDSA.RecoverError.NoError;
}

/// @inheritdoc ERC165
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
}
}
10 changes: 10 additions & 0 deletions contracts/account/AccountEIP7702.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {AccountECDSA} from "./AccountECDSA.sol";
import {EIP712} from "../utils/cryptography/EIP712.sol";

contract AccountEIP7702 is AccountECDSA {
constructor(string memory name, string memory version) AccountECDSA(address(this)) EIP712(name, version) {}
}
65 changes: 65 additions & 0 deletions contracts/account/AccountP256.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {PackedUserOperation} from "../interfaces/IERC4337.sol";
import {AccountBase} from "./AccountBase.sol";
import {ERC1271TypedSigner} from "../utils/cryptography/ERC1271TypedSigner.sol";
import {P256} from "../utils/cryptography/P256.sol";
import {ERC4337Utils} from "./utils/ERC4337Utils.sol";
import {ERC721Holder} from "../token/ERC721/utils/ERC721Holder.sol";
import {ERC1155HolderLean, IERC1155Receiver} from "../token/ERC1155/utils/ERC1155HolderLean.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
import {IERC165} from "../utils/introspection/IERC165.sol";

/**
* @dev Account implementation using {P256} signatures and {ERC1271TypedSigner} for replay protection.
*/
abstract contract AccountP256 is ERC165, ERC1271TypedSigner, ERC721Holder, ERC1155HolderLean, AccountBase {
bytes32 private immutable _qx;
bytes32 private immutable _qy;

/**
* @dev Initializes the account with the P256 public key.
*/
constructor(bytes32 qx, bytes32 qy) {
_qx = qx;
_qy = qy;
}

/**
* @dev Return the account's signer P256 public key.
*/
function signer() public view virtual returns (bytes32 qx, bytes32 qy) {
return (_qx, _qy);
}

/**
* @dev Internal version of {validateUserOp} that relies on {_isValidSignature}.
*/
function _validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) internal virtual override returns (uint256) {
return
_isValidSignature(userOpHash, userOp.signature)
? ERC4337Utils.SIG_VALIDATION_SUCCESS
: ERC4337Utils.SIG_VALIDATION_FAILED;
}

/**
* @dev Validates the signature using the account's signer.
*/
function _validateSignature(bytes32 hash, bytes calldata signature) internal view virtual override returns (bool) {
if (signature.length < 0x40) return false;
bytes32 r = bytes32(signature[0x00:0x20]);
bytes32 s = bytes32(signature[0x20:0x40]);
(bytes32 qx, bytes32 qy) = signer();
return P256.verify(hash, r, s, qx, qy);
}

/// @inheritdoc ERC165
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
}
}
Loading
Loading