@erc6900/reference-implementation 0.8.1 → 1.0.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.

Potentially problematic release.


This version of @erc6900/reference-implementation might be problematic. Click here for more details.

Files changed (39) hide show
  1. package/package.json +5 -26
  2. package/preinstall.js +110 -0
  3. package/LICENSE +0 -21
  4. package/README.md +0 -76
  5. package/src/account/AccountExecutor.sol +0 -20
  6. package/src/account/AccountFactory.sol +0 -126
  7. package/src/account/AccountStorage.sol +0 -99
  8. package/src/account/AccountStorageInitializable.sol +0 -67
  9. package/src/account/ModularAccountView.sol +0 -66
  10. package/src/account/ModuleManagerInternals.sol +0 -325
  11. package/src/account/ReferenceModularAccount.sol +0 -769
  12. package/src/account/SemiModularAccount.sol +0 -214
  13. package/src/account/SemiModularAccount7702.sol +0 -38
  14. package/src/helpers/CollectReturnData.sol +0 -14
  15. package/src/helpers/Constants.sol +0 -11
  16. package/src/helpers/EmptyCalldataSlice.sol +0 -12
  17. package/src/helpers/ValidationResHelpers.sol +0 -50
  18. package/src/interfaces/IERC6900Account.sol +0 -127
  19. package/src/interfaces/IERC6900AccountView.sol +0 -52
  20. package/src/interfaces/IERC6900ExecutionHookModule.sol +0 -26
  21. package/src/interfaces/IERC6900ExecutionModule.sol +0 -37
  22. package/src/interfaces/IERC6900Module.sol +0 -24
  23. package/src/interfaces/IERC6900ValidationHookModule.sol +0 -46
  24. package/src/interfaces/IERC6900ValidationModule.sol +0 -53
  25. package/src/libraries/HookConfigLib.sol +0 -135
  26. package/src/libraries/KnownSelectorsLib.sol +0 -65
  27. package/src/libraries/ModuleEntityLib.sol +0 -37
  28. package/src/libraries/ModuleStorageLib.sol +0 -62
  29. package/src/libraries/SparseCalldataSegmentLib.sol +0 -95
  30. package/src/libraries/ValidationConfigLib.sol +0 -119
  31. package/src/modules/BaseModule.sol +0 -54
  32. package/src/modules/ModuleEIP712.sol +0 -29
  33. package/src/modules/ReplaySafeWrapper.sol +0 -38
  34. package/src/modules/TokenReceiverModule.sol +0 -87
  35. package/src/modules/permissions/AllowlistModule.sol +0 -156
  36. package/src/modules/permissions/ERC20TokenLimitModule.sol +0 -145
  37. package/src/modules/permissions/NativeTokenLimitModule.sol +0 -154
  38. package/src/modules/validation/ISingleSignerValidationModule.sol +0 -22
  39. package/src/modules/validation/SingleSignerValidationModule.sol +0 -138
