Files
core/contracts/LensHub.sol
donosonaumczuk b3bd2da9a5 fix: Tests fixed
Co-authored-by: Victor Naumik <vicnaum@gmail.com>
2023-03-08 18:50:28 +00:00

918 lines
36 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import {IFollowNFT} from 'contracts/interfaces/IFollowNFT.sol';
import {ILensHub} from 'contracts/interfaces/ILensHub.sol';
import {Events} from 'contracts/libraries/constants/Events.sol';
import {Types} from 'contracts/libraries/constants/Types.sol';
import {Errors} from 'contracts/libraries/constants/Errors.sol';
import {ValidationLib} from 'contracts/libraries/ValidationLib.sol';
import {ProfileLib} from 'contracts/libraries/ProfileLib.sol';
import {PublicationLib} from 'contracts/libraries/PublicationLib.sol';
import {ProfileTokenURILib} from 'contracts/libraries/ProfileTokenURILib.sol';
import {LensBaseERC721} from 'contracts/base/LensBaseERC721.sol';
import {LensMultiState} from 'contracts/base/LensMultiState.sol';
import {LensHubStorage} from 'contracts/base/LensHubStorage.sol';
import {VersionedInitializable} from 'contracts/base/upgradeability/VersionedInitializable.sol';
import {IERC721Enumerable} from '@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol';
import {MetaTxLib} from 'contracts/libraries/MetaTxLib.sol';
import {GovernanceLib} from 'contracts/libraries/GovernanceLib.sol';
import {StorageLib} from 'contracts/libraries/StorageLib.sol';
import {FollowLib} from 'contracts/libraries/FollowLib.sol';
import {ActionLib} from 'contracts/libraries/ActionLib.sol';
import {CollectLib} from 'contracts/libraries/CollectLib.sol';
///////////////////////////////////// Migration imports ////////////////////////////////////
import {LensHandles} from 'contracts/misc/namespaces/LensHandles.sol';
import {TokenHandleRegistry} from 'contracts/misc/namespaces/TokenHandleRegistry.sol';
/**
* @title LensHub
* @author Lens Protocol
*
* @notice This is the main entrypoint of the Lens Protocol. It contains governance functionality as well as
* publishing and profile interaction functionality.
*
* NOTE: The Lens Protocol is unique in that frontend operators need to track a potentially overwhelming
* number of NFT contracts and interactions at once. For that reason, we've made two quirky design decisions:
* 1. Both Follow & Collect NFTs invoke an LensHub callback on transfer with the sole purpose of emitting an event.
* 2. Almost every event in the protocol emits the current block timestamp, reducing the need to fetch it manually.
*/
contract LensHub is LensBaseERC721, VersionedInitializable, LensMultiState, LensHubStorage, ILensHub {
// Constant for upgradeability purposes, see VersionedInitializable. Do not confuse with EIP-712 version number.
uint256 internal constant REVISION = 1;
address internal immutable FOLLOW_NFT_IMPL;
address internal immutable COLLECT_NFT_IMPL;
///////////////////////////////////// Migration constants ////////////////////////////////////
uint256 internal constant LENS_PROTOCOL_PROFILE_ID = 1;
LensHandles internal immutable lensHandles;
TokenHandleRegistry internal immutable tokenHandleRegistry;
///////////////////////////////// End of migration constants /////////////////////////////////
/**
* @dev This modifier reverts if the caller is not the configured governance address.
*/
modifier onlyGov() {
_validateCallerIsGovernance();
_;
}
/**
* @dev The constructor sets the immutable follow & collect NFT implementations.
*
* @param followNFTImpl The follow NFT implementation address.
* @param collectNFTImpl The collect NFT implementation address.
*/
constructor(
address followNFTImpl,
address collectNFTImpl,
address lensHandlesAddress,
address tokenHandleRegistryAddress
) {
if (followNFTImpl == address(0)) revert Errors.InitParamsInvalid();
if (collectNFTImpl == address(0)) revert Errors.InitParamsInvalid();
FOLLOW_NFT_IMPL = followNFTImpl;
COLLECT_NFT_IMPL = collectNFTImpl;
lensHandles = LensHandles(lensHandlesAddress);
tokenHandleRegistry = TokenHandleRegistry(tokenHandleRegistryAddress);
}
/// @inheritdoc ILensHub
function initialize(
string calldata name,
string calldata symbol,
address newGovernance
) external override initializer {
super._initialize(name, symbol);
GovernanceLib.initState(Types.ProtocolState.Paused);
GovernanceLib.setGovernance(newGovernance);
}
/////////////////////////////////
/// GOV FUNCTIONS ///
/////////////////////////////////
/// @inheritdoc ILensHub
function setGovernance(address newGovernance) external onlyGov {
GovernanceLib.setGovernance(newGovernance);
}
/// @inheritdoc ILensHub
function setEmergencyAdmin(address newEmergencyAdmin) external override onlyGov {
GovernanceLib.setEmergencyAdmin(newEmergencyAdmin);
}
/// @inheritdoc ILensHub
function setState(Types.ProtocolState newState) external override {
GovernanceLib.setState(newState);
}
// TODO: Move to GovernanceLib?
///@inheritdoc ILensHub
function whitelistProfileCreator(address profileCreator, bool whitelist) external override onlyGov {
_profileCreatorWhitelisted[profileCreator] = whitelist;
emit Events.ProfileCreatorWhitelisted(profileCreator, whitelist, block.timestamp);
}
// TODO: Move to GovernanceLib?
/// @inheritdoc ILensHub
function whitelistFollowModule(address followModule, bool whitelist) external override onlyGov {
_followModuleWhitelisted[followModule] = whitelist;
emit Events.FollowModuleWhitelisted(followModule, whitelist, block.timestamp);
}
// TODO: Move to GovernanceLib?
/// @inheritdoc ILensHub
function whitelistReferenceModule(address referenceModule, bool whitelist) external override onlyGov {
_referenceModuleWhitelisted[referenceModule] = whitelist;
emit Events.ReferenceModuleWhitelisted(referenceModule, whitelist, block.timestamp);
}
// TODO: Move to GovernanceLib?
/// @inheritdoc ILensHub
function whitelistActionModuleId(address actionModule, uint256 whitelistId) external override onlyGov {
_actionModuleWhitelistedId[actionModule] = whitelistId;
_actionModuleById[whitelistId] = actionModule;
emit Events.ActionModuleWhitelistedId(actionModule, whitelistId, block.timestamp);
}
///////////////////////////////////////////
/// V1->V2 MIGRATION FUNCTIONS ///
///////////////////////////////////////////
// Profiles Handles Migration:
event ProfileMigrated(uint256 profileId, address profileDestination, string handle, uint256 handleId);
/**
* @notice Migrates an array of profiles from V1 to V2. This function can be callable by anyone.
* We would still do the migration in batches by ourselves, but good to allow users to migrate on their own if they want to.
*
* @param profileIds The array of profile IDs to migrate.
*/
function batchMigrateProfiles(uint256[] calldata profileIds) external {
for (uint256 i = 0; i < profileIds.length; i++) {
_migrateProfilePublic(profileIds[i]);
}
}
/**
* @notice Migrates a profile from V1 to V2.
*
* @dev We check if the profile exists by checking owner != address(0).
* @dev We check if the profile has already been migrated by checking handleDeprecated != "".
* @dev We check if the profile is the "lensprotocol" profile by checking profileId != 1. This is the only profile
* without a .lens suffix.
* @dev We mint a new handle on LensHandles contract and link it to the profile in TokenHandleRegistry contract.
* @dev The resulting handle NFT is sent to the profile owner.
* @dev We emit ProfileMigrated event.
* @dev We do not revert in any case, as we want to allow the migration to continue even if one profile fails
* (and it usually fails if already migrated or profileNFT moved).
*
* @dev Estimated gas cost of one profile migration is around 150k gas.
*
* @param profileId The profile ID to migrate.
*/
function _migrateProfilePublic(uint256 profileId) internal {
address profileOwner = StorageLib.getTokenData(profileId).owner;
if (profileOwner != address(0)) {
string memory handle = _profileById[profileId].handleDeprecated;
if (bytes(handle).length == 0) return; // Already migrated
bytes32 handleHash = keccak256(bytes(handle));
// "lensprotocol" is the only edge case without .lens suffix:
if (profileId != LENS_PROTOCOL_PROFILE_ID) {
assembly {
let handle_length := mload(handle)
mstore(handle, sub(handle_length, 5)) // Cut 5 chars (.lens) from the end
}
}
uint256 handleId = lensHandles.mintHandle(profileOwner, handle);
tokenHandleRegistry.migrationLinkHandleWithToken(handleId, profileId);
emit ProfileMigrated(profileId, profileOwner, handle, handleId);
delete _profileById[profileId].handleDeprecated;
delete _profileIdByHandleHash[handleHash];
}
}
// FollowNFT Migration:
function batchMigrateFollows(
uint256[] calldata followerProfileIds,
uint256[] calldata idsOfProfileFollowed,
address[] calldata followNFTAddresses,
uint256[] calldata followTokenIds
) external {
if (
followerProfileIds.length != idsOfProfileFollowed.length ||
followerProfileIds.length != followNFTAddresses.length ||
followerProfileIds.length != followTokenIds.length
) {
revert Errors.ArrayMismatch();
}
uint256 i;
while (i < followerProfileIds.length) {
_migrateFollow(followerProfileIds[i], idsOfProfileFollowed[i], followNFTAddresses[i], followTokenIds[i]);
unchecked {
++i;
}
}
}
function _migrateFollow(
uint256 followerProfileId,
uint256 idOfProfileFollowed,
address followNFTAddress,
uint256 followTokenId
) internal {
uint48 mintTimestamp = IFollowNFT(followNFTAddress).migrate({
followerProfileId: followerProfileId,
followerProfileOwner: StorageLib.getTokenData(followerProfileId).owner,
idOfProfileFollowed: idOfProfileFollowed,
followTokenId: followTokenId
});
// `mintTimestamp` will be 0 if already migrated (or not holding both Profile & Follow NFT together)
if (mintTimestamp != 0) {
emit Events.Followed({
followerProfileId: followerProfileId,
idOfProfileFollowed: idOfProfileFollowed,
followTokenIdAssigned: followTokenId,
followModuleData: '',
timestamp: mintTimestamp // The only case where this won't match block.timestamp is during the migration
});
}
}
///////////////////////////////////////////
/// END OF V1->V2 MIGRATION FUNCTIONS ///
///////////////////////////////////////////
///////////////////////////////////////////
/// PROFILE OWNER FUNCTIONS ///
///////////////////////////////////////////
/// @inheritdoc ILensHub
function createProfile(
Types.CreateProfileParams calldata createProfileParams
) external override whenNotPaused returns (uint256) {
unchecked {
uint256 profileId = ++_profileCounter;
_mint(createProfileParams.to, profileId);
ProfileLib.createProfile(createProfileParams, profileId);
return profileId;
}
}
/// @inheritdoc ILensHub
function setProfileMetadataURI(
uint256 profileId,
string calldata metadataURI
) external override whenNotPaused onlyProfileOwnerOrDelegatedExecutor(msg.sender, profileId) {
ProfileLib.setProfileMetadataURI(profileId, metadataURI);
}
/// @inheritdoc ILensHub
function setProfileMetadataURIWithSig(
uint256 profileId,
string calldata metadataURI,
Types.EIP712Signature calldata signature
) external override whenNotPaused onlyProfileOwnerOrDelegatedExecutor(signature.signer, profileId) {
MetaTxLib.validateSetProfileMetadataURISignature(signature, profileId, metadataURI);
ProfileLib.setProfileMetadataURI(profileId, metadataURI);
}
/// @inheritdoc ILensHub
function setFollowModule(
uint256 profileId,
address followModule,
bytes calldata followModuleInitData
) external override whenNotPaused onlyProfileOwnerOrDelegatedExecutor(msg.sender, profileId) {
ProfileLib.setFollowModule(profileId, followModule, followModuleInitData);
}
/// @inheritdoc ILensHub
function setFollowModuleWithSig(
uint256 profileId,
address followModule,
bytes calldata followModuleInitData,
Types.EIP712Signature calldata signature
) external override whenNotPaused onlyProfileOwnerOrDelegatedExecutor(signature.signer, profileId) {
MetaTxLib.validateSetFollowModuleSignature(signature, profileId, followModule, followModuleInitData);
ProfileLib.setFollowModule(profileId, followModule, followModuleInitData);
}
/// @inheritdoc ILensHub
function changeDelegatedExecutorsConfig(
uint256 delegatorProfileId,
address[] calldata executors,
bool[] calldata approvals,
uint64 configNumber,
bool switchToGivenConfig
) external override whenNotPaused onlyProfileOwner(msg.sender, delegatorProfileId) {
ProfileLib.changeGivenDelegatedExecutorsConfig(
delegatorProfileId,
executors,
approvals,
configNumber,
switchToGivenConfig
);
}
function changeCurrentDelegatedExecutorsConfig(
uint256 delegatorProfileId,
address[] calldata executors,
bool[] calldata approvals
) external override whenNotPaused onlyProfileOwner(msg.sender, delegatorProfileId) {
ProfileLib.changeCurrentDelegatedExecutorsConfig(delegatorProfileId, executors, approvals);
}
/// @inheritdoc ILensHub
function changeDelegatedExecutorsConfigWithSig(
uint256 delegatorProfileId,
address[] calldata executors,
bool[] calldata approvals,
uint64 configNumber,
bool switchToGivenConfig,
Types.EIP712Signature calldata signature
) external override whenNotPaused onlyProfileOwner(signature.signer, delegatorProfileId) {
MetaTxLib.validateChangeDelegatedExecutorsConfigSignature(
signature,
delegatorProfileId,
executors,
approvals,
configNumber,
switchToGivenConfig
);
ProfileLib.changeGivenDelegatedExecutorsConfig(
delegatorProfileId,
executors,
approvals,
configNumber,
switchToGivenConfig
);
}
/// @inheritdoc ILensHub
function setProfileImageURI(
uint256 profileId,
string calldata imageURI
) external override whenNotPaused onlyProfileOwnerOrDelegatedExecutor(msg.sender, profileId) {
ProfileLib.setProfileImageURI(profileId, imageURI);
}
/// @inheritdoc ILensHub
function setProfileImageURIWithSig(
uint256 profileId,
string calldata imageURI,
Types.EIP712Signature calldata signature
) external override whenNotPaused onlyProfileOwnerOrDelegatedExecutor(signature.signer, profileId) {
MetaTxLib.validateSetProfileImageURISignature(signature, profileId, imageURI);
ProfileLib.setProfileImageURI(profileId, imageURI);
}
/// @inheritdoc ILensHub
function setFollowNFTURI(
uint256 profileId,
string calldata followNFTURI
) external override whenNotPaused onlyProfileOwnerOrDelegatedExecutor(msg.sender, profileId) {
ProfileLib.setFollowNFTURI(profileId, followNFTURI);
}
/// @inheritdoc ILensHub
function setFollowNFTURIWithSig(
uint256 profileId,
string calldata followNFTURI,
Types.EIP712Signature calldata signature
) external override whenNotPaused onlyProfileOwnerOrDelegatedExecutor(signature.signer, profileId) {
MetaTxLib.validateSetFollowNFTURISignature(signature, profileId, followNFTURI);
ProfileLib.setFollowNFTURI(profileId, followNFTURI);
}
////////////////////////////////////////
/// PUBLISHING FUNCTIONS ///
////////////////////////////////////////
/// @inheritdoc ILensHub
function post(
Types.PostParams calldata postParams
)
external
override
whenPublishingEnabled
onlyProfileOwnerOrDelegatedExecutor(msg.sender, postParams.profileId)
returns (uint256)
{
return PublicationLib.post({postParams: postParams, transactionExecutor: msg.sender});
}
/// @inheritdoc ILensHub
function postWithSig(
Types.PostParams calldata postParams,
Types.EIP712Signature calldata signature
)
external
override
whenPublishingEnabled
onlyProfileOwnerOrDelegatedExecutor(signature.signer, postParams.profileId)
returns (uint256)
{
MetaTxLib.validatePostSignature(signature, postParams);
return PublicationLib.post({postParams: postParams, transactionExecutor: signature.signer});
}
/// @inheritdoc ILensHub
function comment(
Types.CommentParams calldata commentParams
)
external
override
whenPublishingEnabled
onlyProfileOwnerOrDelegatedExecutor(msg.sender, commentParams.profileId)
onlyValidPointedPub(commentParams.pointedProfileId, commentParams.pointedPubId)
whenNotBlocked(commentParams.profileId, commentParams.pointedProfileId)
returns (uint256)
{
return PublicationLib.comment({commentParams: commentParams, transactionExecutor: msg.sender});
}
/// @inheritdoc ILensHub
function commentWithSig(
Types.CommentParams calldata commentParams,
Types.EIP712Signature calldata signature
)
external
override
whenPublishingEnabled
onlyProfileOwnerOrDelegatedExecutor(signature.signer, commentParams.profileId)
onlyValidPointedPub(commentParams.pointedProfileId, commentParams.pointedPubId)
whenNotBlocked(commentParams.profileId, commentParams.pointedProfileId)
returns (uint256)
{
MetaTxLib.validateCommentSignature(signature, commentParams);
return PublicationLib.comment({commentParams: commentParams, transactionExecutor: signature.signer});
}
/// @inheritdoc ILensHub
function mirror(
Types.MirrorParams calldata mirrorParams
)
external
override
whenPublishingEnabled
onlyProfileOwnerOrDelegatedExecutor(msg.sender, mirrorParams.profileId)
onlyValidPointedPub(mirrorParams.pointedProfileId, mirrorParams.pointedPubId)
whenNotBlocked(mirrorParams.profileId, mirrorParams.pointedProfileId)
returns (uint256)
{
return PublicationLib.mirror({mirrorParams: mirrorParams, transactionExecutor: msg.sender});
}
/// @inheritdoc ILensHub
function mirrorWithSig(
Types.MirrorParams calldata mirrorParams,
Types.EIP712Signature calldata signature
)
external
override
whenPublishingEnabled
onlyProfileOwnerOrDelegatedExecutor(signature.signer, mirrorParams.profileId)
onlyValidPointedPub(mirrorParams.pointedProfileId, mirrorParams.pointedPubId)
whenNotBlocked(mirrorParams.profileId, mirrorParams.pointedProfileId)
returns (uint256)
{
MetaTxLib.validateMirrorSignature(signature, mirrorParams);
return PublicationLib.mirror({mirrorParams: mirrorParams, transactionExecutor: signature.signer});
}
/// @inheritdoc ILensHub
function quote(
Types.QuoteParams calldata quoteParams
)
external
override
whenPublishingEnabled
onlyProfileOwnerOrDelegatedExecutor(msg.sender, quoteParams.profileId)
onlyValidPointedPub(quoteParams.pointedProfileId, quoteParams.pointedPubId)
whenNotBlocked(quoteParams.profileId, quoteParams.pointedProfileId)
returns (uint256)
{
return PublicationLib.quote({quoteParams: quoteParams, transactionExecutor: msg.sender});
}
/// @inheritdoc ILensHub
function quoteWithSig(
Types.QuoteParams calldata quoteParams,
Types.EIP712Signature calldata signature
)
external
override
whenPublishingEnabled
onlyProfileOwnerOrDelegatedExecutor(signature.signer, quoteParams.profileId)
onlyValidPointedPub(quoteParams.pointedProfileId, quoteParams.pointedPubId)
whenNotBlocked(quoteParams.profileId, quoteParams.pointedProfileId)
returns (uint256)
{
MetaTxLib.validateQuoteSignature(signature, quoteParams);
return PublicationLib.quote({quoteParams: quoteParams, transactionExecutor: signature.signer});
}
/**
* @notice Burns a profile, this maintains the profile data struct.
*/
function burn(uint256 tokenId) public override whenNotPaused onlyProfileOwner(msg.sender, tokenId) {
_burn(tokenId);
}
/////////////////////////////////////////////////
/// PROFILE INTERACTION FUNCTIONS ///
/////////////////////////////////////////////////
/// @inheritdoc ILensHub
function follow(
uint256 followerProfileId,
uint256[] calldata idsOfProfilesToFollow,
uint256[] calldata followTokenIds,
bytes[] calldata datas
)
external
override
whenNotPaused
onlyProfileOwnerOrDelegatedExecutor(msg.sender, followerProfileId)
returns (uint256[] memory)
{
return
FollowLib.follow({
followerProfileId: followerProfileId,
idsOfProfilesToFollow: idsOfProfilesToFollow,
followTokenIds: followTokenIds,
followModuleDatas: datas,
transactionExecutor: msg.sender
});
}
/// @inheritdoc ILensHub
function followWithSig(
uint256 followerProfileId,
uint256[] calldata idsOfProfilesToFollow,
uint256[] calldata followTokenIds,
bytes[] calldata datas,
Types.EIP712Signature calldata signature
)
external
override
whenNotPaused
onlyProfileOwnerOrDelegatedExecutor(signature.signer, followerProfileId)
returns (uint256[] memory)
{
MetaTxLib.validateFollowSignature(signature, followerProfileId, idsOfProfilesToFollow, followTokenIds, datas);
return
FollowLib.follow({
followerProfileId: followerProfileId,
idsOfProfilesToFollow: idsOfProfilesToFollow,
followTokenIds: followTokenIds,
followModuleDatas: datas,
transactionExecutor: signature.signer
});
}
/// @inheritdoc ILensHub
function unfollow(
uint256 unfollowerProfileId,
uint256[] calldata idsOfProfilesToUnfollow
) external override whenNotPaused onlyProfileOwnerOrDelegatedExecutor(msg.sender, unfollowerProfileId) {
return
FollowLib.unfollow({
unfollowerProfileId: unfollowerProfileId,
idsOfProfilesToUnfollow: idsOfProfilesToUnfollow,
transactionExecutor: msg.sender
});
}
/// @inheritdoc ILensHub
function unfollowWithSig(
uint256 unfollowerProfileId,
uint256[] calldata idsOfProfilesToUnfollow,
Types.EIP712Signature calldata signature
) external override whenNotPaused onlyProfileOwnerOrDelegatedExecutor(signature.signer, unfollowerProfileId) {
MetaTxLib.validateUnfollowSignature(signature, unfollowerProfileId, idsOfProfilesToUnfollow);
return
FollowLib.unfollow({
unfollowerProfileId: unfollowerProfileId,
idsOfProfilesToUnfollow: idsOfProfilesToUnfollow,
transactionExecutor: signature.signer
});
}
/// @inheritdoc ILensHub
function setBlockStatus(
uint256 byProfileId,
uint256[] calldata idsOfProfilesToSetBlockStatus,
bool[] calldata blockStatus
) external override whenNotPaused onlyProfileOwnerOrDelegatedExecutor(msg.sender, byProfileId) {
return ProfileLib.setBlockStatus(byProfileId, idsOfProfilesToSetBlockStatus, blockStatus);
}
/// @inheritdoc ILensHub
function setBlockStatusWithSig(
uint256 byProfileId,
uint256[] calldata idsOfProfilesToSetBlockStatus,
bool[] calldata blockStatus,
Types.EIP712Signature calldata signature
) external override whenNotPaused onlyProfileOwnerOrDelegatedExecutor(signature.signer, byProfileId) {
MetaTxLib.validateSetBlockStatusSignature(signature, byProfileId, idsOfProfilesToSetBlockStatus, blockStatus);
return ProfileLib.setBlockStatus(byProfileId, idsOfProfilesToSetBlockStatus, blockStatus);
}
/// @inheritdoc ILensHub
function collect(
Types.CollectParams calldata collectParams
)
external
override
whenNotPaused
onlyProfileOwnerOrDelegatedExecutor(msg.sender, collectParams.collectorProfileId)
whenNotBlocked(collectParams.collectorProfileId, collectParams.publicationCollectedProfileId)
returns (uint256)
{
return
CollectLib.collect({
collectParams: collectParams,
transactionExecutor: msg.sender,
collectorProfileOwner: ownerOf(collectParams.collectorProfileId),
collectNFTImpl: COLLECT_NFT_IMPL // TODO: Think how we can not pass this
});
}
/// @inheritdoc ILensHub
function collectWithSig(
Types.CollectParams calldata collectParams,
Types.EIP712Signature calldata signature
)
external
override
whenNotPaused
onlyProfileOwnerOrDelegatedExecutor(signature.signer, collectParams.collectorProfileId)
whenNotBlocked(collectParams.collectorProfileId, collectParams.publicationCollectedProfileId)
returns (uint256)
{
MetaTxLib.validateCollectSignature(signature, collectParams);
return
CollectLib.collect({
collectParams: collectParams,
transactionExecutor: signature.signer,
collectorProfileOwner: ownerOf(collectParams.collectorProfileId),
collectNFTImpl: COLLECT_NFT_IMPL
});
}
/// @inheritdoc ILensHub
function act(
Types.PublicationActionParams calldata publicationActionParams
)
external
override
whenNotPaused
onlyProfileOwnerOrDelegatedExecutor(msg.sender, publicationActionParams.actorProfileId)
whenNotBlocked(publicationActionParams.actorProfileId, publicationActionParams.publicationActedProfileId)
returns (bytes memory)
{
return
ActionLib.act({
publicationActionParams: publicationActionParams,
transactionExecutor: msg.sender,
actorProfileOwner: ownerOf(publicationActionParams.actorProfileId)
});
}
/// @inheritdoc ILensHub
function actWithSig(
Types.PublicationActionParams calldata publicationActionParams,
Types.EIP712Signature calldata signature
)
external
override
whenNotPaused
onlyProfileOwnerOrDelegatedExecutor(signature.signer, publicationActionParams.actorProfileId)
whenNotBlocked(publicationActionParams.actorProfileId, publicationActionParams.publicationActedProfileId)
returns (bytes memory)
{
MetaTxLib.validateActSignature(signature, publicationActionParams);
return
ActionLib.act({
publicationActionParams: publicationActionParams,
transactionExecutor: signature.signer,
actorProfileOwner: ownerOf(publicationActionParams.actorProfileId)
});
}
/// @inheritdoc ILensHub
function emitUnfollowedEvent(uint256 unfollowerProfileId, uint256 idOfProfileUnfollowed) external override {
address expectedFollowNFT = _profileById[idOfProfileUnfollowed].followNFT;
if (msg.sender != expectedFollowNFT) {
revert Errors.CallerNotFollowNFT();
}
emit Events.Unfollowed(unfollowerProfileId, idOfProfileUnfollowed, block.timestamp);
}
///////////////////////////////////////////
/// EXTERNAL VIEW FUNCTIONS ///
///////////////////////////////////////////
function isFollowing(uint256 followerProfileId, uint256 followedProfileId) external view returns (bool) {
address followNFT = _profileById[followedProfileId].followNFT;
return followNFT != address(0) && IFollowNFT(followNFT).isFollowing(followerProfileId);
}
/// @inheritdoc ILensHub
function isProfileCreatorWhitelisted(address profileCreator) external view override returns (bool) {
return _profileCreatorWhitelisted[profileCreator];
}
/// @inheritdoc ILensHub
function isFollowModuleWhitelisted(address followModule) external view override returns (bool) {
return _followModuleWhitelisted[followModule];
}
/// @inheritdoc ILensHub
function isReferenceModuleWhitelisted(address referenceModule) external view override returns (bool) {
return _referenceModuleWhitelisted[referenceModule];
}
/// @inheritdoc ILensHub
function isActionModuleWhitelisted(address actionModule) external view override returns (bool) {
return _actionModuleWhitelistedId[actionModule] > 0;
}
/// @inheritdoc ILensHub
function getGovernance() external view override returns (address) {
return _governance;
}
/// @inheritdoc ILensHub
function isDelegatedExecutorApproved(
uint256 delegatorProfileId,
address executor,
uint64 configNumber
) external view returns (bool) {
return StorageLib.getDelegatedExecutorsConfig(delegatorProfileId).isApproved[configNumber][executor];
}
/// @inheritdoc ILensHub
function isDelegatedExecutorApproved(uint256 delegatorProfileId, address executor) external view returns (bool) {
return ProfileLib.isExecutorApproved(delegatorProfileId, executor);
}
/// @inheritdoc ILensHub
function getDelegatedExecutorsConfigNumber(uint256 delegatorProfileId) external view returns (uint64) {
return StorageLib.getDelegatedExecutorsConfig(delegatorProfileId).configNumber;
}
/// @inheritdoc ILensHub
function getDelegatedExecutorsPrevConfigNumber(uint256 delegatorProfileId) external view returns (uint64) {
return StorageLib.getDelegatedExecutorsConfig(delegatorProfileId).prevConfigNumber;
}
/// @inheritdoc ILensHub
function getDelegatedExecutorsMaxConfigNumberSet(uint256 delegatorProfileId) external view returns (uint64) {
return StorageLib.getDelegatedExecutorsConfig(delegatorProfileId).maxConfigNumberSet;
}
/// @inheritdoc ILensHub
function isBlocked(uint256 profileId, uint256 byProfileId) external view returns (bool) {
return _blockedStatus[byProfileId][profileId];
}
/// @inheritdoc ILensHub
function getProfileMetadataURI(uint256 profileId) external view override returns (string memory) {
return StorageLib.getProfile(profileId).metadataURI;
}
/// @inheritdoc ILensHub
function getPubCount(uint256 profileId) external view override returns (uint256) {
return _profileById[profileId].pubCount;
}
/// @inheritdoc ILensHub
function getProfileImageURI(uint256 profileId) external view override returns (string memory) {
return _profileById[profileId].imageURI;
}
/// @inheritdoc ILensHub
function getFollowNFT(uint256 profileId) external view override returns (address) {
return _profileById[profileId].followNFT;
}
/// @inheritdoc ILensHub
function getFollowNFTURI(uint256 profileId) external view override returns (string memory) {
return _profileById[profileId].followNFTURI;
}
/// @inheritdoc ILensHub
function getCollectNFT(uint256 profileId, uint256 pubId) external view override returns (address) {
return _pubByIdByProfile[profileId][pubId].__DEPRECATED__collectNFT;
}
/// @inheritdoc ILensHub
function getFollowModule(uint256 profileId) external view override returns (address) {
return _profileById[profileId].followModule;
}
/// @inheritdoc ILensHub
function getCollectModule(uint256 profileId, uint256 pubId) external view override returns (address) {
return _pubByIdByProfile[profileId][pubId].__DEPRECATED__collectModule;
}
/// @inheritdoc ILensHub
function getReferenceModule(uint256 profileId, uint256 pubId) external view override returns (address) {
return _pubByIdByProfile[profileId][pubId].referenceModule;
}
/// @inheritdoc ILensHub
function getPubPointer(uint256 profileId, uint256 pubId) external view override returns (uint256, uint256) {
return (_pubByIdByProfile[profileId][pubId].pointedProfileId, _pubByIdByProfile[profileId][pubId].pointedPubId);
}
/// @inheritdoc ILensHub
function getContentURI(uint256 profileId, uint256 pubId) external view override returns (string memory) {
// This function is used by the Collect NFTs' tokenURI function.
return PublicationLib.getContentURI(profileId, pubId);
}
/// @inheritdoc ILensHub
function getProfile(uint256 profileId) external view override returns (Types.Profile memory) {
return _profileById[profileId];
}
/// @inheritdoc ILensHub
function getPub(uint256 profileId, uint256 pubId) external view override returns (Types.Publication memory) {
return _pubByIdByProfile[profileId][pubId];
}
/// @inheritdoc ILensHub
function getPublicationType(
uint256 profileId,
uint256 pubId
) external view override returns (Types.PublicationType) {
return PublicationLib.getPublicationType(profileId, pubId);
}
/// @inheritdoc ILensHub
function getFollowNFTImpl() external view override returns (address) {
return FOLLOW_NFT_IMPL;
}
/// @inheritdoc ILensHub
function getCollectNFTImpl() external view override returns (address) {
return COLLECT_NFT_IMPL;
}
function getWhitelistedActionModuleById(uint256 id) external view returns (address) {
return _actionModuleById[id];
}
/**
* @dev Overrides the ERC721 tokenURI function to return the associated URI with a given profile.
*/
function tokenURI(uint256 tokenId) public view override returns (string memory) {
address followNFT = _profileById[tokenId].followNFT;
return
ProfileTokenURILib.getProfileTokenURI(
tokenId,
followNFT == address(0) ? 0 : IERC721Enumerable(followNFT).totalSupply(),
ownerOf(tokenId),
'Lens Profile',
_profileById[tokenId].imageURI
);
}
//////////////////////////////////////
/// INTERNAL FUNCTIONS ///
//////////////////////////////////////
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override whenNotPaused {
// Switches to new fresh delegated executors configuration (except on minting, as it already has a fresh setup).
if (from != address(0)) {
ProfileLib.switchToNewFreshDelegatedExecutorsConfig(tokenId);
}
super._beforeTokenTransfer(from, to, tokenId);
}
function _validateCallerIsGovernance() internal view {
if (msg.sender != _governance) revert Errors.NotGovernance();
}
function getRevision() internal pure virtual override returns (uint256) {
return REVISION;
}
//////////////////////////////////////
/// DEPRECATED FUNCTIONS ///
//////////////////////////////////////
// Deprecated in V2. Kept here just for backwards compatibility with Lens V1 Collect NFTs.
function emitCollectNFTTransferEvent(uint256, uint256, uint256, address, address) external {}
}