mirror of
https://github.com/lens-protocol/core.git
synced 2026-01-10 14:48:15 -05:00
177 lines
7.8 KiB
Solidity
177 lines
7.8 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
|
|
pragma solidity ^0.8.15;
|
|
|
|
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 {ICollectNFT} from 'contracts/interfaces/ICollectNFT.sol';
|
|
import {ILegacyCollectModule} from 'contracts/interfaces/ILegacyCollectModule.sol';
|
|
import {Clones} from '@openzeppelin/contracts/proxy/Clones.sol';
|
|
import {Strings} from '@openzeppelin/contracts/utils/Strings.sol';
|
|
import {StorageLib} from 'contracts/libraries/StorageLib.sol';
|
|
import {FollowLib} from 'contracts/libraries/FollowLib.sol';
|
|
|
|
/**
|
|
* @title LegacyCollectLib
|
|
* @author Lens Protocol
|
|
* @notice Library containing the logic for legacy collect operation.
|
|
*/
|
|
library LegacyCollectLib {
|
|
using Strings for uint256;
|
|
|
|
/**
|
|
* @dev Emitted upon a successful legacy collect action.
|
|
*
|
|
* @param publicationCollectedProfileId The profile ID of the publication being collected.
|
|
* @param publicationCollectedId The publication ID of the publication being collected.
|
|
* @param collectorProfileId The profile ID of the profile that collected the publication.
|
|
* @param transactionExecutor The address of the account that executed the collect transaction.
|
|
* @param referrerProfileId The profile ID of the referrer, if any. Zero if no referrer.
|
|
* @param referrerPubId The publication ID of the referrer, if any. Zero if no referrer.
|
|
* @param collectModule The address of the collect module that was used to collect the publication.
|
|
* @param collectModuleData The data passed to the collect module's collect action. This is ABI-encoded and depends
|
|
* on the collect module chosen.
|
|
* @param tokenId The token ID of the collect NFT that was minted as a collect of the publication.
|
|
* @param timestamp The current block timestamp.
|
|
*/
|
|
event CollectedLegacy(
|
|
uint256 indexed publicationCollectedProfileId,
|
|
uint256 indexed publicationCollectedId,
|
|
uint256 indexed collectorProfileId,
|
|
address transactionExecutor,
|
|
uint256 referrerProfileId,
|
|
uint256 referrerPubId,
|
|
address collectModule,
|
|
bytes collectModuleData,
|
|
uint256 tokenId,
|
|
uint256 timestamp
|
|
);
|
|
|
|
function collect(
|
|
Types.LegacyCollectParams calldata collectParams,
|
|
address transactionExecutor,
|
|
address collectorProfileOwner,
|
|
address collectNFTImpl
|
|
) external returns (uint256) {
|
|
ValidationLib.validateNotBlocked({
|
|
profile: collectParams.collectorProfileId,
|
|
byProfile: collectParams.publicationCollectedProfileId
|
|
});
|
|
|
|
address collectModule;
|
|
uint256 tokenId;
|
|
address collectNFT;
|
|
{
|
|
Types.Publication storage _collectedPublication = StorageLib.getPublication(
|
|
collectParams.publicationCollectedProfileId,
|
|
collectParams.publicationCollectedId
|
|
);
|
|
// This is a legacy collect operation, so we get the collect module from the deprecated storage field.
|
|
collectModule = _collectedPublication.__DEPRECATED__collectModule;
|
|
if (collectModule == address(0)) {
|
|
// It doesn't have collect module, thus it cannot be collected (a mirror or non-existent).
|
|
revert Errors.CollectNotAllowed();
|
|
}
|
|
|
|
if (collectParams.referrerProfileId != 0 || collectParams.referrerPubId != 0) {
|
|
ValidationLib.validateLegacyCollectReferrer(
|
|
collectParams.referrerProfileId,
|
|
collectParams.referrerPubId,
|
|
collectParams.publicationCollectedProfileId,
|
|
collectParams.publicationCollectedId
|
|
);
|
|
}
|
|
|
|
collectNFT = _getOrDeployCollectNFT(
|
|
_collectedPublication,
|
|
collectParams.publicationCollectedProfileId,
|
|
collectParams.publicationCollectedId,
|
|
collectNFTImpl
|
|
);
|
|
tokenId = ICollectNFT(collectNFT).mint(collectorProfileOwner);
|
|
}
|
|
|
|
_prefillLegacyCollectFollowValidationHelper({
|
|
profileId: collectParams.publicationCollectedProfileId,
|
|
collectorProfileId: collectParams.collectorProfileId,
|
|
collector: transactionExecutor
|
|
});
|
|
|
|
ILegacyCollectModule(collectModule).processCollect({
|
|
// Legacy collect modules expect referrer profile ID to match the collected pub's author if no referrer set.
|
|
referrerProfileId: collectParams.referrerProfileId == 0
|
|
? collectParams.publicationCollectedProfileId
|
|
: collectParams.referrerProfileId,
|
|
// Collect NFT is minted to the `collectorProfileOwner`. Some follow-based constraints are expected to be
|
|
// broken in legacy collect modules if the `transactionExecutor` does not match the `collectorProfileOwner`.
|
|
collector: transactionExecutor,
|
|
profileId: collectParams.publicationCollectedProfileId,
|
|
pubId: collectParams.publicationCollectedId,
|
|
data: collectParams.collectModuleData
|
|
});
|
|
|
|
emit CollectedLegacy({
|
|
publicationCollectedProfileId: collectParams.publicationCollectedProfileId,
|
|
publicationCollectedId: collectParams.publicationCollectedId,
|
|
collectorProfileId: collectParams.collectorProfileId,
|
|
transactionExecutor: transactionExecutor,
|
|
referrerProfileId: collectParams.referrerProfileId,
|
|
referrerPubId: collectParams.referrerPubId,
|
|
collectModule: collectModule,
|
|
collectModuleData: collectParams.collectModuleData,
|
|
tokenId: tokenId,
|
|
timestamp: block.timestamp
|
|
});
|
|
|
|
return tokenId;
|
|
}
|
|
|
|
function _getOrDeployCollectNFT(
|
|
Types.Publication storage _collectedPublication,
|
|
uint256 publicationCollectedProfileId,
|
|
uint256 publicationCollectedId,
|
|
address collectNFTImpl
|
|
) private returns (address) {
|
|
address collectNFT = _collectedPublication.__DEPRECATED__collectNFT;
|
|
if (collectNFT == address(0)) {
|
|
collectNFT = _deployCollectNFT(publicationCollectedProfileId, publicationCollectedId, collectNFTImpl);
|
|
_collectedPublication.__DEPRECATED__collectNFT = collectNFT;
|
|
}
|
|
return collectNFT;
|
|
}
|
|
|
|
/**
|
|
* @notice Deploys the given profile's Collect NFT contract.
|
|
*
|
|
* @param profileId The token ID of the profile which Collect NFT should be deployed.
|
|
* @param pubId The publication ID of the publication being collected, which Collect NFT should be deployed.
|
|
* @param collectNFTImpl The address of the Collect NFT implementation that should be used for the deployment.
|
|
*
|
|
* @return address The address of the deployed Collect NFT contract.
|
|
*/
|
|
function _deployCollectNFT(uint256 profileId, uint256 pubId, address collectNFTImpl) private returns (address) {
|
|
address collectNFT = Clones.clone(collectNFTImpl);
|
|
|
|
ICollectNFT(collectNFT).initialize(profileId, pubId);
|
|
emit Events.LegacyCollectNFTDeployed(profileId, pubId, collectNFT, block.timestamp);
|
|
|
|
return collectNFT;
|
|
}
|
|
|
|
function _prefillLegacyCollectFollowValidationHelper(
|
|
uint256 profileId,
|
|
uint256 collectorProfileId,
|
|
address collector
|
|
) private {
|
|
// It's important to set it as zero if not following, as the storage could be dirty from a previous transaction.
|
|
StorageLib.legacyCollectFollowValidationHelper()[collector] = FollowLib.isFollowing({
|
|
followerProfileId: collectorProfileId,
|
|
followedProfileId: profileId
|
|
})
|
|
? profileId
|
|
: 0;
|
|
}
|
|
}
|