-
Notifications
You must be signed in to change notification settings - Fork 34
/
TimeRangeModule.sol
139 lines (122 loc) · 6.18 KB
/
TimeRangeModule.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// This file is part of Modular Account.
//
// Copyright 2024 Alchemy Insights, Inc.
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
// Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with this program. If not, see
// <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.26;
import {IModule} from "@erc6900/reference-implementation/interfaces/IModule.sol";
import {IValidationHookModule} from "@erc6900/reference-implementation/interfaces/IValidationHookModule.sol";
import {_packValidationData} from "@eth-infinitism/account-abstraction/core/Helpers.sol";
import {PackedUserOperation} from "@eth-infinitism/account-abstraction/interfaces/PackedUserOperation.sol";
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
import {ModuleBase} from "../../modules/ModuleBase.sol";
/// @title Time Range Module
/// @author Alchemy
/// @notice This module allows for the setting and enforcement of time ranges for an entity ID.
/// - Enforcement relies on `block.timestamp`, either within this module for runtime validation, or by the
/// EntryPoint for user op validation.
/// - Time ranges are inclusive of both the beginning and ending timestamps.
/// - None of the functions are installed on the account. Account states are to be retrieved from this global
/// singleton directly.
/// - Uninstallation will NOT disable all installed hooks for an account. It only uninstalls hooks for the entity
/// ID that is passed in. Account must remove access for each entity ID if want to disable all hooks.
contract TimeRangeModule is IValidationHookModule, ModuleBase {
struct TimeRange {
uint48 validUntil;
uint48 validAfter;
}
mapping(uint32 entityId => mapping(address account => TimeRange)) public timeRanges;
event TimeRangeSet(uint32 indexed entityId, address indexed account, uint48 validUntil, uint48 validAfter);
error TimeRangeNotValid();
/// @inheritdoc IModule
/// @notice Initializes the module with the given time range for `msg.sender` with a given entity id.
/// @dev data is abi-encoded as (uint32 entityId, uint48 validUntil, uint48 validAfter)
function onInstall(bytes calldata data) external override {
(uint32 entityId, uint48 validUntil, uint48 validAfter) = abi.decode(data, (uint32, uint48, uint48));
setTimeRange(entityId, validUntil, validAfter);
}
/// @inheritdoc IModule
/// @notice Resets module state for `msg.sender` with the given entity id.
/// @dev data is abi-encoded as (uint32 entityId)
function onUninstall(bytes calldata data) external override {
uint32 entityId = abi.decode(data, (uint32));
delete timeRanges[entityId][msg.sender];
}
/// @inheritdoc IValidationHookModule
/// @notice Enforces the time range for a user op by returning the range in the ERC-4337 validation data.
function preUserOpValidationHook(uint32 entityId, PackedUserOperation calldata userOp, bytes32)
external
view
override
assertNoData(userOp.signature)
returns (uint256)
{
TimeRange memory timeRange = timeRanges[entityId][msg.sender];
return _packValidationData({
sigFailed: false,
validUntil: timeRange.validUntil,
validAfter: timeRange.validAfter
});
}
/// @inheritdoc IValidationHookModule
/// @notice Enforces the time range for a runtime validation by reverting if `block.timestamp` is not within
/// the range.
function preRuntimeValidationHook(uint32 entityId, address, uint256, bytes calldata, bytes calldata)
external
view
override
{
TimeRange memory timeRange = timeRanges[entityId][msg.sender];
uint48 validUntil = timeRange.validUntil == 0 ? type(uint48).max : timeRange.validUntil;
if (block.timestamp > validUntil || block.timestamp < timeRange.validAfter) {
revert TimeRangeNotValid();
}
}
/// @inheritdoc IValidationHookModule
/// @dev No-op, signature checking is not enforced to be within a time range, due to uncertainty about whether
/// the `timestamp` opcode is allowed during this operation. If the validation should not be allowed to
/// generate 1271 signatures, the flag `isSignatureValidation` should be set to false when calling
/// `installValidation`.
// solhint-disable-next-line no-empty-blocks
function preSignatureValidationHook(uint32, address, bytes32, bytes calldata) external pure override {}
/// @inheritdoc IModule
function moduleId() external pure returns (string memory) {
return "alchemy.time-range-module.1.0.0";
}
/// @notice Sets the time range for the sending account (`msg.sender`) and a given entity id.
/// @param entityId The entity id to set the time range for.
/// @param validUntil The timestamp until which the time range is valid, inclusive.
/// @param validAfter The timestamp after which the time range is valid, inclusive.
function setTimeRange(uint32 entityId, uint48 validUntil, uint48 validAfter) public {
if (validUntil == 0) {
if (validAfter == validUntil) {
revert TimeRangeNotValid();
}
} else if (validUntil <= validAfter) {
revert TimeRangeNotValid();
}
timeRanges[entityId][msg.sender] = TimeRange(validUntil, validAfter);
emit TimeRangeSet(entityId, msg.sender, validUntil, validAfter);
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(ModuleBase, IERC165)
returns (bool)
{
return interfaceId == type(IValidationHookModule).interfaceId || super.supportsInterface(interfaceId);
}
}