mirror of
https://github.com/lens-protocol/core.git
synced 2026-01-11 07:08:09 -05:00
176 lines
6.7 KiB
Solidity
176 lines
6.7 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
|
|
pragma solidity ^0.8.15;
|
|
|
|
import {IFollowModule} from 'contracts/interfaces/IFollowModule.sol';
|
|
import {ValidationLib} from 'contracts/libraries/ValidationLib.sol';
|
|
import {Types} from 'contracts/libraries/constants/Types.sol';
|
|
import {Errors} from 'contracts/libraries/constants/Errors.sol';
|
|
import {Events} from 'contracts/libraries/constants/Events.sol';
|
|
import {StorageLib} from 'contracts/libraries/StorageLib.sol';
|
|
import {IFollowNFT} from 'contracts/interfaces/IFollowNFT.sol';
|
|
import {FollowNFTProxy} from 'contracts/base/upgradeability/FollowNFTProxy.sol';
|
|
|
|
library FollowLib {
|
|
function follow(
|
|
uint256 followerProfileId,
|
|
address transactionExecutor,
|
|
uint256[] calldata idsOfProfilesToFollow,
|
|
uint256[] calldata followTokenIds,
|
|
bytes[] calldata followModuleDatas
|
|
) external returns (uint256[] memory) {
|
|
if (
|
|
idsOfProfilesToFollow.length != followTokenIds.length ||
|
|
idsOfProfilesToFollow.length != followModuleDatas.length
|
|
) {
|
|
revert Errors.ArrayMismatch();
|
|
}
|
|
uint256[] memory followTokenIdsAssigned = new uint256[](idsOfProfilesToFollow.length);
|
|
uint256 i;
|
|
while (i < idsOfProfilesToFollow.length) {
|
|
ValidationLib.validateProfileExists({profileId: idsOfProfilesToFollow[i]});
|
|
|
|
ValidationLib.validateNotBlocked({
|
|
profile: followerProfileId,
|
|
byProfile: idsOfProfilesToFollow[i],
|
|
unidirectionalCheck: true // We allow to follow a blocked profile. Rest of interactions are restricted.
|
|
});
|
|
|
|
if (followerProfileId == idsOfProfilesToFollow[i]) {
|
|
revert Errors.SelfFollow();
|
|
}
|
|
|
|
followTokenIdsAssigned[i] = _follow({
|
|
followerProfileId: followerProfileId,
|
|
transactionExecutor: transactionExecutor,
|
|
idOfProfileToFollow: idsOfProfilesToFollow[i],
|
|
followTokenId: followTokenIds[i],
|
|
followModuleData: followModuleDatas[i]
|
|
});
|
|
|
|
unchecked {
|
|
++i;
|
|
}
|
|
}
|
|
return followTokenIdsAssigned;
|
|
}
|
|
|
|
function unfollow(
|
|
uint256 unfollowerProfileId,
|
|
address transactionExecutor,
|
|
uint256[] calldata idsOfProfilesToUnfollow
|
|
) external {
|
|
uint256 i;
|
|
while (i < idsOfProfilesToUnfollow.length) {
|
|
uint256 idOfProfileToUnfollow = idsOfProfilesToUnfollow[i];
|
|
|
|
address followNFT = StorageLib.getProfile(idOfProfileToUnfollow).followNFT;
|
|
|
|
// We don't validate the profile exists because we want to allow unfollowing a burnt profile.
|
|
// Because, if the profile never existed, followNFT will be address(0) and the call will revert.
|
|
if (followNFT == address(0)) {
|
|
revert Errors.NotFollowing();
|
|
}
|
|
|
|
IFollowNFT(followNFT).unfollow(unfollowerProfileId);
|
|
|
|
emit Events.Unfollowed(unfollowerProfileId, idOfProfileToUnfollow, transactionExecutor, block.timestamp);
|
|
|
|
unchecked {
|
|
++i;
|
|
}
|
|
}
|
|
}
|
|
|
|
function isFollowing(uint256 followerProfileId, uint256 followedProfileId) internal view returns (bool) {
|
|
address followNFT = StorageLib.getProfile(followedProfileId).followNFT;
|
|
return followNFT != address(0) && IFollowNFT(followNFT).isFollowing(followerProfileId);
|
|
}
|
|
|
|
/**
|
|
* @notice Deploys the given profile's Follow NFT contract.
|
|
*
|
|
* @param profileId The token ID of the profile which Follow NFT should be deployed.
|
|
*
|
|
* @return address The address of the deployed Follow NFT contract.
|
|
*/
|
|
function _deployFollowNFT(uint256 profileId) private returns (address) {
|
|
bytes memory functionData = abi.encodeCall(IFollowNFT.initialize, profileId);
|
|
address followNFT = address(new FollowNFTProxy(functionData));
|
|
emit Events.FollowNFTDeployed(profileId, followNFT, block.timestamp);
|
|
|
|
return followNFT;
|
|
}
|
|
|
|
function _follow(
|
|
uint256 followerProfileId,
|
|
address transactionExecutor,
|
|
uint256 idOfProfileToFollow,
|
|
uint256 followTokenId,
|
|
bytes calldata followModuleData
|
|
) private returns (uint256) {
|
|
Types.Profile storage _profileToFollow = StorageLib.getProfile(idOfProfileToFollow);
|
|
|
|
address followNFT = _profileToFollow.followNFT;
|
|
if (followNFT == address(0)) {
|
|
followNFT = _deployFollowNFT(idOfProfileToFollow);
|
|
_profileToFollow.followNFT = followNFT;
|
|
}
|
|
|
|
return
|
|
_processFollow(
|
|
ProcessFollowParams({
|
|
followNFT: followNFT,
|
|
followerProfileId: followerProfileId,
|
|
transactionExecutor: transactionExecutor,
|
|
idOfProfileToFollow: idOfProfileToFollow,
|
|
followTokenId: followTokenId,
|
|
followModule: _profileToFollow.followModule,
|
|
followModuleData: followModuleData
|
|
})
|
|
);
|
|
}
|
|
|
|
// Struct defined for the sole purpose of avoiding 'stack too deep' error.
|
|
struct ProcessFollowParams {
|
|
address followNFT;
|
|
uint256 followerProfileId;
|
|
address transactionExecutor;
|
|
uint256 idOfProfileToFollow;
|
|
uint256 followTokenId;
|
|
address followModule;
|
|
bytes followModuleData;
|
|
}
|
|
|
|
function _processFollow(ProcessFollowParams memory processFollowParams) private returns (uint256) {
|
|
uint256 followTokenIdAssigned = IFollowNFT(processFollowParams.followNFT).follow({
|
|
followerProfileId: processFollowParams.followerProfileId,
|
|
transactionExecutor: processFollowParams.transactionExecutor,
|
|
followTokenId: processFollowParams.followTokenId
|
|
});
|
|
|
|
bytes memory processFollowModuleReturnData;
|
|
if (processFollowParams.followModule != address(0)) {
|
|
processFollowModuleReturnData = IFollowModule(processFollowParams.followModule).processFollow(
|
|
processFollowParams.followerProfileId,
|
|
processFollowParams.followTokenId,
|
|
processFollowParams.transactionExecutor,
|
|
processFollowParams.idOfProfileToFollow,
|
|
processFollowParams.followModuleData
|
|
);
|
|
}
|
|
|
|
emit Events.Followed(
|
|
processFollowParams.followerProfileId,
|
|
processFollowParams.idOfProfileToFollow,
|
|
followTokenIdAssigned,
|
|
processFollowParams.followModuleData,
|
|
processFollowModuleReturnData,
|
|
processFollowParams.transactionExecutor,
|
|
block.timestamp
|
|
);
|
|
|
|
return followTokenIdAssigned;
|
|
}
|
|
}
|