misc: Created deprecated modules and minor cleanups.

This commit is contained in:
Peter Michael
2022-09-02 13:47:50 +01:00
parent bcdb95b2e0
commit 81787fffe2
13 changed files with 1399 additions and 1 deletions

View File

@@ -0,0 +1,188 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {IDeprecatedCollectModule} from '../../../../interfaces/IDeprecatedCollectModule.sol';
import {Errors} from '../../../../libraries/Errors.sol';
import {FeeModuleBase} from '../../FeeModuleBase.sol';
import {ModuleBase} from '../../ModuleBase.sol';
import {FollowValidationModuleBase} from '../../FollowValidationModuleBase.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
/**
* @notice A struct containing the necessary data to execute collect actions on a publication.
*
* @param amount The collecting cost associated with this publication.
* @param currency The currency associated with this publication.
* @param recipient The recipient address associated with this publication.
* @param referralFee The referral fee associated with this publication.
* @param followerOnly Whether only followers should be able to collect.
*/
struct ProfilePublicationData {
uint256 amount;
address currency;
address recipient;
uint16 referralFee;
bool followerOnly;
}
/**
* @title FeeCollectModule
* @author Lens Protocol
*
* @notice This is a simple Lens CollectModule implementation, inheriting from the ICollectModule interface and
* the FeeCollectModuleBase abstract contract.
*
* This module works by allowing unlimited collects for a publication at a given price.
*/
contract DeprecatedFeeCollectModule is FeeModuleBase, FollowValidationModuleBase, IDeprecatedCollectModule {
using SafeERC20 for IERC20;
mapping(uint256 => mapping(uint256 => ProfilePublicationData))
internal _dataByPublicationByProfile;
constructor(address hub, address moduleGlobals) FeeModuleBase(moduleGlobals) ModuleBase(hub) {}
/**
* @notice This collect module levies a fee on collects and supports referrals. Thus, we need to decode data.
*
* @param profileId The token ID of the profile of the publisher, passed by the hub.
* @param pubId The publication ID of the newly created publication, passed by the hub.
* @param data The arbitrary data parameter, decoded into:
* uint256 amount: The currency total amount to levy.
* address currency: The currency address, must be internally whitelisted.
* address recipient: The custom recipient address to direct earnings to.
* uint16 referralFee: The referral fee to set.
* bool followerOnly: Whether only followers should be able to collect.
*
* @return bytes An abi encoded bytes parameter, which is the same as the passed data parameter.
*/
function initializePublicationCollectModule(
uint256 profileId,
uint256 pubId,
bytes calldata data
) external override onlyHub returns (bytes memory) {
(
uint256 amount,
address currency,
address recipient,
uint16 referralFee,
bool followerOnly
) = abi.decode(data, (uint256, address, address, uint16, bool));
if (
!_currencyWhitelisted(currency) ||
recipient == address(0) ||
referralFee > BPS_MAX ||
amount == 0
) revert Errors.InitParamsInvalid();
_dataByPublicationByProfile[profileId][pubId].amount = amount;
_dataByPublicationByProfile[profileId][pubId].currency = currency;
_dataByPublicationByProfile[profileId][pubId].recipient = recipient;
_dataByPublicationByProfile[profileId][pubId].referralFee = referralFee;
_dataByPublicationByProfile[profileId][pubId].followerOnly = followerOnly;
return data;
}
/**
* @dev Processes a collect by:
* 1. Ensuring the collector is a follower
* 2. Charging a fee
*/
function processCollect(
uint256 referrerProfileId,
address collector,
uint256 profileId,
uint256 pubId,
bytes calldata data
) external virtual override onlyHub {
if (_dataByPublicationByProfile[profileId][pubId].followerOnly)
_checkFollowValidity(profileId, collector);
if (referrerProfileId == profileId) {
_processCollect(collector, profileId, pubId, data);
} else {
_processCollectWithReferral(referrerProfileId, collector, profileId, pubId, data);
}
}
/**
* @notice Returns the publication data for a given publication, or an empty struct if that publication was not
* initialized with this module.
*
* @param profileId The token ID of the profile mapped to the publication to query.
* @param pubId The publication ID of the publication to query.
*
* @return ProfilePublicationData The ProfilePublicationData struct mapped to that publication.
*/
function getPublicationData(uint256 profileId, uint256 pubId)
external
view
returns (ProfilePublicationData memory)
{
return _dataByPublicationByProfile[profileId][pubId];
}
function _processCollect(
address collector,
uint256 profileId,
uint256 pubId,
bytes calldata data
) internal {
uint256 amount = _dataByPublicationByProfile[profileId][pubId].amount;
address currency = _dataByPublicationByProfile[profileId][pubId].currency;
_validateDataIsExpected(data, currency, amount);
(address treasury, uint16 treasuryFee) = _treasuryData();
address recipient = _dataByPublicationByProfile[profileId][pubId].recipient;
uint256 treasuryAmount = (amount * treasuryFee) / BPS_MAX;
uint256 adjustedAmount = amount - treasuryAmount;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
function _processCollectWithReferral(
uint256 referrerProfileId,
address collector,
uint256 profileId,
uint256 pubId,
bytes calldata data
) internal {
uint256 amount = _dataByPublicationByProfile[profileId][pubId].amount;
address currency = _dataByPublicationByProfile[profileId][pubId].currency;
_validateDataIsExpected(data, currency, amount);
uint256 referralFee = _dataByPublicationByProfile[profileId][pubId].referralFee;
address treasury;
uint256 treasuryAmount;
// Avoids stack too deep
{
uint16 treasuryFee;
(treasury, treasuryFee) = _treasuryData();
treasuryAmount = (amount * treasuryFee) / BPS_MAX;
}
uint256 adjustedAmount = amount - treasuryAmount;
if (referralFee != 0) {
// The reason we levy the referral fee on the adjusted amount is so that referral fees
// don't bypass the treasury fee, in essence referrals pay their fair share to the treasury.
uint256 referralAmount = (adjustedAmount * referralFee) / BPS_MAX;
adjustedAmount = adjustedAmount - referralAmount;
address referralRecipient = IERC721(HUB).ownerOf(referrerProfileId);
IERC20(currency).safeTransferFrom(collector, referralRecipient, referralAmount);
}
address recipient = _dataByPublicationByProfile[profileId][pubId].recipient;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
}

View File

@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {IDeprecatedCollectModule} from '../../../../interfaces/IDeprecatedCollectModule.sol';
import {ModuleBase} from '../../ModuleBase.sol';
import {FollowValidationModuleBase} from '../../FollowValidationModuleBase.sol';
/**
* @title FreeCollectModule
* @author Lens Protocol
*
* @notice This is a simple Lens CollectModule implementation, inheriting from the ICollectModule interface.
*
* This module works by allowing all collects.
*/
contract DeprecatedFreeCollectModule is FollowValidationModuleBase, IDeprecatedCollectModule {
constructor(address hub) ModuleBase(hub) {}
mapping(uint256 => mapping(uint256 => bool)) internal _followerOnlyByPublicationByProfile;
/**
* @dev There is nothing needed at initialization.
*/
function initializePublicationCollectModule(
uint256 profileId,
uint256 pubId,
bytes calldata data
) external override onlyHub returns (bytes memory) {
bool followerOnly = abi.decode(data, (bool));
if (followerOnly) _followerOnlyByPublicationByProfile[profileId][pubId] = true;
return data;
}
/**
* @dev Processes a collect by:
* 1. Ensuring the collector is a follower, if needed
*/
function processCollect(
uint256,
address collector,
uint256 profileId,
uint256 pubId,
bytes calldata
) external view override {
if (_followerOnlyByPublicationByProfile[profileId][pubId])
_checkFollowValidity(profileId, collector);
}
}

View File

@@ -0,0 +1,205 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {IDeprecatedCollectModule} from '../../../../interfaces/IDeprecatedCollectModule.sol';
import {Errors} from '../../../../libraries/Errors.sol';
import {FeeModuleBase} from '../../FeeModuleBase.sol';
import {ModuleBase} from '../../ModuleBase.sol';
import {FollowValidationModuleBase} from '../../FollowValidationModuleBase.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
/**
* @notice A struct containing the necessary data to execute collect actions on a publication.
*
* @param collectLimit The maximum number of collects for this publication.
* @param currentCollects The current number of collects for this publication.
* @param amount The collecting cost associated with this publication.
* @param currency The currency associated with this publication.
* @param recipient The recipient address associated with this publication.
* @param referralFee The referral fee associated with this publication.
* @param followerOnly Whether only followers should be able to collect.
*/
struct ProfilePublicationData {
uint256 collectLimit;
uint256 currentCollects;
uint256 amount;
address currency;
address recipient;
uint16 referralFee;
bool followerOnly;
}
/**
* @title LimitedFeeCollectModule
* @author Lens Protocol
*
* @notice This is a simple Lens CollectModule implementation, inheriting from the ICollectModule interface and
* the FeeCollectModuleBase abstract contract.
*
* This module works by allowing limited collects for a publication indefinitely.
*/
contract DeprecatedLimitedFeeCollectModule is FeeModuleBase, FollowValidationModuleBase, IDeprecatedCollectModule {
using SafeERC20 for IERC20;
mapping(uint256 => mapping(uint256 => ProfilePublicationData))
internal _dataByPublicationByProfile;
constructor(address hub, address moduleGlobals) FeeModuleBase(moduleGlobals) ModuleBase(hub) {}
/**
* @notice This collect module levies a fee on collects and supports referrals. Thus, we need to decode data.
*
* @param profileId The profile ID of the publication to initialize this module for's publishing profile.
* @param pubId The publication ID of the publication to initialize this module for.
* @param data The arbitrary data parameter, decoded into:
* uint256 collectLimit: The maximum amount of collects.
* uint256 amount: The currency total amount to levy.
* address currency: The currency address, must be internally whitelisted.
* address recipient: The custom recipient address to direct earnings to.
* uint16 referralFee: The referral fee to set.
* bool followerOnly: Whether only followers should be able to collect.
*
* @return bytes An abi encoded bytes parameter, which is the same as the passed data parameter.
*/
function initializePublicationCollectModule(
uint256 profileId,
uint256 pubId,
bytes calldata data
) external override onlyHub returns (bytes memory) {
(
uint256 collectLimit,
uint256 amount,
address currency,
address recipient,
uint16 referralFee,
bool followerOnly
) = abi.decode(data, (uint256, uint256, address, address, uint16, bool));
if (
collectLimit == 0 ||
!_currencyWhitelisted(currency) ||
recipient == address(0) ||
referralFee > BPS_MAX ||
amount == 0
) revert Errors.InitParamsInvalid();
_dataByPublicationByProfile[profileId][pubId].collectLimit = collectLimit;
_dataByPublicationByProfile[profileId][pubId].amount = amount;
_dataByPublicationByProfile[profileId][pubId].currency = currency;
_dataByPublicationByProfile[profileId][pubId].recipient = recipient;
_dataByPublicationByProfile[profileId][pubId].referralFee = referralFee;
_dataByPublicationByProfile[profileId][pubId].followerOnly = followerOnly;
return data;
}
/**
* @dev Processes a collect by:
* 1. Ensuring the collector is a follower
* 2. Ensuring the collect does not pass the collect limit
* 3. Charging a fee
*/
function processCollect(
uint256 referrerProfileId,
address collector,
uint256 profileId,
uint256 pubId,
bytes calldata data
) external override onlyHub {
if (_dataByPublicationByProfile[profileId][pubId].followerOnly)
_checkFollowValidity(profileId, collector);
if (
_dataByPublicationByProfile[profileId][pubId].currentCollects >=
_dataByPublicationByProfile[profileId][pubId].collectLimit
) {
revert Errors.MintLimitExceeded();
} else {
++_dataByPublicationByProfile[profileId][pubId].currentCollects;
if (referrerProfileId == profileId) {
_processCollect(collector, profileId, pubId, data);
} else {
_processCollectWithReferral(referrerProfileId, collector, profileId, pubId, data);
}
}
}
/**
* @notice Returns the publication data for a given publication, or an empty struct if that publication was not
* initialized with this module.
*
* @param profileId The token ID of the profile mapped to the publication to query.
* @param pubId The publication ID of the publication to query.
*
* @return ProfilePublicationData The ProfilePublicationData struct mapped to that publication.
*/
function getPublicationData(uint256 profileId, uint256 pubId)
external
view
returns (ProfilePublicationData memory)
{
return _dataByPublicationByProfile[profileId][pubId];
}
function _processCollect(
address collector,
uint256 profileId,
uint256 pubId,
bytes calldata data
) internal {
uint256 amount = _dataByPublicationByProfile[profileId][pubId].amount;
address currency = _dataByPublicationByProfile[profileId][pubId].currency;
_validateDataIsExpected(data, currency, amount);
(address treasury, uint16 treasuryFee) = _treasuryData();
address recipient = _dataByPublicationByProfile[profileId][pubId].recipient;
uint256 treasuryAmount = (amount * treasuryFee) / BPS_MAX;
uint256 adjustedAmount = amount - treasuryAmount;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
function _processCollectWithReferral(
uint256 referrerProfileId,
address collector,
uint256 profileId,
uint256 pubId,
bytes calldata data
) internal {
uint256 amount = _dataByPublicationByProfile[profileId][pubId].amount;
address currency = _dataByPublicationByProfile[profileId][pubId].currency;
_validateDataIsExpected(data, currency, amount);
uint256 referralFee = _dataByPublicationByProfile[profileId][pubId].referralFee;
address treasury;
uint256 treasuryAmount;
// Avoids stack too deep
{
uint16 treasuryFee;
(treasury, treasuryFee) = _treasuryData();
treasuryAmount = (amount * treasuryFee) / BPS_MAX;
}
uint256 adjustedAmount = amount - treasuryAmount;
if (referralFee != 0) {
// The reason we levy the referral fee on the adjusted amount is so that referral fees
// don't bypass the treasury fee, in essence referrals pay their fair share to the treasury.
uint256 referralAmount = (adjustedAmount * referralFee) / BPS_MAX;
adjustedAmount = adjustedAmount - referralAmount;
address referralRecipient = IERC721(HUB).ownerOf(referrerProfileId);
IERC20(currency).safeTransferFrom(collector, referralRecipient, referralAmount);
}
address recipient = _dataByPublicationByProfile[profileId][pubId].recipient;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
}

View File

@@ -0,0 +1,228 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {IDeprecatedCollectModule} from '../../../../interfaces/IDeprecatedCollectModule.sol';
import {Errors} from '../../../../libraries/Errors.sol';
import {FeeModuleBase} from '../../FeeModuleBase.sol';
import {ModuleBase} from '../../ModuleBase.sol';
import {FollowValidationModuleBase} from '../../FollowValidationModuleBase.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
/**
* @notice A struct containing the necessary data to execute collect actions on a publication.
*
* @param collectLimit The maximum number of collects for this publication.
* @param currentCollects The current number of collects for this publication.
* @param amount The collecting cost associated with this publication.
* @param currency The currency associated with this publication.
* @param recipient The recipient address associated with this publication.
* @param referralFee The referral fee associated with this publication.
* @param endTimestamp The end timestamp after which collecting is impossible.
* @param followerOnly Whether only followers should be able to collect.
*/
struct ProfilePublicationData {
uint256 collectLimit;
uint256 currentCollects;
uint256 amount;
address currency;
address recipient;
uint16 referralFee;
bool followerOnly;
uint40 endTimestamp;
}
/**
* @title LimitedTimedFeeCollectModule
* @author Lens Protocol
*
* @notice This is a simple Lens CollectModule implementation, inheriting from the ICollectModule interface and
* the FeeCollectModuleBase abstract contract. To optimize on gas, this module uses a constant 24 hour maximum
* collection time.
*
* This module works by allowing limited collects for a publication within the allotted time with a given fee.
*/
contract DeprecatedLimitedTimedFeeCollectModule is FeeModuleBase, FollowValidationModuleBase, IDeprecatedCollectModule {
using SafeERC20 for IERC20;
uint24 internal constant ONE_DAY = 24 hours;
mapping(uint256 => mapping(uint256 => ProfilePublicationData))
internal _dataByPublicationByProfile;
constructor(address hub, address moduleGlobals) FeeModuleBase(moduleGlobals) ModuleBase(hub) {}
/**
* @notice This collect module levies a fee on collects and supports referrals. Thus, we need to decode data.
*
* @param profileId The profile ID of the publication to initialize this module for's publishing profile.
* @param pubId The publication ID of the publication to initialize this module for.
* @param data The arbitrary data parameter, decoded into:
* uint256 collectLimit: The maximum amount of collects.
* uint256 amount: The currency total amount to levy.
* address currency: The currency address, must be internally whitelisted.
* address recipient: The custom recipient address to direct earnings to.
* uint16 referralFee: The referral fee to set.
* bool followerOnly: Whether only followers should be able to collect.
*
* @return bytes An abi encoded bytes parameter, containing (in order): collectLimit, amount, currency, recipient, referral fee & end timestamp.
*/
function initializePublicationCollectModule(
uint256 profileId,
uint256 pubId,
bytes calldata data
) external override onlyHub returns (bytes memory) {
unchecked {
uint40 endTimestamp = uint40(block.timestamp) + ONE_DAY;
(
uint256 collectLimit,
uint256 amount,
address currency,
address recipient,
uint16 referralFee,
bool followerOnly
) = abi.decode(data, (uint256, uint256, address, address, uint16, bool));
if (
collectLimit == 0 ||
!_currencyWhitelisted(currency) ||
recipient == address(0) ||
referralFee > BPS_MAX ||
amount == 0
) revert Errors.InitParamsInvalid();
_dataByPublicationByProfile[profileId][pubId].collectLimit = collectLimit;
_dataByPublicationByProfile[profileId][pubId].amount = amount;
_dataByPublicationByProfile[profileId][pubId].currency = currency;
_dataByPublicationByProfile[profileId][pubId].recipient = recipient;
_dataByPublicationByProfile[profileId][pubId].referralFee = referralFee;
_dataByPublicationByProfile[profileId][pubId].followerOnly = followerOnly;
_dataByPublicationByProfile[profileId][pubId].endTimestamp = endTimestamp;
return
abi.encode(
collectLimit,
amount,
currency,
recipient,
referralFee,
followerOnly,
endTimestamp
);
}
}
/**
* @dev Processes a collect by:
* 1. Ensuring the collector is a follower
* 2. Ensuring the current timestamp is less than or equal to the collect end timestamp
* 3. Ensuring the collect does not pass the collect limit
* 4. Charging a fee
*/
function processCollect(
uint256 referrerProfileId,
address collector,
uint256 profileId,
uint256 pubId,
bytes calldata data
) external override onlyHub {
if (_dataByPublicationByProfile[profileId][pubId].followerOnly)
_checkFollowValidity(profileId, collector);
uint256 endTimestamp = _dataByPublicationByProfile[profileId][pubId].endTimestamp;
if (block.timestamp > endTimestamp) revert Errors.CollectExpired();
if (
_dataByPublicationByProfile[profileId][pubId].currentCollects >=
_dataByPublicationByProfile[profileId][pubId].collectLimit
) {
revert Errors.MintLimitExceeded();
} else {
++_dataByPublicationByProfile[profileId][pubId].currentCollects;
if (referrerProfileId == profileId) {
_processCollect(collector, profileId, pubId, data);
} else {
_processCollectWithReferral(referrerProfileId, collector, profileId, pubId, data);
}
}
}
/**
* @notice Returns the publication data for a given publication, or an empty struct if that publication was not
* initialized with this module.
*
* @param profileId The token ID of the profile mapped to the publication to query.
* @param pubId The publication ID of the publication to query.
*
* @return ProfilepublicationData The ProfilePublicationData struct mapped to that publication.
*/
function getPublicationData(uint256 profileId, uint256 pubId)
external
view
returns (ProfilePublicationData memory)
{
return _dataByPublicationByProfile[profileId][pubId];
}
function _processCollect(
address collector,
uint256 profileId,
uint256 pubId,
bytes calldata data
) internal {
uint256 amount = _dataByPublicationByProfile[profileId][pubId].amount;
address currency = _dataByPublicationByProfile[profileId][pubId].currency;
_validateDataIsExpected(data, currency, amount);
(address treasury, uint16 treasuryFee) = _treasuryData();
address recipient = _dataByPublicationByProfile[profileId][pubId].recipient;
uint256 treasuryAmount = (amount * treasuryFee) / BPS_MAX;
uint256 adjustedAmount = amount - treasuryAmount;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
function _processCollectWithReferral(
uint256 referrerProfileId,
address collector,
uint256 profileId,
uint256 pubId,
bytes calldata data
) internal {
uint256 amount = _dataByPublicationByProfile[profileId][pubId].amount;
address currency = _dataByPublicationByProfile[profileId][pubId].currency;
_validateDataIsExpected(data, currency, amount);
uint256 referralFee = _dataByPublicationByProfile[profileId][pubId].referralFee;
address treasury;
uint256 treasuryAmount;
// Avoids stack too deep
{
uint16 treasuryFee;
(treasury, treasuryFee) = _treasuryData();
treasuryAmount = (amount * treasuryFee) / BPS_MAX;
}
uint256 adjustedAmount = amount - treasuryAmount;
if (referralFee != 0) {
// The reason we levy the referral fee on the adjusted amount is so that referral fees
// don't bypass the treasury fee, in essence referrals pay their fair share to the treasury.
uint256 referralAmount = (adjustedAmount * referralFee) / BPS_MAX;
adjustedAmount = adjustedAmount - referralAmount;
address referralRecipient = IERC721(HUB).ownerOf(referrerProfileId);
IERC20(currency).safeTransferFrom(collector, referralRecipient, referralAmount);
}
address recipient = _dataByPublicationByProfile[profileId][pubId].recipient;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
}

View File

@@ -0,0 +1,41 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {IDeprecatedCollectModule} from '../../../../interfaces/IDeprecatedCollectModule.sol';
import {Errors} from '../../../../libraries/Errors.sol';
/**
* @title RevertCollectModule
* @author Lens Protocol
*
* @notice This is a simple Lens CollectModule implementation, inheriting from the ICollectModule interface.
*
* This module works by disallowing all collects.
*/
contract DeprecatedRevertCollectModule is IDeprecatedCollectModule {
/**
* @dev There is nothing needed at initialization.
*/
function initializePublicationCollectModule(
uint256,
uint256,
bytes calldata
) external pure override returns (bytes memory) {
return new bytes(0);
}
/**
* @dev Processes a collect by:
* 1. Always reverting
*/
function processCollect(
uint256,
address,
uint256,
uint256,
bytes calldata
) external pure override {
revert Errors.CollectNotAllowed();
}
}

View File

@@ -0,0 +1,205 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {IDeprecatedCollectModule} from '../../../../interfaces/IDeprecatedCollectModule.sol';
import {ILensHub} from '../../../../interfaces/ILensHub.sol';
import {Errors} from '../../../../libraries/Errors.sol';
import {FeeModuleBase} from '../../FeeModuleBase.sol';
import {ModuleBase} from '../../ModuleBase.sol';
import {FollowValidationModuleBase} from '../../FollowValidationModuleBase.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
/**
* @notice A struct containing the necessary data to execute collect actions on a publication.
*
* @param amount The collecting cost associated with this publication.
* @param currency The currency associated with this publication.
* @param recipient The recipient address associated with this publication.
* @param referralFee The referral fee associated with this publication.
* @param endTimestamp The end timestamp after which collecting is impossible.
* @param followerOnly Whether only followers should be able to collect.
*/
struct ProfilePublicationData {
uint256 amount;
address currency;
address recipient;
uint16 referralFee;
bool followerOnly;
uint40 endTimestamp;
}
/**
* @title TimedFeeCollectModule
* @author Lens Protocol
*
* @notice This is a simple Lens CollectModule implementation, inheriting from the ICollectModule interface and
* the FeeCollectModuleBase abstract contract. To optimize on gas, this module uses a constant 24 hour maximum
* collection time.
*
* This module works by allowing unlimited collects for a publication within the allotted time with a given fee.
*
* NOTE: If data passed on initialization is empty, this module will only check for the time limit.
*/
contract DeprecatedTimedFeeCollectModule is FeeModuleBase, FollowValidationModuleBase, IDeprecatedCollectModule {
using SafeERC20 for IERC20;
uint24 internal constant ONE_DAY = 24 hours;
mapping(uint256 => mapping(uint256 => ProfilePublicationData))
internal _dataByPublicationByProfile;
constructor(address hub, address moduleGlobals) FeeModuleBase(moduleGlobals) ModuleBase(hub) {}
/**
* @notice This collect module levies a fee on collects and supports referrals. Thus, we need to decode data.
*
* @param profileId The profile ID of the publication to initialize this module for's publishing profile.
* @param pubId The publication ID of the publication to initialize this module for.
* @param data The arbitrary data parameter, decoded into:
* uint256 amount: The currency total amount to levy.
* address currency: The currency address, must be internally whitelisted.
* address recipient: The custom recipient address to direct earnings to.
* uint16 referralFee: The referral fee to set.
* bool followerOnly: Whether only followers should be able to collect.
*
* @return bytes An abi encoded bytes parameter, containing (in order): amount, currency, recipient, referral fee & end timestamp.
*/
function initializePublicationCollectModule(
uint256 profileId,
uint256 pubId,
bytes calldata data
) external override onlyHub returns (bytes memory) {
unchecked {
uint40 endTimestamp = uint40(block.timestamp) + ONE_DAY;
(
uint256 amount,
address currency,
address recipient,
uint16 referralFee,
bool followerOnly
) = abi.decode(data, (uint256, address, address, uint16, bool));
if (
!_currencyWhitelisted(currency) ||
recipient == address(0) ||
referralFee > BPS_MAX ||
amount == 0
) revert Errors.InitParamsInvalid();
_dataByPublicationByProfile[profileId][pubId].amount = amount;
_dataByPublicationByProfile[profileId][pubId].currency = currency;
_dataByPublicationByProfile[profileId][pubId].recipient = recipient;
_dataByPublicationByProfile[profileId][pubId].referralFee = referralFee;
_dataByPublicationByProfile[profileId][pubId].followerOnly = followerOnly;
_dataByPublicationByProfile[profileId][pubId].endTimestamp = endTimestamp;
return abi.encode(amount, currency, recipient, referralFee, followerOnly, endTimestamp);
}
}
/**
* @dev Processes a collect by:
* 1. Ensuring the collector is a follower
* 2. Ensuring the current timestamp is less than or equal to the collect end timestamp
* 3. Charging a fee
*/
function processCollect(
uint256 referrerProfileId,
address collector,
uint256 profileId,
uint256 pubId,
bytes calldata data
) external override onlyHub {
if (_dataByPublicationByProfile[profileId][pubId].followerOnly)
_checkFollowValidity(profileId, collector);
uint256 endTimestamp = _dataByPublicationByProfile[profileId][pubId].endTimestamp;
if (block.timestamp > endTimestamp) revert Errors.CollectExpired();
if (referrerProfileId == profileId) {
_processCollect(collector, profileId, pubId, data);
} else {
_processCollectWithReferral(referrerProfileId, collector, profileId, pubId, data);
}
}
/**
* @notice Returns the publication data for a given publication, or an empty struct if that publication was not
* initialized with this module.
*
* @param profileId The token ID of the profile mapped to the publication to query.
* @param pubId The publication ID of the publication to query.
*
* @return ProfilePublicationData The ProfilePublicationData struct mapped to that publication.
*/
function getPublicationData(uint256 profileId, uint256 pubId)
external
view
returns (ProfilePublicationData memory)
{
return _dataByPublicationByProfile[profileId][pubId];
}
function _processCollect(
address collector,
uint256 profileId,
uint256 pubId,
bytes calldata data
) internal {
uint256 amount = _dataByPublicationByProfile[profileId][pubId].amount;
address currency = _dataByPublicationByProfile[profileId][pubId].currency;
_validateDataIsExpected(data, currency, amount);
(address treasury, uint16 treasuryFee) = _treasuryData();
address recipient = _dataByPublicationByProfile[profileId][pubId].recipient;
uint256 treasuryAmount = (amount * treasuryFee) / BPS_MAX;
uint256 adjustedAmount = amount - treasuryAmount;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
function _processCollectWithReferral(
uint256 referrerProfileId,
address collector,
uint256 profileId,
uint256 pubId,
bytes calldata data
) internal {
uint256 amount = _dataByPublicationByProfile[profileId][pubId].amount;
address currency = _dataByPublicationByProfile[profileId][pubId].currency;
_validateDataIsExpected(data, currency, amount);
uint256 referralFee = _dataByPublicationByProfile[profileId][pubId].referralFee;
address treasury;
uint256 treasuryAmount;
// Avoids stack too deep
{
uint16 treasuryFee;
(treasury, treasuryFee) = _treasuryData();
treasuryAmount = (amount * treasuryFee) / BPS_MAX;
}
uint256 adjustedAmount = amount - treasuryAmount;
if (referralFee != 0) {
// The reason we levy the referral fee on the adjusted amount is so that referral fees
// don't bypass the treasury fee, in essence referrals pay their fair share to the treasury.
uint256 referralAmount = (adjustedAmount * referralFee) / BPS_MAX;
adjustedAmount = adjustedAmount - referralAmount;
address referralRecipient = IERC721(HUB).ownerOf(referrerProfileId);
IERC20(currency).safeTransferFrom(collector, referralRecipient, referralAmount);
}
address recipient = _dataByPublicationByProfile[profileId][pubId].recipient;
IERC20(currency).safeTransferFrom(collector, recipient, adjustedAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(collector, treasury, treasuryAmount);
}
}

View File

@@ -0,0 +1,147 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {Errors} from '../../../../libraries/Errors.sol';
import {Events} from '../../../../libraries/Events.sol';
import {ModuleBase} from '../../ModuleBase.sol';
import {DeprecatedFollowValidatorFollowModuleBase} from './DeprecatedFollowValidatorFollowModuleBase.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
/**
* @title ApprovalFollowModule
* @author Lens Protocol
*
* @notice This follow module only allows addresses that are approved for a profile by the profile owner to follow.
*/
contract DeprecatedApprovalFollowModule is DeprecatedFollowValidatorFollowModuleBase {
// We use a triple nested mapping so that, on profile transfer, the previous approved address list is invalid;
mapping(address => mapping(uint256 => mapping(address => bool)))
internal _approvedByProfileByOwner;
constructor(address hub) ModuleBase(hub) {}
/**
* @notice A custom function that allows profile owners to customize approved addresses.
*
* @param profileId The profile ID to approve/disapprove follower addresses for.
* @param addresses The addresses to approve/disapprove for following the profile.
* @param toApprove Whether to approve or disapprove the addresses for following the profile.
*/
function approve(
uint256 profileId,
address[] calldata addresses,
bool[] calldata toApprove
) external {
if (addresses.length != toApprove.length) revert Errors.InitParamsInvalid();
address owner = IERC721(HUB).ownerOf(profileId);
if (msg.sender != owner) revert Errors.NotProfileOwner();
uint256 addressesLength = addresses.length;
for (uint256 i = 0; i < addressesLength; ) {
_approvedByProfileByOwner[owner][profileId][addresses[i]] = toApprove[i];
unchecked {
++i;
}
}
emit Events.FollowsApproved(owner, profileId, addresses, toApprove, block.timestamp);
}
/**
* @notice This follow module works on custom profile owner approvals.
*
* @param profileId The profile ID of the profile to initialize this module for.
* @param data The arbitrary data parameter, decoded into:
* address[] addresses: The array of addresses to approve initially.
*
* @return bytes An abi encoded bytes parameter, which is the same as the passed data parameter.
*/
function initializeFollowModule(uint256 profileId, bytes calldata data)
external
override
onlyHub
returns (bytes memory)
{
address owner = IERC721(HUB).ownerOf(profileId);
if (data.length > 0) {
address[] memory addresses = abi.decode(data, (address[]));
uint256 addressesLength = addresses.length;
for (uint256 i = 0; i < addressesLength; ) {
_approvedByProfileByOwner[owner][profileId][addresses[i]] = true;
unchecked {
++i;
}
}
}
return data;
}
/**
* @dev Processes a follow by:
* 1. Validating that the follower has been approved for that profile by the profile owner
*/
function processFollow(
address follower,
uint256 profileId,
bytes calldata
) external override onlyHub {
address owner = IERC721(HUB).ownerOf(profileId);
if (!_approvedByProfileByOwner[owner][profileId][follower])
revert Errors.FollowNotApproved();
_approvedByProfileByOwner[owner][profileId][follower] = false; // prevents repeat follows
}
/**
* @dev We don't need to execute any additional logic on transfers in this follow module.
*/
function followModuleTransferHook(
uint256 profileId,
address from,
address to,
uint256 followNFTTokenId
) external override {}
/**
* @notice Returns whether the given address is approved for the profile owned by a given address.
*
* @param profileOwner The profile owner of the profile to query the approval with.
* @param profileId The token ID of the profile to query approval with.
* @param toCheck The address to query approval for.
*
* @return bool True if the address is approved and false otherwise.
*/
function isApproved(
address profileOwner,
uint256 profileId,
address toCheck
) external view returns (bool) {
return _approvedByProfileByOwner[profileOwner][profileId][toCheck];
}
/**
* @notice Returns whether the given addresses are approved for the profile owned by a given address.
*
* @param profileOwner The profile owner of the profile to query the approvals with.
* @param profileId The token ID of the profile to query approvals with.
* @param toCheck The address array to query approvals for.
*
* @return bool[] true if the address at the specified index is approved and false otherwise.
*/
function isApprovedArray(
address profileOwner,
uint256 profileId,
address[] calldata toCheck
) external view returns (bool[] memory) {
bool[] memory approved = new bool[](toCheck.length);
uint256 toCheckLength = toCheck.length;
for (uint256 i = 0; i < toCheckLength; ) {
approved[i] = _approvedByProfileByOwner[profileOwner][profileId][toCheck[i]];
unchecked {
++i;
}
}
return approved;
}
}

View File

@@ -0,0 +1,115 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {ILensHub} from '../../../../interfaces/ILensHub.sol';
import {Errors} from '../../../../libraries/Errors.sol';
import {FeeModuleBase} from '../../FeeModuleBase.sol';
import {ModuleBase} from '../../ModuleBase.sol';
import {DeprecatedFollowValidatorFollowModuleBase} from './DeprecatedFollowValidatorFollowModuleBase.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
/**
* @notice A struct containing the necessary data to execute follow actions on a given profile.
*
* @param currency The currency associated with this profile.
* @param amount The following cost associated with this profile.
* @param recipient The recipient address associated with this profile.
*/
struct ProfileData {
address currency;
uint256 amount;
address recipient;
}
/**
* @title FeeFollowModule
* @author Lens Protocol
*
* @notice This is a simple Lens FollowModule implementation, inheriting from the IFollowModule interface, but with additional
* variables that can be controlled by governance, such as the governance & treasury addresses as well as the treasury fee.
*/
contract DeprecatedFeeFollowModule is FeeModuleBase, DeprecatedFollowValidatorFollowModuleBase {
using SafeERC20 for IERC20;
mapping(uint256 => ProfileData) internal _dataByProfile;
constructor(address hub, address moduleGlobals) FeeModuleBase(moduleGlobals) ModuleBase(hub) {}
/**
* @notice This follow module levies a fee on follows.
*
* @param profileId The profile ID of the profile to initialize this module for.
* @param data The arbitrary data parameter, decoded into:
* address currency: The currency address, must be internally whitelisted.
* uint256 amount: The currency total amount to levy.
* address recipient: The custom recipient address to direct earnings to.
*
* @return bytes An abi encoded bytes parameter, which is the same as the passed data parameter.
*/
function initializeFollowModule(uint256 profileId, bytes calldata data)
external
override
onlyHub
returns (bytes memory)
{
(uint256 amount, address currency, address recipient) = abi.decode(
data,
(uint256, address, address)
);
if (!_currencyWhitelisted(currency) || recipient == address(0) || amount == 0)
revert Errors.InitParamsInvalid();
_dataByProfile[profileId].amount = amount;
_dataByProfile[profileId].currency = currency;
_dataByProfile[profileId].recipient = recipient;
return data;
}
/**
* @dev Processes a follow by:
* 1. Charging a fee
*/
function processFollow(
address follower,
uint256 profileId,
bytes calldata data
) external override onlyHub {
uint256 amount = _dataByProfile[profileId].amount;
address currency = _dataByProfile[profileId].currency;
_validateDataIsExpected(data, currency, amount);
(address treasury, uint16 treasuryFee) = _treasuryData();
address recipient = _dataByProfile[profileId].recipient;
uint256 treasuryAmount = (amount * treasuryFee) / BPS_MAX;
uint256 adjustedAmount = amount - treasuryAmount;
IERC20(currency).safeTransferFrom(follower, recipient, adjustedAmount);
if (treasuryAmount > 0)
IERC20(currency).safeTransferFrom(follower, treasury, treasuryAmount);
}
/**
* @dev We don't need to execute any additional logic on transfers in this follow module.
*/
function followModuleTransferHook(
uint256 profileId,
address from,
address to,
uint256 followNFTTokenId
) external override {}
/**
* @notice Returns the profile data for a given profile, or an empty struct if that profile was not initialized
* with this module.
*
* @param profileId The token ID of the profile to query.
*
* @return ProfileData The ProfileData struct mapped to that profile.
*/
function getProfileData(uint256 profileId) external view returns (ProfileData memory) {
return _dataByProfile[profileId];
}
}

View File

@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {IDeprecatedFollowModule} from '../../../../interfaces/IDeprecatedFollowModule.sol';
import {ILensHub} from '../../../../interfaces/ILensHub.sol';
import {Errors} from '../../../../libraries/Errors.sol';
import {ModuleBase} from '../../ModuleBase.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
/**
* @title FollowValidatorFollowModuleBase
* @author Lens Protocol
*
* @notice This abstract contract adds the default expected behavior for follow validation in a follow module
* to inheriting contracts.
*/
abstract contract DeprecatedFollowValidatorFollowModuleBase is ModuleBase, IDeprecatedFollowModule {
/**
* @notice Standard function to validate follow NFT ownership. This module is agnostic to follow NFT token IDs
* and other properties.
*/
function isFollowing(
uint256 profileId,
address follower,
uint256 followNFTTokenId
) external view override returns (bool) {
address followNFT = ILensHub(HUB).getFollowNFT(profileId);
if (followNFT == address(0)) {
return false;
} else {
return
followNFTTokenId == 0
? IERC721(followNFT).balanceOf(follower) != 0
: IERC721(followNFT).ownerOf(followNFTTokenId) == follower;
}
}
}

View File

@@ -0,0 +1,70 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {Errors} from '../../../../libraries/Errors.sol';
import {ModuleBase} from '../../ModuleBase.sol';
import {DeprecatedFollowValidatorFollowModuleBase} from './DeprecatedFollowValidatorFollowModuleBase.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
/**
* @title ProfileFollowModule
* @author Lens Protocol
*
* @notice A Lens Profile NFT token-gated follow module with single follow per token validation.
*/
contract DeprecatedProfileFollowModule is DeprecatedFollowValidatorFollowModuleBase {
/**
* Given two profile IDs tells if the former has already been used to follow the latter.
*/
mapping(uint256 => mapping(uint256 => bool)) public isProfileFollowing;
constructor(address hub) ModuleBase(hub) {}
/**
* @notice This follow module allows users to follow using a profile once.
*
* @return bytes Empty bytes.
*/
function initializeFollowModule(uint256, bytes calldata)
external
view
override
onlyHub
returns (bytes memory)
{
return new bytes(0);
}
/**
* @dev Processes a follow by:
* 1. Validating that the follower owns the profile passed through the data param.
* 2. Validating that the profile that is being used to execute the follow was not already used for following the
* given profile.
*/
function processFollow(
address follower,
uint256 profileId,
bytes calldata data
) external override onlyHub {
uint256 followerProfileId = abi.decode(data, (uint256));
if (IERC721(HUB).ownerOf(followerProfileId) != follower) {
revert Errors.NotProfileOwner();
}
if (isProfileFollowing[followerProfileId][profileId]) {
revert Errors.FollowInvalid();
} else {
isProfileFollowing[followerProfileId][profileId] = true;
}
}
/**
* @dev We don't need to execute any additional logic on transfers in this follow module.
*/
function followModuleTransferHook(
uint256 profileId,
address from,
address to,
uint256 followNFTTokenId
) external override {}
}

View File

@@ -0,0 +1,53 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {Errors} from '../../../../libraries/Errors.sol';
import {ModuleBase} from '../../ModuleBase.sol';
import {DeprecatedFollowValidatorFollowModuleBase} from './DeprecatedFollowValidatorFollowModuleBase.sol';
/**
* @title RevertFollowModule
* @author Lens Protocol
*
* @notice This follow module rejects all follow attempts.
*/
contract DeprecatedRevertFollowModule is DeprecatedFollowValidatorFollowModuleBase {
constructor(address hub) ModuleBase(hub) {}
/**
* @notice This follow module always reverts.
*
* @return bytes Empty bytes.
*/
function initializeFollowModule(uint256, bytes calldata)
external
view
override
onlyHub
returns (bytes memory)
{
return new bytes(0);
}
/**
* @dev Processes a follow by rejecting it reverting the transaction.
*/
function processFollow(
address,
uint256,
bytes calldata
) external view override onlyHub {
revert Errors.FollowInvalid();
}
/**
* @dev We don't need to execute any additional logic on transfers in this follow module.
*/
function followModuleTransferHook(
uint256 profileId,
address from,
address to,
uint256 followNFTTokenId
) external override {}
}

View File

@@ -0,0 +1,60 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {IDeprecatedReferenceModule} from '../../../../interfaces/IDeprecatedReferenceModule.sol';
import {ModuleBase} from '../../ModuleBase.sol';
import {FollowValidationModuleBase} from '../../FollowValidationModuleBase.sol';
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
/**
* @title FollowerOnlyReferenceModule
* @author Lens Protocol
*
* @notice A simple reference module that validates that comments or mirrors originate from a profile owned
* by a follower.
*/
contract DeprecatedFollowerOnlyReferenceModule is FollowValidationModuleBase, IDeprecatedReferenceModule {
constructor(address hub) ModuleBase(hub) {}
/**
* @dev There is nothing needed at initialization.
*/
function initializeReferenceModule(
uint256,
uint256,
bytes calldata
) external pure override returns (bytes memory) {
return new bytes(0);
}
/**
* @notice Validates that the commenting profile's owner is a follower.
*
* NOTE: We don't need to care what the pointed publication is in this context.
*/
function processComment(
uint256 profileId,
uint256 profileIdPointed,
uint256,
bytes calldata
) external view override {
address commentCreator = IERC721(HUB).ownerOf(profileId);
_checkFollowValidity(profileIdPointed, commentCreator);
}
/**
* @notice Validates that the commenting profile's owner is a follower.
*
* NOTE: We don't need to care what the pointed publication is in this context.
*/
function processMirror(
uint256 profileId,
uint256 profileIdPointed,
uint256,
bytes calldata
) external view override {
address mirrorCreator = IERC721(HUB).ownerOf(profileId);
_checkFollowValidity(profileIdPointed, mirrorCreator);
}
}

View File

@@ -846,7 +846,6 @@ library GeneralLib {
collectModuleInitData
);
// Reference module initialization
bytes memory referenceModuleReturnData = _initPubReferenceModule(
profileId,
executor,