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

Account implementation and identities #5182

Draft
wants to merge 36 commits into
base: account-abstraction
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 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
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
9 changes: 5 additions & 4 deletions contracts/account/AccountERC7579.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ abstract contract AccountERC7579 is
mapping(bytes4 => address) private _fallbacks;

modifier onlyModule(uint256 moduleTypeId) {
_checkModule(moduleTypeId);
_checkModule(moduleTypeId, msg.sender);
_;
}

/// @inheritdoc IERC1271
function isValidSignature(bytes32 hash, bytes calldata signature) public view virtual override returns (bytes4) {
address module = address(bytes20(signature[0:20]));
_checkModule(MODULE_TYPE_VALIDATOR, module);
return IERC7579Validator(module).isValidSignatureWithSender(msg.sender, hash, signature);
}

Expand Down Expand Up @@ -186,9 +187,9 @@ abstract contract AccountERC7579 is
return true;
}

function _checkModule(uint256 moduleTypeId) internal view virtual {
if (!_isModuleInstalled(moduleTypeId, msg.sender, msg.data)) {
revert ERC7579UninstalledModule(moduleTypeId, msg.sender);
function _checkModule(uint256 moduleTypeId, address module) internal view virtual {
if (!_isModuleInstalled(moduleTypeId, module, msg.data)) {
revert ERC7579UninstalledModule(moduleTypeId, module);
}
}

Expand Down
27 changes: 0 additions & 27 deletions contracts/account/AccountFactory.sol

This file was deleted.

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

pragma solidity ^0.8.20;

import {PackedUserOperation} from "../interfaces/IERC4337.sol";
import {ERC4337Utils} from "./utils/ERC4337Utils.sol";
import {EIP712ReadableSigner} from "./signers/EIP712ReadableSigner.sol";
import {AccountBase} from "./AccountBase.sol";
import {ShortStrings} from "../utils/ShortStrings.sol";

abstract contract AccountSigner is AccountBase, EIP712ReadableSigner {
function _validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) internal virtual override returns (address signer, uint256 validationData) {
if (!_isValidSignature(userOpHash, userOp.signature)) return (address(0), ERC4337Utils.SIG_VALIDATION_FAILED);
return (address(this), ERC4337Utils.SIG_VALIDATION_SUCCESS);
}
}
34 changes: 34 additions & 0 deletions contracts/account/AccountSignerERC7579.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

import {IERC1271} from "../interfaces/IERC1271.sol";
import {PackedUserOperation} from "../interfaces/IERC4337.sol";
import {AccountERC7579} from "./AccountERC7579.sol";
import {AccountSigner, EIP712ReadableSigner} from "./AccountSigner.sol";
import {SignerECDSA} from "./signers/SignerECDSA.sol";
import {ERC4337Utils} from "./utils/ERC4337Utils.sol";
import {EIP712ReadableSigner} from "./signers/EIP712ReadableSigner.sol";

abstract contract AccountSignerERC7579 is AccountERC7579, AccountSigner, SignerECDSA {
function isValidSignature(
bytes32 hash,
bytes calldata signature
) public view override(AccountERC7579, EIP712ReadableSigner) returns (bytes4) {
bytes4 validModuleSignature = AccountERC7579.isValidSignature(hash, signature);
bytes4 validAccountSignature = EIP712ReadableSigner.isValidSignature(hash, signature);
bool isValid = validModuleSignature == IERC1271.isValidSignature.selector ||
validAccountSignature == IERC1271.isValidSignature.selector;
return isValid ? IERC1271.isValidSignature.selector : bytes4(0xffffffff);
}

function _validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) internal virtual override(AccountERC7579, AccountSigner) returns (address signer, uint256 validationData) {
(address accountSigner, uint256 accountValidationData) = AccountSigner._validateUserOp(userOp, userOpHash);
if (accountValidationData == ERC4337Utils.SIG_VALIDATION_SUCCESS) return (accountSigner, accountValidationData);
(address moduleSigner, uint256 moduleValidationData) = AccountERC7579._validateUserOp(userOp, userOpHash);
return (moduleSigner, moduleValidationData);
}
}
45 changes: 45 additions & 0 deletions contracts/account/modules/ERC7579Executor.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

