Merge pull request #88 from aave/feat/return-ids

This commit is contained in:
Zer0dot
2022-04-04 17:55:56 -04:00
committed by GitHub
16 changed files with 1243 additions and 419 deletions

View File

@@ -48,11 +48,12 @@ contract CollectNFT is ICollectNFT, LensNFTBase {
}
/// @inheritdoc ICollectNFT
function mint(address to) external override {
function mint(address to) external override returns (uint256) {
if (msg.sender != HUB) revert Errors.NotHub();
unchecked {
uint256 tokenId = ++_tokenIdCounter;
_mint(to, tokenId);
return tokenId;
}
}

View File

@@ -64,11 +64,12 @@ contract FollowNFT is LensNFTBase, IFollowNFT {
}
/// @inheritdoc IFollowNFT
function mint(address to) external override {
function mint(address to) external override returns (uint256) {
if (msg.sender != HUB) revert Errors.NotHub();
unchecked {
uint256 tokenId = ++_tokenIdCounter;
_mint(to, tokenId);
return tokenId;
}
}
@@ -83,21 +84,23 @@ contract FollowNFT is LensNFTBase, IFollowNFT {
address delegatee,
DataTypes.EIP712Signature calldata sig
) external override {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
DELEGATE_BY_SIG_TYPEHASH,
delegator,
delegatee,
sigNonces[delegator]++,
sig.deadline
unchecked {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
DELEGATE_BY_SIG_TYPEHASH,
delegator,
delegatee,
sigNonces[delegator]++,
sig.deadline
)
)
)
),
delegator,
sig
);
),
delegator,
sig
);
}
_delegate(delegator, delegatee);
}

View File

