lens-modules 1.0.2 → 1.0.3

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,76 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.19;
3
+
4
+ import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
5
+
6
+ interface IToken {
7
+ /**
8
+ * @dev Returns the amount of tokens owned by `account`.
9
+ */
10
+ function balanceOf(address account) external view returns (uint256);
11
+ }
12
+
13
+ library TokenGateLib {
14
+ uint256 internal constant UINT256_BYTES = 32;
15
+
16
+ /**
17
+ * @notice A struct containing the necessary data to execute TokenGated actions.
18
+ *
19
+ * @param tokenAddress The address of ERC20/ERC721 token used for gating the action
20
+ * @param minThreshold The minimum balance threshold of the gated token required to execute an action
21
+ */
22
+ struct GateParams {
23
+ address tokenAddress;
24
+ uint256 minThreshold;
25
+ }
26
+
27
+ error GateParamsInvalid();
28
+ error NotEnoughBalance();
29
+
30
+ /**
31
+ * @dev Validates the gate parameters.
32
+ * @dev Checks if the tokenAddress is a valid ERC20/ERC721 token and minThreshold is a valid uint256.
33
+ */
34
+ function validateGateParams(GateParams memory gateParams) internal view {
35
+ // Checking if the tokenAddress resembles ERC20/ERC721 token (by calling balanceOf() function)
36
+ (bool success, bytes memory result) = gateParams
37
+ .tokenAddress
38
+ .staticcall(
39
+ abi.encodeWithSignature("balanceOf(address)", address(this))
40
+ );
41
+ // We don't check if the contract exists cause we expect the return data anyway
42
+ if (
43
+ gateParams.minThreshold == 0 ||
44
+ !success ||
45
+ result.length != UINT256_BYTES
46
+ ) revert GateParamsInvalid();
47
+ }
48
+
49
+ /**
50
+ * @dev Validates the profile's owner balance of gating token.
51
+ * @dev Can work with both ERC20 and ERC721 as both interfaces support balanceOf() call
52
+ */
53
+ function validateTokenBalance(
54
+ GateParams memory gateParams,
55
+ address profileOwner
56
+ ) internal view {
57
+ if (
58
+ IToken(gateParams.tokenAddress).balanceOf(profileOwner) <
59
+ gateParams.minThreshold
60
+ ) {
61
+ revert NotEnoughBalance();
62
+ }
63
+ }
64
+
65
+ /**
66
+ * @dev Validates the profile's owner balance of gating token.
67
+ * @dev Can work with both ERC20 and ERC721 as both interfaces support balanceOf() call
68
+ */
69
+ function validateTokenBalance(
70
+ address hub,
71
+ GateParams memory gateParams,
72
+ uint256 profileId
73
+ ) internal view {
74
+ validateTokenBalance(gateParams, IERC721(hub).ownerOf(profileId));
75
+ }
76
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lens-modules",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "All interfaces and base classes from Lens core used for creating follow, collect, and open action modules",
5
5
  "files": [
6
6
  "/contracts/**/*.sol"
@@ -1,284 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
-
3
- pragma solidity ^0.8.15;
4
-
5
- import {ValidationLib} from './ValidationLib.sol';
6
- import {Types} from './constants/Types.sol';
7
- import {Errors} from './constants/Errors.sol';
8
- import {Events} from './constants/Events.sol';
9
- import {StorageLib} from './StorageLib.sol';
10
- import {IFollowModule} from '../interfaces/IFollowModule.sol';
11
- import {IFollowNFT} from '../interfaces/IFollowNFT.sol';
12
- import {IModuleRegistry} from '../interfaces/IModuleRegistry.sol';
13
- import {ILensHub} from '../interfaces/ILensHub.sol';
14
-
15
- library ProfileLib {
16
- function MODULE_REGISTRY() internal view returns (IModuleRegistry) {
17
- return IModuleRegistry(ILensHub(address(this)).getModuleRegistry());
18
- }
19
-
20
- function ownerOf(uint256 profileId) internal view returns (address) {
21
- address profileOwner = StorageLib.getTokenData(profileId).owner;
22
- if (profileOwner == address(0)) {
23
- revert Errors.TokenDoesNotExist();
24
- }
25
- return profileOwner;
26
- }
27
-
28
- function exists(uint256 profileId) internal view returns (bool) {
29
- return StorageLib.getTokenData(profileId).owner != address(0);
30
- }
31
-
32
- /**
33
- * @notice Creates a profile with the given parameters to the given address. Minting happens
34
- * in the hub.
35
- *
36
- * @param createProfileParams The CreateProfileParams struct containing the following parameters:
37
- * to: The address receiving the profile.
38
- * followModule: The follow module to use, can be the zero address.
39
- * followModuleInitData: The follow module initialization data, if any
40
- * @param profileId The profile ID to associate with this profile NFT (token ID).
41
- */
42
- function createProfile(Types.CreateProfileParams calldata createProfileParams, uint256 profileId) external {
43
- emit Events.ProfileCreated(profileId, msg.sender, createProfileParams.to, block.timestamp);
44
- emit Events.DelegatedExecutorsConfigApplied(profileId, 0, block.timestamp);
45
- _setFollowModule(
46
- profileId,
47
- createProfileParams.followModule,
48
- createProfileParams.followModuleInitData,
49
- msg.sender // Sender accounts for any initialization requirements (e.g. pay fees, stake asset, etc.).
50
- );
51
- }
52
-
53
- /**
54
- * @notice Sets the follow module for a given profile.
55
- *
56
- * @param profileId The profile ID to set the follow module for.
57
- * @param followModule The follow module to set for the given profile, if any.
58
- * @param followModuleInitData The data to pass to the follow module for profile initialization.
59
- */
60
- function setFollowModule(
61
- uint256 profileId,
62
- address followModule,
63
- bytes calldata followModuleInitData,
64
- address transactionExecutor
65
- ) external {
66
- _setFollowModule(profileId, followModule, followModuleInitData, transactionExecutor);
67
- }
68
-
69
- function setProfileMetadataURI(
70
- uint256 profileId,
71
- string calldata metadataURI,
72
- address transactionExecutor
73
- ) external {
74
- StorageLib.getProfile(profileId).metadataURI = metadataURI;
75
- emit Events.ProfileMetadataSet(profileId, metadataURI, transactionExecutor, block.timestamp);
76
- }
77
-
78
- function _initFollowModule(
79
- uint256 profileId,
80
- address transactionExecutor,
81
- address followModule,
82
- bytes memory followModuleInitData
83
- ) private returns (bytes memory) {
84
- MODULE_REGISTRY().verifyModule(followModule, uint256(IModuleRegistry.ModuleType.FOLLOW_MODULE));
85
- return IFollowModule(followModule).initializeFollowModule(profileId, transactionExecutor, followModuleInitData);
86
- }
87
-
88
- function setBlockStatus(
89
- uint256 byProfileId,
90
- uint256[] calldata idsOfProfilesToSetBlockStatus,
91
- bool[] calldata blockStatus,
92
- address transactionExecutor
93
- ) external {
94
- if (idsOfProfilesToSetBlockStatus.length != blockStatus.length) {
95
- revert Errors.ArrayMismatch();
96
- }
97
- address followNFT = StorageLib.getProfile(byProfileId).followNFT;
98
- uint256 i;
99
- uint256 idOfProfileToSetBlockStatus;
100
- bool blockedStatus;
101
- mapping(uint256 => bool) storage _blockedStatus = StorageLib.blockedStatus(byProfileId);
102
- while (i < idsOfProfilesToSetBlockStatus.length) {
103
- idOfProfileToSetBlockStatus = idsOfProfilesToSetBlockStatus[i];
104
- ValidationLib.validateProfileExists(idOfProfileToSetBlockStatus);
105
- if (byProfileId == idOfProfileToSetBlockStatus) {
106
- revert Errors.SelfBlock();
107
- }
108
- blockedStatus = blockStatus[i];
109
- if (followNFT != address(0) && blockedStatus) {
110
- bool hasUnfollowed = IFollowNFT(followNFT).processBlock(idOfProfileToSetBlockStatus);
111
- if (hasUnfollowed) {
112
- emit Events.Unfollowed(
113
- idOfProfileToSetBlockStatus,
114
- byProfileId,
115
- transactionExecutor,
116
- block.timestamp
117
- );
118
- }
119
- }
120
- _blockedStatus[idOfProfileToSetBlockStatus] = blockedStatus;
121
- if (blockedStatus) {
122
- emit Events.Blocked(byProfileId, idOfProfileToSetBlockStatus, transactionExecutor, block.timestamp);
123
- } else {
124
- emit Events.Unblocked(byProfileId, idOfProfileToSetBlockStatus, transactionExecutor, block.timestamp);
125
- }
126
- unchecked {
127
- ++i;
128
- }
129
- }
130
- }
131
-
132
- function switchToNewFreshDelegatedExecutorsConfig(uint256 profileId) external {
133
- Types.DelegatedExecutorsConfig storage _delegatedExecutorsConfig = StorageLib.getDelegatedExecutorsConfig({
134
- delegatorProfileId: profileId
135
- });
136
- _changeDelegatedExecutorsConfig({
137
- _delegatedExecutorsConfig: _delegatedExecutorsConfig,
138
- delegatorProfileId: profileId,
139
- delegatedExecutors: new address[](0),
140
- approvals: new bool[](0),
141
- configNumber: _delegatedExecutorsConfig.maxConfigNumberSet + 1,
142
- switchToGivenConfig: true
143
- });
144
- }
145
-
146
- function changeDelegatedExecutorsConfig(
147
- uint256 delegatorProfileId,
148
- address[] calldata delegatedExecutors,
149
- bool[] calldata approvals
150
- ) external {
151
- Types.DelegatedExecutorsConfig storage _delegatedExecutorsConfig = StorageLib.getDelegatedExecutorsConfig(
152
- delegatorProfileId
153
- );
154
- _changeDelegatedExecutorsConfig(
155
- _delegatedExecutorsConfig,
156
- delegatorProfileId,
157
- delegatedExecutors,
158
- approvals,
159
- _delegatedExecutorsConfig.configNumber,
160
- false
161
- );
162
- }
163
-
164
- function changeGivenDelegatedExecutorsConfig(
165
- uint256 delegatorProfileId,
166
- address[] calldata delegatedExecutors,
167
- bool[] calldata approvals,
168
- uint64 configNumber,
169
- bool switchToGivenConfig
170
- ) external {
171
- _changeDelegatedExecutorsConfig(
172
- StorageLib.getDelegatedExecutorsConfig(delegatorProfileId),
173
- delegatorProfileId,
174
- delegatedExecutors,
175
- approvals,
176
- configNumber,
177
- switchToGivenConfig
178
- );
179
- }
180
-
181
- function isExecutorApproved(uint256 delegatorProfileId, address delegatedExecutor) external view returns (bool) {
182
- Types.DelegatedExecutorsConfig storage _delegatedExecutorsConfig = StorageLib.getDelegatedExecutorsConfig(
183
- delegatorProfileId
184
- );
185
- return _delegatedExecutorsConfig.isApproved[_delegatedExecutorsConfig.configNumber][delegatedExecutor];
186
- }
187
-
188
- function _changeDelegatedExecutorsConfig(
189
- Types.DelegatedExecutorsConfig storage _delegatedExecutorsConfig,
190
- uint256 delegatorProfileId,
191
- address[] memory delegatedExecutors,
192
- bool[] memory approvals,
193
- uint64 configNumber,
194
- bool switchToGivenConfig
195
- ) private {
196
- if (delegatedExecutors.length != approvals.length) {
197
- revert Errors.ArrayMismatch();
198
- }
199
- bool configSwitched = _prepareStorageToApplyChangesUnderGivenConfig(
200
- _delegatedExecutorsConfig,
201
- configNumber,
202
- switchToGivenConfig
203
- );
204
- uint256 i;
205
- while (i < delegatedExecutors.length) {
206
- _delegatedExecutorsConfig.isApproved[configNumber][delegatedExecutors[i]] = approvals[i];
207
- unchecked {
208
- ++i;
209
- }
210
- }
211
- emit Events.DelegatedExecutorsConfigChanged(
212
- delegatorProfileId,
213
- configNumber,
214
- delegatedExecutors,
215
- approvals,
216
- block.timestamp
217
- );
218
- if (configSwitched) {
219
- emit Events.DelegatedExecutorsConfigApplied(delegatorProfileId, configNumber, block.timestamp);
220
- }
221
- }
222
-
223
- function _prepareStorageToApplyChangesUnderGivenConfig(
224
- Types.DelegatedExecutorsConfig storage _delegatedExecutorsConfig,
225
- uint64 configNumber,
226
- bool switchToGivenConfig
227
- ) private returns (bool) {
228
- uint64 nextAvailableConfigNumber = _delegatedExecutorsConfig.maxConfigNumberSet + 1;
229
- if (configNumber > nextAvailableConfigNumber) {
230
- revert Errors.InvalidParameter();
231
- }
232
- bool configSwitched;
233
- if (configNumber == nextAvailableConfigNumber) {
234
- // The next configuration available is being changed, it must be marked.
235
- // Otherwise, on a profile transfer, the next owner can inherit a used/dirty configuration.
236
- _delegatedExecutorsConfig.maxConfigNumberSet = nextAvailableConfigNumber;
237
- configSwitched = switchToGivenConfig;
238
- if (configSwitched) {
239
- // The configuration is being switched, previous and current configuration numbers must be updated.
240
- _delegatedExecutorsConfig.prevConfigNumber = _delegatedExecutorsConfig.configNumber;
241
- _delegatedExecutorsConfig.configNumber = nextAvailableConfigNumber;
242
- }
243
- } else {
244
- // The configuration corresponding to the given number is not a fresh/clean one.
245
- uint64 currentConfigNumber = _delegatedExecutorsConfig.configNumber;
246
- // If the given configuration matches the one that is already in use, we keep `configSwitched` as `false`.
247
- if (configNumber != currentConfigNumber) {
248
- configSwitched = switchToGivenConfig;
249
- }
250
- if (configSwitched) {
251
- // The configuration is being switched, previous and current configuration numbers must be updated.
252
- _delegatedExecutorsConfig.prevConfigNumber = currentConfigNumber;
253
- _delegatedExecutorsConfig.configNumber = configNumber;
254
- }
255
- }
256
- return configSwitched;
257
- }
258
-
259
- function _setFollowModule(
260
- uint256 profileId,
261
- address followModule,
262
- bytes calldata followModuleInitData,
263
- address transactionExecutor
264
- ) private {
265
- StorageLib.getProfile(profileId).followModule = followModule;
266
- bytes memory followModuleReturnData;
267
- if (followModule != address(0)) {
268
- followModuleReturnData = _initFollowModule(
269
- profileId,
270
- transactionExecutor,
271
- followModule,
272
- followModuleInitData
273
- );
274
- }
275
- emit Events.FollowModuleSet(
276
- profileId,
277
- followModule,
278
- followModuleInitData,
279
- followModuleReturnData,
280
- transactionExecutor,
281
- block.timestamp
282
- );
283
- }
284
- }