@ensuro/account-abstraction 0.0.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,27 @@
1
+ // This file was autogenerated by Hardhat, do not edit it.
2
+ // prettier-ignore
3
+ // tslint:disable
4
+ // eslint-disable
5
+ // biome-ignore format: see above
6
+
7
+ export interface ERC20With2771$Type {
8
+ readonly _format: "hh3-artifact-1";
9
+ readonly contractName: "ERC20With2771";
10
+ readonly sourceName: "contracts/mock/ERC20With2771.sol";
11
+ readonly abi: [{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint256","name":"initialSupply","type":"uint256"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"address","name":"trustedForwarder","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"forwarder","type":"address"}],"name":"isTrustedForwarder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"trustedForwarder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}];
12
+ readonly bytecode: "0x60c060405234801561000f575f5ffd5b50604051610d48380380610d4883398101604081905261002e91610274565b808585600361003d838261039d565b50600461004a828261039d565b5050506001600160a01b031660805260ff821660a05261006a3384610074565b505050505061047c565b6001600160a01b0382166100a25760405163ec442f0560e01b81525f60048201526024015b60405180910390fd5b6100ad5f83836100b1565b5050565b6001600160a01b0383166100db578060025f8282546100d09190610457565b9091555061014b9050565b6001600160a01b0383165f908152602081905260409020548181101561012d5760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610099565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661016757600280548290039055610185565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516101ca91815260200190565b60405180910390a3505050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f8301126101fa575f5ffd5b81516001600160401b03811115610213576102136101d7565b604051601f8201601f19908116603f011681016001600160401b0381118282101715610241576102416101d7565b604052818152838201602001851015610258575f5ffd5b8160208501602083015e5f918101602001919091529392505050565b5f5f5f5f5f60a08688031215610288575f5ffd5b85516001600160401b0381111561029d575f5ffd5b6102a9888289016101eb565b602088015190965090506001600160401b038111156102c6575f5ffd5b6102d2888289016101eb565b94505060408601519250606086015160ff811681146102ef575f5ffd5b60808701519092506001600160a01b038116811461030b575f5ffd5b809150509295509295909350565b600181811c9082168061032d57607f821691505b60208210810361034b57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561039857805f5260205f20601f840160051c810160208510156103765750805b601f840160051c820191505b81811015610395575f8155600101610382565b50505b505050565b81516001600160401b038111156103b6576103b66101d7565b6103ca816103c48454610319565b84610351565b6020601f8211600181146103fc575f83156103e55750848201515b5f19600385901b1c1916600184901b178455610395565b5f84815260208120601f198516915b8281101561042b578785015182556020948501946001909201910161040b565b508482101561044857868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b8082018082111561047657634e487b7160e01b5f52601160045260245ffd5b92915050565b60805160a05161089d6104ab5f395f61011701525f8181610151015281816101b60152610448015261089d5ff3fe608060405234801561000f575f5ffd5b50600436106100a6575f3560e01c8063572b6c051161006e578063572b6c051461014157806370a08231146101815780637da0a877146101a957806395d89b41146101e0578063a9059cbb146101e8578063dd62ed3e146101fb575f5ffd5b806306fdde03146100aa578063095ea7b3146100c857806318160ddd146100eb57806323b872dd146100fd578063313ce56714610110575b5f5ffd5b6100b2610233565b6040516100bf9190610699565b60405180910390f35b6100db6100d63660046106e9565b6102c3565b60405190151581526020016100bf565b6002545b6040519081526020016100bf565b6100db61010b366004610711565b6102e6565b60405160ff7f00000000000000000000000000000000000000000000000000000000000000001681526020016100bf565b6100db61014f36600461074b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b6100ef61018f36600461074b565b6001600160a01b03165f9081526020819052604090205490565b6040516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681526020016100bf565b6100b2610313565b6100db6101f63660046106e9565b610322565b6100ef61020936600461076b565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6060600380546102429061079c565b80601f016020809104026020016040519081016040528092919081815260200182805461026e9061079c565b80156102b95780601f10610290576101008083540402835291602001916102b9565b820191905f5260205f20905b81548152906001019060200180831161029c57829003601f168201915b5050505050905090565b5f5f6102cd610339565b90506102da818585610347565b60019150505b92915050565b5f5f6102f0610339565b90506102fd858285610359565b6103088585856103da565b506001949350505050565b6060600480546102429061079c565b5f5f61032c610339565b90506102da8185856103da565b5f610342610437565b905090565b61035483838360016104a1565b505050565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f198110156103d457818110156103c657604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b6103d484848484035f6104a1565b50505050565b6001600160a01b03831661040357604051634b637e8f60e11b81525f60048201526024016103bd565b6001600160a01b03821661042c5760405163ec442f0560e01b81525f60048201526024016103bd565b610354838383610573565b5f36601480821080159061047357507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633145b156104995761048636828403815f6107d4565b61048f916107fb565b60601c9250505090565b339250505090565b6001600160a01b0384166104ca5760405163e602df0560e01b81525f60048201526024016103bd565b6001600160a01b0383166104f357604051634a1406b160e11b81525f60048201526024016103bd565b6001600160a01b038085165f90815260016020908152604080832093871683529290522082905580156103d457826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161056591815260200190565b60405180910390a350505050565b6001600160a01b03831661059d578060025f8282546105929190610848565b9091555061060d9050565b6001600160a01b0383165f90815260208190526040902054818110156105ef5760405163391434e360e21b81526001600160a01b038516600482015260248101829052604481018390526064016103bd565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661062957600280548290039055610647565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161068c91815260200190565b60405180910390a3505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b80356001600160a01b03811681146106e4575f5ffd5b919050565b5f5f604083850312156106fa575f5ffd5b610703836106ce565b946020939093013593505050565b5f5f5f60608486031215610723575f5ffd5b61072c846106ce565b925061073a602085016106ce565b929592945050506040919091013590565b5f6020828403121561075b575f5ffd5b610764826106ce565b9392505050565b5f5f6040838503121561077c575f5ffd5b610785836106ce565b9150610793602084016106ce565b90509250929050565b600181811c908216806107b057607f821691505b6020821081036107ce57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f5f858511156107e2575f5ffd5b838611156107ee575f5ffd5b5050820193919092039150565b80356bffffffffffffffffffffffff198116906014841015610841576bffffffffffffffffffffffff196bffffffffffffffffffffffff198560140360031b1b82161691505b5092915050565b808201808211156102e057634e487b7160e01b5f52601160045260245ffdfea26469706673582212209052ca3b7d16208877574fb8688288b972afaa142be43385402a72663c099d9564736f6c634300081e0033";
13
+ readonly deployedBytecode: "0x608060405234801561000f575f5ffd5b50600436106100a6575f3560e01c8063572b6c051161006e578063572b6c051461014157806370a08231146101815780637da0a877146101a957806395d89b41146101e0578063a9059cbb146101e8578063dd62ed3e146101fb575f5ffd5b806306fdde03146100aa578063095ea7b3146100c857806318160ddd146100eb57806323b872dd146100fd578063313ce56714610110575b5f5ffd5b6100b2610233565b6040516100bf9190610699565b60405180910390f35b6100db6100d63660046106e9565b6102c3565b60405190151581526020016100bf565b6002545b6040519081526020016100bf565b6100db61010b366004610711565b6102e6565b60405160ff7f00000000000000000000000000000000000000000000000000000000000000001681526020016100bf565b6100db61014f36600461074b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b6100ef61018f36600461074b565b6001600160a01b03165f9081526020819052604090205490565b6040516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681526020016100bf565b6100b2610313565b6100db6101f63660046106e9565b610322565b6100ef61020936600461076b565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6060600380546102429061079c565b80601f016020809104026020016040519081016040528092919081815260200182805461026e9061079c565b80156102b95780601f10610290576101008083540402835291602001916102b9565b820191905f5260205f20905b81548152906001019060200180831161029c57829003601f168201915b5050505050905090565b5f5f6102cd610339565b90506102da818585610347565b60019150505b92915050565b5f5f6102f0610339565b90506102fd858285610359565b6103088585856103da565b506001949350505050565b6060600480546102429061079c565b5f5f61032c610339565b90506102da8185856103da565b5f610342610437565b905090565b61035483838360016104a1565b505050565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f198110156103d457818110156103c657604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b6103d484848484035f6104a1565b50505050565b6001600160a01b03831661040357604051634b637e8f60e11b81525f60048201526024016103bd565b6001600160a01b03821661042c5760405163ec442f0560e01b81525f60048201526024016103bd565b610354838383610573565b5f36601480821080159061047357507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633145b156104995761048636828403815f6107d4565b61048f916107fb565b60601c9250505090565b339250505090565b6001600160a01b0384166104ca5760405163e602df0560e01b81525f60048201526024016103bd565b6001600160a01b0383166104f357604051634a1406b160e11b81525f60048201526024016103bd565b6001600160a01b038085165f90815260016020908152604080832093871683529290522082905580156103d457826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161056591815260200190565b60405180910390a350505050565b6001600160a01b03831661059d578060025f8282546105929190610848565b9091555061060d9050565b6001600160a01b0383165f90815260208190526040902054818110156105ef5760405163391434e360e21b81526001600160a01b038516600482015260248101829052604481018390526064016103bd565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661062957600280548290039055610647565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161068c91815260200190565b60405180910390a3505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b80356001600160a01b03811681146106e4575f5ffd5b919050565b5f5f604083850312156106fa575f5ffd5b610703836106ce565b946020939093013593505050565b5f5f5f60608486031215610723575f5ffd5b61072c846106ce565b925061073a602085016106ce565b929592945050506040919091013590565b5f6020828403121561075b575f5ffd5b610764826106ce565b9392505050565b5f5f6040838503121561077c575f5ffd5b610785836106ce565b9150610793602084016106ce565b90509250929050565b600181811c908216806107b057607f821691505b6020821081036107ce57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f5f858511156107e2575f5ffd5b838611156107ee575f5ffd5b5050820193919092039150565b80356bffffffffffffffffffffffff198116906014841015610841576bffffffffffffffffffffffff196bffffffffffffffffffffffff198560140360031b1b82161691505b5092915050565b808201808211156102e057634e487b7160e01b5f52601160045260245ffdfea26469706673582212209052ca3b7d16208877574fb8688288b972afaa142be43385402a72663c099d9564736f6c634300081e0033";
14
+ readonly linkReferences: {};
15
+ readonly deployedLinkReferences: {};
16
+ readonly immutableReferences: {"2301":[{"length":32,"start":337},{"length":32,"start":438},{"length":32,"start":1096}],"14976":[{"length":32,"start":279}]};
17
+ readonly inputSourceName: "project/contracts/mock/ERC20With2771.sol";
18
+ readonly buildInfoId: "solc-0_8_30-95f43f1788cfd8898b8023be4a1aea1d69744865";
19
+ };
20
+
21
+ import "hardhat/types/artifacts";
22
+ declare module "hardhat/types/artifacts" {
23
+ interface ArtifactMap {
24
+ ["ERC20With2771"]: ERC20With2771$Type;
25
+ ["contracts/mock/ERC20With2771.sol:ERC20With2771"]: ERC20With2771$Type;
26
+ }
27
+ }
@@ -47,7 +47,7 @@ contract AccessControlAccount is AccessControl, BaseAccount {
47
47
  * @param value the value to pass in this call
48
48
  * @param func the calldata to pass in this call
49
49
  */
50
- function execute(address dest, uint256 value, bytes calldata func) external {
50
+ function execute(address dest, uint256 value, bytes calldata func) external override {
51
51
  _requireFromEntryPointOrExecutor();
52
52
  Address.functionCallWithValue(dest, func, value);
53
53
  }
@@ -40,7 +40,7 @@ contract AccessManagerAccount is AccessManager, BaseAccount {
40
40
  * @param value the value to pass in this call
41
41
  * @param func the calldata to pass in this call
42
42
  */
43
- function execute(address dest, uint256 value, bytes calldata func) external {
43
+ function execute(address dest, uint256 value, bytes calldata func) external override {
44
44
  _requireFromEntryPoint();
45
45
  Address.functionCallWithValue(dest, func, value);
46
46
  }
@@ -0,0 +1,187 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.23;
3
+
4
+ import {Address} from "@openzeppelin/contracts/utils/Address.sol";
5
+ import {BaseAccount} from "@account-abstraction/contracts/core/BaseAccount.sol";
6
+ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
7
+ import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
8
+ import {IAccountExecute} from "@account-abstraction/contracts/interfaces/IAccountExecute.sol";
9
+ import {IEntryPoint} from "@account-abstraction/contracts/interfaces/IEntryPoint.sol";
10
+ import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
11
+ import {PackedUserOperation} from "@account-abstraction/contracts/interfaces/PackedUserOperation.sol";
12
+ import {SIG_VALIDATION_SUCCESS, SIG_VALIDATION_FAILED} from "@account-abstraction/contracts/core/Helpers.sol";
13
+ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
14
+
15
+ /**
16
+ * @title ERC2771ForwarderAccount
17
+ *
18
+ * @dev Smart Account that acts as an ERC2771 Trusted Forwarder, forwarding calls to a pre-defined contract
19
+ * on behalf of the signer of the userOp.
20
+ *
21
+ * Assumes the target contract is designed to work with ERC2771Context and trusts this account as a forwarder.
22
+ *
23
+ * This contract is designed to be used with an AccessManagedProxy for runtime access control management.
24
+ *
25
+ * @custom:security-contact security@ensuro.co
26
+ * @author Ensuro
27
+ */
28
+ contract ERC2771ForwarderAccount is UUPSUpgradeable, BaseAccount, IAccountExecute {
29
+ IEntryPoint private immutable _entryPoint;
30
+
31
+ /// @custom:storage-location erc7201:ensuro.storage.ERC2771ForwarderAccount
32
+ struct ERC2771ForwarderAccountStorage {
33
+ mapping(address => ERC2771Context) targets;
34
+ }
35
+
36
+ // keccak256(abi.encode(uint256(keccak256("ensuro.storage.ERC2771ForwarderAccount")) - 1)) & ~bytes32(uint256(0xff))
37
+ // solhint-disable-next-line const-name-snakecase
38
+ bytes32 internal constant ERC2771ForwarderAccountStorageLocation =
39
+ 0x32800a8a254400b8b55434a22b827759dcb96a572133b419f26b7155e3843000;
40
+
41
+ function _getAccountStorage() internal pure returns (ERC2771ForwarderAccountStorage storage $) {
42
+ // solhint-disable-next-line no-inline-assembly
43
+ assembly {
44
+ $.slot := ERC2771ForwarderAccountStorageLocation
45
+ }
46
+ }
47
+
48
+ event ExecutorAdded(address indexed executor, ERC2771Context indexed target);
49
+ event ExecutorRemoved(address indexed executor);
50
+
51
+ error RequiredEntryPointOrExecutor(address sender);
52
+ error InvalidTarget(ERC2771Context target, address signer);
53
+ error OnlyExecuteUserOpAllowed();
54
+ error InvalidCall();
55
+
56
+ constructor(IEntryPoint anEntryPoint) {
57
+ _entryPoint = anEntryPoint;
58
+ }
59
+
60
+ // solhint-disable-next-line no-empty-blocks
61
+ function _authorizeUpgrade(address newImpl) internal view override {}
62
+
63
+ /**
64
+ * @dev This is a noop, for deployment convenience where an initializer is expected.
65
+ */
66
+ //solhint-disable-next-line no-empty-blocks
67
+ function initialize() external {}
68
+
69
+ // solhint-disable-next-line no-empty-blocks
70
+ receive() external payable {}
71
+
72
+ function executeBatch(Call[] calldata) external virtual override {
73
+ revert OnlyExecuteUserOpAllowed();
74
+ }
75
+
76
+ function execute(address, uint256, bytes calldata) external virtual override {
77
+ revert OnlyExecuteUserOpAllowed();
78
+ }
79
+
80
+ function _getSigner(PackedUserOperation calldata userop, bytes32 userOpHash) internal pure returns (address) {
81
+ bytes32 hash = MessageHashUtils.toEthSignedMessageHash(userOpHash);
82
+ return ECDSA.recover(hash, userop.signature);
83
+ }
84
+
85
+ /**
86
+ * @dev Validates that the user operation is well formed and that the destination is correct. Does not validate signature.
87
+ * @return call A Call struct containing the call to be made
88
+ */
89
+ function _validateAndDecodeCall(
90
+ PackedUserOperation calldata userOp,
91
+ bytes32 userOpHash
92
+ ) internal pure returns (Call memory call) {
93
+ require(userOp.callData.length >= 56 && bytes4(userOp.callData[0:4]) == this.executeUserOp.selector, InvalidCall());
94
+ (call.target, call.value, call.data) = abi.decode(userOp.callData[4:], (address, uint256, bytes));
95
+ if (call.target == address(0)) {
96
+ // This is an if and not a require to avoid evaluating the _getSigner call in the happy path
97
+ revert InvalidTarget(ERC2771Context(call.target), _getSigner(userOp, userOpHash));
98
+ }
99
+ }
100
+
101
+ function _isAuthorized(address signer, address target) internal view returns (bool) {
102
+ ERC2771ForwarderAccountStorage storage $ = _getAccountStorage();
103
+ return $.targets[signer] == ERC2771Context(target);
104
+ }
105
+
106
+ /**
107
+ * @notice Add an executor and its corresponding target contract.
108
+ * @param executor The executor address to add
109
+ * @param target The ERC2771Context target contract for this executor
110
+ */
111
+ function addExecutor(address executor, ERC2771Context target) external {
112
+ ERC2771ForwarderAccountStorage storage $ = _getAccountStorage();
113
+ $.targets[executor] = target;
114
+ emit ExecutorAdded(executor, target);
115
+ }
116
+
117
+ /**
118
+ * @notice Remove an executor by setting its target to the zero address.
119
+ * @param executor The executor address to remove
120
+ */
121
+ function removeExecutor(address executor) external {
122
+ ERC2771ForwarderAccountStorage storage $ = _getAccountStorage();
123
+ $.targets[executor] = ERC2771Context(address(0));
124
+ emit ExecutorRemoved(executor);
125
+ }
126
+
127
+ /// implement template method of BaseAccount
128
+ function _validateSignature(
129
+ PackedUserOperation calldata userOp,
130
+ bytes32 userOpHash
131
+ ) internal virtual override returns (uint256 validationData) {
132
+ Call memory call = _validateAndDecodeCall(userOp, userOpHash);
133
+ address signer = _getSigner(userOp, userOpHash);
134
+ if (!_isAuthorized(signer, call.target)) {
135
+ return SIG_VALIDATION_FAILED;
136
+ }
137
+ return SIG_VALIDATION_SUCCESS;
138
+ }
139
+
140
+ /**
141
+ * @dev Executes a user operation by forwarding the call to the target contract with the signer as the msgSender.
142
+ * It re-validates the signature and checks that the signer is authorized. Reverts with InvalidTarget if it isn't.
143
+ * The calldata is expected to contain this function's selector followed by an ABI-encoded Call:
144
+ * - dest (address): the target contract address (must be the same as _target)
145
+ * - value (uint256): the amount of ETH to send with the call
146
+ * - func (bytes): the calldata for the target function
147
+ *
148
+ * @param userOp The packed user operation containing the call data and signature.
149
+ * @param userOpHash The hash of the user operation, used for signature verification.
150
+ */
151
+ function executeUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external override {
152
+ Call memory call = _validateAndDecodeCall(userOp, userOpHash);
153
+ address signer = _getSigner(userOp, userOpHash);
154
+
155
+ require(_isAuthorized(signer, call.target), InvalidTarget(ERC2771Context(call.target), signer));
156
+
157
+ Address.functionCallWithValue(call.target, abi.encodePacked(call.data, signer), call.value);
158
+ }
159
+
160
+ /**
161
+ * check current account deposit in the entryPoint
162
+ */
163
+ function getDeposit() public view returns (uint256) {
164
+ return entryPoint().balanceOf(address(this));
165
+ }
166
+
167
+ /**
168
+ * deposit more funds for this account in the entryPoint
169
+ */
170
+ function addDeposit() public payable {
171
+ entryPoint().depositTo{value: msg.value}(address(this));
172
+ }
173
+
174
+ /**
175
+ * withdraw value from the account's deposit
176
+ * @param withdrawAddress target to send to
177
+ * @param amount to withdraw
178
+ */
179
+ function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public {
180
+ entryPoint().withdrawTo(withdrawAddress, amount);
181
+ }
182
+
183
+ /// @inheritdoc BaseAccount
184
+ function entryPoint() public view virtual override returns (IEntryPoint) {
185
+ return _entryPoint;
186
+ }
187
+ }
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Contracts (last updated v5.0.0) (access/manager/AccessManager.sol)
2
+ // OpenZeppelin Contracts (last updated v5.1.0) (access/manager/AccessManager.sol)
3
3
 
4
4
  pragma solidity ^0.8.20;
5
5
 
@@ -56,8 +56,8 @@ import {FrozenTime} from "./FrozenTime.sol";
56
56
  * will be {AccessManager} itself.
57
57
  *
58
58
  * WARNING: When granting permissions over an {Ownable} or {AccessControl} contract to an {AccessManager}, be very
59
- * mindful of the danger associated with functions such as {{Ownable-renounceOwnership}} or
60
- * {{AccessControl-renounceRole}}.
59
+ * mindful of the danger associated with functions such as {Ownable-renounceOwnership} or
60
+ * {AccessControl-renounceRole}.
61
61
  */
62
62
  contract AccessManager is Context, Multicall, IAccessManager {
63
63
  using Time for *;
@@ -98,7 +98,15 @@ contract AccessManager is Context, Multicall, IAccessManager {
98
98
  uint32 nonce;
99
99
  }
100
100
 
101
+ /**
102
+ * @dev The identifier of the admin role. Required to perform most configuration operations including
103
+ * other roles' management and target restrictions.
104
+ */
101
105
  uint64 public constant ADMIN_ROLE = type(uint64).min; // 0
106
+
107
+ /**
108
+ * @dev The identifier of the public role. Automatically granted to all addresses with no delay.
109
+ */
102
110
  uint64 public constant PUBLIC_ROLE = type(uint64).max; // 2**64-1
103
111
 
104
112
  mapping(address target => TargetConfig mode) private _targets;
@@ -109,11 +117,11 @@ contract AccessManager is Context, Multicall, IAccessManager {
109
117
  // This should be transient storage when supported by the EVM.
110
118
  bytes32 private _executionId;
111
119
 
112
- bool private _isFrozen;
120
+ bool transient _isFrozen;
113
121
 
114
122
  /**
115
- * @dev Check that the caller is authorized to perform the operation, following the restrictions encoded in
116
- * {_getAdminRestrictions}.
123
+ * @dev Check that the caller is authorized to perform the operation.
124
+ * See {AccessManager} description for a detailed breakdown of the authorization logic.
117
125
  */
118
126
  modifier onlyAuthorized() {
119
127
  _checkAuthorized();
@@ -425,9 +433,6 @@ contract AccessManager is Context, Multicall, IAccessManager {
425
433
  * Emits a {TargetClosed} event.
426
434
  */
427
435
  function _setTargetClosed(address target, bool closed) internal virtual {
428
- if (target == address(this)) {
429
- revert AccessManagerLockedAccount(target);
430
- }
431
436
  _targets[target].closed = closed;
432
437
  emit TargetClosed(target, closed);
433
438
  }
@@ -457,7 +462,7 @@ contract AccessManager is Context, Multicall, IAccessManager {
457
462
 
458
463
  uint48 minWhen = Time.timestamp() + setback;
459
464
 
460
- // if call with delay is not authorized, or if requested timing is too soon
465
+ // If call with delay is not authorized, or if requested timing is too soon, revert
461
466
  if (setback == 0 || (when > 0 && when < minWhen)) {
462
467
  revert AccessManagerUnauthorizedCall(caller, target, _checkSelector(data));
463
468
  }
@@ -483,7 +488,8 @@ contract AccessManager is Context, Multicall, IAccessManager {
483
488
 
484
489
  /**
485
490
  * @dev Reverts if the operation is currently scheduled and has not expired.
486
- * (Note: This function was introduced due to stack too deep errors in schedule.)
491
+ *
492
+ * NOTE: This function was introduced due to stack too deep errors in schedule.
487
493
  */
488
494
  function _checkNotScheduled(bytes32 operationId) private view {
489
495
  uint48 prevTimepoint = _schedules[operationId].timepoint;
@@ -502,7 +508,7 @@ contract AccessManager is Context, Multicall, IAccessManager {
502
508
  // Fetch restrictions that apply to the caller on the targeted function
503
509
  (bool immediate, uint32 setback) = _canCallExtended(caller, target, data);
504
510
 
505
- // If caller is not authorised, revert
511
+ // If call is not authorized, revert
506
512
  if (!immediate && setback == 0) {
507
513
  revert AccessManagerUnauthorizedCall(caller, target, _checkSelector(data));
508
514
  }
@@ -598,7 +604,9 @@ contract AccessManager is Context, Multicall, IAccessManager {
598
604
 
599
605
  // ================================================= ADMIN LOGIC ==================================================
600
606
  /**
601
- * @dev Check if the current call is authorized according to admin logic.
607
+ * @dev Check if the current call is authorized according to admin and roles logic.
608
+ *
609
+ * WARNING: Carefully review the considerations of {AccessManaged-restricted} since they apply to this modifier.
602
610
  */
603
611
  function _checkAuthorized() private {
604
612
  address caller = _msgSender();
@@ -623,7 +631,7 @@ contract AccessManager is Context, Multicall, IAccessManager {
623
631
  */
624
632
  function _getAdminRestrictions(
625
633
  bytes calldata data
626
- ) private view returns (bool restricted, uint64 roleAdminId, uint32 executionDelay) {
634
+ ) private view returns (bool adminRestricted, uint64 roleAdminId, uint32 executionDelay) {
627
635
  if (data.length < 4) {
628
636
  return (false, 0, 0);
629
637
  }
@@ -660,7 +668,7 @@ contract AccessManager is Context, Multicall, IAccessManager {
660
668
  return (true, getRoleAdmin(roleId), 0);
661
669
  }
662
670
 
663
- return (false, 0, 0);
671
+ return (false, getTargetFunctionRole(address(this), selector), 0);
664
672
  }
665
673
 
666
674
  // =================================================== HELPERS ====================================================
@@ -685,7 +693,7 @@ contract AccessManager is Context, Multicall, IAccessManager {
685
693
  }
686
694
 
687
695
  /**
688
- * @dev A version of {canCall} that checks for admin restrictions in this contract.
696
+ * @dev A version of {canCall} that checks for restrictions in this contract.
689
697
  */
690
698
  function _canCallSelf(address caller, bytes calldata data) private view returns (bool immediate, uint32 delay) {
691
699
  if (data.length < 4) {
@@ -698,8 +706,10 @@ contract AccessManager is Context, Multicall, IAccessManager {
698
706
  return (_isExecuting(address(this), _checkSelector(data)), 0);
699
707
  }
700
708
 
701
- (bool enabled, uint64 roleId, uint32 operationDelay) = _getAdminRestrictions(data);
702
- if (!enabled) {
709
+ (bool adminRestricted, uint64 roleId, uint32 operationDelay) = _getAdminRestrictions(data);
710
+
711
+ // isTargetClosed apply to non-admin-restricted function
712
+ if (!adminRestricted && isTargetClosed(address(this))) {
703
713
  return (false, 0);
704
714
  }
705
715
 
@@ -88,7 +88,7 @@ library FrozenTime {
88
88
  /**
89
89
  * @dev Get the current value.
90
90
  */
91
- function get(Delay self) internal view returns (uint32) {
91
+ function get(Delay self) internal pure returns (uint32) {
92
92
  (uint32 delay, , ) = self.getFull();
93
93
  return delay;
94
94
  }
@@ -102,7 +102,7 @@ library FrozenTime {
102
102
  Delay self,
103
103
  uint32 newValue,
104
104
  uint32 minSetback
105
- ) internal view returns (Delay updatedDelay, uint48 effect) {
105
+ ) internal pure returns (Delay updatedDelay, uint48 effect) {
106
106
  uint32 value = self.get();
107
107
  uint32 setback = uint32(Math.max(minSetback, value > newValue ? value - newValue : 0));
108
108
  effect = timestamp() + setback;
@@ -0,0 +1,40 @@
1
+ //SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.0;
3
+
4
+ import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
5
+ import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
6
+ import {Context} from "@openzeppelin/contracts/utils/Context.sol";
7
+
8
+ contract ERC20With2771 is ERC20, ERC2771Context {
9
+ uint8 internal immutable _decimals;
10
+
11
+ constructor(
12
+ string memory name_,
13
+ string memory symbol_,
14
+ uint256 initialSupply,
15
+ uint8 decimals_,
16
+ address trustedForwarder
17
+ ) ERC20(name_, symbol_) ERC2771Context(trustedForwarder) {
18
+ _decimals = decimals_;
19
+ _mint(msg.sender, initialSupply);
20
+ }
21
+
22
+ function decimals() public view virtual override returns (uint8) {
23
+ return _decimals;
24
+ }
25
+
26
+ /// @inheritdoc ERC2771Context
27
+ function _contextSuffixLength() internal view override(Context, ERC2771Context) returns (uint256) {
28
+ return ERC2771Context._contextSuffixLength();
29
+ }
30
+
31
+ /// @inheritdoc ERC2771Context
32
+ function _msgSender() internal view override(Context, ERC2771Context) returns (address) {
33
+ return ERC2771Context._msgSender();
34
+ }
35
+
36
+ /// @inheritdoc ERC2771Context
37
+ function _msgData() internal view override(Context, ERC2771Context) returns (bytes calldata) {
38
+ return ERC2771Context._msgData();
39
+ }
40
+ }
package/js/userOp.js CHANGED
@@ -1,13 +1,13 @@
1
- const ethers = require("ethers");
1
+ import * as ethers from "ethers";
2
2
  const { ZeroAddress } = ethers;
3
3
 
4
4
  const defaultAbiCoder = ethers.AbiCoder.defaultAbiCoder();
5
5
 
6
- function packAccountGasLimits(verificationGasLimit, callGasLimit) {
6
+ export function packAccountGasLimits(verificationGasLimit, callGasLimit) {
7
7
  return ethers.toBeHex(verificationGasLimit, 16) + ethers.toBeHex(callGasLimit, 16).slice(2);
8
8
  }
9
9
 
10
- function packUserOp(userOp) {
10
+ export function packUserOp(userOp) {
11
11
  const accountGasLimits = packAccountGasLimits(userOp.verificationGasLimit, userOp.callGasLimit);
12
12
  const gasFees = packAccountGasLimits(userOp.maxPriorityFeePerGas, userOp.maxFeePerGas);
13
13
  let paymasterAndData = "0x";
@@ -32,7 +32,7 @@ function packUserOp(userOp) {
32
32
  };
33
33
  }
34
34
 
35
- function packedUserOpAsArray(packedUserOp, includeSignature = true) {
35
+ export function packedUserOpAsArray(packedUserOp, includeSignature = true) {
36
36
  if (includeSignature) {
37
37
  return [
38
38
  packedUserOp.sender,
@@ -59,7 +59,7 @@ function packedUserOpAsArray(packedUserOp, includeSignature = true) {
59
59
  }
60
60
  }
61
61
 
62
- function encodeUserOp(userOp, forSignature = true) {
62
+ export function encodeUserOp(userOp, forSignature = true) {
63
63
  const packedUserOp = packUserOp(userOp);
64
64
  if (forSignature) {
65
65
  return defaultAbiCoder.encode(
@@ -94,47 +94,39 @@ function encodeUserOp(userOp, forSignature = true) {
94
94
  }
95
95
  }
96
96
 
97
- function getUserOpHash(op, entryPoint, chainId) {
97
+ export function getUserOpHash(op, entryPoint, chainId) {
98
98
  const userOpHash = ethers.keccak256(encodeUserOp(op, true));
99
99
  const enc = defaultAbiCoder.encode(["bytes32", "address", "uint256"], [userOpHash, entryPoint, chainId]);
100
100
  return ethers.keccak256(enc);
101
101
  }
102
102
 
103
- const DefaultsForUserOp = {
103
+ export const DefaultsForUserOp = {
104
104
  sender: ZeroAddress,
105
105
  nonce: 0,
106
106
  initCode: "0x",
107
107
  callData: "0x",
108
- callGasLimit: 0,
109
- verificationGasLimit: 150000, // default verification gas. will add create2 cost (3200+200*length) if initCode exists
110
- preVerificationGas: 21000, // should also cover calldata cost.
111
- maxFeePerGas: 0,
108
+ callGasLimit: 0n,
109
+ verificationGasLimit: 150000n, // default verification gas. will add create2 cost (3200+200*length) if initCode exists
110
+ preVerificationGas: 21000n, // should also cover calldata cost.
111
+ maxFeePerGas: 0n,
112
112
  maxPriorityFeePerGas: 1e9,
113
113
  paymaster: ZeroAddress,
114
114
  paymasterData: "0x",
115
- paymasterVerificationGasLimit: 3e5,
115
+ paymasterVerificationGasLimit: 300000n,
116
116
  paymasterPostOpGasLimit: 0,
117
117
  signature: "0x",
118
118
  };
119
119
 
120
- function signUserOp(op, signer, entryPoint, chainId) {
121
- const message = getUserOpHash(op, entryPoint, chainId);
122
- const msg1 = Buffer.concat([
123
- Buffer.from("\x19Ethereum Signed Message:\n32", "ascii"),
124
- Buffer.from(arrayify(message)),
125
- ]);
126
-
127
- const sig = ecsign(keccak256_buffer(msg1), Buffer.from(arrayify(signer.privateKey)));
128
- // that's equivalent of: await signer.signMessage(message);
129
- // (but without "async"
130
- const signedMessage1 = toRpcSig(sig.v, sig.r, sig.s);
120
+ export async function signUserOp(op, signer, entryPoint, chainId) {
121
+ const userOpHash = getUserOpHash(op, entryPoint, chainId);
122
+ const signature = await signer.signMessage(ethers.getBytes(userOpHash));
131
123
  return {
132
124
  ...op,
133
- signature: signedMessage1,
125
+ signature,
134
126
  };
135
127
  }
136
128
 
137
- function fillUserOpDefaults(op, defaults = DefaultsForUserOp) {
129
+ export function fillUserOpDefaults(op, defaults = DefaultsForUserOp) {
138
130
  const partial = { ...op };
139
131
  // we want "item:undefined" to be used from defaults, and not override defaults, so we must explicitly
140
132
  // remove those so "merge" will succeed.
@@ -147,14 +139,3 @@ function fillUserOpDefaults(op, defaults = DefaultsForUserOp) {
147
139
  const filled = { ...defaults, ...partial };
148
140
  return filled;
149
141
  }
150
-
151
- module.exports = {
152
- packAccountGasLimits,
153
- packUserOp,
154
- packedUserOpAsArray,
155
- encodeUserOp,
156
- getUserOpHash,
157
- DefaultsForUserOp,
158
- signUserOp,
159
- fillUserOpDefaults,
160
- };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ensuro/account-abstraction",
3
3
  "description": "ERC-4337 related contracts by the Ensuro Team",
4
- "version": "0.0.5",
4
+ "version": "0.2.0",
5
5
  "files": [
6
6
  "**/*.sol",
7
7
  "/build",