import {IERC7579Execution} from "../../interfaces/IERC7579Account.sol";
import {IERC7579Module, MODULE_TYPE_EXECUTOR} from "../../interfaces/IERC7579Module.sol";
import {ERC7579Utils, Execution, Mode} from "../utils/ERC7579Utils.sol";

abstract contract ERC7579Executor is IERC7579Module {
/// @inheritdoc IERC7579Module
function isModuleType(uint256 moduleTypeId) public pure virtual returns (bool) {
return moduleTypeId == MODULE_TYPE_EXECUTOR;
}

function _executeSingle(
Mode mode,
address account,
address target,
uint256 value,
bytes memory data
) internal virtual {
IERC7579Execution(account).executeFromExecutor(
Mode.unwrap(mode),
ERC7579Utils.encodeSingle(target, value, data)
);
}

function _executeBatch(
Mode mode,
address account,
address[] memory targets,
uint256[] memory values,
bytes[] memory datas

Check failure on line 33 in contracts/account/modules/ERC7579Executor.sol

View workflow job for this annotation

GitHub Actions / codespell

datas ==> data
) internal virtual {
Execution[] memory executions = new Execution[](targets.length);
for (uint256 i = 0; i < targets.length; i++) {
executions[i] = Execution(targets[i], values[i], datas[i]);

Check failure on line 37 in contracts/account/modules/ERC7579Executor.sol

View workflow job for this annotation

GitHub Actions / codespell

datas ==> data
}
IERC7579Execution(account).executeFromExecutor(Mode.unwrap(mode), ERC7579Utils.encodeBatch(executions));
}

function _executeDelegate(Mode mode, address account, address target, bytes memory data) internal virtual {
IERC7579Execution(account).executeFromExecutor(Mode.unwrap(mode), ERC7579Utils.encodeDelegate(target, data));
}
}
173 changes: 97 additions & 76 deletions contracts/account/modules/ERC7579MultisigValidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,138 +5,159 @@ pragma solidity ^0.8.24;
import {IERC1271} from "../../interfaces/IERC1271.sol";
import {PackedUserOperation} from "../../interfaces/IERC4337.sol";
import {IERC7579Validator, IERC7579Module, MODULE_TYPE_VALIDATOR} from "../../interfaces/IERC7579Module.sol";
import {IERC7579Execution} from "../../interfaces/IERC7579Account.sol";
import {EnumerableSet} from "../../utils/structs/EnumerableSet.sol";
import {SignatureChecker} from "../../utils/cryptography/SignatureChecker.sol";
import {ERC4337Utils} from "../utils/ERC4337Utils.sol";
import {ERC7579Utils, CallType, ExecType, Execution, Mode, ModeSelector, ModePayload} from "../utils/ERC7579Utils.sol";
import {EIP712} from "../../utils//cryptography/EIP712.sol";
import {EIP712NestedUtils} from "../../utils/cryptography/EIP712NestedUtils.sol";