@@ -1,38 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.20;
3
-
4
- import {ModuleEIP712} from "./ModuleEIP712.sol";
5
-
6
- import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
7
-
8
- // A contract mixin for modules that wish to use EIP-712 to wrap the hashes sent to the EIP-1271 function
9
- // `isValidSignature`.
10
- // This makes signatures generated by owners of contract accounts non-replayable across multiple accounts owned by
11
- // the same owner.
12
- abstract contract ReplaySafeWrapper is ModuleEIP712 {
13
- // keccak256("ReplaySafeHash(bytes32 hash)")
14
- bytes32 private constant _REPLAY_SAFE_HASH_TYPEHASH =
15
- 0x294a8735843d4afb4f017c76faf3b7731def145ed0025fc9b1d5ce30adf113ff;
16
-
17
- /// @notice Wraps a hash in an EIP-712 envelope to prevent cross-account replay attacks.
18
- /// Uses the ModuleEIP712 domain separator, which includes the chainId, module address, and account address.
19
- /// @param account The account that will validate the message hash.
20
- /// @param hash The hash to wrap.
21
- /// @return The the replay-safe hash, computed by wrapping the input hash in an EIP-712 struct.
22
- function replaySafeHash(address account, bytes32 hash) public view virtual returns (bytes32) {
23
- return MessageHashUtils.toTypedDataHash({
24
- domainSeparator: _domainSeparator(account),
25
- structHash: _hashStruct(hash)
26
- });
27
- }
28
-
29
- function _hashStruct(bytes32 hash) internal pure virtual returns (bytes32) {
30
- bytes32 res;
31
- assembly ("memory-safe") {
32
- mstore(0x00, _REPLAY_SAFE_HASH_TYPEHASH)
33
- mstore(0x20, hash)
34
- res := keccak256(0x00, 0x40)
35
- }
36
- return res;
37
- }
38
- }
@@ -1,87 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.20;
3
-
4
- import {IERC1155Receiver} from "@openzeppelin/contracts/interfaces/IERC1155Receiver.sol";
5
- import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
6
-
7
- import {ExecutionManifest, ManifestExecutionFunction} from "../interfaces/IERC6900ExecutionModule.sol";
8
- import {ExecutionManifest, IERC6900ExecutionModule} from "../interfaces/IERC6900ExecutionModule.sol";
9
- import {IERC6900Module} from "../interfaces/IERC6900Module.sol";
10
- import {BaseModule} from "./BaseModule.sol";
11
-
12
- /// @title Token Receiver Module
13
- /// @author ERC-6900 Authors
14
- /// @notice This module allows modular accounts to receive various types of tokens by implementing
15
- /// required token receiver interfaces.
16
- contract TokenReceiverModule is BaseModule, IERC6900ExecutionModule, IERC721Receiver, IERC1155Receiver {
17
- // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
18
- // ┃ Execution functions ┃
19
- // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
20
-
21
- function onERC721Received(address, address, uint256, bytes calldata) external pure override returns (bytes4) {
22
- return IERC721Receiver.onERC721Received.selector;
23
- }
24
-
25
- function onERC1155Received(address, address, uint256, uint256, bytes calldata)
26
- external
27
- pure
28
- override
29
- returns (bytes4)
30
- {
31
- return IERC1155Receiver.onERC1155Received.selector;
32
- }
33
-
34
- function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata)
35
- external
36
- pure
37
- override
38
- returns (bytes4)
39
- {
40
- return IERC1155Receiver.onERC1155BatchReceived.selector;
41
- }
42
-
43
- // ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
44
- // ┃ Module interface functions ┃
45
- // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
46
-
47
- /// @inheritdoc IERC6900Module
48
- // solhint-disable-next-line no-empty-blocks
49
- function onInstall(bytes calldata) external pure override {}
50
-
51
- /// @inheritdoc IERC6900Module
52
- // solhint-disable-next-line no-empty-blocks
53
- function onUninstall(bytes calldata) external pure override {}
54
-
55
- /// @inheritdoc IERC6900ExecutionModule
56
- function executionManifest() external pure override returns (ExecutionManifest memory) {
57
- ExecutionManifest memory manifest;
58
-
59
- manifest.executionFunctions = new ManifestExecutionFunction[](3);
60
- manifest.executionFunctions[0] = ManifestExecutionFunction({
61
- executionSelector: this.onERC721Received.selector,
62
- skipRuntimeValidation: true,
63
- allowGlobalValidation: false
64
- });
65
- manifest.executionFunctions[1] = ManifestExecutionFunction({
66
- executionSelector: this.onERC1155Received.selector,
67
- skipRuntimeValidation: true,
68
- allowGlobalValidation: false
69
- });
70
- manifest.executionFunctions[2] = ManifestExecutionFunction({
71
- executionSelector: this.onERC1155BatchReceived.selector,
72
- skipRuntimeValidation: true,
73
- allowGlobalValidation: false
74
- });
75
-
76
- manifest.interfaceIds = new bytes4[](2);
77
- manifest.interfaceIds[0] = type(IERC721Receiver).interfaceId;
78
- manifest.interfaceIds[1] = type(IERC1155Receiver).interfaceId;
79
-
80
- return manifest;
81
- }
82
-
83
- /// @inheritdoc IERC6900Module
84
- function moduleId() external pure returns (string memory) {
85
- return "erc6900.token-receiver-module.1.0.0";
86
- }
87
- }
@@ -1,156 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.20;
3
-
4
- import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
5
- import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
6
-
7
- import {IERC6900Module} from "../../interfaces/IERC6900Module.sol";
8
-
9
- import {Call, IERC6900Account} from "../../interfaces/IERC6900Account.sol";
10
- import {IERC6900ValidationHookModule} from "../../interfaces/IERC6900ValidationHookModule.sol";
11
- import {BaseModule} from "../../modules/BaseModule.sol";
12
-
13
- contract AllowlistModule is IERC6900ValidationHookModule, BaseModule {
14
- struct AllowlistInit {
15
- address target;
16
- bool hasSelectorAllowlist;
17
- bytes4[] selectors;
18
- }
19
-
20
- struct AllowlistEntry {
21
- bool allowed;
22
- bool hasSelectorAllowlist;
23
- }
24
-
25
- mapping(uint32 entityId => mapping(address target => mapping(address account => AllowlistEntry))) public
26
- targetAllowlist;
27
- mapping(
28
- uint32 entityId => mapping(address target => mapping(bytes4 selector => mapping(address account => bool)))
29
- ) public selectorAllowlist;
30
-
31
- event AllowlistTargetUpdated(
32
- uint32 indexed entityId, address indexed account, address indexed target, AllowlistEntry entry
33
- );
34
- event AllowlistSelectorUpdated(
35
- uint32 indexed entityId, address indexed account, bytes24 indexed targetAndSelector, bool allowed
36
- );
37
-
38
- error TargetNotAllowed();
39
- error SelectorNotAllowed();
40
- error NoSelectorSpecified();
41
-
42
- function onInstall(bytes calldata data) external override {
43
- (uint32 entityId, AllowlistInit[] memory init) = abi.decode(data, (uint32, AllowlistInit[]));
44
-
45
- for (uint256 i = 0; i < init.length; i++) {
46
- setAllowlistTarget(entityId, init[i].target, true, init[i].hasSelectorAllowlist);
47
-
48
- if (init[i].hasSelectorAllowlist) {
49
- for (uint256 j = 0; j < init[i].selectors.length; j++) {
50
- setAllowlistSelector(entityId, init[i].target, init[i].selectors[j], true);
51
- }
52
- }
53
- }
54
- }
55
-
56
- function onUninstall(bytes calldata data) external override {
57
- (uint32 entityId, AllowlistInit[] memory init) = abi.decode(data, (uint32, AllowlistInit[]));
58
-
59
- for (uint256 i = 0; i < init.length; i++) {
60
- setAllowlistTarget(entityId, init[i].target, false, false);
61
-
62
- if (init[i].hasSelectorAllowlist) {
63
- for (uint256 j = 0; j < init[i].selectors.length; j++) {
64
- setAllowlistSelector(entityId, init[i].target, init[i].selectors[j], false);
65
- }
66
- }
67
- }
68
- }
69
-
70
- function preUserOpValidationHook(uint32 entityId, PackedUserOperation calldata userOp, bytes32)
71
- external
72
- view
73
- override
74
- returns (uint256)
75
- {
76
- checkAllowlistCalldata(entityId, userOp.callData);
77
- return 0;
78
- }
79
-
80
- function preRuntimeValidationHook(uint32 entityId, address, uint256, bytes calldata data, bytes calldata)
81
- external
82
- view
83
- override
84
- {
85
- checkAllowlistCalldata(entityId, data);
86
- return;
87
- }
88
-
89
- // solhint-disable-next-line no-empty-blocks
90
- function preSignatureValidationHook(uint32, address, bytes32, bytes calldata) external pure override {}
91
-
92
- /// @inheritdoc IERC6900Module
93
- function moduleId() external pure returns (string memory) {
94
- return "erc6900.allowlist-module.0.0.1";
95
- }
96
-
97
- function setAllowlistTarget(uint32 entityId, address target, bool allowed, bool hasSelectorAllowlist) public {
98
- AllowlistEntry memory entry = AllowlistEntry(allowed, hasSelectorAllowlist);
99
- targetAllowlist[entityId][target][msg.sender] = entry;
100
- emit AllowlistTargetUpdated(entityId, msg.sender, target, entry);
101
- }
102
-
103
- function setAllowlistSelector(uint32 entityId, address target, bytes4 selector, bool allowed) public {
104
- selectorAllowlist[entityId][target][selector][msg.sender] = allowed;
105
- bytes24 targetAndSelector = bytes24(bytes24(bytes20(target)) | (bytes24(selector) >> 160));
106
- emit AllowlistSelectorUpdated(entityId, msg.sender, targetAndSelector, allowed);
107
- }
108
-
109
- function checkAllowlistCalldata(uint32 entityId, bytes calldata callData) public view {
110
- if (bytes4(callData[:4]) == IERC6900Account.execute.selector) {
111
- (address target,, bytes memory data) = abi.decode(callData[4:], (address, uint256, bytes));
112
- _checkCallPermission(entityId, msg.sender, target, data);
113
- } else if (bytes4(callData[:4]) == IERC6900Account.executeBatch.selector) {
114
- Call[] memory calls = abi.decode(callData[4:], (Call[]));
115
-
116
- for (uint256 i = 0; i < calls.length; i++) {
117
- _checkCallPermission(entityId, msg.sender, calls[i].target, calls[i].data);
118
- }
119
- }
120
- }
121
-
122
- function supportsInterface(bytes4 interfaceId)
123
- public
124
- view
125
- virtual
126
- override(BaseModule, IERC165)
127
- returns (bool)
128
- {
129
- return
130
- interfaceId == type(IERC6900ValidationHookModule).interfaceId || super.supportsInterface(interfaceId);
131
- }
132
-
133
- function _checkCallPermission(uint32 entityId, address account, address target, bytes memory data)
134
- internal
135
- view
136
- {
137
- AllowlistEntry storage entry = targetAllowlist[entityId][target][account];
138
- (bool allowed, bool hasSelectorAllowlist) = (entry.allowed, entry.hasSelectorAllowlist);
139
-
140
- if (!allowed) {
141
- revert TargetNotAllowed();
142
- }
143
-
144
- if (hasSelectorAllowlist) {
145
- if (data.length < 4) {
146
- revert NoSelectorSpecified();
147
- }
148
-
149
- bytes4 selector = bytes4(data);
150
-
151
- if (!selectorAllowlist[entityId][target][selector][account]) {
152
- revert SelectorNotAllowed();
153
- }
154
- }
155
- }
156
- }
@@ -1,145 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.20;
3
-
4
- import {UserOperationLib} from "@eth-infinitism/account-abstraction/core/UserOperationLib.sol";
5
- import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
6
- import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
7
-
8
- import {Call, IERC6900Account} from "../../interfaces/IERC6900Account.sol";
9
-
10
- import {IERC6900ExecutionHookModule} from "../../interfaces/IERC6900ExecutionHookModule.sol";
11
- import {IERC6900Module} from "../../interfaces/IERC6900Module.sol";
12
-
13
- import {BaseModule, IERC165} from "../BaseModule.sol";
14
-
15
- /// @title ERC20 Token Limit Module
16
- /// @author ERC-6900 Authors
17
- /// @notice This module supports an ERC20 token spend limit. This should be combined with a contract whitelist
18
- /// module to make sure that token transfers not tracked by the module don't happen.
19
- /// Note: this module is opinionated on what selectors can be called for token contracts to guard against weird
20
- /// edge cases like DAI. You wouldn't be able to use uni v2 pairs directly as the pair contract is also the LP
21
- /// token contract
22
- contract ERC20TokenLimitModule is BaseModule, IERC6900ExecutionHookModule {
23
- using UserOperationLib for PackedUserOperation;
24
-
25
- struct ERC20SpendLimit {
26
- address token;
27
- uint256 limit;
28
- }
29
-
30
- struct SpendLimit {
31
- bool hasLimit;
32
- uint256 limit;
33
- }
34
-
35
- mapping(uint32 entityId => mapping(address token => mapping(address account => SpendLimit))) public limits;
36
-
37
- error ERC20NotAllowed(address);
38
- error ExceededTokenLimit();
39
- error InvalidCalldataLength();
40
- error SelectorNotAllowed();
41
- error SpendingRequestNotAllowed(bytes4);
42
-
43
- /// @inheritdoc IERC6900ExecutionHookModule
44
- function preExecutionHook(uint32 entityId, address, uint256, bytes calldata data)
45
- external
46
- override
47
- returns (bytes memory)
48
- {
49
- (bytes4 selector, bytes memory callData) = _getSelectorAndCalldata(data);
50
-
51
- if (selector == IERC6900Account.execute.selector) {
52
- // when calling execute or ERC20 functions directly
53
- (address token,, bytes memory innerCalldata) = abi.decode(callData, (address, uint256, bytes));
54
- _decrementLimitIfApplies(entityId, token, innerCalldata);
55
- } else if (selector == IERC6900Account.executeBatch.selector) {
56
- Call[] memory calls = abi.decode(callData, (Call[]));
57
- for (uint256 i = 0; i < calls.length; i++) {
58
- _decrementLimitIfApplies(entityId, calls[i].target, calls[i].data);
59
- }
60
- } else {
61
- revert SpendingRequestNotAllowed(selector);
62
- }
63
- return "";
64
- }
65
-
66
- /// @inheritdoc IERC6900Module
67
- /// @param data should be encoded with the entityId of the validation and a list of ERC20 spend limits
68
- function onInstall(bytes calldata data) external override {
69
- (uint32 entityId, ERC20SpendLimit[] memory spendLimits) = abi.decode(data, (uint32, ERC20SpendLimit[]));
70
-
71
- for (uint8 i = 0; i < spendLimits.length; i++) {
72
- address token = spendLimits[i].token;
73
- updateLimits(entityId, token, true, spendLimits[i].limit);
74
- }
75
- }
76
-
77
- /// @inheritdoc IERC6900Module
78
- /// @notice uninstall this module can only clear limit for one token of one entity. To clear all limits, users
79
- /// are recommended to use updateLimit for each token and entityId.
80
- /// @param data should be encoded with the entityId of the validation and the token address to be uninstalled
81
- function onUninstall(bytes calldata data) external override {
82
- (address token, uint32 entityId) = abi.decode(data, (address, uint32));
83
- delete limits[entityId][token][msg.sender];
84
- }
85
-
86
- /// @inheritdoc IERC6900ExecutionHookModule
87
- function postExecutionHook(uint32, bytes calldata) external pure override {
88
- revert NotImplemented();
89
- }
90
-
91
- /// @inheritdoc IERC6900Module
92
- function moduleId() external pure returns (string memory) {
93
- return "erc6900.erc20-token-limit-module.1.0.0";
94
- }
95
-
96
- /// @notice Update the token limit of a validation
97
- /// @param entityId The validation entityId to update
98
- /// @param token The token address whose limit will be updated
99
- /// @param newLimit The new limit of the token for the validation
100
- function updateLimits(uint32 entityId, address token, bool hasLimit, uint256 newLimit) public {
101
- if (token == address(0)) {
102
- revert ERC20NotAllowed(address(0));
103
- }
104
- limits[entityId][token][msg.sender] = SpendLimit({hasLimit: hasLimit, limit: newLimit});
105
- }
106
-
107
- /// @inheritdoc BaseModule
108
- function supportsInterface(bytes4 interfaceId) public view override(BaseModule, IERC165) returns (bool) {
109
- return interfaceId == type(IERC6900ExecutionHookModule).interfaceId || super.supportsInterface(interfaceId);
110
- }
111
-
112
- function _decrementLimitIfApplies(uint32 entityId, address token, bytes memory innerCalldata) internal {
113
- SpendLimit storage spendLimit = limits[entityId][token][msg.sender];
114
-
115
- if (!spendLimit.hasLimit) {
116
- return;
117
- }
118
-
119
- if (innerCalldata.length < 68) {
120
- revert InvalidCalldataLength();
121
- }
122
-
123
- bytes4 selector;
124
- uint256 spend;
125
- assembly ("memory-safe") {
126
- selector := mload(add(innerCalldata, 32)) // 0:32 is arr len, 32:36 is selector
127
- spend := mload(add(innerCalldata, 68)) // 36:68 is recipient, 68:100 is spend
128
- }
129
- if (_isAllowedERC20Function(selector)) {
130
- uint256 limit = spendLimit.limit;
131
- if (spend > limit) {
132
- revert ExceededTokenLimit();
133
- }
134
- unchecked {
135
- spendLimit.limit = limit - spend;
136
- }
137
- } else {
138
- revert SelectorNotAllowed();
139
- }
140
- }
141
-
142
- function _isAllowedERC20Function(bytes4 selector) internal pure returns (bool) {
143
- return selector == IERC20.transfer.selector || selector == IERC20.approve.selector;
144
- }
145
- }
@@ -1,154 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.20;
3
-
4
- import {UserOperationLib} from "@eth-infinitism/account-abstraction/core/UserOperationLib.sol";
5
- import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
6
- import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
7
-
8
- import {Call, IERC6900Account} from "../../interfaces/IERC6900Account.sol";
9
-
10
- import {IERC6900ExecutionHookModule} from "../../interfaces/IERC6900ExecutionHookModule.sol";
11
- import {IERC6900Module} from "../../interfaces/IERC6900Module.sol";
12
- import {IERC6900ValidationHookModule} from "../../interfaces/IERC6900ValidationHookModule.sol";
13
- import {BaseModule, IERC165} from "../BaseModule.sol";
14
-
15
- /// @title Native Token Limit Module
16
- /// @author ERC-6900 Authors
17
- /// @notice This module supports a single total native token spend limit.
18
- /// It tracks a total spend limit across UserOperation gas limits and native token transfers.
19
- /// If a non whitelisted paymaster is used, UO gas would not cause the limit to decrease.
20
- /// If a whitelisted paymaster is used, gas is still counted towards the limit
21
- contract NativeTokenLimitModule is BaseModule, IERC6900ExecutionHookModule, IERC6900ValidationHookModule {
22
- using UserOperationLib for PackedUserOperation;
23
- using EnumerableSet for EnumerableSet.Bytes32Set;
24
-
25
- mapping(uint256 funcIds => mapping(address account => uint256 limit)) public limits;
26
- // Accounts should add paymasters that still use the accounts tokens here
27
- // E.g. ERC20 paymasters that pull funds from the account
28
- mapping(address paymaster => mapping(address account => bool allowed)) public specialPaymasters;
29
-
30
- error ExceededNativeTokenLimit();
31
- error ExceededNumberOfEntities();
32
-
33
- function updateLimits(uint32 entityId, uint256 newLimit) external {
34
- limits[entityId][msg.sender] = newLimit;
35
- }
36
-
37
- function updateSpecialPaymaster(address paymaster, bool allowed) external {
38
- specialPaymasters[paymaster][msg.sender] = allowed;
39
- }
40
-
41
- /// @inheritdoc IERC6900ValidationHookModule
42
- function preUserOpValidationHook(uint32 entityId, PackedUserOperation calldata userOp, bytes32)
43
- external
44
- returns (uint256)
45
- {
46
- // Decrease limit only if no paymaster is used, or if its a special paymaster
47
- if (
48
- userOp.paymasterAndData.length == 0
49
- || specialPaymasters[address(bytes20(userOp.paymasterAndData[:20]))][msg.sender]
50
- ) {
51
- uint256 vgl = UserOperationLib.unpackVerificationGasLimit(userOp);
52
- uint256 cgl = UserOperationLib.unpackCallGasLimit(userOp);
53
- uint256 pvgl;
54
- uint256 ppogl;
55
- if (userOp.paymasterAndData.length > 0) {
56
- // Can skip the EP length check here since it would have reverted there if it was invalid
57
- (, pvgl, ppogl) = UserOperationLib.unpackPaymasterStaticFields(userOp.paymasterAndData);
58
- }
59
- uint256 totalGas = userOp.preVerificationGas + vgl + cgl + pvgl + ppogl;
60
- uint256 usage = totalGas * UserOperationLib.unpackMaxFeePerGas(userOp);
61
-
62
- uint256 limit = limits[entityId][msg.sender];
63
- if (usage > limit) {
64
- revert ExceededNativeTokenLimit();
65
- }
66
- limits[entityId][msg.sender] = limit - usage;
67
- }
68
- return 0;
69
- }
70
-
71
- /// @inheritdoc IERC6900ExecutionHookModule
72
- function preExecutionHook(uint32 entityId, address, uint256, bytes calldata data)
73
- external
74
- override
75
- returns (bytes memory)
76
- {
77
- return _checkAndDecrementLimit(entityId, data);
78
- }
79
-
80
- /// @inheritdoc IERC6900Module
81
- function onInstall(bytes calldata data) external override {
82
- (uint32 startEntityId, uint256[] memory spendLimits) = abi.decode(data, (uint32, uint256[]));
83
-
84
- if (startEntityId + spendLimits.length > type(uint32).max) {
85
- revert ExceededNumberOfEntities();
86
- }
87
-
88
- for (uint256 i = 0; i < spendLimits.length; i++) {
89
- limits[i + startEntityId][msg.sender] = spendLimits[i];
90
- }
91
- }
92
-
93
- /// @inheritdoc IERC6900Module
94
- function onUninstall(bytes calldata data) external override {
95
- // This is the highest entityId that's being used by the account
96
- uint32 entityId = abi.decode(data, (uint32));
97
- for (uint256 i = 0; i < entityId; i++) {
98
- delete limits[i][msg.sender];
99
- }
100
- }
101
-
102
- /// @inheritdoc IERC6900ExecutionHookModule
103
- function postExecutionHook(uint32, bytes calldata) external pure override {
104
- revert NotImplemented();
105
- }
106
-
107
- // No implementation, no revert
108
- // Runtime spends no account gas, and we check native token spend limits in exec hooks
109
- function preRuntimeValidationHook(uint32, address, uint256, bytes calldata, bytes calldata)
110
- external
111
- pure
112
- override
113
- {} // solhint-disable-line no-empty-blocks
114
-
115
- // solhint-disable-next-line no-empty-blocks
116
- function preSignatureValidationHook(uint32, address, bytes32, bytes calldata) external pure override {}
117
-
118
- /// @inheritdoc IERC6900Module
119
- function moduleId() external pure returns (string memory) {
120
- return "erc6900.native-token-limit-module.1.0.0";
121
- }
122
-
123
- // ┏━━━━━━━━━━━━━━━┓
124
- // ┃ EIP-165 ┃
125
- // ┗━━━━━━━━━━━━━━━┛
126
-
127
- /// @inheritdoc BaseModule
128
- function supportsInterface(bytes4 interfaceId) public view override(BaseModule, IERC165) returns (bool) {
129
- return interfaceId == type(IERC6900ExecutionHookModule).interfaceId || super.supportsInterface(interfaceId);
130
- }
131
-
132
- function _checkAndDecrementLimit(uint32 entityId, bytes calldata data) internal returns (bytes memory) {
133
- (bytes4 selector, bytes memory callData) = _getSelectorAndCalldata(data);
134
-
135
- uint256 value;
136
- // Get value being sent
137
- if (selector == IERC6900Account.execute.selector) {
138
- (, value) = abi.decode(callData, (address, uint256));
139
- } else if (selector == IERC6900Account.executeBatch.selector) {
140
- Call[] memory calls = abi.decode(callData, (Call[]));
141
- for (uint256 i = 0; i < calls.length; i++) {
142
- value += calls[i].value;
143
- }
144
- }
145
-
146
- uint256 limit = limits[entityId][msg.sender];
147
- if (value > limit) {
148
- revert ExceededNativeTokenLimit();
149
- }
150
- limits[entityId][msg.sender] = limit - value;
151
-
152
- return "";
153
- }
154
- }
@@ -1,22 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.20;
3
-
4
- import {IERC6900ValidationModule} from "../../interfaces/IERC6900ValidationModule.sol";
5
-
6
- interface ISingleSignerValidationModule is IERC6900ValidationModule {
7
- /// @notice This event is emitted when Signer of the account's validation changes.
8
- /// @param account The account whose validation Signer changed.
9
- /// @param entityId The entityId for the account and the signer.
10
- /// @param newSigner The address of the new signer.
11
- /// @param previousSigner The address of the previous signer.
12
- event SignerTransferred(
13
- address indexed account, uint32 indexed entityId, address indexed newSigner, address previousSigner
14
- ) anonymous;
15
-
16
- error NotAuthorized();
17
-
18
- /// @notice Transfer Signer of the account's validation to `newSigner`.
19
- /// @param entityId The entityId for the account and the signer.
20
- /// @param newSigner The address of the new signer.
21
- function transferSigner(uint32 entityId, address newSigner) external;
22
- }