lens-modules 1.0.13 → 1.1.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.
- package/contracts/FollowNFT.sol +507 -0
- package/contracts/LensHub.sol +590 -0
- package/contracts/base/LensGovernable.sol +114 -0
- package/contracts/base/LensHubEventHooks.sol +42 -0
- package/contracts/base/LensHubStorage.sol +69 -0
- package/contracts/base/LensImplGetters.sol +32 -0
- package/contracts/base/LensProfiles.sol +162 -0
- package/contracts/base/LensVersion.sol +36 -0
- package/contracts/base/upgradeability/FollowNFTProxy.sol +21 -0
- package/contracts/interfaces/IFollowTokenURI.sol +11 -0
- package/contracts/interfaces/ILegacyCollectModule.sol +45 -0
- package/contracts/interfaces/ILegacyCollectNFT.sol +46 -0
- package/contracts/interfaces/ILegacyFollowModule.sol +82 -0
- package/contracts/interfaces/IProfileTokenURI.sol +7 -0
- package/contracts/interfaces/ITokenHandleRegistry.sol +90 -0
- package/contracts/libraries/ActionLib.sol +69 -0
- package/contracts/libraries/FollowLib.sol +175 -0
- package/contracts/libraries/GovernanceLib.sol +111 -0
- package/contracts/libraries/LegacyCollectLib.sol +176 -0
- package/contracts/libraries/ProfileLib.sol +284 -0
- package/contracts/libraries/PublicationLib.sol +567 -0
- package/contracts/libraries/ValidationLib.sol +228 -0
- package/dist/index.d.ts +5 -0
- package/dist/{src/index.js → index.js} +4 -4
- package/dist/verified-modules/follow-modules.d.ts +12 -0
- package/dist/verified-modules/follow-modules.js +15 -0
- package/dist/verified-modules/open-actions.d.ts +42 -0
- package/dist/verified-modules/open-actions.js +45 -0
- package/dist/verified-modules/reference-modules.d.ts +17 -0
- package/dist/verified-modules/reference-modules.js +20 -0
- package/package.json +1 -1
- package/dist/src/index.d.ts +0 -5
- /package/dist/{deployments/lens-contracts.d.ts → lens-contracts.d.ts} +0 -0
- /package/dist/{deployments/lens-contracts.js → lens-contracts.js} +0 -0
- /package/dist/{src/lenshub-abi.d.ts → lenshub-abi.d.ts} +0 -0
- /package/dist/{src/lenshub-abi.js → lenshub-abi.js} +0 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.15;
|
|
4
|
+
|
|
5
|
+
import {ILensGovernable} from '../interfaces/ILensGovernable.sol';
|
|
6
|
+
import {GovernanceLib} from '../libraries/GovernanceLib.sol';
|
|
7
|
+
import {ValidationLib} from '../libraries/ValidationLib.sol';
|
|
8
|
+
import {StorageLib} from '../libraries/StorageLib.sol';
|
|
9
|
+
import {Types} from '../libraries/constants/Types.sol';
|
|
10
|
+
import {Events} from '../libraries/constants/Events.sol';
|
|
11
|
+
|
|
12
|
+
abstract contract LensGovernable is ILensGovernable {
|
|
13
|
+
/**
|
|
14
|
+
* @dev This modifier reverts if the caller is not the configured governance address.
|
|
15
|
+
*/
|
|
16
|
+
modifier onlyGov() {
|
|
17
|
+
ValidationLib.validateCallerIsGovernance();
|
|
18
|
+
_;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/////////////////////////////////
|
|
22
|
+
/// GOV FUNCTIONS ///
|
|
23
|
+
/////////////////////////////////
|
|
24
|
+
|
|
25
|
+
/// @inheritdoc ILensGovernable
|
|
26
|
+
function setGovernance(address newGovernance) external override onlyGov {
|
|
27
|
+
GovernanceLib.setGovernance(newGovernance);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/// @inheritdoc ILensGovernable
|
|
31
|
+
function setEmergencyAdmin(address newEmergencyAdmin) external override onlyGov {
|
|
32
|
+
GovernanceLib.setEmergencyAdmin(newEmergencyAdmin);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/// @inheritdoc ILensGovernable
|
|
36
|
+
function setState(Types.ProtocolState newState) external override {
|
|
37
|
+
// Access control is handled inside the library because we need to check for both EmergencyAdmin and Governance.
|
|
38
|
+
GovernanceLib.setState(newState);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// @inheritdoc ILensGovernable
|
|
42
|
+
function setTreasury(address newTreasury) external override onlyGov {
|
|
43
|
+
GovernanceLib.setTreasury(newTreasury);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// @inheritdoc ILensGovernable
|
|
47
|
+
function setTreasuryFee(uint16 newTreasuryFee) external override onlyGov {
|
|
48
|
+
GovernanceLib.setTreasuryFee(newTreasuryFee);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
///@inheritdoc ILensGovernable
|
|
52
|
+
function whitelistProfileCreator(address profileCreator, bool whitelist) external override onlyGov {
|
|
53
|
+
GovernanceLib.whitelistProfileCreator(profileCreator, whitelist);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function setProfileTokenURIContract(address profileTokenURIContract) external override onlyGov {
|
|
57
|
+
GovernanceLib.setProfileTokenURIContract(profileTokenURIContract);
|
|
58
|
+
emit Events.BatchMetadataUpdate({fromTokenId: 0, toTokenId: type(uint256).max});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function setFollowTokenURIContract(address followTokenURIContract) external override onlyGov {
|
|
62
|
+
GovernanceLib.setFollowTokenURIContract(followTokenURIContract);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
///////////////////////////////////////////
|
|
66
|
+
/// EXTERNAL VIEW FUNCTIONS ///
|
|
67
|
+
///////////////////////////////////////////
|
|
68
|
+
|
|
69
|
+
/// @inheritdoc ILensGovernable
|
|
70
|
+
function getGovernance() external view override returns (address) {
|
|
71
|
+
return StorageLib.getGovernance();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getProfileTokenURIContract() external view override returns (address) {
|
|
75
|
+
return StorageLib.getProfileTokenURIContract();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getFollowTokenURIContract() external view override returns (address) {
|
|
79
|
+
return StorageLib.getFollowTokenURIContract();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @notice Returns the current protocol state.
|
|
84
|
+
*
|
|
85
|
+
* @return ProtocolState The Protocol state, an enum, where:
|
|
86
|
+
* 0: Unpaused
|
|
87
|
+
* 1: PublishingPaused
|
|
88
|
+
* 2: Paused
|
|
89
|
+
*/
|
|
90
|
+
function getState() external view override returns (Types.ProtocolState) {
|
|
91
|
+
return StorageLib.getState();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// @inheritdoc ILensGovernable
|
|
95
|
+
function isProfileCreatorWhitelisted(address profileCreator) external view override returns (bool) {
|
|
96
|
+
return StorageLib.profileCreatorWhitelisted()[profileCreator];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// @inheritdoc ILensGovernable
|
|
100
|
+
function getTreasury() external view override returns (address) {
|
|
101
|
+
return StorageLib.getTreasuryData().treasury;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// @inheritdoc ILensGovernable
|
|
105
|
+
function getTreasuryFee() external view override returns (uint16) {
|
|
106
|
+
return StorageLib.getTreasuryData().treasuryFeeBPS;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/// @inheritdoc ILensGovernable
|
|
110
|
+
function getTreasuryData() external view override returns (address, uint16) {
|
|
111
|
+
Types.TreasuryData storage treasuryData = StorageLib.getTreasuryData();
|
|
112
|
+
return (treasuryData.treasury, treasuryData.treasuryFeeBPS);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.15;
|
|
4
|
+
|
|
5
|
+
import {ILensHubEventHooks} from '../interfaces/ILensHubEventHooks.sol';
|
|
6
|
+
import {Errors} from '../libraries/constants/Errors.sol';
|
|
7
|
+
import {StorageLib} from '../libraries/StorageLib.sol';
|
|
8
|
+
import {Events} from '../libraries/constants/Events.sol';
|
|
9
|
+
|
|
10
|
+
abstract contract LensHubEventHooks is ILensHubEventHooks {
|
|
11
|
+
/// @inheritdoc ILensHubEventHooks
|
|
12
|
+
function emitUnfollowedEvent(
|
|
13
|
+
uint256 unfollowerProfileId,
|
|
14
|
+
uint256 idOfProfileUnfollowed,
|
|
15
|
+
address transactionExecutor
|
|
16
|
+
) external override {
|
|
17
|
+
address expectedFollowNFT = StorageLib.getProfile(idOfProfileUnfollowed).followNFT;
|
|
18
|
+
if (msg.sender != expectedFollowNFT) {
|
|
19
|
+
revert Errors.CallerNotFollowNFT();
|
|
20
|
+
}
|
|
21
|
+
emit Events.Unfollowed(unfollowerProfileId, idOfProfileUnfollowed, transactionExecutor, block.timestamp);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
//////////////////////////////////////
|
|
25
|
+
/// DEPRECATED FUNCTIONS ///
|
|
26
|
+
//////////////////////////////////////
|
|
27
|
+
|
|
28
|
+
// Deprecated in V2. Kept here just for backwards compatibility with Lens V1 Collect NFTs.
|
|
29
|
+
function emitCollectNFTTransferEvent(
|
|
30
|
+
uint256 profileId,
|
|
31
|
+
uint256 pubId,
|
|
32
|
+
uint256 collectNFTId,
|
|
33
|
+
address from,
|
|
34
|
+
address to
|
|
35
|
+
) external {
|
|
36
|
+
address expectedCollectNFT = StorageLib.getPublication(profileId, pubId).__DEPRECATED__collectNFT;
|
|
37
|
+
if (msg.sender != expectedCollectNFT) {
|
|
38
|
+
revert Errors.CallerNotCollectNFT();
|
|
39
|
+
}
|
|
40
|
+
emit Events.CollectNFTTransferred(profileId, pubId, collectNFTId, from, to, block.timestamp);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.18;
|
|
4
|
+
|
|
5
|
+
import {Types} from '../libraries/constants/Types.sol';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @title LensHubStorage
|
|
9
|
+
* @author Lens Protocol
|
|
10
|
+
*
|
|
11
|
+
* @notice This is an abstract contract that ONLY contains storage for the LensHub contract. This MUST be inherited last
|
|
12
|
+
* to preserve the LensHub storage layout. Adding storage variables should be done ONLY at the bottom of this contract.
|
|
13
|
+
*/
|
|
14
|
+
abstract contract LensHubStorage {
|
|
15
|
+
// For upgradeability purposes, used at `VersionedInitializable` file, which needs to be included if the LensHub
|
|
16
|
+
// has an initializer function.
|
|
17
|
+
uint256 private _lastInitializedRevision; // Slot 11.
|
|
18
|
+
|
|
19
|
+
Types.ProtocolState internal _state; // Slot 12
|
|
20
|
+
|
|
21
|
+
mapping(address profileCreator => bool isWhitelisted) internal _profileCreatorWhitelisted; // Slot 13
|
|
22
|
+
|
|
23
|
+
mapping(address => bool isWhitelisted) internal __DEPRECATED__followModuleWhitelisted; // Slot 14
|
|
24
|
+
|
|
25
|
+
mapping(address collectModule => bool isWhitelisted) internal __DEPRECATED__collectModuleWhitelisted; // Slot 15
|
|
26
|
+
|
|
27
|
+
mapping(address referenceModule => bool isWhitelisted) internal __DEPRECATED__referenceModuleWhitelisted; // Slot 16
|
|
28
|
+
|
|
29
|
+
mapping(uint256 profileId => address dispatcher) internal __DEPRECATED__dispatcherByProfile; // Slot 17
|
|
30
|
+
|
|
31
|
+
mapping(bytes32 handleHash => uint256 profileId) internal __DEPRECATED__profileIdByHandleHash; // Slot 18
|
|
32
|
+
|
|
33
|
+
mapping(uint256 profileId => Types.Profile profile) internal _profiles; // Slot 19
|
|
34
|
+
|
|
35
|
+
mapping(uint256 profileId => mapping(uint256 pubId => Types.Publication publication)) internal _publications; // Slot 20
|
|
36
|
+
|
|
37
|
+
mapping(address userAddress => uint256 profileId) internal __DEPRECATED__defaultProfiles; // Slot 21
|
|
38
|
+
|
|
39
|
+
uint256 internal _profileCounter; // Slot 22 - different from totalSupply, as this is not decreased when burning profiles
|
|
40
|
+
|
|
41
|
+
address internal _governance; // Slot 23
|
|
42
|
+
|
|
43
|
+
address internal _emergencyAdmin; // Slot 24
|
|
44
|
+
|
|
45
|
+
////////////////////////////////////////////
|
|
46
|
+
// Slots introduced by Lens V1.3 upgrade. //
|
|
47
|
+
////////////////////////////////////////////
|
|
48
|
+
mapping(address => uint256) internal _tokenGuardianDisablingTimestamp; // Slot 25
|
|
49
|
+
|
|
50
|
+
////////////////////////////////////////////
|
|
51
|
+
// Slots introduced by Lens V2 upgrade. //
|
|
52
|
+
////////////////////////////////////////////
|
|
53
|
+
|
|
54
|
+
mapping(uint256 profileId => Types.DelegatedExecutorsConfig config) internal _delegatedExecutorsConfigs; // Slot 26
|
|
55
|
+
|
|
56
|
+
mapping(uint256 blockerProfileId => mapping(uint256 blockedProfileId => bool isBlocked)) internal _blockedStatus; // Slot 27
|
|
57
|
+
|
|
58
|
+
uint256 internal _profileRoyaltiesBps; // Slot 28
|
|
59
|
+
|
|
60
|
+
mapping(address migrationAdmin => bool allowed) internal _migrationAdminWhitelisted; // Slot 29
|
|
61
|
+
|
|
62
|
+
Types.TreasuryData internal _treasuryData; // Slot 30
|
|
63
|
+
|
|
64
|
+
address internal _profileTokenURIContract; // Slot 31
|
|
65
|
+
|
|
66
|
+
address internal _followTokenURIContract; // Slot 32
|
|
67
|
+
|
|
68
|
+
mapping(address => uint256) internal _legacyCollectFollowValidationHelper; // Slot 33
|
|
69
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.15;
|
|
4
|
+
|
|
5
|
+
import {ILensImplGetters} from '../interfaces/ILensImplGetters.sol';
|
|
6
|
+
|
|
7
|
+
contract LensImplGetters is ILensImplGetters {
|
|
8
|
+
address internal immutable FOLLOW_NFT_IMPL;
|
|
9
|
+
address internal immutable __LEGACY__COLLECT_NFT_IMPL;
|
|
10
|
+
address internal immutable MODULE_REGISTRY;
|
|
11
|
+
|
|
12
|
+
constructor(address followNFTImpl, address collectNFTImpl, address moduleRegistry) {
|
|
13
|
+
FOLLOW_NFT_IMPL = followNFTImpl;
|
|
14
|
+
__LEGACY__COLLECT_NFT_IMPL = collectNFTImpl;
|
|
15
|
+
MODULE_REGISTRY = moduleRegistry;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// @inheritdoc ILensImplGetters
|
|
19
|
+
function getFollowNFTImpl() external view override returns (address) {
|
|
20
|
+
return FOLLOW_NFT_IMPL;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// @inheritdoc ILensImplGetters
|
|
24
|
+
function getLegacyCollectNFTImpl() external view override returns (address) {
|
|
25
|
+
return __LEGACY__COLLECT_NFT_IMPL; // LEGACY support: Used only for compatibility with V1 collectible posts.
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// @inheritdoc ILensImplGetters
|
|
29
|
+
function getModuleRegistry() external view override returns (address) {
|
|
30
|
+
return MODULE_REGISTRY;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.15;
|
|
4
|
+
|
|
5
|
+
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
|
|
6
|
+
import {IERC721Metadata} from '@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol';
|
|
7
|
+
import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol';
|
|
8
|
+
import {ILensProfiles} from '../interfaces/ILensProfiles.sol';
|
|
9
|
+
import {IERC721Burnable} from '../interfaces/IERC721Burnable.sol';
|
|
10
|
+
|
|
11
|
+
import {LensBaseERC721} from './LensBaseERC721.sol';
|
|
12
|
+
import {ProfileLib} from '../libraries/ProfileLib.sol';
|
|
13
|
+
import {StorageLib} from '../libraries/StorageLib.sol';
|
|
14
|
+
import {ValidationLib} from '../libraries/ValidationLib.sol';
|
|
15
|
+
import {IProfileTokenURI} from '../interfaces/IProfileTokenURI.sol';
|
|
16
|
+
|
|
17
|
+
import {ERC2981CollectionRoyalties} from './ERC2981CollectionRoyalties.sol';
|
|
18
|
+
|
|
19
|
+
import {Errors} from '../libraries/constants/Errors.sol';
|
|
20
|
+
import {Types} from '../libraries/constants/Types.sol';
|
|
21
|
+
import {Events} from '../libraries/constants/Events.sol';
|
|
22
|
+
|
|
23
|
+
import {Address} from '@openzeppelin/contracts/utils/Address.sol';
|
|
24
|
+
|
|
25
|
+
abstract contract LensProfiles is LensBaseERC721, ERC2981CollectionRoyalties, ILensProfiles {
|
|
26
|
+
using Address for address;
|
|
27
|
+
|
|
28
|
+
uint256 public immutable TOKEN_GUARDIAN_COOLDOWN;
|
|
29
|
+
|
|
30
|
+
constructor(uint256 tokenGuardianCooldown) {
|
|
31
|
+
TOKEN_GUARDIAN_COOLDOWN = tokenGuardianCooldown;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
modifier whenNotPaused() {
|
|
35
|
+
if (StorageLib.getState() == Types.ProtocolState.Paused) {
|
|
36
|
+
revert Errors.Paused();
|
|
37
|
+
}
|
|
38
|
+
_;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
modifier onlyProfileOwner(address expectedOwner, uint256 profileId) {
|
|
42
|
+
ValidationLib.validateAddressIsProfileOwner(expectedOwner, profileId);
|
|
43
|
+
_;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
modifier onlyEOA() {
|
|
47
|
+
if (msg.sender.isContract()) {
|
|
48
|
+
revert Errors.NotEOA();
|
|
49
|
+
}
|
|
50
|
+
_;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/// @inheritdoc ILensProfiles
|
|
54
|
+
function getTokenGuardianDisablingTimestamp(address wallet) external view returns (uint256) {
|
|
55
|
+
return StorageLib.tokenGuardianDisablingTimestamp()[wallet];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/// @inheritdoc ILensProfiles
|
|
59
|
+
function DANGER__disableTokenGuardian() external onlyEOA {
|
|
60
|
+
if (StorageLib.tokenGuardianDisablingTimestamp()[msg.sender] != 0) {
|
|
61
|
+
revert Errors.DisablingAlreadyTriggered();
|
|
62
|
+
}
|
|
63
|
+
StorageLib.tokenGuardianDisablingTimestamp()[msg.sender] = block.timestamp + TOKEN_GUARDIAN_COOLDOWN;
|
|
64
|
+
emit Events.TokenGuardianStateChanged({
|
|
65
|
+
wallet: msg.sender,
|
|
66
|
+
enabled: false,
|
|
67
|
+
tokenGuardianDisablingTimestamp: block.timestamp + TOKEN_GUARDIAN_COOLDOWN,
|
|
68
|
+
timestamp: block.timestamp
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// @inheritdoc ILensProfiles
|
|
73
|
+
function enableTokenGuardian() external onlyEOA {
|
|
74
|
+
if (StorageLib.tokenGuardianDisablingTimestamp()[msg.sender] == 0) {
|
|
75
|
+
revert Errors.AlreadyEnabled();
|
|
76
|
+
}
|
|
77
|
+
StorageLib.tokenGuardianDisablingTimestamp()[msg.sender] = 0;
|
|
78
|
+
emit Events.TokenGuardianStateChanged({
|
|
79
|
+
wallet: msg.sender,
|
|
80
|
+
enabled: true,
|
|
81
|
+
tokenGuardianDisablingTimestamp: 0,
|
|
82
|
+
timestamp: block.timestamp
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @notice Burns a profile, this maintains the profile data struct.
|
|
88
|
+
*/
|
|
89
|
+
function burn(
|
|
90
|
+
uint256 tokenId
|
|
91
|
+
) public override(LensBaseERC721, IERC721Burnable) whenNotPaused onlyProfileOwner(msg.sender, tokenId) {
|
|
92
|
+
_burn(tokenId);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @dev Overrides the ERC721 tokenURI function to return the associated URI with a given profile.
|
|
97
|
+
*/
|
|
98
|
+
function tokenURI(uint256 tokenId) public view override(LensBaseERC721, IERC721Metadata) returns (string memory) {
|
|
99
|
+
if (!_exists(tokenId)) {
|
|
100
|
+
revert Errors.TokenDoesNotExist();
|
|
101
|
+
}
|
|
102
|
+
uint256 mintTimestamp = StorageLib.getTokenData(tokenId).mintTimestamp;
|
|
103
|
+
return IProfileTokenURI(StorageLib.getProfileTokenURIContract()).getTokenURI(tokenId, mintTimestamp);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function approve(address to, uint256 tokenId) public override(LensBaseERC721, IERC721) {
|
|
107
|
+
// We allow removing approvals even if the wallet has the token guardian enabled
|
|
108
|
+
if (to != address(0) && _hasTokenGuardianEnabled(ownerOf(tokenId))) {
|
|
109
|
+
revert Errors.GuardianEnabled();
|
|
110
|
+
}
|
|
111
|
+
super.approve(to, tokenId);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function setApprovalForAll(address operator, bool approved) public override(LensBaseERC721, IERC721) {
|
|
115
|
+
// We allow removing approvals even if the wallet has the token guardian enabled
|
|
116
|
+
if (approved && _hasTokenGuardianEnabled(msg.sender)) {
|
|
117
|
+
revert Errors.GuardianEnabled();
|
|
118
|
+
}
|
|
119
|
+
super.setApprovalForAll(operator, approved);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @dev See {IERC165-supportsInterface}.
|
|
124
|
+
*/
|
|
125
|
+
function supportsInterface(
|
|
126
|
+
bytes4 interfaceId
|
|
127
|
+
) public view virtual override(LensBaseERC721, ERC2981CollectionRoyalties, IERC165) returns (bool) {
|
|
128
|
+
return
|
|
129
|
+
LensBaseERC721.supportsInterface(interfaceId) || ERC2981CollectionRoyalties.supportsInterface(interfaceId);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function _hasTokenGuardianEnabled(address wallet) internal view returns (bool) {
|
|
133
|
+
return
|
|
134
|
+
!wallet.isContract() &&
|
|
135
|
+
(StorageLib.tokenGuardianDisablingTimestamp()[wallet] == 0 ||
|
|
136
|
+
block.timestamp < StorageLib.tokenGuardianDisablingTimestamp()[wallet]);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function _getRoyaltiesInBasisPointsSlot() internal pure override returns (uint256) {
|
|
140
|
+
return StorageLib.PROFILE_ROYALTIES_BPS_SLOT;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function _getReceiver(uint256 /* tokenId */) internal view override returns (address) {
|
|
144
|
+
return StorageLib.getTreasuryData().treasury;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function _beforeRoyaltiesSet(uint256 /* royaltiesInBasisPoints */) internal view override {
|
|
148
|
+
ValidationLib.validateCallerIsGovernance();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override whenNotPaused {
|
|
152
|
+
if (from != address(0) && _hasTokenGuardianEnabled(from)) {
|
|
153
|
+
// Cannot transfer profile if the guardian is enabled, except at minting time.
|
|
154
|
+
revert Errors.GuardianEnabled();
|
|
155
|
+
}
|
|
156
|
+
// Switches to new fresh delegated executors configuration (except on minting, as it already has a fresh setup).
|
|
157
|
+
if (from != address(0)) {
|
|
158
|
+
ProfileLib.switchToNewFreshDelegatedExecutorsConfig(tokenId);
|
|
159
|
+
}
|
|
160
|
+
super._beforeTokenTransfer(from, to, tokenId);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.15;
|
|
4
|
+
|
|
5
|
+
import {ILensVersion} from '../interfaces/ILensVersion.sol';
|
|
6
|
+
import {Errors} from '../libraries/constants/Errors.sol';
|
|
7
|
+
import {TransparentUpgradeableProxy} from '@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol';
|
|
8
|
+
|
|
9
|
+
contract LensVersion is ILensVersion {
|
|
10
|
+
string internal constant version = '2.0.3';
|
|
11
|
+
|
|
12
|
+
bytes20 internal constant gitCommit = hex'3bb1438b28b69f584ab9a290f261e3452fd34ad0';
|
|
13
|
+
|
|
14
|
+
event LensUpgradeVersion(address implementation, string version, bytes20 gitCommit, uint256 timestamp);
|
|
15
|
+
|
|
16
|
+
/// @inheritdoc ILensVersion
|
|
17
|
+
function getVersion() external pure override returns (string memory) {
|
|
18
|
+
return version;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// @inheritdoc ILensVersion
|
|
22
|
+
function getGitCommit() external pure override returns (bytes20) {
|
|
23
|
+
return gitCommit;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function emitVersion() external {
|
|
27
|
+
(, bytes memory adminData) = address(this).delegatecall(abi.encodeCall(TransparentUpgradeableProxy.admin, ()));
|
|
28
|
+
(, bytes memory implementationData) = address(this).delegatecall(
|
|
29
|
+
abi.encodeCall(TransparentUpgradeableProxy.implementation, ())
|
|
30
|
+
);
|
|
31
|
+
if (msg.sender != abi.decode(adminData, (address))) {
|
|
32
|
+
revert Errors.NotHub();
|
|
33
|
+
}
|
|
34
|
+
emit LensUpgradeVersion(abi.decode(implementationData, (address)), version, gitCommit, block.timestamp);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.15;
|
|
4
|
+
|
|
5
|
+
import {ILensHub} from '../../interfaces/ILensHub.sol';
|
|
6
|
+
import {Proxy} from '@openzeppelin/contracts/proxy/Proxy.sol';
|
|
7
|
+
import {Address} from '@openzeppelin/contracts/utils/Address.sol';
|
|
8
|
+
|
|
9
|
+
contract FollowNFTProxy is Proxy {
|
|
10
|
+
using Address for address;
|
|
11
|
+
address immutable HUB;
|
|
12
|
+
|
|
13
|
+
constructor(bytes memory data) {
|
|
14
|
+
HUB = msg.sender;
|
|
15
|
+
ILensHub(msg.sender).getFollowNFTImpl().functionDelegateCall(data);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function _implementation() internal view override returns (address) {
|
|
19
|
+
return ILensHub(HUB).getFollowNFTImpl();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity >=0.6.0;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @title ICollectModule
|
|
7
|
+
* @author Lens Protocol
|
|
8
|
+
* @custom:pending-deprecation
|
|
9
|
+
*
|
|
10
|
+
* @notice This is the deprecated interface for previously Lens-compatible CollectModules.
|
|
11
|
+
*/
|
|
12
|
+
interface ILegacyCollectModule {
|
|
13
|
+
/**
|
|
14
|
+
* @notice Initializes data for a given publication being published. This can only be called by the hub.
|
|
15
|
+
*
|
|
16
|
+
* @param profileId The token ID of the profile publishing the publication.
|
|
17
|
+
* @param pubId The associated publication's LensHub publication ID.
|
|
18
|
+
* @param data Arbitrary data __passed from the user!__ to be decoded.
|
|
19
|
+
*
|
|
20
|
+
* @return bytes An ABI-encoded encapsulating the execution's state changes. This will be emitted by the
|
|
21
|
+
* hub alongside the collect module's address and should be consumed by front ends.
|
|
22
|
+
*/
|
|
23
|
+
function initializePublicationCollectModule(
|
|
24
|
+
uint256 profileId,
|
|
25
|
+
uint256 pubId,
|
|
26
|
+
bytes calldata data
|
|
27
|
+
) external returns (bytes memory);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @notice Processes a collect action for a given publication, this can only be called by the hub.
|
|
31
|
+
*
|
|
32
|
+
* @param referrerProfileId The LensHub profile token ID of the referrer's profile (only different in case of mirrors).
|
|
33
|
+
* @param collector The collector address.
|
|
34
|
+
* @param profileId The token ID of the profile associated with the publication being collected.
|
|
35
|
+
* @param pubId The LensHub publication ID associated with the publication being collected.
|
|
36
|
+
* @param data Arbitrary data __passed from the collector!__ to be decoded.
|
|
37
|
+
*/
|
|
38
|
+
function processCollect(
|
|
39
|
+
uint256 referrerProfileId,
|
|
40
|
+
address collector,
|
|
41
|
+
uint256 profileId,
|
|
42
|
+
uint256 pubId,
|
|
43
|
+
bytes calldata data
|
|
44
|
+
) external;
|
|
45
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity >=0.6.0;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @title ILegacyCollectNFT
|
|
7
|
+
* @author Lens Protocol
|
|
8
|
+
*
|
|
9
|
+
* @notice This is the interface for the Lens V1 CollectNFT contract. Which is cloned upon the first collect for any
|
|
10
|
+
* given publication.
|
|
11
|
+
*/
|
|
12
|
+
interface ILegacyCollectNFT {
|
|
13
|
+
/**
|
|
14
|
+
* @notice Initializes the collect NFT, setting the feed as the privileged minter, storing the collected publication
|
|
15
|
+
* pointer and initializing the name and symbol in the LensNFTBase contract.
|
|
16
|
+
* @custom:permissions LensHub.
|
|
17
|
+
*
|
|
18
|
+
* @param profileId The token ID of the profile in the hub that this Collect NFT points to.
|
|
19
|
+
* @param pubId The profile publication ID in the hub that this Collect NFT points to.
|
|
20
|
+
* @param name The name to set for this NFT.
|
|
21
|
+
* @param symbol The symbol to set for this NFT.
|
|
22
|
+
*/
|
|
23
|
+
function initialize(
|
|
24
|
+
uint256 profileId,
|
|
25
|
+
uint256 pubId,
|
|
26
|
+
string calldata name,
|
|
27
|
+
string calldata symbol
|
|
28
|
+
) external;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @notice Mints a collect NFT to the specified address.
|
|
32
|
+
* @custom:permissions LensHub.
|
|
33
|
+
*
|
|
34
|
+
* @param to The address to mint the NFT to.
|
|
35
|
+
*
|
|
36
|
+
* @return uint256 An integer representing the minted token ID.
|
|
37
|
+
*/
|
|
38
|
+
function mint(address to) external returns (uint256);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @notice Returns the source publication of this collect NFT.
|
|
42
|
+
*
|
|
43
|
+
* @return tuple First is the profile ID, and second is the publication ID.
|
|
44
|
+
*/
|
|
45
|
+
function getSourcePublicationPointer() external view returns (uint256, uint256);
|
|
46
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity ^0.8.15;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @title IFollowModule
|
|
7
|
+
* @author Lens Protocol
|
|
8
|
+
*
|
|
9
|
+
* @notice This is the deprecated interface for previously Lens-compatible FollowModules.
|
|
10
|
+
*/
|
|
11
|
+
interface ILegacyFollowModule {
|
|
12
|
+
/**
|
|
13
|
+
* @notice Initializes a follow module for a given Lens profile. This can only be called by the hub contract.
|
|
14
|
+
*
|
|
15
|
+
* @param profileId The token ID of the profile to initialize this follow module for.
|
|
16
|
+
* @param data Arbitrary data passed by the profile creator.
|
|
17
|
+
*
|
|
18
|
+
* @return bytes The encoded data to emit in the hub.
|
|
19
|
+
*/
|
|
20
|
+
function initializeFollowModule(uint256 profileId, bytes calldata data) external returns (bytes memory);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @notice Processes a given follow, this can only be called from the LensHub contract.
|
|
24
|
+
*
|
|
25
|
+
* @param follower The follower address.
|
|
26
|
+
* @param profileId The token ID of the profile being followed.
|
|
27
|
+
* @param data Arbitrary data passed by the follower.
|
|
28
|
+
*/
|
|
29
|
+
function processFollow(
|
|
30
|
+
address follower,
|
|
31
|
+
uint256 profileId,
|
|
32
|
+
bytes calldata data
|
|
33
|
+
) external;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @notice This is a transfer hook that is called upon follow NFT transfer in `beforeTokenTransfer. This can
|
|
37
|
+
* only be called from the LensHub contract.
|
|
38
|
+
*
|
|
39
|
+
* NOTE: Special care needs to be taken here: It is possible that follow NFTs were issued before this module
|
|
40
|
+
* was initialized if the profile's follow module was previously different. This transfer hook should take this
|
|
41
|
+
* into consideration, especially when the module holds a state associated with individual follow NFTs.
|
|
42
|
+
*
|
|
43
|
+
* @param profileId The token ID of the profile associated with the follow NFT being transferred.
|
|
44
|
+
* @param from The address sending the follow NFT.
|
|
45
|
+
* @param to The address receiving the follow NFT.
|
|
46
|
+
* @param followNFTTokenId The token ID of the follow NFT being transferred.
|
|
47
|
+
*/
|
|
48
|
+
function followModuleTransferHook(
|
|
49
|
+
uint256 profileId,
|
|
50
|
+
address from,
|
|
51
|
+
address to,
|
|
52
|
+
uint256 followNFTTokenId
|
|
53
|
+
) external;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @notice This is a helper function that could be used in conjunction with specific collect modules.
|
|
57
|
+
*
|
|
58
|
+
* NOTE: This function IS meant to replace a check on follower NFT ownership.
|
|
59
|
+
*
|
|
60
|
+
* NOTE: It is assumed that not all collect modules are aware of the token ID to pass. In these cases,
|
|
61
|
+
* this should receive a `followNFTTokenId` of 0, which is impossible regardless.
|
|
62
|
+
*
|
|
63
|
+
* One example of a use case for this would be a subscription-based following system:
|
|
64
|
+
* 1. The collect module:
|
|
65
|
+
* - Decodes a follower NFT token ID from user-passed data.
|
|
66
|
+
* - Fetches the follow module from the hub.
|
|
67
|
+
* - Calls `isFollowing` passing the profile ID, follower & follower token ID and checks it returned true.
|
|
68
|
+
* 2. The follow module:
|
|
69
|
+
* - Validates the subscription status for that given NFT, reverting on an invalid subscription.
|
|
70
|
+
*
|
|
71
|
+
* @param profileId The token ID of the profile to validate the follow for.
|
|
72
|
+
* @param follower The follower address to validate the follow for.
|
|
73
|
+
* @param followNFTTokenId The followNFT token ID to validate the follow for.
|
|
74
|
+
*
|
|
75
|
+
* @return true if the given address is following the given profile ID, false otherwise.
|
|
76
|
+
*/
|
|
77
|
+
function isFollowing(
|
|
78
|
+
uint256 profileId,
|
|
79
|
+
address follower,
|
|
80
|
+
uint256 followNFTTokenId
|
|
81
|
+
) external view returns (bool);
|
|
82
|
+
}
|