@@ -142,6 +142,7 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
external
override
whenNotPaused
returns (uint256)
{
if (!_profileCreatorWhitelisted[msg.sender]) revert Errors.ProfileCreatorNotWhitelisted();
unchecked {
@@ -154,6 +155,7 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
_profileById,
_followModuleWhitelisted
);
return profileId;
}
}
@@ -168,22 +170,24 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
override
whenNotPaused
{
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
SET_DEFAULT_PROFILE_WITH_SIG_TYPEHASH,
vars.wallet,
vars.profileId,
sigNonces[vars.wallet]++,
vars.sig.deadline
unchecked {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
SET_DEFAULT_PROFILE_WITH_SIG_TYPEHASH,
vars.wallet,
vars.profileId,
sigNonces[vars.wallet]++,
vars.sig.deadline
)
)
)
),
vars.wallet,
vars.sig
);
_setDefaultProfile(vars.wallet, vars.profileId);
),
vars.wallet,
vars.sig
);
_setDefaultProfile(vars.wallet, vars.profileId);
}
}
/// @inheritdoc ILensHub
@@ -209,22 +213,24 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
whenNotPaused
{
address owner = ownerOf(vars.profileId);
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
SET_FOLLOW_MODULE_WITH_SIG_TYPEHASH,
vars.profileId,
vars.followModule,
keccak256(vars.followModuleData),
sigNonces[owner]++,
vars.sig.deadline
unchecked {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
SET_FOLLOW_MODULE_WITH_SIG_TYPEHASH,
vars.profileId,
vars.followModule,
keccak256(vars.followModuleData),
sigNonces[owner]++,
vars.sig.deadline
)
)
)
),
owner,
vars.sig
);
),
owner,
vars.sig
);
}
PublishingLogic.setFollowModule(
vars.profileId,
vars.followModule,
@@ -247,21 +253,23 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
whenNotPaused
{
address owner = ownerOf(vars.profileId);
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
SET_DISPATCHER_WITH_SIG_TYPEHASH,
vars.profileId,
vars.dispatcher,
sigNonces[owner]++,
vars.sig.deadline
unchecked {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
SET_DISPATCHER_WITH_SIG_TYPEHASH,
vars.profileId,
vars.dispatcher,
sigNonces[owner]++,
vars.sig.deadline
)
)
)
),
owner,
vars.sig
);
),
owner,
vars.sig
);
}
_setDispatcher(vars.profileId, vars.dispatcher);
}
@@ -282,21 +290,23 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
whenNotPaused
{
address owner = ownerOf(vars.profileId);
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
SET_PROFILE_IMAGE_URI_WITH_SIG_TYPEHASH,
vars.profileId,
keccak256(bytes(vars.imageURI)),
sigNonces[owner]++,
vars.sig.deadline
unchecked {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
SET_PROFILE_IMAGE_URI_WITH_SIG_TYPEHASH,
vars.profileId,
keccak256(bytes(vars.imageURI)),
sigNonces[owner]++,
vars.sig.deadline
)
)
)
),
owner,
vars.sig
);
),
owner,
vars.sig
);
}
_setProfileImageURI(vars.profileId, vars.imageURI);
}
@@ -317,35 +327,43 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
whenNotPaused
{
address owner = ownerOf(vars.profileId);
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
SET_FOLLOW_NFT_URI_WITH_SIG_TYPEHASH,
vars.profileId,
keccak256(bytes(vars.followNFTURI)),
sigNonces[owner]++,
vars.sig.deadline
unchecked {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
SET_FOLLOW_NFT_URI_WITH_SIG_TYPEHASH,
vars.profileId,
keccak256(bytes(vars.followNFTURI)),
sigNonces[owner]++,
vars.sig.deadline
)
)
)
),
owner,
vars.sig
);
),
owner,
vars.sig
);
}
_setFollowNFTURI(vars.profileId, vars.followNFTURI);
}
/// @inheritdoc ILensHub
function post(DataTypes.PostData calldata vars) external override whenPublishingEnabled {
function post(DataTypes.PostData calldata vars)
external
override
whenPublishingEnabled
returns (uint256)
{
_validateCallerIsProfileOwnerOrDispatcher(vars.profileId);
_createPost(
vars.profileId,
vars.contentURI,
vars.collectModule,
vars.collectModuleData,
vars.referenceModule,
vars.referenceModuleData
);
return
_createPost(
vars.profileId,
vars.contentURI,
vars.collectModule,
vars.collectModuleData,
vars.referenceModule,
vars.referenceModuleData
);
}
/// @inheritdoc ILensHub
@@ -353,41 +371,50 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
external
override
whenPublishingEnabled
returns (uint256)
{
address owner = ownerOf(vars.profileId);
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
POST_WITH_SIG_TYPEHASH,
vars.profileId,
keccak256(bytes(vars.contentURI)),
vars.collectModule,
keccak256(vars.collectModuleData),
vars.referenceModule,
keccak256(vars.referenceModuleData),
sigNonces[owner]++,
vars.sig.deadline
unchecked {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
POST_WITH_SIG_TYPEHASH,
vars.profileId,
keccak256(bytes(vars.contentURI)),
vars.collectModule,
keccak256(vars.collectModuleData),
vars.referenceModule,
keccak256(vars.referenceModuleData),
sigNonces[owner]++,
vars.sig.deadline
)
)
)
),
owner,
vars.sig
);
_createPost(
vars.profileId,
vars.contentURI,
vars.collectModule,
vars.collectModuleData,
vars.referenceModule,
vars.referenceModuleData
);
),
owner,
vars.sig
);
}
return
_createPost(
vars.profileId,
vars.contentURI,
vars.collectModule,
vars.collectModuleData,
vars.referenceModule,
vars.referenceModuleData
);
}
/// @inheritdoc ILensHub
function comment(DataTypes.CommentData calldata vars) external override whenPublishingEnabled {
function comment(DataTypes.CommentData calldata vars)
external
override
whenPublishingEnabled
returns (uint256)
{
_validateCallerIsProfileOwnerOrDispatcher(vars.profileId);
_createComment(vars);
return _createComment(vars);
}
/// @inheritdoc ILensHub
@@ -395,53 +422,63 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
external
override
whenPublishingEnabled
returns (uint256)
{
address owner = ownerOf(vars.profileId);
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
COMMENT_WITH_SIG_TYPEHASH,
vars.profileId,
keccak256(bytes(vars.contentURI)),
vars.profileIdPointed,
vars.pubIdPointed,
vars.collectModule,
keccak256(vars.collectModuleData),
vars.referenceModule,
keccak256(vars.referenceModuleData),
sigNonces[owner]++,
vars.sig.deadline
unchecked {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
COMMENT_WITH_SIG_TYPEHASH,
vars.profileId,
keccak256(bytes(vars.contentURI)),
vars.profileIdPointed,
vars.pubIdPointed,
vars.collectModule,
keccak256(vars.collectModuleData),
vars.referenceModule,
keccak256(vars.referenceModuleData),
sigNonces[owner]++,
vars.sig.deadline
)
)
),
owner,
vars.sig
);
}
return
_createComment(
DataTypes.CommentData(
vars.profileId,
vars.contentURI,
vars.profileIdPointed,
vars.pubIdPointed,
vars.collectModule,
vars.collectModuleData,
vars.referenceModule,
vars.referenceModuleData
)
),
owner,
vars.sig
);
_createComment(
DataTypes.CommentData(
vars.profileId,
vars.contentURI,
vars.profileIdPointed,
vars.pubIdPointed,
vars.collectModule,
vars.collectModuleData,
vars.referenceModule,
vars.referenceModuleData
)
);
);
}
/// @inheritdoc ILensHub
function mirror(DataTypes.MirrorData calldata vars) external override whenPublishingEnabled {
function mirror(DataTypes.MirrorData calldata vars)
external
override
whenPublishingEnabled
returns (uint256)
{
_validateCallerIsProfileOwnerOrDispatcher(vars.profileId);
_createMirror(
vars.profileId,
vars.profileIdPointed,
vars.pubIdPointed,
vars.referenceModule,
vars.referenceModuleData
);
return
_createMirror(
vars.profileId,
vars.profileIdPointed,
vars.pubIdPointed,
vars.referenceModule,
vars.referenceModuleData
);
}
/// @inheritdoc ILensHub
@@ -449,33 +486,37 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
external
override
whenPublishingEnabled
returns (uint256)
{
address owner = ownerOf(vars.profileId);
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
MIRROR_WITH_SIG_TYPEHASH,
vars.profileId,
vars.profileIdPointed,
vars.pubIdPointed,
vars.referenceModule,
keccak256(vars.referenceModuleData),
sigNonces[owner]++,
vars.sig.deadline
unchecked {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
MIRROR_WITH_SIG_TYPEHASH,
vars.profileId,
vars.profileIdPointed,
vars.pubIdPointed,
vars.referenceModule,
keccak256(vars.referenceModuleData),
sigNonces[owner]++,
vars.sig.deadline
)
)
)
),
owner,
vars.sig
);
_createMirror(
vars.profileId,
vars.profileIdPointed,
vars.pubIdPointed,
vars.referenceModule,
vars.referenceModuleData
);
),
owner,
vars.sig
);
}
return
_createMirror(
vars.profileId,
vars.profileIdPointed,
vars.pubIdPointed,
vars.referenceModule,
vars.referenceModuleData
);
}
/**
@@ -515,15 +556,17 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
external
override
whenNotPaused
returns (uint256[] memory)
{
InteractionLogic.follow(
msg.sender,
profileIds,
datas,
FOLLOW_NFT_IMPL,
_profileById,
_profileIdByHandleHash
);
return
InteractionLogic.follow(
msg.sender,
profileIds,
datas,
FOLLOW_NFT_IMPL,
_profileById,
_profileIdByHandleHash
);
}
/// @inheritdoc ILensHub
@@ -531,6 +574,7 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
external
override
whenNotPaused
returns (uint256[] memory)
{
uint256 dataLength = vars.datas.length;
bytes32[] memory dataHashes = new bytes32[](dataLength);
@@ -540,29 +584,32 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
++i;
}
}
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
FOLLOW_WITH_SIG_TYPEHASH,
keccak256(abi.encodePacked(vars.profileIds)),
keccak256(abi.encodePacked(dataHashes)),
sigNonces[vars.follower]++,
vars.sig.deadline
unchecked {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
FOLLOW_WITH_SIG_TYPEHASH,
keccak256(abi.encodePacked(vars.profileIds)),
keccak256(abi.encodePacked(dataHashes)),
sigNonces[vars.follower]++,
vars.sig.deadline
)
)
)
),
vars.follower,
vars.sig
);
InteractionLogic.follow(
vars.follower,
vars.profileIds,
vars.datas,
FOLLOW_NFT_IMPL,
_profileById,
_profileIdByHandleHash
);
),
vars.follower,
vars.sig
);
}
return
InteractionLogic.follow(
vars.follower,
vars.profileIds,
vars.datas,
FOLLOW_NFT_IMPL,
_profileById,
_profileIdByHandleHash
);
}
/// @inheritdoc ILensHub
@@ -570,16 +617,17 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
uint256 profileId,
uint256 pubId,
bytes calldata data
) external override whenNotPaused {
InteractionLogic.collect(
msg.sender,
profileId,
pubId,
data,
COLLECT_NFT_IMPL,
_pubByIdByProfile,
_profileById
);
) external override whenNotPaused returns (uint256) {
return
InteractionLogic.collect(
msg.sender,
profileId,
pubId,
data,
COLLECT_NFT_IMPL,
_pubByIdByProfile,
_profileById
);
}
/// @inheritdoc ILensHub
@@ -587,32 +635,36 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
external
override
whenNotPaused
returns (uint256)
{
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
COLLECT_WITH_SIG_TYPEHASH,
vars.profileId,
vars.pubId,
keccak256(vars.data),
sigNonces[vars.collector]++,
vars.sig.deadline
unchecked {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
COLLECT_WITH_SIG_TYPEHASH,
vars.profileId,
vars.pubId,
keccak256(vars.data),
sigNonces[vars.collector]++,
vars.sig.deadline
)
)
)
),
vars.collector,
vars.sig
);
InteractionLogic.collect(
vars.collector,
vars.profileId,
vars.pubId,
vars.data,
COLLECT_NFT_IMPL,
_pubByIdByProfile,
_profileById
);
),
vars.collector,
vars.sig
);
}
return
InteractionLogic.collect(
vars.collector,
vars.profileId,
vars.pubId,
vars.data,
COLLECT_NFT_IMPL,
_pubByIdByProfile,
_profileById
);
}
/// @inheritdoc ILensHub
@@ -861,19 +913,23 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
bytes memory collectModuleData,
address referenceModule,
bytes memory referenceModuleData
) internal {
PublishingLogic.createPost(
profileId,
contentURI,
collectModule,
collectModuleData,
referenceModule,
referenceModuleData,
++_profileById[profileId].pubCount,
_pubByIdByProfile,
_collectModuleWhitelisted,
_referenceModuleWhitelisted
);
) internal returns (uint256) {
unchecked {
uint256 pubId = ++_profileById[profileId].pubCount;
PublishingLogic.createPost(
profileId,
contentURI,
collectModule,
collectModuleData,
referenceModule,
referenceModuleData,
pubId,
_pubByIdByProfile,
_collectModuleWhitelisted,
_referenceModuleWhitelisted
);
return pubId;
}
}
function _setDefaultProfile(address wallet, uint256 profileId) internal {
@@ -885,15 +941,19 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
emit Events.DefaultProfileSet(wallet, profileId, block.timestamp);
}
function _createComment(DataTypes.CommentData memory vars) internal {
PublishingLogic.createComment(
vars,
++_profileById[vars.profileId].pubCount,
_profileById,
_pubByIdByProfile,
_collectModuleWhitelisted,
_referenceModuleWhitelisted
);
function _createComment(DataTypes.CommentData memory vars) internal returns (uint256) {
unchecked {
uint256 pubId = ++_profileById[vars.profileId].pubCount;
PublishingLogic.createComment(
vars,
pubId,
_profileById,
_pubByIdByProfile,
_collectModuleWhitelisted,
_referenceModuleWhitelisted
);
return pubId;
}
}
function _createMirror(
@@ -902,17 +962,21 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
uint256 pubIdPointed,
address referenceModule,
bytes calldata referenceModuleData
) internal {
PublishingLogic.createMirror(
profileId,
profileIdPointed,
pubIdPointed,
referenceModule,
referenceModuleData,
++_profileById[profileId].pubCount,
_pubByIdByProfile,
_referenceModuleWhitelisted
);
) internal returns (uint256) {
unchecked {
uint256 pubId = ++_profileById[profileId].pubCount;
PublishingLogic.createMirror(
profileId,
profileIdPointed,
pubIdPointed,
referenceModule,
referenceModuleData,
pubId,
_pubByIdByProfile,
_referenceModuleWhitelisted
);
return pubId;
}
}
function _setDispatcher(uint256 profileId, address dispatcher) internal {

View File

@@ -49,15 +49,23 @@ abstract contract LensNFTBase is ILensNFTBase, ERC721Enumerable {
) external override {
if (spender == address(0)) revert Errors.ZeroSpender();
address owner = ownerOf(tokenId);
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(PERMIT_TYPEHASH, spender, tokenId, sigNonces[owner]++, sig.deadline)
)
),
owner,
sig
);
unchecked {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
PERMIT_TYPEHASH,
spender,
tokenId,
sigNonces[owner]++,
sig.deadline
)
)
),
owner,
sig
);
}
_approve(spender, tokenId);
}
@@ -69,22 +77,24 @@ abstract contract LensNFTBase is ILensNFTBase, ERC721Enumerable {
DataTypes.EIP712Signature calldata sig
) external override {
if (operator == address(0)) revert Errors.ZeroSpender();
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
PERMIT_FOR_ALL_TYPEHASH,
owner,
operator,
approved,
sigNonces[owner]++,
sig.deadline
unchecked {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
PERMIT_FOR_ALL_TYPEHASH,
owner,
operator,
approved,
sigNonces[owner]++,
sig.deadline
)
)
)
),
owner,
sig
);
),
owner,
sig
);
}
_setOperatorApproval(owner, operator, approved);
}
@@ -106,16 +116,22 @@ abstract contract LensNFTBase is ILensNFTBase, ERC721Enumerable {
override
{
address owner = ownerOf(tokenId);
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(BURN_WITH_SIG_TYPEHASH, tokenId, sigNonces[owner]++, sig.deadline)
)
),
owner,
sig
);
unchecked {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
BURN_WITH_SIG_TYPEHASH,
tokenId,
sigNonces[owner]++,
sig.deadline
)
)
),
owner,
sig
);
}
_burn(tokenId);
}

View File

@@ -31,8 +31,10 @@ interface ICollectNFT {
* upon collection.
*
* @param to The address to mint the NFT to.
*
* @return uint256 An interger representing the minted token ID.
*/
function mint(address to) external;
function mint(address to) external returns (uint256);
/**
* @notice Returns the source publication pointer mapped to this collect NFT.

View File

@@ -30,8 +30,10 @@ interface IFollowNFT {
* upon follow.
*
* @param to The address to mint the NFT to.
*
* @return uint256 An interger representing the minted token ID.
*/
function mint(address to) external;
function mint(address to) external returns (uint256);
/**
* @notice Delegates the caller's governance power to the given delegatee address.

View File

@@ -97,7 +97,7 @@ interface ILensHub {
* followModule: The follow module to use, can be the zero address.
* followModuleData: The follow module initialization data, if any.
*/
function createProfile(DataTypes.CreateProfileData calldata vars) external;
function createProfile(DataTypes.CreateProfileData calldata vars) external returns (uint256);
/**
* @notice Sets the mapping between wallet and its main profile identity.
@@ -184,44 +184,55 @@ interface ILensHub {
* @notice Publishes a post to a given profile, must be called by the profile owner.
*
* @param vars A PostData struct containing the needed parameters.
*
* @return uint256 An integer representing the post's publication ID.
*/
function post(DataTypes.PostData calldata vars) external;
function post(DataTypes.PostData calldata vars) external returns (uint256);
/**
* @notice Publishes a post to a given profile via signature with the specified parameters.
*
* @param vars A PostWithSigData struct containing the regular parameters and an EIP712Signature struct.
*
* @return uint256 An integer representing the post's publication ID.
*/
function postWithSig(DataTypes.PostWithSigData calldata vars) external;
function postWithSig(DataTypes.PostWithSigData calldata vars) external returns (uint256);
/**
* @notice Publishes a comment to a given profile, must be called by the profile owner.
*
* @param vars A CommentData struct containing the needed parameters.
*
* @return uint256 An integer representing the comment's publication ID.
*/
function comment(DataTypes.CommentData calldata vars) external;
function comment(DataTypes.CommentData calldata vars) external returns (uint256);
/**
* @notice Publishes a comment to a given profile via signature with the specified parameters.
*
*@param vars A CommentWithSigData struct containing the regular parameters and an EIP712Signature struct.
* @param vars A CommentWithSigData struct containing the regular parameters and an EIP712Signature struct.
*
* @return uint256 An integer representing the comment's publication ID.
*/
function commentWithSig(DataTypes.CommentWithSigData calldata vars) external;
function commentWithSig(DataTypes.CommentWithSigData calldata vars) external returns (uint256);
/**
* @notice Publishes a mirror to a given profile, must be called by the profile owner.
*
* @param vars A MirrorData struct containing the necessary parameters.
*
* @return uint256 An integer representing the mirror's publication ID.
*/
function mirror(DataTypes.MirrorData calldata vars) external;
function mirror(DataTypes.MirrorData calldata vars) external returns (uint256);
/**
* @notice Publishes a mirror to a given profile via signature with the specified parameters.
*
* @param vars A MirrorWithSigData struct containing the regular parameters and an EIP712Signature struct.
*
* @return uint256 An integer representing the mirror's publication ID.
*/
function mirrorWithSig(DataTypes.MirrorWithSigData calldata vars) external;
function mirrorWithSig(DataTypes.MirrorWithSigData calldata vars) external returns (uint256);
/**
* @notice Follows the given profiles, executing each profile's follow module logic (if any) and minting followNFTs to the caller.
@@ -230,16 +241,24 @@ interface ILensHub {
*
* @param profileIds The token ID array of the profiles to follow.
* @param datas The arbitrary data array to pass to the follow module for each profile if needed.
*
* @return uint256[] An array of integers representing the minted follow NFTs token IDs.
*/
function follow(uint256[] calldata profileIds, bytes[] calldata datas) external;
function follow(uint256[] calldata profileIds, bytes[] calldata datas)
external
returns (uint256[] memory);
/**
* @notice Follows a given profile via signature with the specified parameters.
*
* @param vars A FollowWithSigData struct containing the regular parameters as well as the signing follower's address
* and an EIP712Signature struct.
*
* @return uint256[] An array of integers representing the minted follow NFTs token IDs.
*/
function followWithSig(DataTypes.FollowWithSigData calldata vars) external;
function followWithSig(DataTypes.FollowWithSigData calldata vars)
external
returns (uint256[] memory);
/**
* @notice Collects a given publication, executing collect module logic and minting a collectNFT to the caller.
@@ -247,20 +266,24 @@ interface ILensHub {
* @param profileId The token ID of the profile that published the publication to collect.
* @param pubId The publication to collect's publication ID.
* @param data The arbitrary data to pass to the collect module if needed.
*
* @return uint256 An integer representing the minted token ID.
*/
function collect(
uint256 profileId,
uint256 pubId,
bytes calldata data
) external;
) external returns (uint256);
/**
* @notice Collects a given publication via signature with the specified parameters.
*
* @param vars A CollectWithSigData struct containing the regular parameters as well as the collector's address and
* an EIP712Signature struct.
*
* @return uint256 An integer representing the minted token ID.
*/
function collectWithSig(DataTypes.CollectWithSigData calldata vars) external;
function collectWithSig(DataTypes.CollectWithSigData calldata vars) external returns (uint256);
/**
* @dev Helper function to emit a detailed followNFT transfer event from the hub, to be consumed by frontends to track

View File

@@ -35,6 +35,8 @@ library InteractionLogic {
* @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.
*
* @return uint256[] An array of integers representing the minted follow NFTs token IDs.
*/
function follow(
address follower,
@@ -43,8 +45,9 @@ library InteractionLogic {
address followNFTImpl,
mapping(uint256 => DataTypes.ProfileStruct) storage _profileById,
mapping(bytes32 => uint256) storage _profileIdByHandleHash
) external {
) external returns (uint256[] memory) {
if (profileIds.length != followModuleDatas.length) revert Errors.ArrayMismatch();
uint256[] memory tokenIds = new uint256[](profileIds.length);
for (uint256 i = 0; i < profileIds.length; ) {
string memory handle = _profileById[profileIds[i]].handle;
if (_profileIdByHandleHash[keccak256(bytes(handle))] != profileIds[i])
@@ -55,23 +58,11 @@ library InteractionLogic {
address followNFT = _profileById[profileIds[i]].followNFT;
if (followNFT == address(0)) {
followNFT = Clones.clone(followNFTImpl);
followNFT = _deployFollowNFT(profileIds[i], handle, followNFTImpl);
_profileById[profileIds[i]].followNFT = followNFT;
bytes4 firstBytes = bytes4(bytes(handle));
string memory followNFTName = string(
abi.encodePacked(handle, Constants.FOLLOW_NFT_NAME_SUFFIX)
);
string memory followNFTSymbol = string(
abi.encodePacked(firstBytes, Constants.FOLLOW_NFT_SYMBOL_SUFFIX)
);
IFollowNFT(followNFT).initialize(profileIds[i], followNFTName, followNFTSymbol);
emit Events.FollowNFTDeployed(profileIds[i], followNFT, block.timestamp);
}
IFollowNFT(followNFT).mint(follower);
tokenIds[i] = IFollowNFT(followNFT).mint(follower);
if (followModule != address(0)) {
IFollowModule(followModule).processFollow(
@@ -84,6 +75,7 @@ library InteractionLogic {
++i;
}
}
return tokenIds;
}
/**
@@ -97,6 +89,8 @@ library InteractionLogic {
* @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.
*/
function collect(
address collector,
@@ -107,41 +101,26 @@ library InteractionLogic {
mapping(uint256 => mapping(uint256 => DataTypes.PublicationStruct))
storage _pubByIdByProfile,
mapping(uint256 => DataTypes.ProfileStruct) storage _profileById
) external {
) external returns (uint256) {
(uint256 rootProfileId, uint256 rootPubId, address rootCollectModule) = Helpers
.getPointedIfMirror(profileId, pubId, _pubByIdByProfile);
address collectNFT = _pubByIdByProfile[rootProfileId][rootPubId].collectNFT;
if (collectNFT == address(0)) {
collectNFT = Clones.clone(collectNFTImpl);
_pubByIdByProfile[rootProfileId][rootPubId].collectNFT = collectNFT;
string memory handle = _profileById[rootProfileId].handle;
bytes4 firstBytes = bytes4(bytes(handle));
string memory collectNFTName = string(
abi.encodePacked(handle, Constants.COLLECT_NFT_NAME_INFIX, rootPubId.toString())
);
string memory collectNFTSymbol = string(
abi.encodePacked(
firstBytes,
Constants.COLLECT_NFT_SYMBOL_INFIX,
rootPubId.toString()
)
);
ICollectNFT(collectNFT).initialize(
rootProfileId,
rootPubId,
collectNFTName,
collectNFTSymbol
);
emit Events.CollectNFTDeployed(rootProfileId, rootPubId, collectNFT, block.timestamp);
uint256 tokenId;
// Avoids stack too deep
{
address collectNFT = _pubByIdByProfile[rootProfileId][rootPubId].collectNFT;
if (collectNFT == address(0)) {
collectNFT = _deployCollectNFT(
rootProfileId,
rootPubId,
_profileById[rootProfileId].handle,
collectNFTImpl
);
_pubByIdByProfile[rootProfileId][rootPubId].collectNFT = collectNFT;
}
tokenId = ICollectNFT(collectNFT).mint(collector);
}
ICollectNFT(collectNFT).mint(collector);
ICollectModule(rootCollectModule).processCollect(
profileId,
collector,
@@ -149,6 +128,93 @@ library InteractionLogic {
rootPubId,
collectModuleData
);
_emitCollectedEvent(collector, profileId, pubId, rootProfileId, rootPubId);
return tokenId;
}
/**
* @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)
);
string memory followNFTSymbol = string(
abi.encodePacked(firstBytes, Constants.FOLLOW_NFT_SYMBOL_SUFFIX)
);
IFollowNFT(followNFT).initialize(profileId, followNFTName, followNFTSymbol);
emit Events.FollowNFTDeployed(profileId, followNFT, block.timestamp);
return followNFT;
}
/**
* @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 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())
);
ICollectNFT(collectNFT).initialize(profileId, pubId, collectNFTName, collectNFTSymbol);
emit Events.CollectNFTDeployed(profileId, pubId, collectNFT, block.timestamp);
return collectNFT;
}
/**
* @notice Emits the `Collected` event that signals that a successful collect action has occurred.
*
* @dev This is done through this function to prevent stack too deep compilation error.
*
* @param collector The address collecting the publication.
* @param profileId The token ID of the profile that the collect was initiated towards, useful to differentiate mirrors.
* @param pubId The publication ID that the collect was initiated towards, useful to differentiate mirrors.
* @param rootProfileId The profile token ID of the profile whose publication is being collected.
* @param rootPubId The publication ID of the publication being collected.
*/
function _emitCollectedEvent(
address collector,
uint256 profileId,
uint256 pubId,
uint256 rootProfileId,
uint256 rootPubId
) private {
emit Events.Collected(
collector,
profileId,

View File

@@ -110,21 +110,24 @@ contract LensPeriphery {
* and an EIP712Signature struct.
*/
function toggleFollowWithSig(DataTypes.ToggleFollowWithSigData calldata vars) external {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
TOGGLE_FOLLOW_WITH_SIG_TYPEHASH,
keccak256(abi.encodePacked(vars.profileIds)),
keccak256(abi.encodePacked(vars.enables)),
sigNonces[vars.follower]++,
vars.sig.deadline
unchecked {
_validateRecoveredAddress(
_calculateDigest(
keccak256(
abi.encode(
TOGGLE_FOLLOW_WITH_SIG_TYPEHASH,
keccak256(abi.encodePacked(vars.profileIds)),
keccak256(abi.encodePacked(vars.enables)),
sigNonces[vars.follower]++,
vars.sig.deadline
)
)
)
),
vars.follower,
vars.sig
);
),
vars.follower,
vars.sig
);
}
_toggleFollow(vars.follower, vars.profileIds, vars.enables);
}

View File

@@ -1,5 +1,5 @@
import '@nomiclabs/hardhat-ethers';
import { BigNumberish, Bytes, logger, utils, BigNumber, Contract } from 'ethers';
import { BigNumberish, Bytes, logger, utils, BigNumber, Contract, Signer } from 'ethers';
import {
eventsLib,
helper,
@@ -8,15 +8,27 @@ import {
lensPeriphery,
LENS_PERIPHERY_NAME,
testWallet,
user,
} from '../__setup.spec';
import { expect } from 'chai';
import { HARDHAT_CHAINID, MAX_UINT256 } from './constants';
import { hexlify, keccak256, RLP, toUtf8Bytes } from 'ethers/lib/utils';
import { BytesLike, hexlify, keccak256, RLP, toUtf8Bytes } from 'ethers/lib/utils';
import { LensHub__factory } from '../../typechain-types';
import { TransactionReceipt, TransactionResponse } from '@ethersproject/providers';
import hre, { ethers } from 'hardhat';
import { readFileSync } from 'fs';
import { join } from 'path';
import {
CollectWithSigDataStruct,
CommentDataStruct,
CommentWithSigDataStruct,
CreateProfileDataStruct,
FollowWithSigDataStruct,
MirrorDataStruct,
MirrorWithSigDataStruct,
PostDataStruct,
PostWithSigDataStruct,
} from '../../typechain-types/LensHub';
export enum ProtocolState {
Unpaused,
@@ -466,6 +478,151 @@ export async function getCollectWithSigParts(
return await getSig(msgParams);
}
export function expectEqualArrays(actual: BigNumberish[], expected: BigNumberish[]) {
if (actual.length != expected.length) {
logger.throwError(
`${actual} length ${actual.length} does not match ${expected} length ${expect.length}`
);
}
let areEquals = true;
for (let i = 0; areEquals && i < actual.length; i++) {
areEquals = BigNumber.from(actual[i]).eq(BigNumber.from(expected[i]));
}
if (!areEquals) {
logger.throwError(`${actual} does not match ${expected}`);
}
}
export interface CreateProfileReturningTokenIdStruct {
sender?: Signer;
vars: CreateProfileDataStruct;
}
export async function createProfileReturningTokenId({
sender = user,
vars,
}: CreateProfileReturningTokenIdStruct): Promise<BigNumber> {
const tokenId = await lensHub.connect(sender).callStatic.createProfile(vars);
await expect(lensHub.connect(sender).createProfile(vars)).to.not.be.reverted;
return tokenId;
}
export interface FollowDataStruct {
profileIds: BigNumberish[];
datas: BytesLike[];
}
export interface FollowReturningTokenIdsStruct {
sender?: Signer;
vars: FollowDataStruct | FollowWithSigDataStruct;
}
export async function followReturningTokenIds({
sender = user,
vars,
}: FollowReturningTokenIdsStruct): Promise<BigNumber[]> {
let tokenIds;
if ('sig' in vars) {
tokenIds = await lensHub.connect(sender).callStatic.followWithSig(vars);
await expect(lensHub.connect(sender).followWithSig(vars)).to.not.be.reverted;
} else {
tokenIds = await lensHub.connect(sender).callStatic.follow(vars.profileIds, vars.datas);
await expect(lensHub.connect(sender).follow(vars.profileIds, vars.datas)).to.not.be.reverted;
}
return tokenIds;
}
export interface CollectDataStruct {
profileId: BigNumberish;
pubId: BigNumberish;
data: BytesLike;
}
export interface CollectReturningTokenIdsStruct {
sender?: Signer;
vars: CollectDataStruct | CollectWithSigDataStruct;
}
export async function collectReturningTokenIds({
sender = user,
vars,
}: CollectReturningTokenIdsStruct): Promise<BigNumber> {
let tokenId;
if ('sig' in vars) {
tokenId = await lensHub.connect(sender).callStatic.collectWithSig(vars);
await expect(lensHub.connect(sender).collectWithSig(vars)).to.not.be.reverted;
} else {
tokenId = await lensHub
.connect(sender)
.callStatic.collect(vars.profileId, vars.pubId, vars.data);
await expect(lensHub.connect(sender).collect(vars.profileId, vars.pubId, vars.data)).to.not.be
.reverted;
}
return tokenId;
}
export interface CommentReturningTokenIdStruct {
sender?: Signer;
vars: CommentDataStruct | CommentWithSigDataStruct;
}
export async function commentReturningTokenId({
sender = user,
vars,
}: CommentReturningTokenIdStruct): Promise<BigNumber> {
let tokenId;
if ('sig' in vars) {
tokenId = await lensHub.connect(sender).callStatic.commentWithSig(vars);
await expect(lensHub.connect(sender).commentWithSig(vars)).to.not.be.reverted;
} else {
tokenId = await lensHub.connect(sender).callStatic.comment(vars);
await expect(lensHub.connect(sender).comment(vars)).to.not.be.reverted;
}
return tokenId;
}
export interface MirrorReturningTokenIdStruct {
sender?: Signer;
vars: MirrorDataStruct | MirrorWithSigDataStruct;
}
export async function mirrorReturningTokenId({
sender = user,
vars,
}: MirrorReturningTokenIdStruct): Promise<BigNumber> {
let tokenId;
if ('sig' in vars) {
tokenId = await lensHub.connect(sender).callStatic.mirrorWithSig(vars);
await expect(lensHub.connect(sender).mirrorWithSig(vars)).to.not.be.reverted;
} else {
tokenId = await lensHub.connect(sender).callStatic.mirror(vars);
await expect(lensHub.connect(sender).mirror(vars)).to.not.be.reverted;
}
return tokenId;
}
export interface PostReturningTokenIdStruct {
sender?: Signer;
vars: PostDataStruct | PostWithSigDataStruct;
}
export async function postReturningTokenId({
sender = user,
vars,
}: PostReturningTokenIdStruct): Promise<BigNumber> {
let tokenId;
if ('sig' in vars) {
tokenId = await lensHub.connect(sender).callStatic.postWithSig(vars);
await expect(lensHub.connect(sender).postWithSig(vars)).to.not.be.reverted;
} else {
tokenId = await lensHub.connect(sender).callStatic.post(vars);
await expect(lensHub.connect(sender).post(vars)).to.not.be.reverted;
}
return tokenId;
}
export interface TokenUriMetadataAttribute {
trait_type: string;
value: string;

View File

@@ -5,6 +5,7 @@ import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
import { ERRORS } from '../../helpers/errors';
import {
cancelWithPermitForAll,
collectReturningTokenIds,
getAbbreviation,
getCollectWithSigParts,
getTimestamp,
@@ -95,6 +96,69 @@ makeSuiteCleanRoom('Collecting', function () {
await expect(lensHub.collect(FIRST_PROFILE_ID, 1, [])).to.not.be.reverted;
});
it('Should return the expected token IDs when collecting publications', async function () {
await expect(lensHub.connect(userTwo).follow([FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
await expect(
lensHub.connect(testWallet).follow([FIRST_PROFILE_ID], [[]])
).to.not.be.reverted;
expect(
await collectReturningTokenIds({
vars: {
profileId: FIRST_PROFILE_ID,
pubId: 1,
data: [],
},
})
).to.eq(1);
expect(
await collectReturningTokenIds({
sender: userTwo,
vars: {
profileId: FIRST_PROFILE_ID,
pubId: 1,
data: [],
},
})
).to.eq(2);
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
const { v, r, s } = await getCollectWithSigParts(
FIRST_PROFILE_ID,
'1',
[],
nonce,
MAX_UINT256
);
expect(
await collectReturningTokenIds({
vars: {
collector: testWallet.address,
profileId: FIRST_PROFILE_ID,
pubId: '1',
data: [],
sig: {
v,
r,
s,
deadline: MAX_UINT256,
},
},
})
).to.eq(3);
expect(
await collectReturningTokenIds({
vars: {
profileId: FIRST_PROFILE_ID,
pubId: 1,
data: [],
},
})
).to.eq(4);
});
it('UserTwo should follow, then collect, receive a collect NFT with the expected properties', async function () {
await expect(lensHub.connect(userTwo).follow([FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
await expect(lensHub.connect(userTwo).collect(FIRST_PROFILE_ID, 1, [])).to.not.be.reverted;

View File

@@ -1,10 +1,13 @@
import '@nomiclabs/hardhat-ethers';
import { expect } from 'chai';
import { BigNumber } from 'ethers';
import { FollowNFT__factory } from '../../../typechain-types';
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
import { ERRORS } from '../../helpers/errors';
import {
cancelWithPermitForAll,
expectEqualArrays,
followReturningTokenIds,
getAbbreviation,
getFollowWithSigParts,
getTimestamp,
@@ -96,7 +99,11 @@ makeSuiteCleanRoom('Following', function () {
});
it('UserTwo should follow profile 1 3 times in the same call, receive IDs 1,2 and 3', async function () {
await expect(lensHub.connect(userTwo).follow([FIRST_PROFILE_ID, FIRST_PROFILE_ID, FIRST_PROFILE_ID], [[], [], []])).to.not.be.reverted;
await expect(
lensHub
.connect(userTwo)
.follow([FIRST_PROFILE_ID, FIRST_PROFILE_ID, FIRST_PROFILE_ID], [[], [], []])
).to.not.be.reverted;
const followNFTAddress = await lensHub.getFollowNFT(FIRST_PROFILE_ID);
const followNFT = FollowNFT__factory.connect(followNFTAddress, user);
const idOne = await followNFT.tokenOfOwnerByIndex(userTwoAddress, 0);
@@ -106,6 +113,63 @@ makeSuiteCleanRoom('Following', function () {
expect(idTwo).to.eq(2);
expect(idThree).to.eq(3);
});
it('Should return the expected token IDs when following profiles', async function () {
expectEqualArrays(
await followReturningTokenIds({
vars: {
profileIds: [FIRST_PROFILE_ID, FIRST_PROFILE_ID],
datas: [[], []],
},
}),
[1, 2]
);
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
const { v, r, s } = await getFollowWithSigParts(
[FIRST_PROFILE_ID],
[[]],
nonce,
MAX_UINT256
);
expectEqualArrays(
await followReturningTokenIds({
vars: {
follower: testWallet.address,
profileIds: [FIRST_PROFILE_ID],
datas: [[]],
sig: {
v,
r,
s,
deadline: MAX_UINT256,
},
},
}),
[3]
);
expectEqualArrays(
await followReturningTokenIds({
sender: userTwo,
vars: {
profileIds: [FIRST_PROFILE_ID],
datas: [[]],
},
}),
[4]
);
expectEqualArrays(
await followReturningTokenIds({
vars: {
profileIds: [FIRST_PROFILE_ID],
datas: [[]],
},
}),
[5]
);
});
});
});

View File

@@ -2,7 +2,11 @@ import '@nomiclabs/hardhat-ethers';
import { expect } from 'chai';
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
import { ERRORS } from '../../helpers/errors';
import { cancelWithPermitForAll, getCommentWithSigParts } from '../../helpers/utils';
import {
cancelWithPermitForAll,
commentReturningTokenId,
getCommentWithSigParts,
} from '../../helpers/utils';
import {
abiCoder,
freeCollectModule,
@@ -20,6 +24,7 @@ import {
timedFeeCollectModule,
userAddress,
userTwo,
userTwoAddress,
} from '../../__setup.spec';
makeSuiteCleanRoom('Publishing Comments', function () {
@@ -191,6 +196,112 @@ makeSuiteCleanRoom('Publishing Comments', function () {
expect(pub.referenceModule).to.eq(ZERO_ADDRESS);
});
it('Should return the expected token IDs when commenting publications', async function () {
await expect(
lensHub.connect(testWallet).createProfile({
to: testWallet.address,
handle: 'testwallet',
imageURI: MOCK_PROFILE_URI,
followModule: ZERO_ADDRESS,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
})
).to.not.be.reverted;
await expect(
lensHub.connect(testWallet).createProfile({
to: userTwoAddress,
handle: 'usertwo',
imageURI: MOCK_PROFILE_URI,
followModule: ZERO_ADDRESS,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
})
).to.not.be.reverted;
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
const collectModuleData = abiCoder.encode(['bool'], [true]);
const referenceModuleData = [];
const { v, r, s } = await getCommentWithSigParts(
FIRST_PROFILE_ID + 1,
OTHER_MOCK_URI,
FIRST_PROFILE_ID,
'1',
freeCollectModule.address,
collectModuleData,
ZERO_ADDRESS,
referenceModuleData,
nonce,
MAX_UINT256
);
expect(
await commentReturningTokenId({
vars: {
profileId: FIRST_PROFILE_ID + 1,
contentURI: OTHER_MOCK_URI,
profileIdPointed: FIRST_PROFILE_ID,
pubIdPointed: '1',
collectModule: freeCollectModule.address,
collectModuleData: collectModuleData,
referenceModule: ZERO_ADDRESS,
referenceModuleData: referenceModuleData,
sig: {
v,
r,
s,
deadline: MAX_UINT256,
},
},
})
).to.eq(1);
expect(
await commentReturningTokenId({
sender: userTwo,
vars: {
profileId: FIRST_PROFILE_ID + 2,
contentURI: MOCK_URI,
profileIdPointed: FIRST_PROFILE_ID,
pubIdPointed: 1,
collectModule: freeCollectModule.address,
collectModuleData: collectModuleData,
referenceModule: ZERO_ADDRESS,
referenceModuleData: referenceModuleData,
},
})
).to.eq(1);
expect(
await commentReturningTokenId({
sender: testWallet,
vars: {
profileId: FIRST_PROFILE_ID + 1,
contentURI: MOCK_URI,
profileIdPointed: FIRST_PROFILE_ID,
pubIdPointed: 1,
collectModule: freeCollectModule.address,
collectModuleData: collectModuleData,
referenceModule: ZERO_ADDRESS,
referenceModuleData: referenceModuleData,
},
})
).to.eq(2);
expect(
await commentReturningTokenId({
vars: {
profileId: FIRST_PROFILE_ID,
contentURI: MOCK_URI,
profileIdPointed: FIRST_PROFILE_ID,
pubIdPointed: 1,
collectModule: freeCollectModule.address,
collectModuleData: collectModuleData,
referenceModule: ZERO_ADDRESS,
referenceModuleData: referenceModuleData,
},
})
).to.eq(2);
});
it('User should create a post using the mock reference module as reference module, then comment on that post', async function () {
const data = abiCoder.encode(['uint256'], ['1']);
await expect(

View File

@@ -2,7 +2,11 @@ import '@nomiclabs/hardhat-ethers';
import { expect } from 'chai';
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
import { ERRORS } from '../../helpers/errors';
import { cancelWithPermitForAll, getMirrorWithSigParts } from '../../helpers/utils';
import {
cancelWithPermitForAll,
getMirrorWithSigParts,
mirrorReturningTokenId,
} from '../../helpers/utils';
import {
abiCoder,
freeCollectModule,
@@ -18,6 +22,7 @@ import {
testWallet,
userAddress,
userTwo,
userTwoAddress,
} from '../../__setup.spec';
makeSuiteCleanRoom('Publishing mirrors', function () {
@@ -105,6 +110,95 @@ makeSuiteCleanRoom('Publishing mirrors', function () {
});
context('Scenarios', function () {
it('Should return the expected token IDs when mirroring publications', async function () {
await expect(
lensHub.createProfile({
to: testWallet.address,
handle: 'testwallet',
imageURI: MOCK_PROFILE_URI,
followModule: ZERO_ADDRESS,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
})
).to.not.be.reverted;
await expect(
lensHub.createProfile({
to: userTwoAddress,
handle: 'usertwo',
imageURI: MOCK_PROFILE_URI,
followModule: ZERO_ADDRESS,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
})
).to.not.be.reverted;
expect(
await mirrorReturningTokenId({
vars: {
profileId: FIRST_PROFILE_ID,
profileIdPointed: FIRST_PROFILE_ID,
pubIdPointed: 1,
referenceModule: ZERO_ADDRESS,
referenceModuleData: [],
},
})
).to.eq(2);
expect(
await mirrorReturningTokenId({
sender: userTwo,
vars: {
profileId: FIRST_PROFILE_ID + 2,
profileIdPointed: FIRST_PROFILE_ID,
pubIdPointed: 2,
referenceModule: ZERO_ADDRESS,
referenceModuleData: [],
},
})
).to.eq(1);
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
const referenceModuleData = [];
const { v, r, s } = await getMirrorWithSigParts(
FIRST_PROFILE_ID + 1,
FIRST_PROFILE_ID,
'1',
ZERO_ADDRESS,
referenceModuleData,
nonce,
MAX_UINT256
);
expect(
await mirrorReturningTokenId({
vars: {
profileId: FIRST_PROFILE_ID + 1,
profileIdPointed: FIRST_PROFILE_ID,
pubIdPointed: '1',
referenceModule: ZERO_ADDRESS,
referenceModuleData: referenceModuleData,
sig: {
v,
r,
s,
deadline: MAX_UINT256,
},
},
})
).to.eq(1);
expect(
await mirrorReturningTokenId({
vars: {
profileId: FIRST_PROFILE_ID,
profileIdPointed: FIRST_PROFILE_ID + 1,
pubIdPointed: 1,
referenceModule: ZERO_ADDRESS,
referenceModuleData: [],
},
})
).to.eq(3);
});
it('User should create a mirror with empty reference module and reference module data, fetched mirror data should be accurate', async function () {
await expect(
lensHub.mirror({

View File

@@ -2,7 +2,11 @@ import '@nomiclabs/hardhat-ethers';
import { expect } from 'chai';
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
import { ERRORS } from '../../helpers/errors';
import { cancelWithPermitForAll, getPostWithSigParts } from '../../helpers/utils';
import {
cancelWithPermitForAll,
getPostWithSigParts,
postReturningTokenId,
} from '../../helpers/utils';
import {
freeCollectModule,
FIRST_PROFILE_ID,
@@ -20,6 +24,7 @@ import {
userAddress,
userTwo,
abiCoder,
userTwoAddress,
} from '../../__setup.spec';
makeSuiteCleanRoom('Publishing Posts', function () {
@@ -121,6 +126,105 @@ makeSuiteCleanRoom('Publishing Posts', function () {
});
context('Scenarios', function () {
it('Should return the expected token IDs when mirroring publications', async function () {
await expect(
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)
).to.not.be.reverted;
await expect(
lensHub.createProfile({
to: testWallet.address,
handle: 'testwallet',
imageURI: MOCK_PROFILE_URI,
followModule: ZERO_ADDRESS,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
})
).to.not.be.reverted;
await expect(
lensHub.createProfile({
to: userTwoAddress,
handle: 'usertwo',
imageURI: MOCK_PROFILE_URI,
followModule: ZERO_ADDRESS,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
})
).to.not.be.reverted;
expect(
await postReturningTokenId({
vars: {
profileId: FIRST_PROFILE_ID,
contentURI: MOCK_URI,
collectModule: freeCollectModule.address,
collectModuleData: abiCoder.encode(['bool'], [true]),
referenceModule: ZERO_ADDRESS,
referenceModuleData: [],
},
})
).to.eq(1);
expect(
await postReturningTokenId({
sender: userTwo,
vars: {
profileId: FIRST_PROFILE_ID + 2,
contentURI: MOCK_URI,
collectModule: freeCollectModule.address,
collectModuleData: abiCoder.encode(['bool'], [true]),
referenceModule: ZERO_ADDRESS,
referenceModuleData: [],
},
})
).to.eq(1);
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
const collectModuleData = abiCoder.encode(['bool'], [true]);
const referenceModuleData = [];
const { v, r, s } = await getPostWithSigParts(
FIRST_PROFILE_ID + 1,
MOCK_URI,
freeCollectModule.address,
collectModuleData,
ZERO_ADDRESS,
referenceModuleData,
nonce,
MAX_UINT256
);
expect(
await postReturningTokenId({
vars: {
profileId: FIRST_PROFILE_ID + 1,
contentURI: MOCK_URI,
collectModule: freeCollectModule.address,
collectModuleData: collectModuleData,
referenceModule: ZERO_ADDRESS,
referenceModuleData: referenceModuleData,
sig: {
v,
r,
s,
deadline: MAX_UINT256,
},
},
})
).to.eq(1);
expect(
await postReturningTokenId({
vars: {
profileId: FIRST_PROFILE_ID,
contentURI: MOCK_URI,
collectModule: freeCollectModule.address,
collectModuleData: abiCoder.encode(['bool'], [true]),
referenceModule: ZERO_ADDRESS,
referenceModuleData: [],
},
})
).to.eq(2);
});
it('User should create a post with empty collect and reference module data, fetched post data should be accurate', async function () {
await expect(
lensHub.connect(governance).whitelistCollectModule(freeCollectModule.address, true)

View File

@@ -2,9 +2,9 @@ import '@nomiclabs/hardhat-ethers';
import { expect } from 'chai';
import { BigNumber } from 'ethers';
import { TokenDataStructOutput } from '../../../typechain-types/LensHub';
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
import { ZERO_ADDRESS } from '../../helpers/constants';
import { ERRORS } from '../../helpers/errors';
import { cancelWithPermitForAll, getTimestamp } from '../../helpers/utils';
import { createProfileReturningTokenId, getTimestamp, waitForTx } from '../../helpers/utils';
import {
FIRST_PROFILE_ID,
governance,
@@ -134,16 +134,18 @@ makeSuiteCleanRoom('Profile Creation', function () {
let mintTimestamp: BigNumber;
let tokenData: TokenDataStructOutput;
await expect(
lensHub.createProfile({
to: userAddress,
handle: MOCK_PROFILE_HANDLE,
imageURI: MOCK_PROFILE_URI,
followModule: ZERO_ADDRESS,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
expect(
await createProfileReturningTokenId({
vars: {
to: userAddress,
handle: MOCK_PROFILE_HANDLE,
imageURI: MOCK_PROFILE_URI,
followModule: ZERO_ADDRESS,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
},
})
).to.not.be.reverted;
).to.eq(FIRST_PROFILE_ID);
timestamp = await getTimestamp();
owner = await lensHub.ownerOf(FIRST_PROFILE_ID);
@@ -159,21 +161,25 @@ makeSuiteCleanRoom('Profile Creation', function () {
expect(tokenData.mintTimestamp).to.eq(timestamp);
const secondProfileId = FIRST_PROFILE_ID + 1;
await expect(
lensHub.connect(userTwo).createProfile({
to: userTwoAddress,
handle: 'test',
imageURI: MOCK_PROFILE_URI,
followModule: ZERO_ADDRESS,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
const secondProfileHandle = '2nd_profile';
expect(
await createProfileReturningTokenId({
sender: userTwo,
vars: {
to: userTwoAddress,
handle: secondProfileHandle,
imageURI: MOCK_PROFILE_URI,
followModule: ZERO_ADDRESS,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
},
})
).to.not.be.reverted;
).to.eq(secondProfileId);
timestamp = await getTimestamp();
owner = await lensHub.ownerOf(secondProfileId);
totalSupply = await lensHub.totalSupply();
profileId = await lensHub.getProfileIdByHandle('test');
profileId = await lensHub.getProfileIdByHandle(secondProfileHandle);
mintTimestamp = await lensHub.mintTimestampOf(secondProfileId);
tokenData = await lensHub.tokenDataOf(secondProfileId);
expect(owner).to.eq(userTwoAddress);
@@ -184,6 +190,50 @@ makeSuiteCleanRoom('Profile Creation', function () {
expect(tokenData.mintTimestamp).to.eq(timestamp);
});
it('Should return the expected token IDs when creating profiles', async function () {
expect(
await createProfileReturningTokenId({
vars: {
to: userAddress,
handle: 'token.id_1',
imageURI: MOCK_PROFILE_URI,
followModule: ZERO_ADDRESS,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
},
})
).to.eq(FIRST_PROFILE_ID);
const secondProfileId = FIRST_PROFILE_ID + 1;
expect(
await createProfileReturningTokenId({
sender: userTwo,
vars: {
to: userTwoAddress,
handle: 'token.id_2',
imageURI: MOCK_PROFILE_URI,
followModule: ZERO_ADDRESS,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
},
})
).to.eq(secondProfileId);
const thirdProfileId = secondProfileId + 1;
expect(
await createProfileReturningTokenId({
vars: {
to: userAddress,
handle: 'token.id_3',
imageURI: MOCK_PROFILE_URI,
followModule: ZERO_ADDRESS,
followModuleData: [],
followNFTURI: MOCK_FOLLOW_NFT_URI,
},
})
).to.eq(thirdProfileId);
});
it('User should be able to create a profile with a handle including "-" and "_" characters', async function () {
await expect(
lensHub.createProfile({