mirror of
https://github.com/lens-protocol/core.git
synced 2026-04-22 03:02:03 -04:00
Merge pull request #68 from lens-protocol/fix/referral-system
fix: Referral system
This commit is contained in:
@@ -22,7 +22,7 @@ import {LensHubEventHooks} from 'contracts/base/LensHubEventHooks.sol';
|
||||
|
||||
// Libraries
|
||||
import {ActionLib} from 'contracts/libraries/ActionLib.sol';
|
||||
import {CollectLib} from 'contracts/libraries/CollectLib.sol';
|
||||
import {LegacyCollectLib} from 'contracts/libraries/LegacyCollectLib.sol';
|
||||
import {FollowLib} from 'contracts/libraries/FollowLib.sol';
|
||||
import {GovernanceLib} from 'contracts/libraries/GovernanceLib.sol';
|
||||
import {MetaTxLib} from 'contracts/libraries/MetaTxLib.sol';
|
||||
@@ -440,7 +440,7 @@ contract LensHub is
|
||||
returns (uint256)
|
||||
{
|
||||
return
|
||||
CollectLib.collect({
|
||||
LegacyCollectLib.collect({
|
||||
collectParams: collectParams,
|
||||
transactionExecutor: msg.sender,
|
||||
collectorProfileOwner: ownerOf(collectParams.collectorProfileId),
|
||||
@@ -459,9 +459,9 @@ contract LensHub is
|
||||
onlyProfileOwnerOrDelegatedExecutor(signature.signer, collectParams.collectorProfileId)
|
||||
returns (uint256)
|
||||
{
|
||||
MetaTxLib.validateCollectSignature(signature, collectParams);
|
||||
MetaTxLib.validateLegacyCollectSignature(signature, collectParams);
|
||||
return
|
||||
CollectLib.collect({
|
||||
LegacyCollectLib.collect({
|
||||
collectParams: collectParams,
|
||||
transactionExecutor: signature.signer,
|
||||
collectorProfileOwner: ownerOf(collectParams.collectorProfileId),
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
// 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 {ICollectModule} from 'contracts/interfaces/ICollectModule.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';
|
||||
|
||||
/**
|
||||
* @title CollectLib
|
||||
* @author Lens Protocol
|
||||
*/
|
||||
library CollectLib {
|
||||
using Strings for uint256;
|
||||
|
||||
string constant COLLECT_NFT_NAME_INFIX = '-Collect-';
|
||||
string constant COLLECT_NFT_SYMBOL_INFIX = '-Cl-';
|
||||
|
||||
function collect(
|
||||
Types.CollectParams calldata collectParams,
|
||||
address transactionExecutor,
|
||||
address collectorProfileOwner,
|
||||
address collectNFTImpl
|
||||
) external returns (uint256) {
|
||||
ValidationLib.validateNotBlocked({
|
||||
profile: collectParams.collectorProfileId,
|
||||
byProfile: collectParams.publicationCollectedProfileId
|
||||
});
|
||||
|
||||
address collectModule;
|
||||
Types.PublicationType[] memory referrerPubTypes;
|
||||
uint256 tokenId;
|
||||
address collectNFT;
|
||||
{
|
||||
Types.Publication storage _collectedPublication = StorageLib.getPublication(
|
||||
collectParams.publicationCollectedProfileId,
|
||||
collectParams.publicationCollectedId
|
||||
);
|
||||
collectModule = _collectedPublication.__DEPRECATED__collectModule;
|
||||
if (collectModule == address(0)) {
|
||||
// Doesn't have collectModule, thus it cannot be collected (a mirror or non-existent).
|
||||
revert Errors.CollectNotAllowed();
|
||||
}
|
||||
|
||||
referrerPubTypes = ValidationLib.validateReferrersAndGetReferrersPubTypes(
|
||||
collectParams.referrerProfileIds,
|
||||
collectParams.referrerPubIds,
|
||||
collectParams.publicationCollectedProfileId,
|
||||
collectParams.publicationCollectedId
|
||||
);
|
||||
collectNFT = _getOrDeployCollectNFT(
|
||||
_collectedPublication,
|
||||
collectParams.publicationCollectedProfileId,
|
||||
collectParams.publicationCollectedId,
|
||||
collectNFTImpl
|
||||
);
|
||||
tokenId = ICollectNFT(collectNFT).mint(collectorProfileOwner);
|
||||
}
|
||||
|
||||
_processCollect(
|
||||
collectParams,
|
||||
ProcessCollectParams({
|
||||
transactionExecutor: transactionExecutor,
|
||||
collectorProfileOwner: collectorProfileOwner,
|
||||
referrerPubTypes: referrerPubTypes,
|
||||
collectModule: collectModule
|
||||
})
|
||||
);
|
||||
|
||||
emit Events.Collected({
|
||||
collectActionParams: Types.ProcessActionParams({
|
||||
publicationActedProfileId: collectParams.publicationCollectedProfileId,
|
||||
publicationActedId: collectParams.publicationCollectedId,
|
||||
actorProfileId: collectParams.collectorProfileId,
|
||||
actorProfileOwner: collectorProfileOwner,
|
||||
transactionExecutor: transactionExecutor,
|
||||
referrerProfileIds: collectParams.referrerProfileIds,
|
||||
referrerPubIds: collectParams.referrerPubIds,
|
||||
referrerPubTypes: referrerPubTypes,
|
||||
actionModuleData: collectParams.collectModuleData
|
||||
}),
|
||||
collectModule: collectModule,
|
||||
collectNFT: collectNFT,
|
||||
tokenId: tokenId,
|
||||
collectActionResult: '',
|
||||
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;
|
||||
}
|
||||
|
||||
// Stack too deep, so we need to use a struct.
|
||||
struct ProcessCollectParams {
|
||||
address transactionExecutor;
|
||||
address collectorProfileOwner;
|
||||
Types.PublicationType[] referrerPubTypes;
|
||||
address collectModule;
|
||||
}
|
||||
|
||||
function _processCollect(
|
||||
Types.CollectParams calldata collectParams,
|
||||
ProcessCollectParams memory processCollectParams
|
||||
) private {
|
||||
try
|
||||
ICollectModule(processCollectParams.collectModule).processCollect(
|
||||
Types.ProcessCollectParams({
|
||||
publicationCollectedProfileId: collectParams.publicationCollectedProfileId,
|
||||
publicationCollectedId: collectParams.publicationCollectedId,
|
||||
collectorProfileId: collectParams.collectorProfileId,
|
||||
collectorProfileOwner: processCollectParams.collectorProfileOwner,
|
||||
transactionExecutor: processCollectParams.transactionExecutor,
|
||||
referrerProfileIds: collectParams.referrerProfileIds,
|
||||
referrerPubIds: collectParams.referrerPubIds,
|
||||
referrerPubTypes: processCollectParams.referrerPubTypes,
|
||||
data: collectParams.collectModuleData
|
||||
})
|
||||
)
|
||||
{} catch (bytes memory err) {
|
||||
assembly {
|
||||
// Equivalent to reverting with the returned error selector if
|
||||
// the length is not zero.
|
||||
let length := mload(err)
|
||||
if iszero(iszero(length)) {
|
||||
revert(add(err, 32), length)
|
||||
}
|
||||
}
|
||||
uint256 referrerProfileId;
|
||||
uint256 referrerPubId;
|
||||
if (collectParams.referrerProfileIds.length > 0) {
|
||||
if (collectParams.referrerProfileIds.length > 1) {
|
||||
// Deprecated modules only support one referrer.
|
||||
revert Errors.DeprecaredModulesOnlySupportOneReferrer();
|
||||
}
|
||||
// Only one referral was passed.
|
||||
referrerProfileId = collectParams.referrerProfileIds[0];
|
||||
referrerPubId = collectParams.referrerPubIds[0];
|
||||
}
|
||||
ILegacyCollectModule(processCollectParams.collectModule).processCollect(
|
||||
collectParams.publicationCollectedProfileId,
|
||||
processCollectParams.transactionExecutor,
|
||||
referrerProfileId,
|
||||
referrerPubId,
|
||||
collectParams.collectModuleData
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
string memory collectNFTName = string(
|
||||
abi.encodePacked(profileId.toString(), COLLECT_NFT_NAME_INFIX, pubId.toString())
|
||||
);
|
||||
string memory collectNFTSymbol = string(
|
||||
abi.encodePacked(profileId.toString(), COLLECT_NFT_SYMBOL_INFIX, pubId.toString())
|
||||
);
|
||||
|
||||
ICollectNFT(collectNFT).initialize(profileId, pubId, collectNFTName, collectNFTSymbol);
|
||||
emit Events.CollectNFTDeployed(profileId, pubId, collectNFT, block.timestamp);
|
||||
|
||||
return collectNFT;
|
||||
}
|
||||
}
|
||||
158
contracts/libraries/LegacyCollectLib.sol
Normal file
158
contracts/libraries/LegacyCollectLib.sol
Normal file
@@ -0,0 +1,158 @@
|
||||
// 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 {ICollectModule} from 'contracts/interfaces/ICollectModule.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 {PublicationLib} from 'contracts/libraries/PublicationLib.sol';
|
||||
|
||||
/**
|
||||
* @title LegacyCollectLib
|
||||
* @author Lens Protocol
|
||||
* @notice Library containing the logic for legacy collect operation.
|
||||
*/
|
||||
library LegacyCollectLib {
|
||||
using Strings for uint256;
|
||||
|
||||
string constant COLLECT_NFT_NAME_INFIX = '-Collect-';
|
||||
string constant COLLECT_NFT_SYMBOL_INFIX = '-Cl-';
|
||||
|
||||
/**
|
||||
* @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 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 collectModuleData The data passed to the collect module's collect action. This is ABI-encoded and depends
|
||||
* on the collect module chosen.
|
||||
* @param timestamp The current block timestamp.
|
||||
*/
|
||||
event CollectedLegacy(
|
||||
uint256 indexed publicationCollectedProfileId,
|
||||
uint256 indexed publicationCollectedId,
|
||||
address transactionExecutor,
|
||||
uint256 referrerProfileId,
|
||||
uint256 referrerPubId,
|
||||
bytes collectModuleData,
|
||||
uint256 timestamp
|
||||
);
|
||||
|
||||
function collect(
|
||||
Types.CollectParams 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);
|
||||
}
|
||||
|
||||
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,
|
||||
transactionExecutor: transactionExecutor,
|
||||
referrerProfileId: collectParams.referrerProfileId,
|
||||
referrerPubId: collectParams.referrerPubId,
|
||||
collectModuleData: collectParams.collectModuleData,
|
||||
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);
|
||||
|
||||
string memory collectNFTName = string(
|
||||
abi.encodePacked(profileId.toString(), COLLECT_NFT_NAME_INFIX, pubId.toString())
|
||||
);
|
||||
string memory collectNFTSymbol = string(
|
||||
abi.encodePacked(profileId.toString(), COLLECT_NFT_SYMBOL_INFIX, pubId.toString())
|
||||
);
|
||||
|
||||
ICollectNFT(collectNFT).initialize(profileId, pubId, collectNFTName, collectNFTSymbol);
|
||||
emit Events.CollectNFTDeployed(profileId, pubId, collectNFT, block.timestamp);
|
||||
|
||||
return collectNFT;
|
||||
}
|
||||
}
|
||||
@@ -398,7 +398,7 @@ library MetaTxLib {
|
||||
);
|
||||
}
|
||||
|
||||
function validateCollectSignature(
|
||||
function validateLegacyCollectSignature(
|
||||
Types.EIP712Signature calldata signature,
|
||||
Types.CollectParams calldata collectParams
|
||||
) external {
|
||||
@@ -406,12 +406,12 @@ library MetaTxLib {
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
Typehash.COLLECT,
|
||||
Typehash.LEGACY_COLLECT,
|
||||
collectParams.publicationCollectedProfileId,
|
||||
collectParams.publicationCollectedId,
|
||||
collectParams.collectorProfileId,
|
||||
collectParams.referrerProfileIds,
|
||||
collectParams.referrerPubIds,
|
||||
collectParams.referrerProfileId,
|
||||
collectParams.referrerPubId,
|
||||
keccak256(collectParams.collectModuleData),
|
||||
_getAndIncrementNonce(signature.signer),
|
||||
signature.deadline
|
||||
|
||||
@@ -259,7 +259,7 @@ library PublicationLib {
|
||||
}
|
||||
|
||||
function _fillReferencePublicationStorage(
|
||||
Types.ReferencePubParams memory referencePubParams,
|
||||
Types.ReferencePubParams calldata referencePubParams,
|
||||
Types.PublicationType referencePubType
|
||||
) private returns (uint256) {
|
||||
uint256 pubIdAssigned = ++StorageLib.getProfile(referencePubParams.profileId).pubCount;
|
||||
@@ -273,14 +273,20 @@ library PublicationLib {
|
||||
referencePubParams.pointedProfileId,
|
||||
referencePubParams.pointedPubId
|
||||
);
|
||||
if (_pubPointed.pubType == Types.PublicationType.Post) {
|
||||
Types.PublicationType pubPointedType = _pubPointed.pubType;
|
||||
if (pubPointedType == Types.PublicationType.Post) {
|
||||
// The publication pointed is a Lens V2 post.
|
||||
_referencePub.rootProfileId = referencePubParams.pointedProfileId;
|
||||
_referencePub.rootPubId = referencePubParams.pointedPubId;
|
||||
} else {
|
||||
// The publication pointed is either a comment or a quote.
|
||||
} else if (pubPointedType == Types.PublicationType.Comment || pubPointedType == Types.PublicationType.Quote) {
|
||||
// The publication pointed is either a Lens V2 comment or a Lens V2 quote.
|
||||
// Note that even when the publication pointed is a V2 one, it will lack `rootProfileId` and `rootPubId` if
|
||||
// there is a Lens V1 Legacy publication in the thread of interactions (including the root post itself).
|
||||
_referencePub.rootProfileId = _pubPointed.rootProfileId;
|
||||
_referencePub.rootPubId = _pubPointed.rootPubId;
|
||||
}
|
||||
// Otherwise the root is not filled, as the pointed publication is a Lens V1 Legacy publication, which does not
|
||||
// support Lens V2 referral system.
|
||||
return pubIdAssigned;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,8 +87,8 @@ library ValidationLib {
|
||||
function validateReferrersAndGetReferrersPubTypes(
|
||||
uint256[] memory referrerProfileIds,
|
||||
uint256[] memory referrerPubIds,
|
||||
uint256 profileId,
|
||||
uint256 pubId
|
||||
uint256 targetedProfileId,
|
||||
uint256 targetedPubId
|
||||
) internal view returns (Types.PublicationType[] memory) {
|
||||
if (referrerProfileIds.length != referrerPubIds.length) {
|
||||
revert Errors.ArrayMismatch();
|
||||
@@ -105,8 +105,8 @@ library ValidationLib {
|
||||
referrerPubTypes[i] = _validateReferrerAndGetReferrerPubType(
|
||||
referrerProfileId,
|
||||
referrerPubId,
|
||||
profileId,
|
||||
pubId
|
||||
targetedProfileId,
|
||||
targetedPubId
|
||||
);
|
||||
unchecked {
|
||||
i++;
|
||||
@@ -115,15 +115,34 @@ library ValidationLib {
|
||||
return referrerPubTypes;
|
||||
}
|
||||
|
||||
function validateLegacyCollectReferrer(
|
||||
uint256 referrerProfileId,
|
||||
uint256 referrerPubId,
|
||||
uint256 publicationCollectedProfileId,
|
||||
uint256 publicationCollectedId
|
||||
) external view {
|
||||
if (PublicationLib.getPublicationType(referrerProfileId, referrerPubId) != Types.PublicationType.Mirror) {
|
||||
revert Errors.InvalidReferrer();
|
||||
}
|
||||
_validateReferrerAsMirror(
|
||||
referrerProfileId,
|
||||
referrerPubId,
|
||||
publicationCollectedProfileId,
|
||||
publicationCollectedId
|
||||
);
|
||||
}
|
||||
|
||||
function _validateReferrerAndGetReferrerPubType(
|
||||
uint256 referrerProfileId,
|
||||
uint256 referrerPubId,
|
||||
uint256 profileId,
|
||||
uint256 pubId
|
||||
uint256 targetedProfileId,
|
||||
uint256 targetedPubId
|
||||
) private view returns (Types.PublicationType) {
|
||||
if (referrerPubId == 0) {
|
||||
// Unchecked/Unverified referral. Profile referrer, not attached to a publication.
|
||||
if (StorageLib.getTokenData(referrerProfileId).owner == address(0) || referrerProfileId == profileId) {
|
||||
if (
|
||||
StorageLib.getTokenData(referrerProfileId).owner == address(0) || referrerProfileId == targetedProfileId
|
||||
) {
|
||||
revert Errors.InvalidReferrer();
|
||||
}
|
||||
return Types.PublicationType.Nonexistent;
|
||||
@@ -131,19 +150,19 @@ library ValidationLib {
|
||||
// Checked/Verified referral. Publication referrer.
|
||||
if (
|
||||
// Cannot pass itself as a referrer.
|
||||
referrerProfileId == profileId && referrerPubId == pubId
|
||||
referrerProfileId == targetedProfileId && referrerPubId == targetedPubId
|
||||
) {
|
||||
revert Errors.InvalidReferrer();
|
||||
}
|
||||
Types.PublicationType referrerPubType = PublicationLib.getPublicationType(referrerProfileId, referrerPubId);
|
||||
if (referrerPubType == Types.PublicationType.Mirror) {
|
||||
_validateReferrerAsMirror(referrerProfileId, referrerPubId, profileId, pubId);
|
||||
_validateReferrerAsMirror(referrerProfileId, referrerPubId, targetedProfileId, targetedPubId);
|
||||
} else if (
|
||||
referrerPubType == Types.PublicationType.Comment || referrerPubType == Types.PublicationType.Quote
|
||||
) {
|
||||
_validateReferrerAsCommentOrQuote(referrerProfileId, referrerPubId, profileId, pubId);
|
||||
_validateReferrerAsCommentOrQuote(referrerProfileId, referrerPubId, targetedProfileId, targetedPubId);
|
||||
} else if (referrerPubType == Types.PublicationType.Post) {
|
||||
_validateReferrerAsPost(referrerProfileId, referrerPubId, profileId, pubId);
|
||||
_validateReferrerAsPost(referrerProfileId, referrerPubId, targetedProfileId, targetedPubId);
|
||||
} else {
|
||||
revert Errors.InvalidReferrer();
|
||||
}
|
||||
@@ -154,16 +173,13 @@ library ValidationLib {
|
||||
function _validateReferrerAsPost(
|
||||
uint256 referrerProfileId,
|
||||
uint256 referrerPubId,
|
||||
uint256 profileId,
|
||||
uint256 pubId
|
||||
uint256 targetedProfileId,
|
||||
uint256 targetedPubId
|
||||
) private view {
|
||||
Types.Publication storage _publication = StorageLib.getPublication(profileId, pubId);
|
||||
if (
|
||||
// Publication being collected/referenced is not pointing to the referrer post and...
|
||||
(_publication.pointedProfileId != referrerProfileId || _publication.pointedPubId != referrerPubId) &&
|
||||
// ...publication being collected/referenced does not have the referrer post as the root.
|
||||
(_publication.rootProfileId != referrerProfileId || _publication.rootPubId != referrerPubId)
|
||||
) {
|
||||
Types.Publication storage _targetedPub = StorageLib.getPublication(targetedProfileId, targetedPubId);
|
||||
// Publication targeted must have the referrer post as the root. This enables the use case of rewarding the
|
||||
// root publication for an action over any of its descendants.
|
||||
if (_targetedPub.rootProfileId != referrerProfileId || _targetedPub.rootPubId != referrerPubId) {
|
||||
revert Errors.InvalidReferrer();
|
||||
}
|
||||
}
|
||||
@@ -171,13 +187,13 @@ library ValidationLib {
|
||||
function _validateReferrerAsMirror(
|
||||
uint256 referrerProfileId,
|
||||
uint256 referrerPubId,
|
||||
uint256 profileId,
|
||||
uint256 pubId
|
||||
uint256 targetedProfileId,
|
||||
uint256 targetedPubId
|
||||
) private view {
|
||||
Types.Publication storage _referrerMirror = StorageLib.getPublication(referrerProfileId, referrerPubId);
|
||||
if (
|
||||
// A mirror can only be a referrer of a publication if it is pointing to it.
|
||||
_referrerMirror.pointedProfileId != profileId || _referrerMirror.pointedPubId != pubId
|
||||
_referrerMirror.pointedProfileId != targetedProfileId || _referrerMirror.pointedPubId != targetedPubId
|
||||
) {
|
||||
revert Errors.InvalidReferrer();
|
||||
}
|
||||
@@ -188,30 +204,35 @@ library ValidationLib {
|
||||
*
|
||||
* @param referrerProfileId The profile id of the referrer.
|
||||
* @param referrerPubId The publication id of the referrer.
|
||||
* @param profileId This is the ID of the profile who authored the publication being collected or referenced.
|
||||
* @param pubId This is the pub user collects or references.
|
||||
* @param targetedProfileId The ID of the profile who authored the publication being acted or referenced.
|
||||
* @param targetedPubId The pub ID being acted or referenced.
|
||||
*/
|
||||
function _validateReferrerAsCommentOrQuote(
|
||||
uint256 referrerProfileId,
|
||||
uint256 referrerPubId,
|
||||
uint256 profileId,
|
||||
uint256 pubId
|
||||
uint256 targetedProfileId,
|
||||
uint256 targetedPubId
|
||||
) private view {
|
||||
Types.Publication storage _referrerPub = StorageLib.getPublication(referrerProfileId, referrerPubId);
|
||||
Types.PublicationType typeOfPubPointedByReferrer = PublicationLib.getPublicationType(profileId, pubId);
|
||||
// We already know that the publication being collected/referenced is not a mirror nor a non-existent one.
|
||||
if (typeOfPubPointedByReferrer == Types.PublicationType.Post) {
|
||||
// If the publication collected/referenced is a post, the referrer comment/quote must have it as the root.
|
||||
if (_referrerPub.rootProfileId != profileId || _referrerPub.rootPubId != pubId) {
|
||||
Types.PublicationType typeOfTargetedPub = PublicationLib.getPublicationType(targetedProfileId, targetedPubId);
|
||||
// We already know that the publication being acted/referenced is not a mirror nor a non-existent one.
|
||||
if (typeOfTargetedPub == Types.PublicationType.Post) {
|
||||
// If the publication acted/referenced is a post, the referrer comment/quote must have it as the root.
|
||||
if (_referrerPub.rootProfileId != targetedProfileId || _referrerPub.rootPubId != targetedPubId) {
|
||||
revert Errors.InvalidReferrer();
|
||||
}
|
||||
} else {
|
||||
// The publication collected/referenced is a comment or a quote.
|
||||
Types.Publication storage _pubPointedByReferrer = StorageLib.getPublication(profileId, pubId);
|
||||
// The referrer publication and the collected/referenced publication must share the same root.
|
||||
// The publication acted/referenced is a comment or a quote.
|
||||
Types.Publication storage _targetedPub = StorageLib.getPublication(targetedProfileId, targetedPubId);
|
||||
if (
|
||||
_referrerPub.rootProfileId != _pubPointedByReferrer.rootProfileId ||
|
||||
_referrerPub.rootPubId != _pubPointedByReferrer.rootPubId
|
||||
// Targeted pub must be a "pure" Lens V2 comment/quote, which means there is no Lens V1 Legacy comment
|
||||
// or post on its tree of interactions, and its root pub is filled.
|
||||
// Otherwise, two Lens V2 "non-pure" publications could be passed as a referrer to each other,
|
||||
// even without having any interaction in common.
|
||||
_targetedPub.rootPubId == 0 ||
|
||||
// The referrer publication and the acted/referenced publication must share the same root.
|
||||
_referrerPub.rootProfileId != _targetedPub.rootProfileId ||
|
||||
_referrerPub.rootPubId != _targetedPub.rootPubId
|
||||
) {
|
||||
revert Errors.InvalidReferrer();
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ library Errors {
|
||||
error NotFollowing();
|
||||
error SelfFollow();
|
||||
error InvalidReferrer();
|
||||
error DeprecaredModulesOnlySupportOneReferrer();
|
||||
error InvalidPointedPub();
|
||||
error NonERC721ReceiverImplementer();
|
||||
|
||||
@@ -38,7 +37,7 @@ library Errors {
|
||||
error InitParamsInvalid();
|
||||
error ActionNotAllowed();
|
||||
|
||||
error CollectNotAllowed(); // Used in CollectLib (pending deprecation)
|
||||
error CollectNotAllowed(); // Used in LegacyCollectLib (pending deprecation)
|
||||
|
||||
// MultiState Errors
|
||||
error Paused();
|
||||
|
||||
@@ -10,7 +10,7 @@ library Typehash {
|
||||
|
||||
bytes32 constant CHANGE_DELEGATED_EXECUTORS_CONFIG = keccak256('ChangeDelegatedExecutorsConfig(uint256 delegatorProfileId,address[] delegatedExecutors,bool[] approvals,uint64 configNumber,bool switchToGivenConfig,uint256 nonce,uint256 deadline)');
|
||||
|
||||
bytes32 constant COLLECT = keccak256('Collect(uint256 publicationCollectedProfileId,uint256 publicationCollectedId,uint256 collectorProfileId,uint256[] referrerProfileIds,uint256[] referrerPubIds,bytes collectModuleData,uint256 nonce,uint256 deadline)');
|
||||
bytes32 constant LEGACY_COLLECT = keccak256('Collect(uint256 publicationCollectedProfileId,uint256 publicationCollectedId,uint256 collectorProfileId,uint256 referrerProfileId,uint256 referrerPubId,bytes collectModuleData,uint256 nonce,uint256 deadline)');
|
||||
|
||||
bytes32 constant COMMENT = keccak256('Comment(uint256 profileId,string contentURI,uint256 pointedProfileId,uint256 pointedPubId,uint256[] referrerProfileIds,uint256[] referrerPubIds,bytes referenceModuleData,address collectModule,bytes collectModuleInitData,address referenceModule,bytes referenceModuleInitData,uint256 nonce,uint256 deadline)');
|
||||
|
||||
|
||||
@@ -261,21 +261,22 @@ library Types {
|
||||
|
||||
/**
|
||||
* Deprecated in V2: Will be removed after some time after upgrading to V2.
|
||||
* @notice A struct containing the parameters required for the `collect()` function.
|
||||
* @notice A struct containing the parameters required for the legacy `collect()` function.
|
||||
* @dev The referrer can only be a mirror of the publication being collected.
|
||||
*
|
||||
* @param publicationCollectedProfileId The token ID of the profile that published the publication to collect.
|
||||
* @param publicationCollectedId The publication to collect's publication ID.
|
||||
* @param collectorProfileId The collector profile.
|
||||
* @param referrerProfileId
|
||||
* @param referrerPubId
|
||||
* @param referrerProfileId The ID of a profile that authored a mirror that helped discovering the collected pub.
|
||||
* @param referrerPubId The ID of the mirror that helped discovering the collected pub.
|
||||
* @param collectModuleData The arbitrary data to pass to the collectModule if needed.
|
||||
*/
|
||||
struct CollectParams {
|
||||
uint256 publicationCollectedProfileId;
|
||||
uint256 publicationCollectedId;
|
||||
uint256 collectorProfileId;
|
||||
uint256[] referrerProfileIds;
|
||||
uint256[] referrerPubIds;
|
||||
uint256 referrerProfileId;
|
||||
uint256 referrerPubId;
|
||||
bytes collectModuleData;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ test = 'test/foundry'
|
||||
cache_path = 'forge-cache'
|
||||
fs_permissions = [{ access = "read-write", path = "./"}]
|
||||
optimizer = true
|
||||
optimizer_runs = 65
|
||||
optimizer_runs = 10
|
||||
ignored_error_codes = []
|
||||
|
||||
[rpc_endpoints]
|
||||
|
||||
@@ -311,8 +311,8 @@ contract UpgradeForkTest is BaseTest {
|
||||
publicationCollectedProfileId: profileId,
|
||||
publicationCollectedId: 1,
|
||||
collectorProfileId: profileId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerProfileId: 0,
|
||||
referrerPubId: 0,
|
||||
collectModuleData: ''
|
||||
})
|
||||
);
|
||||
@@ -321,8 +321,8 @@ contract UpgradeForkTest is BaseTest {
|
||||
publicationCollectedProfileId: profileId,
|
||||
publicationCollectedId: 2,
|
||||
collectorProfileId: profileId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerProfileId: 0,
|
||||
referrerPubId: 0,
|
||||
collectModuleData: ''
|
||||
})
|
||||
);
|
||||
@@ -331,8 +331,8 @@ contract UpgradeForkTest is BaseTest {
|
||||
publicationCollectedProfileId: profileId,
|
||||
publicationCollectedId: 3,
|
||||
collectorProfileId: profileId,
|
||||
referrerProfileIds: _emptyUint256Array(),
|
||||
referrerPubIds: _emptyUint256Array(),
|
||||
referrerProfileId: 0,
|
||||
referrerPubId: 0,
|
||||
collectModuleData: ''
|
||||
})
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user