contract ERC7579MultisigValidator is IERC7579Validator, IERC1271 {
abstract contract ERC7579MultisigValidator is IERC7579Validator, EIP712, IERC1271 {
using EnumerableSet for EnumerableSet.AddressSet;
using SignatureChecker for address;

event SignersAdded(address[] indexed accounts);
event SignersRemoved(address[] indexed accounts);
event ThresholdChanged(uint256 threshold);
event SignersAdded(address indexed account, address[] indexed signers);
event SignersRemoved(address indexed account, address[] indexed signers);
event ThresholdChanged(address indexed account, uint256 threshold);

error MultisigSignerAlreadyExists(address account);
error MultisigSignerDoesNotExist(address account);
error MultisigUnreachableThreshold(uint256 signers, uint256 threshold);
error MultisigRemainingSigners(uint256 remaining);
error MultisigMismatchedSignaturesLength(uint256 signersLength, uint256 signaturesLength);
error MultisigUnorderedSigners(address prev, address current);
error MultisigSignerAlreadyExists(address account, address signer);
error MultisigSignerDoesNotExist(address account, address signer);
error MultisigUnreachableThreshold(address account, uint256 signers, uint256 threshold);
error MultisigRemainingSigners(address account, uint256 remaining);
error MultisigMismatchedSignaturesLength(address account, uint256 signersLength, uint256 signaturesLength);
error MultisigUnorderedSigners(address account, address prev, address current);

uint256 private _threshold;
EnumerableSet.AddressSet private _signers;
error MultisigUnauthorizedExecution(address account, address sender);

function threshold() public view virtual returns (uint256) {
return _threshold;
}

function isSigner(address account) public view virtual returns (bool) {
return _signers.contains(account);
}
mapping(address => EnumerableSet.AddressSet) private _associatedSigners;
mapping(address => uint256) private _associatedThreshold;

function addSigners(address[] memory accounts) external virtual {
_addSigners(accounts);
_validateThreshold();
function threshold(address account) public view virtual returns (uint256) {
return _associatedThreshold[account];
}

function _addSigners(address[] memory accounts) internal virtual {
for (uint256 i = 0; i < accounts.length; i++) {
if (!_signers.add(accounts[i])) revert MultisigSignerAlreadyExists(accounts[i]);
}
emit SignersAdded(accounts);
}

function removeSigners(address[] memory accounts) external virtual {
_removeSigners(accounts);
_validateThreshold();
}

function _removeSigners(address[] memory accounts) internal virtual {
for (uint256 i = 0; i < accounts.length; i++) {
if (!_signers.remove(accounts[i])) revert MultisigSignerDoesNotExist(accounts[i]);
}
emit SignersRemoved(accounts);
function isSigner(address account, address signer) public view virtual returns (bool) {
return _associatedSigners[account].contains(signer);
}

function setThreshold(uint256 threshold_) external virtual {
_setThreshold(threshold_);
_validateThreshold();
function addSigners(address[] memory signers) public virtual {
address account = msg.sender;
_addSigners(account, signers);
_validateThreshold(account);
}

function _setThreshold(uint256 threshold_) internal virtual {
_threshold = threshold_;
emit ThresholdChanged(threshold_);
function removeSigners(address[] memory signers) public virtual {
address account = msg.sender;
_removeSigners(account, signers);
_validateThreshold(account);
}

function _validateThreshold() internal view virtual {
uint256 signers = _signers.length();
if (signers < _threshold) revert MultisigUnreachableThreshold(signers, _threshold);
function setThreshold(uint256 threshold_) public virtual {
address account = msg.sender;
_setThreshold(account, threshold_);
_validateThreshold(account);
}

/// @inheritdoc IERC7579Module
function onInstall(bytes calldata data) external {
function onInstall(bytes memory data) public virtual {
address account = msg.sender;
(address[] memory signers, uint256 threshold_) = abi.decode(data, (address[], uint256));
_threshold = threshold_;
_addSigners(signers);
_validateThreshold();
_associatedThreshold[account] = threshold_;
_addSigners(account, signers);
_validateThreshold(account);
}

/// @inheritdoc IERC7579Module
function onUninstall(bytes calldata data) external {
function onUninstall(bytes memory data) public virtual {
address account = msg.sender;
address[] memory signers = abi.decode(data, (address[]));
_threshold = 0;
_removeSigners(signers);
uint256 remaining = _signers.length();
if (remaining != 0) revert MultisigRemainingSigners(remaining);
_associatedThreshold[account] = 0;
_removeSigners(account, signers);
uint256 remaining = _associatedSigners[account].length();
if (remaining != 0) revert MultisigRemainingSigners(account, remaining);
}

/// @inheritdoc IERC7579Module
function isModuleType(uint256 moduleTypeId) external pure returns (bool) {
function isModuleType(uint256 moduleTypeId) public pure virtual returns (bool) {
return moduleTypeId == MODULE_TYPE_VALIDATOR;
}

/// @inheritdoc IERC7579Validator
function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external view returns (uint256) {
function validateUserOp(
PackedUserOperation calldata userOp,
bytes32 userOpHash
) public view virtual returns (uint256) {
return
_isValidSignature(userOpHash, userOp.signature)
_isValidSignature(msg.sender, userOpHash, userOp.signature)
? ERC4337Utils.SIG_VALIDATION_SUCCESS
: ERC4337Utils.SIG_VALIDATION_FAILED;
}

/// @inheritdoc IERC1271
function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4) {
return _isValidSignature(hash, signature) ? IERC1271.isValidSignature.selector : bytes4(0xffffffff);
function isValidSignature(bytes32 hash, bytes calldata signature) public view virtual returns (bytes4) {
address account = address(bytes20(signature[0:20]));
bytes calldata sig = signature[20:];
return _isValidSignature(account, hash, sig) ? IERC1271.isValidSignature.selector : bytes4(0xffffffff);
}

/// @inheritdoc IERC7579Validator
function isValidSignatureWithSender(
address,
bytes32 hash,
bytes calldata signature
) public view virtual returns (bytes4) {
return _isValidSignature(msg.sender, hash, signature) ? IERC1271.isValidSignature.selector : bytes4(0xffffffff);
}

function _addSigners(address account, address[] memory signers) internal virtual {
for (uint256 i = 0; i < signers.length; i++) {
if (!_associatedSigners[account].add(signers[i])) revert MultisigSignerAlreadyExists(account, signers[i]);
}
emit SignersAdded(account, signers);
}

function _removeSigners(address account, address[] memory signers) internal virtual {
for (uint256 i = 0; i < signers.length; i++) {
if (!_associatedSigners[account].remove(signers[i])) revert MultisigSignerDoesNotExist(account, signers[i]);
}
emit SignersRemoved(account, signers);
}

function _setThreshold(address account, uint256 threshold_) internal virtual {
_associatedThreshold[account] = threshold_;
emit ThresholdChanged(msg.sender, threshold_);
}

function _validateThreshold(address account) internal view virtual {
uint256 signers = _associatedSigners[account].length();
uint256 _threshold = _associatedThreshold[account];
if (signers < _threshold) revert MultisigUnreachableThreshold(account, signers, _threshold);
}

function _isValidSignature(bytes32 hash, bytes calldata signature) internal view returns (bool) {
function _isValidSignature(
address account,
bytes32 hash,
bytes memory signature
) internal view virtual returns (bool) {
(address[] memory signers, bytes[] memory signatures) = abi.decode(signature, (address[], bytes[]));
if (signers.length != signatures.length)
revert MultisigMismatchedSignaturesLength(signers.length, signatures.length);
revert MultisigMismatchedSignaturesLength(account, signers.length, signatures.length);

uint256 count = 0;
address currentSigner = address(0);

for (uint256 i = 0; i < signers.length; i++) {
// Signers must be in order to ensure no duplicates
address signer = signers[i];
if (currentSigner >= signer) revert MultisigUnorderedSigners(currentSigner, signer);
if (currentSigner >= signer) revert MultisigUnorderedSigners(account, currentSigner, signer);
currentSigner = signer;

bool canSign = isSigner(signer);
bool canSign = _associatedSigners[account].contains(signer);
bool isValid = signer.isValidSignatureNow(hash, signatures[i]);
if (canSign && isValid) count++;
}

return count >= threshold();
}

/// @inheritdoc IERC7579Validator
function isValidSignatureWithSender(
address,
bytes32 hash,
bytes calldata signature
) external view returns (bytes4) {
return _isValidSignature(hash, signature) ? IERC1271.isValidSignature.selector : bytes4(0xffffffff);
return count >= _associatedThreshold[account];
}
}
Loading
Loading