generated from Hats-Protocol/hats-module-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHatControlledModule.sol
169 lines (143 loc) · 7.71 KB
/
HatControlledModule.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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
// import { console2 } from "forge-std/Test.sol"; // remove before deploy
import { HatsEligibilityModule, HatsModule } from "hats-module/HatsEligibilityModule.sol";
import { IHatsToggle } from "hats-module/HatsToggleModule.sol";
/*//////////////////////////////////////////////////////////////
CUSTOM ERRORS
//////////////////////////////////////////////////////////////*/
/// @notice Thrown when the caller is not wearing the {hatId} hat
error NotAuthorized();
/**
* @title HatControlledModule
* @author spengrah
* @author Haberdasher Labs
* @notice This module allows the wearer(s) of a given "controller" hat to serve as the eligibilty and/or toggle module
* for a different hat. It is compatible with module chaining.
* @dev This contract inherits from HatsModule, and is intended to be deployed as minimal proxy clone(s) via
* HatsModuleFactory. For this contract to be used, it must be set as either the eligibility or toggle module for
* another hat.
*/
contract HatControlledModule is HatsEligibilityModule, IHatsToggle {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
/// @notice Emitted when the wearer status is set
event WearerStatusSet(address wearer, uint256 hatId, bool eligible, bool standing);
/// @notice Emitted when the hat status is set
event HatStatusSet(uint256 hatId, bool active);
/*//////////////////////////////////////////////////////////////
DATA MODELS
//////////////////////////////////////////////////////////////*/
/**
* @notice Ineligibility and standing data for an account, defaulting to positives.
* @param ineligible Whether the account is ineligible to wear the hat. Defaults to eligible.
* @param badStanding Whether the account is in bad standing for the hat. Defaults to good standing.
*/
struct IneligibilityData {
bool ineligible;
bool badStanding;
}
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/**
* This contract is a clone with immutable args, which means that it is deployed with a set of
* immutable storage variables (ie constants). Accessing these constants is cheaper than accessing
* regular storage variables (such as those set on initialization of a typical EIP-1167 clone),
* but requires a slightly different approach since they are read from calldata instead of storage.
*
* Below is a table of constants and their location.
*
* For more, see here: https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args
*
* ----------------------------------------------------------------------+
* CLONE IMMUTABLE "STORAGE" |
* ----------------------------------------------------------------------|
* Offset | Constant | Type | Length | Source |
* ----------------------------------------------------------------------|
* 0 | IMPLEMENTATION | address | 20 | HatsModule |
* 20 | HATS | address | 20 | HatsModule |
* 40 | hatId | uint256 | 32 | HatsModule |
* 72 | CONTROLLER_HAT | uint256 | 32 | HatControlledModule |
* ----------------------------------------------------------------------+
*/
/// @notice The hat that controls this module instance and can set wearer and hat statuses
function CONTROLLER_HAT() public pure returns (uint256) {
return _getArgUint256(72);
}
/*//////////////////////////////////////////////////////////////
MUTABLE STORAGE
//////////////////////////////////////////////////////////////*/
/// @notice Ineligibility and standing data for a given hat and wearer, defaulting to eligible and good standing
mapping(uint256 hatId => mapping(address wearer => IneligibilityData ineligibility)) internal wearerIneligibility;
/// @notice Status of a given hat
mapping(uint256 hatId => bool inactive) internal hatInactivity;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
/// @notice Deploy the implementation contract and set its version
/// @dev This is only used to deploy the implementation contract, and should not be used to deploy clones
constructor(string memory _version) HatsModule(_version) { }
/*//////////////////////////////////////////////////////////////
INITIALIZER
//////////////////////////////////////////////////////////////*/
/// @inheritdoc HatsModule
function _setUp(bytes calldata) internal override {
// no initial values to set
}
/*//////////////////////////////////////////////////////////////
ELIGIBILITY FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Set the eligibility status of a `_hatId` for a `_wearer`, in this contract. When this contract is set as
* the eligibility module for that `hatId`, including as part of a module chain, Hats Protocol will pull this data
* when checking the wearer's eligibility.
* @dev Only callable by the wearer(s) of the {hatId} hat.
* @param _wearer The address to set the eligibility status for
* @param _hatId The hat to set the eligibility status for
* @param _eligible The new _wearer's eligibility, where TRUE = eligible
* @param _standing The new _wearer's standing, where TRUE = in good standing
*/
function setWearerStatus(address _wearer, uint256 _hatId, bool _eligible, bool _standing) public onlyController {
wearerIneligibility[_hatId][_wearer] = IneligibilityData(!_eligible, !_standing);
emit WearerStatusSet(_wearer, _hatId, _eligible, _standing);
}
/// @inheritdoc HatsEligibilityModule
function getWearerStatus(address _wearer, uint256 _hatId) public view override returns (bool eligible, bool standing) {
IneligibilityData memory data = wearerIneligibility[_hatId][_wearer];
// bad standing means not eligible, as well
if (data.badStanding) return (false, false);
// good standing but ineligible
if (data.ineligible) return (false, true);
// eligible and in good standing
return (true, true);
}
/*//////////////////////////////////////////////////////////////
TOGGLE FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Toggle the status of `_hatId` in this contract. When this contract is set as the toggle module for that
* `hatId`, including as part of a module chain, Hats Protocol will pull this data when checking the status of the
* hat.
* @dev Only callable by the wearer(s) of the {hatId} hat.
* @param _hatId The hat to set the status for
* @param _newStatus The new status, where TRUE = active
*/
function setHatStatus(uint256 _hatId, bool _newStatus) public onlyController {
hatInactivity[_hatId] = !_newStatus;
emit HatStatusSet(_hatId, _newStatus);
}
/// @inheritdoc IHatsToggle
function getHatStatus(uint256 _hatId) public view override returns (bool active) {
return !hatInactivity[_hatId];
}
/*//////////////////////////////////////////////////////////////
MODIFIERS
//////////////////////////////////////////////////////////////*/
/// @notice Reverts if the caller is not wearing the {hatId} hat
modifier onlyController() {
if (!HATS().isWearerOfHat(msg.sender, CONTROLLER_HAT())) revert NotAuthorized();
_;
}
}