Skip to content

Commit

Permalink
refactor: Refactored to make follow & collect NFTs query the hub for …
Browse files Browse the repository at this point in the history
…the handle and implementation.
  • Loading branch information
Zer0dot committed Apr 14, 2022
1 parent ddd0a46 commit e1ecb54
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 107 deletions.
28 changes: 21 additions & 7 deletions contracts/core/CollectNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import {ICollectNFT} from '../interfaces/ICollectNFT.sol';
import {ILensHub} from '../interfaces/ILensHub.sol';
import {Errors} from '../libraries/Errors.sol';
import {Events} from '../libraries/Events.sol';
import {Constants} from '../libraries/Constants.sol';
import {LensNFTBase} from './base/LensNFTBase.sol';
import {Strings} from '@openzeppelin/contracts/utils/Strings.sol';

/**
* @title CollectNFT
Expand All @@ -16,6 +18,8 @@ import {LensNFTBase} from './base/LensNFTBase.sol';
* the first collect for a given publication, and the token URI points to the original publication's contentURI.
*/
contract CollectNFT is LensNFTBase, ICollectNFT {
using Strings for uint256;

address public immutable HUB;

uint256 internal _profileId;
Expand All @@ -33,17 +37,12 @@ contract CollectNFT is LensNFTBase, ICollectNFT {
}

/// @inheritdoc ICollectNFT
function initialize(
uint256 profileId,
uint256 pubId,
string calldata name,
string calldata symbol
) external override {
function initialize(uint256 profileId, uint256 pubId) external override {
if (_initialized) revert Errors.Initialized();
_initialized = true;
_profileId = profileId;
_pubId = pubId;
super._initialize(name, symbol);
// super._initialize(name, symbol);
emit Events.CollectNFTInitialized(profileId, pubId, block.timestamp);
}

Expand All @@ -67,6 +66,21 @@ contract CollectNFT is LensNFTBase, ICollectNFT {
return ILensHub(HUB).getContentURI(_profileId, _pubId);
}

function name() public view override returns (string memory) {
string memory handle = ILensHub(HUB).getHandle(_profileId);
return
string(abi.encodePacked(handle, Constants.COLLECT_NFT_NAME_INFIX, _pubId.toString()));
}

function symbol() public view override returns (string memory) {
string memory handle = ILensHub(HUB).getHandle(_profileId);
bytes4 firstBytes = bytes4(bytes(handle));
return
string(
abi.encodePacked(firstBytes, Constants.COLLECT_NFT_SYMBOL_INFIX, _pubId.toString())
);
}

/**
* @dev Upon transfers, we emit the transfer event in the hub.
*/
Expand Down
19 changes: 13 additions & 6 deletions contracts/core/FollowNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {ILensHub} from '../interfaces/ILensHub.sol';
import {Errors} from '../libraries/Errors.sol';
import {Events} from '../libraries/Events.sol';
import {DataTypes} from '../libraries/DataTypes.sol';
import {Constants} from '../libraries/Constants.sol';
import {LensNFTBase} from './base/LensNFTBase.sol';

/**
Expand Down Expand Up @@ -50,15 +51,10 @@ contract FollowNFT is LensNFTBase, IFollowNFT {
}

/// @inheritdoc IFollowNFT
function initialize(
uint256 profileId,
string calldata name,
string calldata symbol
) external override {
function initialize(uint256 profileId) external override {
if (_initialized) revert Errors.Initialized();
_initialized = true;
_profileId = profileId;
super._initialize(name, symbol);
emit Events.FollowNFTInitialized(profileId, block.timestamp);
}

Expand Down Expand Up @@ -129,6 +125,17 @@ contract FollowNFT is LensNFTBase, IFollowNFT {
return _getSnapshotValueByBlockNumber(_delSupplySnapshots, blockNumber, snapshotCount);
}

function name() public view override returns (string memory) {
string memory handle = ILensHub(HUB).getHandle(_profileId);
return string(abi.encodePacked(handle, Constants.FOLLOW_NFT_NAME_SUFFIX));
}

function symbol() public view override returns (string memory) {
string memory handle = ILensHub(HUB).getHandle(_profileId);
bytes4 firstBytes = bytes4(bytes(handle));
return string(abi.encodePacked(firstBytes, Constants.FOLLOW_NFT_SYMBOL_SUFFIX));
}

function _getSnapshotValueByBlockNumber(
mapping(uint256 => Snapshot) storage _shots,
uint256 blockNumber,
Expand Down
27 changes: 12 additions & 15 deletions contracts/core/LensHub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,6 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
msg.sender,
profileIds,
datas,
FOLLOW_NFT_IMPL,
_profileById,
_profileIdByHandleHash
);
Expand Down Expand Up @@ -605,7 +604,6 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
vars.follower,
vars.profileIds,
vars.datas,
FOLLOW_NFT_IMPL,
_profileById,
_profileIdByHandleHash
);
Expand All @@ -617,16 +615,7 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
uint256 pubId,
bytes calldata data
) external override whenNotPaused returns (uint256) {
return
InteractionLogic.collect(
msg.sender,
profileId,
pubId,
data,
COLLECT_NFT_IMPL,
_pubByIdByProfile,
_profileById
);
return InteractionLogic.collect(msg.sender, profileId, pubId, data, _pubByIdByProfile);
}

/// @inheritdoc ILensHub
Expand Down Expand Up @@ -660,9 +649,7 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
vars.profileId,
vars.pubId,
vars.data,
COLLECT_NFT_IMPL,
_pubByIdByProfile,
_profileById
_pubByIdByProfile
);
}

Expand Down Expand Up @@ -893,6 +880,16 @@ contract LensHub is LensNFTBase, VersionedInitializable, LensMultiState, LensHub
);
}

/// @inheritdoc ILensHub
function getFollowNFTImpl() external view override returns (address) {
return FOLLOW_NFT_IMPL;
}

/// @inheritdoc ILensHub
function getCollectNFTImpl() external view override returns (address) {
return COLLECT_NFT_IMPL;
}

/// ****************************
/// *****INTERNAL FUNCTIONS*****
/// ****************************
Expand Down
13 changes: 3 additions & 10 deletions contracts/interfaces/ICollectNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,13 @@ pragma solidity 0.8.10;
*/
interface ICollectNFT {
/**
* @notice Initializes the collect NFT, setting the feed as the privileged minter, storing the collected publication pointer
* and initializing the name and symbol in the LensNFTBase contract.
* @notice Initializes the collect NFT, setting the hub as the privileged minter and storing the associated
* profile and publication ID.
*
* @param profileId The token ID of the profile in the hub that this collectNFT points to.
* @param pubId The profile publication ID in the hub that this collectNFT points to.
* @param name The name to set for this NFT.
* @param symbol The symbol to set for this NFT.
*/
function initialize(
uint256 profileId,
uint256 pubId,
string calldata name,
string calldata symbol
) external;
function initialize(uint256 profileId, uint256 pubId) external;

/**
* @notice Mints a collect NFT to the specified address. This can only be called by the hub, and is called
Expand Down
11 changes: 2 additions & 9 deletions contracts/interfaces/IFollowNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,11 @@ import {DataTypes} from '../libraries/DataTypes.sol';
*/
interface IFollowNFT {
/**
* @notice Initializes the follow NFT, setting the feed as the privileged minter, initializing the name and
* symbol in the LensNFTBase contract.
* @notice Initializes the follow NFT, setting the hub as the privileged minter and storing the associated profile ID.
*
* @param profileId The token ID of the profile in the hub associated with this followNFT, used for transfer hooks.
* @param name The name to set for this NFT.
* @param symbol The symbol to set for this NFT.
*/
function initialize(
uint256 profileId,
string calldata name,
string calldata symbol
) external;
function initialize(uint256 profileId) external;

/**
* @notice Mints a follow NFT to the specified address. This can only be called by the hub, and is called
Expand Down
15 changes: 15 additions & 0 deletions contracts/interfaces/ILensHub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -523,4 +523,19 @@ interface ILensHub {
* @return PubType The publication type, as a member of an enum (either "post," "comment" or "mirror").
*/
function getPubType(uint256 profileId, uint256 pubId) external view returns (DataTypes.PubType);

/**
* @notice Returns the follow NFT implementation address.
*
* @return address The follow NFT implementation address.
*/
function getFollowNFTImpl() external view returns (address);

/**
* @notice Returns the collect NFT implementation address.
*
* @return address The collect NFT implementation address.
*/
function getCollectNFTImpl() external view returns (address);

}
78 changes: 23 additions & 55 deletions contracts/libraries/InteractionLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

pragma solidity 0.8.10;

import {FollowNFTProxy} from '../upgradeability/FollowNFTProxy.sol';
import {CollectNFTProxy} from '../upgradeability/CollectNFTProxy.sol';
import {Helpers} from './Helpers.sol';
import {DataTypes} from './DataTypes.sol';
import {Errors} from './Errors.sol';
Expand All @@ -11,8 +13,6 @@ import {IFollowNFT} from '../interfaces/IFollowNFT.sol';
import {ICollectNFT} from '../interfaces/ICollectNFT.sol';
import {IFollowModule} from '../interfaces/IFollowModule.sol';
import {ICollectModule} from '../interfaces/ICollectModule.sol';
import {Clones} from '@openzeppelin/contracts/proxy/Clones.sol';
import {Strings} from '@openzeppelin/contracts/utils/Strings.sol';

/**
* @title InteractionLogic
Expand All @@ -23,16 +23,13 @@ import {Strings} from '@openzeppelin/contracts/utils/Strings.sol';
* @dev The functions are external, so they are called from the hub via `delegateCall` under the hood.
*/
library InteractionLogic {
using Strings for uint256;

/**
* @notice Follows the given profiles, executing the necessary logic and module calls before minting the follow
* NFT(s) to the follower.
*
* @param follower The address executing the follow.
* @param profileIds The array of profile token IDs to follow.
* @param followModuleDatas The array of follow module data parameters to pass to each profile's follow module.
* @param followNFTImpl The address of the follow NFT implementation, which has to be passed because it's an immutable in the hub.
* @param _profileById A pointer to the storage mapping of profile structs by profile ID.
* @param _profileIdByHandleHash A pointer to the storage mapping of profile IDs by handle hash.
*
Expand All @@ -42,7 +39,6 @@ library InteractionLogic {
address follower,
uint256[] calldata profileIds,
bytes[] calldata followModuleDatas,
address followNFTImpl,
mapping(uint256 => DataTypes.ProfileStruct) storage _profileById,
mapping(bytes32 => uint256) storage _profileIdByHandleHash
) external returns (uint256[] memory) {
Expand All @@ -57,7 +53,7 @@ library InteractionLogic {
address followNFT = _profileById[profileIds[i]].followNFT;

if (followNFT == address(0)) {
followNFT = _deployFollowNFT(profileIds[i], handle, followNFTImpl);
followNFT = _deployFollowNFT(profileIds[i]);
_profileById[profileIds[i]].followNFT = followNFT;
}

Expand Down Expand Up @@ -86,9 +82,7 @@ library InteractionLogic {
* @param profileId The token ID of the publication being collected's parent profile.
* @param pubId The publication ID of the publication being collected.
* @param collectModuleData The data to pass to the publication's collect module.
* @param collectNFTImpl The address of the collect NFT implementation, which has to be passed because it's an immutable in the hub.
* @param _pubByIdByProfile A pointer to the storage mapping of publications by pubId by profile ID.
* @param _profileById A pointer to the storage mapping of profile structs by profile ID.
*
* @return uint256 An integer representing the minted token ID.
*/
Expand All @@ -97,10 +91,8 @@ library InteractionLogic {
uint256 profileId,
uint256 pubId,
bytes calldata collectModuleData,
address collectNFTImpl,
mapping(uint256 => mapping(uint256 => DataTypes.PublicationStruct))
storage _pubByIdByProfile,
mapping(uint256 => DataTypes.ProfileStruct) storage _profileById
storage _pubByIdByProfile
) external returns (uint256) {
(uint256 rootProfileId, uint256 rootPubId, address rootCollectModule) = Helpers
.getPointedIfMirror(profileId, pubId, _pubByIdByProfile);
Expand All @@ -110,12 +102,7 @@ library InteractionLogic {
{
address collectNFT = _pubByIdByProfile[rootProfileId][rootPubId].collectNFT;
if (collectNFT == address(0)) {
collectNFT = _deployCollectNFT(
rootProfileId,
rootPubId,
_profileById[rootProfileId].handle,
collectNFTImpl
);
collectNFT = _deployCollectNFT(rootProfileId, rootPubId);
_pubByIdByProfile[rootProfileId][rootPubId].collectNFT = collectNFT;
}
tokenId = ICollectNFT(collectNFT).mint(collector);
Expand Down Expand Up @@ -144,28 +131,22 @@ library InteractionLogic {
* @notice Deploys the given profile's Follow NFT contract.
*
* @param profileId The token ID of the profile which Follow NFT should be deployed.
* @param handle The profile's associated handle.
* @param followNFTImpl The address of the Follow NFT implementation that should be used for the deployment.
*
* @return address The address of the deployed Follow NFT contract.
*/
function _deployFollowNFT(
uint256 profileId,
string memory handle,
address followNFTImpl
) private returns (address) {
address followNFT = Clones.clone(followNFTImpl);

bytes4 firstBytes = bytes4(bytes(handle));

string memory followNFTName = string(
abi.encodePacked(handle, Constants.FOLLOW_NFT_NAME_SUFFIX)
function _deployFollowNFT(uint256 profileId)
private
returns (
// string memory handle,
// address followNFTImpl
address
)
{
bytes memory functionData = abi.encodeWithSelector(
IFollowNFT.initialize.selector,
profileId
);
string memory followNFTSymbol = string(
abi.encodePacked(firstBytes, Constants.FOLLOW_NFT_SYMBOL_SUFFIX)
);

IFollowNFT(followNFT).initialize(profileId, followNFTName, followNFTSymbol);
address followNFT = address(new FollowNFTProxy(functionData));
emit Events.FollowNFTDeployed(profileId, followNFT, block.timestamp);

return followNFT;
Expand All @@ -176,29 +157,16 @@ library InteractionLogic {
*
* @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 handle The profile's associated handle.
* @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,
string memory handle,
address collectNFTImpl
) private returns (address) {
address collectNFT = Clones.clone(collectNFTImpl);

bytes4 firstBytes = bytes4(bytes(handle));

string memory collectNFTName = string(
abi.encodePacked(handle, Constants.COLLECT_NFT_NAME_INFIX, pubId.toString())
);
string memory collectNFTSymbol = string(
abi.encodePacked(firstBytes, Constants.COLLECT_NFT_SYMBOL_INFIX, pubId.toString())
function _deployCollectNFT(uint256 profileId, uint256 pubId) private returns (address) {
bytes memory functionData = abi.encodeWithSelector(
ICollectNFT.initialize.selector,
profileId,
pubId
);

ICollectNFT(collectNFT).initialize(profileId, pubId, collectNFTName, collectNFTSymbol);
address collectNFT = address(new CollectNFTProxy(functionData));
emit Events.CollectNFTDeployed(profileId, pubId, collectNFT, block.timestamp);

return collectNFT;
Expand Down
Loading

0 comments on commit e1ecb54

Please sign in to comment.