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,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
|
-
}
|