@ensuro/account-abstraction 0.0.1 → 0.0.2

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.
@@ -2,6 +2,7 @@
2
2
  pragma solidity ^0.8.23;
3
3
 
4
4
  import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
5
+ import {Address} from "@openzeppelin/contracts/utils/Address.sol";
5
6
  import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
6
7
  import {BaseAccount} from "@account-abstraction/contracts/core/BaseAccount.sol";
7
8
  import {SIG_VALIDATION_SUCCESS, SIG_VALIDATION_FAILED} from "@account-abstraction/contracts/core/Helpers.sol";
@@ -48,7 +49,7 @@ contract AccessControlAccount is AccessControl, BaseAccount {
48
49
  */
49
50
  function execute(address dest, uint256 value, bytes calldata func) external {
50
51
  _requireFromEntryPointOrExecutor();
51
- _call(dest, value, func);
52
+ Address.functionCallWithValue(dest, func, value);
52
53
  }
53
54
 
54
55
  /**
@@ -63,11 +64,11 @@ contract AccessControlAccount is AccessControl, BaseAccount {
63
64
  if (dest.length != func.length || (value.length != 0 && value.length != func.length)) revert WrongArrayLength();
64
65
  if (value.length == 0) {
65
66
  for (uint256 i = 0; i < dest.length; i++) {
66
- _call(dest[i], 0, func[i]);
67
+ Address.functionCallWithValue(dest[i], func[i], 0);
67
68
  }
68
69
  } else {
69
70
  for (uint256 i = 0; i < dest.length; i++) {
70
- _call(dest[i], value[i], func[i]);
71
+ Address.functionCallWithValue(dest[i], func[i], value[i]);
71
72
  }
72
73
  }
73
74
  }
@@ -83,16 +84,6 @@ contract AccessControlAccount is AccessControl, BaseAccount {
83
84
  return SIG_VALIDATION_SUCCESS;
84
85
  }
85
86
 
86
- function _call(address target, uint256 value, bytes memory data) internal {
87
- (bool success, bytes memory result) = target.call{value: value}(data);
88
- if (!success) {
89
- // solhint-disable-next-line no-inline-assembly
90
- assembly {
91
- revert(add(result, 32), mload(result))
92
- }
93
- }
94
- }
95
-
96
87
  /**
97
88
  * check current account deposit in the entryPoint
98
89
  */
@@ -0,0 +1,127 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.23;
3
+
4
+ import {AccessManager} from "@openzeppelin/contracts/access/manager/AccessManager.sol";
5
+ import {Address} from "@openzeppelin/contracts/utils/Address.sol";
6
+ import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
7
+ import {BaseAccount} from "@account-abstraction/contracts/core/BaseAccount.sol";
8
+ import {SIG_VALIDATION_SUCCESS, SIG_VALIDATION_FAILED} from "@account-abstraction/contracts/core/Helpers.sol";
9
+ import {IEntryPoint} from "@account-abstraction/contracts/interfaces/IEntryPoint.sol";
10
+ import {PackedUserOperation} from "@account-abstraction/contracts/interfaces/PackedUserOperation.sol";
11
+ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
12
+ import {BytesLib} from "solidity-bytes-utils/contracts/BytesLib.sol";
13
+
14
+ contract AccessManagerAccount is AccessManager, BaseAccount {
15
+ using BytesLib for bytes;
16
+
17
+ IEntryPoint private immutable _entryPoint;
18
+
19
+ bytes4 private constant EXECUTE_SELECTOR = bytes4(keccak256("execute(address,uint256,bytes)"));
20
+
21
+ error OnlyExecuteAllowedFromEntryPoint(bytes4 receivedSelector);
22
+ error OnlyExternalTargets();
23
+ error DelayNotAllowed();
24
+
25
+ /// @inheritdoc BaseAccount
26
+ function entryPoint() public view virtual override returns (IEntryPoint) {
27
+ return _entryPoint;
28
+ }
29
+
30
+ // solhint-disable-next-line no-empty-blocks
31
+ receive() external payable {}
32
+
33
+ constructor(IEntryPoint anEntryPoint, address initialAdmin) AccessManager(initialAdmin) {
34
+ _entryPoint = anEntryPoint;
35
+ }
36
+
37
+ /**
38
+ * execute a transaction (called directly from owner, or by entryPoint)
39
+ * @param dest destination address to call
40
+ * @param value the value to pass in this call
41
+ * @param func the calldata to pass in this call
42
+ */
43
+ function execute(address dest, uint256 value, bytes calldata func) external {
44
+ _requireFromEntryPoint();
45
+ Address.functionCallWithValue(dest, func, value);
46
+ }
47
+
48
+ // hashOperation variant that receives bytes memory
49
+ function _hashOperation(address caller, address target, bytes memory data) internal pure returns (bytes32) {
50
+ return keccak256(abi.encode(caller, target, data));
51
+ }
52
+
53
+ function _checkAAExecuteCall(address signer, bytes calldata userOpCallData) internal returns (uint256) {
54
+ (address target, , bytes memory funcCall) = abi.decode(
55
+ userOpCallData[4:userOpCallData.length - 4],
56
+ (address, uint256, bytes)
57
+ );
58
+ (bool immediate, uint32 delay) = canCall(signer, target, bytes4(funcCall.toBytes32(0)));
59
+ if (immediate || delay == 0) return immediate ? SIG_VALIDATION_SUCCESS : SIG_VALIDATION_FAILED;
60
+ _consumeScheduledOp(_hashOperation(signer, target, funcCall));
61
+ return SIG_VALIDATION_SUCCESS;
62
+ }
63
+
64
+ /// implement template method of BaseAccount
65
+ function _validateSignature(
66
+ PackedUserOperation calldata userOp,
67
+ bytes32 userOpHash
68
+ ) internal virtual override returns (uint256 validationData) {
69
+ // First check the initial selector, from EntryPoint only execute and executeBatch are allowed
70
+ bytes4 selector = bytes4(userOp.callData[0:4]);
71
+ if (selector != EXECUTE_SELECTOR) revert OnlyExecuteAllowedFromEntryPoint(selector);
72
+ address target = abi.decode(userOp.callData[4:36], (address));
73
+ // Calls to address(this) are not allowed through AA. It might be possible to implement, but this
74
+ // complicates the testing and it might introduce security issues
75
+ if (target == address(this)) revert OnlyExternalTargets();
76
+ bytes32 hash = MessageHashUtils.toEthSignedMessageHash(userOpHash);
77
+ address recovered = ECDSA.recover(hash, userOp.signature);
78
+ // Check first the signer can call execute
79
+ if (!_checkCanCall(recovered, userOp.callData, false)) return SIG_VALIDATION_FAILED;
80
+ // Then check it can call the specific target/selector
81
+ return _checkAAExecuteCall(recovered, userOp.callData);
82
+ }
83
+
84
+ /**
85
+ * check current account deposit in the entryPoint
86
+ */
87
+ function getDeposit() public view returns (uint256) {
88
+ return entryPoint().balanceOf(address(this));
89
+ }
90
+
91
+ /**
92
+ * deposit more funds for this account in the entryPoint
93
+ */
94
+ function addDeposit() public payable {
95
+ entryPoint().depositTo{value: msg.value}(address(this));
96
+ }
97
+
98
+ /**
99
+ * @dev Adapted from AccessManaged._checkCanCall, checks a method can be called as if the AccessManagerAccount
100
+ * was an access managed contract (not validating against admin permissions)
101
+ */
102
+ function _checkCanCall(address caller, bytes calldata data, bool fail) internal returns (bool) {
103
+ (bool immediate, uint32 delay) = canCall(caller, address(this), bytes4(data[0:4]));
104
+ if (!immediate) {
105
+ if (delay > 0) {
106
+ revert DelayNotAllowed();
107
+ // Is not possible to handle scheduled operations, because when target=address(this), schedule
108
+ // doesn't work the same way, otherwise here we should do just
109
+ // _consumeScheduledOp(hashOperation(caller, address(this), data));
110
+ } else {
111
+ if (fail)
112
+ revert AccessManagerUnauthorizedAccount(caller, getTargetFunctionRole(address(this), bytes4(data[0:4])));
113
+ else return false;
114
+ }
115
+ }
116
+ return true;
117
+ }
118
+ /**
119
+ * withdraw value from the account's deposit
120
+ * @param withdrawAddress target to send to
121
+ * @param amount to withdraw
122
+ */
123
+ function withdrawDepositTo(address payable withdrawAddress, uint256 amount) public {
124
+ _checkCanCall(_msgSender(), _msgData(), true);
125
+ entryPoint().withdrawTo(withdrawAddress, amount);
126
+ }
127
+ }
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.1",
4
+ "version": "0.0.2",
5
5
  "files": [
6
6
  "**/*.sol",
7
7
  "/build",