mirror of
https://github.com/lens-protocol/core.git
synced 2026-01-11 07:08:09 -05:00
Merge pull request #24 from aave/feat/profile-token-uri
This commit is contained in:
@@ -80,25 +80,21 @@ contract FollowNFT is LensNFTBase, IFollowNFT {
|
||||
address delegatee,
|
||||
DataTypes.EIP712Signature calldata sig
|
||||
) external override {
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
_calculateDomainSeparator(),
|
||||
keccak256(
|
||||
abi.encode(
|
||||
DELEGATE_BY_SIG_TYPEHASH,
|
||||
delegator,
|
||||
delegatee,
|
||||
sigNonces[delegator]++,
|
||||
sig.deadline
|
||||
)
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
DELEGATE_BY_SIG_TYPEHASH,
|
||||
delegator,
|
||||
delegatee,
|
||||
sigNonces[delegator]++,
|
||||
sig.deadline
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
_validateRecoveredAddress(digest, delegator, sig);
|
||||
),
|
||||
delegator,
|
||||
sig
|
||||
);
|
||||
_delegate(delegator, delegatee);
|
||||
}
|
||||
|
||||
@@ -203,7 +199,7 @@ contract FollowNFT is LensNFTBase, IFollowNFT {
|
||||
uint256 tokenId
|
||||
) internal override {
|
||||
address fromDelegatee = _delegates[from];
|
||||
address toDelegatee = _delegates[to];
|
||||
address toDelegatee = _delegates[to];
|
||||
address followModule = ILensHub(HUB).getFollowModule(_profileId);
|
||||
|
||||
_moveDelegate(fromDelegatee, toDelegatee, 1);
|
||||
|
||||
@@ -5,10 +5,13 @@ pragma solidity 0.8.10;
|
||||
import {ILensHub} from '../interfaces/ILensHub.sol';
|
||||
import {Events} from '../libraries/Events.sol';
|
||||
import {Helpers} from '../libraries/Helpers.sol';
|
||||
import {Constants} from '../libraries/Constants.sol';
|
||||
import {DataTypes} from '../libraries/DataTypes.sol';
|
||||
import {Errors} from '../libraries/Errors.sol';
|
||||
import {PublishingLogic} from '../libraries/PublishingLogic.sol';
|
||||
import {ProfileTokenURILogic} from '../libraries/ProfileTokenURILogic.sol';
|
||||
import {InteractionLogic} from '../libraries/InteractionLogic.sol';
|
||||
import {IERC721Enumerable} from '@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol';
|
||||
import {LensNFTBase} from './base/LensNFTBase.sol';
|
||||
import {LensMultiState} from './base/LensMultiState.sol';
|
||||
import {LensHubStorage} from './storage/LensHubStorage.sol';
|
||||
@@ -167,27 +170,21 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
|
||||
override
|
||||
whenNotPaused
|
||||
{
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
_calculateDomainSeparator(),
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_DEFAULT_PROFILE_WITH_SIG_TYPEHASH,
|
||||
vars.wallet,
|
||||
vars.profileId,
|
||||
sigNonces[vars.wallet]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_DEFAULT_PROFILE_WITH_SIG_TYPEHASH,
|
||||
vars.wallet,
|
||||
vars.profileId,
|
||||
sigNonces[vars.wallet]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_validateRecoveredAddress(digest, vars.wallet, vars.sig);
|
||||
|
||||
),
|
||||
vars.wallet,
|
||||
vars.sig
|
||||
);
|
||||
_setDefaultProfile(vars.wallet, vars.profileId);
|
||||
}
|
||||
|
||||
@@ -214,27 +211,22 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
|
||||
whenNotPaused
|
||||
{
|
||||
address owner = ownerOf(vars.profileId);
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
_calculateDomainSeparator(),
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_FOLLOW_MODULE_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
vars.followModule,
|
||||
keccak256(vars.followModuleData),
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_FOLLOW_MODULE_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
vars.followModule,
|
||||
keccak256(vars.followModuleData),
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_validateRecoveredAddress(digest, owner, vars.sig);
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
PublishingLogic.setFollowModule(
|
||||
vars.profileId,
|
||||
vars.followModule,
|
||||
@@ -257,26 +249,21 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
|
||||
whenNotPaused
|
||||
{
|
||||
address owner = ownerOf(vars.profileId);
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
_calculateDomainSeparator(),
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_DISPATCHER_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
vars.dispatcher,
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_DISPATCHER_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
vars.dispatcher,
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_validateRecoveredAddress(digest, owner, vars.sig);
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
_setDispatcher(vars.profileId, vars.dispatcher);
|
||||
}
|
||||
|
||||
@@ -297,26 +284,21 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
|
||||
whenNotPaused
|
||||
{
|
||||
address owner = ownerOf(vars.profileId);
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
_calculateDomainSeparator(),
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_PROFILE_IMAGE_URI_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
keccak256(bytes(vars.imageURI)),
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_PROFILE_IMAGE_URI_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
keccak256(bytes(vars.imageURI)),
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_validateRecoveredAddress(digest, owner, vars.sig);
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
_setProfileImageURI(vars.profileId, vars.imageURI);
|
||||
}
|
||||
|
||||
@@ -337,26 +319,21 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
|
||||
whenNotPaused
|
||||
{
|
||||
address owner = ownerOf(vars.profileId);
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
_calculateDomainSeparator(),
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_FOLLOW_NFT_URI_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
keccak256(bytes(vars.followNFTURI)),
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
SET_FOLLOW_NFT_URI_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
keccak256(bytes(vars.followNFTURI)),
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_validateRecoveredAddress(digest, owner, vars.sig);
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
_setFollowNFTURI(vars.profileId, vars.followNFTURI);
|
||||
}
|
||||
|
||||
@@ -380,30 +357,25 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
|
||||
whenPublishingEnabled
|
||||
{
|
||||
address owner = ownerOf(vars.profileId);
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
_calculateDomainSeparator(),
|
||||
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
|
||||
)
|
||||
_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
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_validateRecoveredAddress(digest, owner, vars.sig);
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
_createPost(
|
||||
vars.profileId,
|
||||
vars.contentURI,
|
||||
@@ -427,32 +399,27 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
|
||||
whenPublishingEnabled
|
||||
{
|
||||
address owner = ownerOf(vars.profileId);
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
_calculateDomainSeparator(),
|
||||
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
|
||||
)
|
||||
_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
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_validateRecoveredAddress(digest, owner, vars.sig);
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
_createComment(
|
||||
DataTypes.CommentData(
|
||||
vars.profileId,
|
||||
@@ -486,29 +453,24 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
|
||||
whenPublishingEnabled
|
||||
{
|
||||
address owner = ownerOf(vars.profileId);
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
_calculateDomainSeparator(),
|
||||
keccak256(
|
||||
abi.encode(
|
||||
MIRROR_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
vars.profileIdPointed,
|
||||
vars.pubIdPointed,
|
||||
vars.referenceModule,
|
||||
keccak256(vars.referenceModuleData),
|
||||
sigNonces[owner]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
_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
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_validateRecoveredAddress(digest, owner, vars.sig);
|
||||
),
|
||||
owner,
|
||||
vars.sig
|
||||
);
|
||||
_createMirror(
|
||||
vars.profileId,
|
||||
vars.profileIdPointed,
|
||||
@@ -576,27 +538,21 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
|
||||
for (uint256 i = 0; i < vars.datas.length; ++i) {
|
||||
dataHashes[i] = keccak256(vars.datas[i]);
|
||||
}
|
||||
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
_calculateDomainSeparator(),
|
||||
keccak256(
|
||||
abi.encode(
|
||||
FOLLOW_WITH_SIG_TYPEHASH,
|
||||
keccak256(abi.encodePacked(vars.profileIds)),
|
||||
keccak256(abi.encodePacked(dataHashes)),
|
||||
sigNonces[vars.follower]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
FOLLOW_WITH_SIG_TYPEHASH,
|
||||
keccak256(abi.encodePacked(vars.profileIds)),
|
||||
keccak256(abi.encodePacked(dataHashes)),
|
||||
sigNonces[vars.follower]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_validateRecoveredAddress(digest, vars.follower, vars.sig);
|
||||
),
|
||||
vars.follower,
|
||||
vars.sig
|
||||
);
|
||||
InteractionLogic.follow(
|
||||
vars.follower,
|
||||
vars.profileIds,
|
||||
@@ -630,27 +586,22 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
|
||||
override
|
||||
whenNotPaused
|
||||
{
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
_calculateDomainSeparator(),
|
||||
keccak256(
|
||||
abi.encode(
|
||||
COLLECT_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
vars.pubId,
|
||||
keccak256(vars.data),
|
||||
sigNonces[vars.collector]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
COLLECT_WITH_SIG_TYPEHASH,
|
||||
vars.profileId,
|
||||
vars.pubId,
|
||||
keccak256(vars.data),
|
||||
sigNonces[vars.collector]++,
|
||||
vars.sig.deadline
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_validateRecoveredAddress(digest, vars.collector, vars.sig);
|
||||
),
|
||||
vars.collector,
|
||||
vars.sig
|
||||
);
|
||||
InteractionLogic.collect(
|
||||
vars.collector,
|
||||
vars.profileId,
|
||||
@@ -880,7 +831,15 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
|
||||
* @dev Overrides the ERC721 tokenURI function to return the associated URI with a given profile.
|
||||
*/
|
||||
function tokenURI(uint256 tokenId) public view override returns (string memory) {
|
||||
return _profileById[tokenId].imageURI; // temp
|
||||
address followNFT = _profileById[tokenId].followNFT;
|
||||
return
|
||||
ProfileTokenURILogic.getProfileTokenURI(
|
||||
tokenId,
|
||||
followNFT == address(0) ? 0 : IERC721Enumerable(followNFT).totalSupply(),
|
||||
ownerOf(tokenId),
|
||||
_profileById[tokenId].handle,
|
||||
_profileById[tokenId].imageURI
|
||||
);
|
||||
}
|
||||
|
||||
/// ****************************
|
||||
@@ -961,6 +920,8 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
|
||||
}
|
||||
|
||||
function _setProfileImageURI(uint256 profileId, string memory imageURI) internal {
|
||||
if (bytes(imageURI).length > Constants.MAX_PROFILE_IMAGE_URI_LENGTH)
|
||||
revert Errors.ProfileImageURILengthInvalid();
|
||||
_profileById[profileId].imageURI = imageURI;
|
||||
emit Events.ProfileImageURISet(profileId, imageURI, block.timestamp);
|
||||
}
|
||||
|
||||
@@ -55,27 +55,15 @@ abstract contract LensNFTBase is ILensNFTBase, ERC721Enumerable {
|
||||
) external override {
|
||||
if (spender == address(0)) revert Errors.ZeroSpender();
|
||||
address owner = ownerOf(tokenId);
|
||||
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
_calculateDomainSeparator(),
|
||||
keccak256(
|
||||
abi.encode(
|
||||
PERMIT_TYPEHASH,
|
||||
spender,
|
||||
tokenId,
|
||||
sigNonces[owner]++,
|
||||
sig.deadline
|
||||
)
|
||||
)
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(PERMIT_TYPEHASH, spender, tokenId, sigNonces[owner]++, sig.deadline)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_validateRecoveredAddress(digest, owner, sig);
|
||||
),
|
||||
owner,
|
||||
sig
|
||||
);
|
||||
_approve(spender, tokenId);
|
||||
}
|
||||
|
||||
@@ -87,28 +75,22 @@ abstract contract LensNFTBase is ILensNFTBase, ERC721Enumerable {
|
||||
DataTypes.EIP712Signature calldata sig
|
||||
) external override {
|
||||
if (operator == address(0)) revert Errors.ZeroSpender();
|
||||
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
_calculateDomainSeparator(),
|
||||
keccak256(
|
||||
abi.encode(
|
||||
PERMIT_FOR_ALL_TYPEHASH,
|
||||
owner,
|
||||
operator,
|
||||
approved,
|
||||
sigNonces[owner]++,
|
||||
sig.deadline
|
||||
)
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(
|
||||
PERMIT_FOR_ALL_TYPEHASH,
|
||||
owner,
|
||||
operator,
|
||||
approved,
|
||||
sigNonces[owner]++,
|
||||
sig.deadline
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_validateRecoveredAddress(digest, owner, sig);
|
||||
),
|
||||
owner,
|
||||
sig
|
||||
);
|
||||
_setOperatorApproval(owner, operator, approved);
|
||||
}
|
||||
|
||||
@@ -131,25 +113,15 @@ abstract contract LensNFTBase is ILensNFTBase, ERC721Enumerable {
|
||||
{
|
||||
address owner = ownerOf(tokenId);
|
||||
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked(
|
||||
'\x19\x01',
|
||||
_calculateDomainSeparator(),
|
||||
keccak256(
|
||||
abi.encode(
|
||||
BURN_WITH_SIG_TYPEHASH,
|
||||
tokenId,
|
||||
sigNonces[owner]++,
|
||||
sig.deadline
|
||||
)
|
||||
)
|
||||
_validateRecoveredAddress(
|
||||
_calculateDigest(
|
||||
keccak256(
|
||||
abi.encode(BURN_WITH_SIG_TYPEHASH, tokenId, sigNonces[owner]++, sig.deadline)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
_validateRecoveredAddress(digest, owner, sig);
|
||||
),
|
||||
owner,
|
||||
sig
|
||||
);
|
||||
_burn(tokenId);
|
||||
}
|
||||
|
||||
@@ -182,4 +154,21 @@ abstract contract LensNFTBase is ILensNFTBase, ERC721Enumerable {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Calculates EIP712 digest based on the current DOMAIN_SEPARATOR.
|
||||
*
|
||||
* @param hashedMessage The message hash from which the digest should be calculated.
|
||||
*
|
||||
* @return A 32-byte output representing the EIP712 digest.
|
||||
*/
|
||||
function _calculateDigest(bytes32 hashedMessage) internal view returns (bytes32) {
|
||||
bytes32 digest;
|
||||
unchecked {
|
||||
digest = keccak256(
|
||||
abi.encodePacked('\x19\x01', _calculateDomainSeparator(), hashedMessage)
|
||||
);
|
||||
}
|
||||
return digest;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,4 +8,5 @@ library Constants {
|
||||
string internal constant COLLECT_NFT_NAME_INFIX = '-Collect-';
|
||||
string internal constant COLLECT_NFT_SYMBOL_INFIX = '-Cl-';
|
||||
uint8 internal constant MAX_HANDLE_LENGTH = 31;
|
||||
uint16 internal constant MAX_PROFILE_IMAGE_URI_LENGTH = 6000;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ library Errors {
|
||||
error HandleTaken();
|
||||
error HandleLengthInvalid();
|
||||
error HandleContainsInvalidCharacters();
|
||||
error ProfileImageURILengthInvalid();
|
||||
error CallerNotFollowNFT();
|
||||
error CallerNotCollectNFT();
|
||||
error BlockNumberInvalid();
|
||||
|
||||
158
contracts/libraries/ProfileTokenURILogic.sol
Normal file
158
contracts/libraries/ProfileTokenURILogic.sol
Normal file
File diff suppressed because one or more lines are too long
11
package-lock.json
generated
11
package-lock.json
generated
@@ -9,7 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "AGPL-3.0-only",
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "4.4.0"
|
||||
"@openzeppelin/contracts": "4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nomiclabs/hardhat-ethers": "2.0.2",
|
||||
@@ -3839,8 +3839,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@openzeppelin/contracts": {
|
||||
"version": "4.4.0",
|
||||
"license": "MIT"
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.5.0.tgz",
|
||||
"integrity": "sha512-fdkzKPYMjrRiPK6K4y64e6GzULR7R7RwxSigHS8DDp7aWDeoReqsQI+cxHV1UuhAqX69L1lAaWDxenfP+xiqzA=="
|
||||
},
|
||||
"node_modules/@resolver-engine/core": {
|
||||
"version": "0.3.3",
|
||||
@@ -26166,7 +26167,9 @@
|
||||
}
|
||||
},
|
||||
"@openzeppelin/contracts": {
|
||||
"version": "4.4.0"
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.5.0.tgz",
|
||||
"integrity": "sha512-fdkzKPYMjrRiPK6K4y64e6GzULR7R7RwxSigHS8DDp7aWDeoReqsQI+cxHV1UuhAqX69L1lAaWDxenfP+xiqzA=="
|
||||
},
|
||||
"@resolver-engine/core": {
|
||||
"version": "0.3.3",
|
||||
|
||||
@@ -49,7 +49,6 @@
|
||||
"eslint": "8.3.0",
|
||||
"eslint-config-prettier": "8.3.0",
|
||||
"eslint-plugin-prettier": "4.0.0",
|
||||
"prettier-plugin-solidity": "1.0.0-beta.19",
|
||||
"ethereum-waffle": "3.4.0",
|
||||
"ethers": "5.5.1",
|
||||
"hardhat": "2.7.0",
|
||||
@@ -59,6 +58,7 @@
|
||||
"hardhat-spdx-license-identifier": "2.0.3",
|
||||
"husky": "7.0.4",
|
||||
"prettier": "2.5.0",
|
||||
"prettier-plugin-solidity": "1.0.0-beta.19",
|
||||
"solidity-coverage": "0.7.17",
|
||||
"ts-generator": "0.1.1",
|
||||
"ts-node": "10.4.0",
|
||||
@@ -71,7 +71,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@openzeppelin/contracts": "4.4.0"
|
||||
"@openzeppelin/contracts": "4.5.0"
|
||||
},
|
||||
"author": "Lens",
|
||||
"contributors": [
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
RevertCollectModule__factory,
|
||||
TimedFeeCollectModule__factory,
|
||||
TransparentUpgradeableProxy__factory,
|
||||
ProfileTokenURILogic__factory,
|
||||
} from '../typechain-types';
|
||||
import { deployWithVerify, waitForTx } from './helpers/utils';
|
||||
|
||||
@@ -79,9 +80,16 @@ task('full-deploy-verify', 'deploys the entire Lens Protocol with explorer verif
|
||||
[],
|
||||
'contracts/libraries/InteractionLogic.sol:InteractionLogic'
|
||||
);
|
||||
const profileTokenURILogic = await deployWithVerify(
|
||||
new ProfileTokenURILogic__factory(deployer).deploy({ nonce: deployerNonce++ }),
|
||||
[],
|
||||
'contracts/libraries/ProfileTokenURILogic.sol:ProfileTokenURILogic'
|
||||
);
|
||||
const hubLibs = {
|
||||
'contracts/libraries/PublishingLogic.sol:PublishingLogic': publishingLogic.address,
|
||||
'contracts/libraries/InteractionLogic.sol:InteractionLogic': interactionLogic.address,
|
||||
'contracts/libraries/ProfileTokenURILogic.sol:ProfileTokenURILogic':
|
||||
profileTokenURILogic.address,
|
||||
};
|
||||
|
||||
// Here, we pre-compute the nonces and addresses used to deploy the contracts.
|
||||
@@ -279,6 +287,7 @@ task('full-deploy-verify', 'deploys the entire Lens Protocol with explorer verif
|
||||
'lensHub impl:': lensHubImpl.address,
|
||||
'publishing logic lib': publishingLogic.address,
|
||||
'interaction logic lib': interactionLogic.address,
|
||||
'profile token URI logic lib': profileTokenURILogic.address,
|
||||
'follow NFT impl': followNFTImplAddress,
|
||||
'collect NFT impl': collectNFTImplAddress,
|
||||
'module globals': moduleGlobals.address,
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
RevertCollectModule__factory,
|
||||
TimedFeeCollectModule__factory,
|
||||
TransparentUpgradeableProxy__factory,
|
||||
ProfileTokenURILogic__factory,
|
||||
} from '../typechain-types';
|
||||
import { deployContract, waitForTx } from './helpers/utils';
|
||||
|
||||
@@ -57,9 +58,14 @@ task('full-deploy', 'deploys the entire Lens Protocol').setAction(async ({}, hre
|
||||
const interactionLogic = await deployContract(
|
||||
new InteractionLogic__factory(deployer).deploy({ nonce: deployerNonce++ })
|
||||
);
|
||||
const profileTokenURILogic = await deployContract(
|
||||
new ProfileTokenURILogic__factory(deployer).deploy({ nonce: deployerNonce++ })
|
||||
);
|
||||
const hubLibs = {
|
||||
'contracts/libraries/PublishingLogic.sol:PublishingLogic': publishingLogic.address,
|
||||
'contracts/libraries/InteractionLogic.sol:InteractionLogic': interactionLogic.address,
|
||||
'contracts/libraries/ProfileTokenURILogic.sol:ProfileTokenURILogic':
|
||||
profileTokenURILogic.address,
|
||||
};
|
||||
|
||||
// Here, we pre-compute the nonces and addresses used to deploy the contracts.
|
||||
@@ -72,7 +78,8 @@ task('full-deploy', 'deploys the entire Lens Protocol').setAction(async ({}, hre
|
||||
'0x' + keccak256(RLP.encode([deployer.address, followNFTNonce])).substr(26);
|
||||
const collectNFTImplAddress =
|
||||
'0x' + keccak256(RLP.encode([deployer.address, collectNFTNonce])).substr(26);
|
||||
const hubProxyAddress = '0x' + keccak256(RLP.encode([deployer.address, hubProxyNonce])).substr(26);
|
||||
const hubProxyAddress =
|
||||
'0x' + keccak256(RLP.encode([deployer.address, hubProxyNonce])).substr(26);
|
||||
|
||||
// Next, we deploy first the hub implementation, then the followNFT implementation, the collectNFT, and finally the
|
||||
// hub proxy with initialization.
|
||||
@@ -187,7 +194,9 @@ task('full-deploy', 'deploys the entire Lens Protocol').setAction(async ({}, hre
|
||||
})
|
||||
);
|
||||
await waitForTx(
|
||||
lensHub.whitelistCollectModule(timedFeeCollectModule.address, true, { nonce: governanceNonce++ })
|
||||
lensHub.whitelistCollectModule(timedFeeCollectModule.address, true, {
|
||||
nonce: governanceNonce++,
|
||||
})
|
||||
);
|
||||
await waitForTx(
|
||||
lensHub.whitelistCollectModule(limitedTimedFeeCollectModule.address, true, {
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
MockReferenceModule__factory,
|
||||
ModuleGlobals,
|
||||
ModuleGlobals__factory,
|
||||
ProfileTokenURILogic__factory,
|
||||
PublishingLogic__factory,
|
||||
RevertCollectModule,
|
||||
RevertCollectModule__factory,
|
||||
@@ -59,18 +60,17 @@ export const CURRENCY_MINT_AMOUNT = parseEther('100');
|
||||
export const BPS_MAX = 10000;
|
||||
export const TREASURY_FEE_BPS = 50;
|
||||
export const REFERRAL_FEE_BPS = 250;
|
||||
export const MAX_PROFILE_IMAGE_URI_LENGTH = 6000;
|
||||
export const LENS_HUB_NFT_NAME = 'Lens Profiles';
|
||||
export const LENS_HUB_NFT_SYMBOL = 'LENS';
|
||||
export const MOCK_PROFILE_HANDLE = 'plant1ghost.eth';
|
||||
export const FIRST_PROFILE_ID = 1;
|
||||
export const MOCK_URI =
|
||||
'https://ipfs.fleek.co/ipfs/plantghostplantghostplantghostplantghostplantghostplantghos';
|
||||
export const OTHER_MOCK_URI =
|
||||
'https://ipfs.fleek.co/ipfs/ghostplantghostplantghostplantghostplantghostplantghostplan';
|
||||
export const MOCK_URI = 'https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR';
|
||||
export const OTHER_MOCK_URI = 'https://ipfs.io/ipfs/QmSfyMcnh1wnJHrAWCBjZHapTS859oNSsuDFiAPPdAHgHP';
|
||||
export const MOCK_PROFILE_URI =
|
||||
'https://ipfs.fleek.co/ipfs/runningoutofthingstowriterunningoutofthingstowriterunningou';
|
||||
'https://ipfs.io/ipfs/Qme7ss3ARVgxv6rXqVPiikMJ8u2NLgmgszg13pYrDKEoiu';
|
||||
export const MOCK_FOLLOW_NFT_URI =
|
||||
'https://ipfs.fleek.co/ipfs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
|
||||
'https://ipfs.fleek.co/ipfs/ghostplantghostplantghostplantghostplantghostplantghostplan';
|
||||
|
||||
export let accounts: Signer[];
|
||||
export let deployer: Signer;
|
||||
@@ -149,9 +149,12 @@ before(async function () {
|
||||
);
|
||||
const publishingLogic = await new PublishingLogic__factory(deployer).deploy();
|
||||
const interactionLogic = await new InteractionLogic__factory(deployer).deploy();
|
||||
const profileTokenURILogic = await new ProfileTokenURILogic__factory(deployer).deploy();
|
||||
hubLibs = {
|
||||
'contracts/libraries/PublishingLogic.sol:PublishingLogic': publishingLogic.address,
|
||||
'contracts/libraries/InteractionLogic.sol:InteractionLogic': interactionLogic.address,
|
||||
'contracts/libraries/ProfileTokenURILogic.sol:ProfileTokenURILogic':
|
||||
profileTokenURILogic.address,
|
||||
};
|
||||
|
||||
// Here, we pre-compute the nonces and addresses used to deploy the contracts.
|
||||
|
||||
@@ -18,6 +18,7 @@ export const ERRORS = {
|
||||
PUBLICATION_DOES_NOT_EXIST: 'PublicationDoesNotExist()',
|
||||
PROFILE_HANDLE_TAKEN: 'HandleTaken()',
|
||||
INVALID_HANDLE_LENGTH: 'HandleLengthInvalid()',
|
||||
INVALID_IMAGE_URI_LENGTH: 'ProfileImageURILengthInvalid()',
|
||||
HANDLE_CONTAINS_INVALID_CHARACTERS: 'HandleContainsInvalidCharacters()',
|
||||
NOT_FOLLOW_NFT: 'CallerNotFollowNFT()',
|
||||
NOT_COLLECT_NFT: 'CallerNotCollectNFT()',
|
||||
@@ -35,6 +36,7 @@ export const ERRORS = {
|
||||
ERC721_TRANSFER_NOT_OWNER_OR_APPROVED: 'ERC721: transfer caller is not owner nor approved',
|
||||
ERC721_QUERY_FOR_NONEXISTENT_TOKEN: 'ERC721: owner query for nonexistent token',
|
||||
ERC20_TRANSFER_EXCEEDS_ALLOWANCE: 'ERC20: transfer amount exceeds allowance',
|
||||
ERC20_INSUFFICIENT_ALLOWANCE: 'ERC20: insufficient allowance',
|
||||
NO_SELECTOR:
|
||||
"Transaction reverted: function selector was not recognized and there's no fallback function",
|
||||
PAUSED: 'Paused()',
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { TransactionReceipt, TransactionResponse } from '@ethersproject/providers';
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { BigNumber, BigNumberish, Bytes, Contract, logger, utils } from 'ethers';
|
||||
import { hexlify, keccak256, RLP, toUtf8Bytes } from 'ethers/lib/utils';
|
||||
import hre from 'hardhat';
|
||||
import { LensHub__factory } from '../../typechain-types';
|
||||
import { BigNumberish, Bytes, logger, utils, BigNumber, Contract } from 'ethers';
|
||||
import { eventsLib, helper, lensHub, LENS_HUB_NFT_NAME, testWallet } from '../__setup.spec';
|
||||
import { expect } from 'chai';
|
||||
import { HARDHAT_CHAINID, MAX_UINT256 } from './constants';
|
||||
import { 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';
|
||||
|
||||
export enum ProtocolState {
|
||||
Unpaused,
|
||||
@@ -429,6 +429,18 @@ export async function getCollectWithSigParts(
|
||||
return await getSig(msgParams);
|
||||
}
|
||||
|
||||
export async function getJsonMetadataFromBase64TokenUri(tokenUri: string) {
|
||||
const splittedTokenUri = tokenUri.split('data:application/json;base64,');
|
||||
if (splittedTokenUri.length != 2) {
|
||||
logger.throwError('Wrong or unrecognized token URI format');
|
||||
} else {
|
||||
const jsonMetadataBase64String = splittedTokenUri[1];
|
||||
const jsonMetadataBytes = ethers.utils.base64.decode(jsonMetadataBase64String);
|
||||
const jsonMetadataString = ethers.utils.toUtf8String(jsonMetadataBytes);
|
||||
return JSON.parse(jsonMetadataString);
|
||||
}
|
||||
}
|
||||
|
||||
// Modified from AaveTokenV2 repo
|
||||
const buildPermitParams = (
|
||||
nft: string,
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { keccak256, toUtf8Bytes } from 'ethers/lib/utils';
|
||||
import { FollowNFT__factory } from '../../../typechain-types';
|
||||
import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants';
|
||||
import { ERRORS } from '../../helpers/errors';
|
||||
import {
|
||||
cancelWithPermitForAll,
|
||||
getJsonMetadataFromBase64TokenUri,
|
||||
getSetFollowNFTURIWithSigParts,
|
||||
getSetProfileImageURIWithSigParts,
|
||||
} from '../../helpers/utils';
|
||||
@@ -12,6 +14,7 @@ import {
|
||||
FIRST_PROFILE_ID,
|
||||
lensHub,
|
||||
makeSuiteCleanRoom,
|
||||
MAX_PROFILE_IMAGE_URI_LENGTH,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
MOCK_PROFILE_HANDLE,
|
||||
MOCK_PROFILE_URI,
|
||||
@@ -46,6 +49,14 @@ makeSuiteCleanRoom('Profile URI Functionality', function () {
|
||||
).to.be.revertedWith(ERRORS.NOT_PROFILE_OWNER_OR_DISPATCHER);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to set the profile URI on profile owned by user 1', async function () {
|
||||
const profileURITooLong = MOCK_URI.repeat(500);
|
||||
expect(profileURITooLong.length).to.be.greaterThan(MAX_PROFILE_IMAGE_URI_LENGTH);
|
||||
await expect(
|
||||
lensHub.setProfileImageURI(FIRST_PROFILE_ID, profileURITooLong)
|
||||
).to.be.revertedWith(ERRORS.INVALID_IMAGE_URI_LENGTH);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to change the follow NFT URI for profile one', async function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).setFollowNFTURI(FIRST_PROFILE_ID, OTHER_MOCK_URI)
|
||||
@@ -54,9 +65,134 @@ makeSuiteCleanRoom('Profile URI Functionality', function () {
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('User should set the profile URI', async function () {
|
||||
it('User should have a custom picture tokenURI after setting the profile imageURI', async function () {
|
||||
await expect(lensHub.setProfileImageURI(FIRST_PROFILE_ID, MOCK_URI)).to.not.be.reverted;
|
||||
expect(await lensHub.tokenURI(FIRST_PROFILE_ID)).to.eq(MOCK_URI);
|
||||
const tokenURI = await lensHub.tokenURI(FIRST_PROFILE_ID);
|
||||
const jsonMetadata = await getJsonMetadataFromBase64TokenUri(tokenURI);
|
||||
expect(jsonMetadata.name).to.eq(`@${MOCK_PROFILE_HANDLE}`);
|
||||
expect(jsonMetadata.description).to.eq(`@${MOCK_PROFILE_HANDLE} - Lens profile`);
|
||||
const expectedAttributes = [
|
||||
{ trait_type: 'id', value: `#${FIRST_PROFILE_ID.toString()}` },
|
||||
{ trait_type: 'followers', value: '0' },
|
||||
{ trait_type: 'owner', value: userAddress.toLowerCase() },
|
||||
{ trait_type: 'handle', value: `@${MOCK_PROFILE_HANDLE}` },
|
||||
];
|
||||
expect(jsonMetadata.attributes).to.eql(expectedAttributes);
|
||||
expect(keccak256(toUtf8Bytes(tokenURI))).to.eq(
|
||||
'0xe1731460db6ce5fa9f3a9f2bd778a8af49e623dceb531df6b1a5c162b7c2d79a'
|
||||
);
|
||||
});
|
||||
|
||||
it('Default image should be used when no imageURI set', async function () {
|
||||
await expect(lensHub.setProfileImageURI(FIRST_PROFILE_ID, '')).to.not.be.reverted;
|
||||
const tokenURI = await lensHub.tokenURI(FIRST_PROFILE_ID);
|
||||
const jsonMetadata = await getJsonMetadataFromBase64TokenUri(tokenURI);
|
||||
expect(jsonMetadata.name).to.eq(`@${MOCK_PROFILE_HANDLE}`);
|
||||
expect(jsonMetadata.description).to.eq(`@${MOCK_PROFILE_HANDLE} - Lens profile`);
|
||||
const expectedAttributes = [
|
||||
{ trait_type: 'id', value: `#${FIRST_PROFILE_ID.toString()}` },
|
||||
{ trait_type: 'followers', value: '0' },
|
||||
{ trait_type: 'owner', value: userAddress.toLowerCase() },
|
||||
{ trait_type: 'handle', value: `@${MOCK_PROFILE_HANDLE}` },
|
||||
];
|
||||
expect(jsonMetadata.attributes).to.eql(expectedAttributes);
|
||||
expect(keccak256(toUtf8Bytes(tokenURI))).to.eq(
|
||||
'0xa21f2a3aa9300a248d3a7acd3f4ff309291653121df87ffe3be545fa1dbd65e5'
|
||||
);
|
||||
});
|
||||
|
||||
it('Default image should be used when imageURI contains double-quotes', async function () {
|
||||
const imageURI =
|
||||
'https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrztRLMiMPL8wBuTGsMnR" <rect x="10" y="10" fill="red';
|
||||
await expect(lensHub.setProfileImageURI(FIRST_PROFILE_ID, imageURI)).to.not.be.reverted;
|
||||
const tokenURI = await lensHub.tokenURI(FIRST_PROFILE_ID);
|
||||
const jsonMetadata = await getJsonMetadataFromBase64TokenUri(tokenURI);
|
||||
expect(jsonMetadata.name).to.eq(`@${MOCK_PROFILE_HANDLE}`);
|
||||
expect(jsonMetadata.description).to.eq(`@${MOCK_PROFILE_HANDLE} - Lens profile`);
|
||||
const expectedAttributes = [
|
||||
{ trait_type: 'id', value: `#${FIRST_PROFILE_ID.toString()}` },
|
||||
{ trait_type: 'followers', value: '0' },
|
||||
{ trait_type: 'owner', value: userAddress.toLowerCase() },
|
||||
{ trait_type: 'handle', value: `@${MOCK_PROFILE_HANDLE}` },
|
||||
];
|
||||
expect(jsonMetadata.attributes).to.eql(expectedAttributes);
|
||||
expect(keccak256(toUtf8Bytes(tokenURI))).to.eq(
|
||||
'0xa21f2a3aa9300a248d3a7acd3f4ff309291653121df87ffe3be545fa1dbd65e5'
|
||||
);
|
||||
});
|
||||
|
||||
it('Should return the correct tokenURI after transfer', async function () {
|
||||
const tokenURIBeforeTransfer = await lensHub.tokenURI(FIRST_PROFILE_ID);
|
||||
const jsonMetadataBeforeTransfer = await getJsonMetadataFromBase64TokenUri(
|
||||
tokenURIBeforeTransfer
|
||||
);
|
||||
expect(jsonMetadataBeforeTransfer.name).to.eq(`@${MOCK_PROFILE_HANDLE}`);
|
||||
expect(jsonMetadataBeforeTransfer.description).to.eq(
|
||||
`@${MOCK_PROFILE_HANDLE} - Lens profile`
|
||||
);
|
||||
const expectedAttributesBeforeTransfer = [
|
||||
{ trait_type: 'id', value: `#${FIRST_PROFILE_ID.toString()}` },
|
||||
{ trait_type: 'followers', value: '0' },
|
||||
{ trait_type: 'owner', value: userAddress.toLowerCase() },
|
||||
{ trait_type: 'handle', value: `@${MOCK_PROFILE_HANDLE}` },
|
||||
];
|
||||
expect(jsonMetadataBeforeTransfer.attributes).to.eql(expectedAttributesBeforeTransfer);
|
||||
|
||||
await expect(
|
||||
lensHub.transferFrom(userAddress, userTwoAddress, FIRST_PROFILE_ID)
|
||||
).to.not.be.reverted;
|
||||
|
||||
const tokenURIAfterTransfer = await lensHub.tokenURI(FIRST_PROFILE_ID);
|
||||
const jsonMetadataAfterTransfer = await getJsonMetadataFromBase64TokenUri(
|
||||
tokenURIAfterTransfer
|
||||
);
|
||||
expect(jsonMetadataAfterTransfer.name).to.eq(`@${MOCK_PROFILE_HANDLE}`);
|
||||
expect(jsonMetadataAfterTransfer.description).to.eq(
|
||||
`@${MOCK_PROFILE_HANDLE} - Lens profile`
|
||||
);
|
||||
const expectedAttributesAfterTransfer = [
|
||||
{ trait_type: 'id', value: `#${FIRST_PROFILE_ID.toString()}` },
|
||||
{ trait_type: 'followers', value: '0' },
|
||||
{ trait_type: 'owner', value: userTwoAddress.toLowerCase() },
|
||||
{ trait_type: 'handle', value: `@${MOCK_PROFILE_HANDLE}` },
|
||||
];
|
||||
expect(jsonMetadataAfterTransfer.attributes).to.eql(expectedAttributesAfterTransfer);
|
||||
});
|
||||
|
||||
it('Should return the correct tokenURI after a follow', async function () {
|
||||
const tokenURIBeforeTransfer = await lensHub.tokenURI(FIRST_PROFILE_ID);
|
||||
const jsonMetadataBeforeTransfer = await getJsonMetadataFromBase64TokenUri(
|
||||
tokenURIBeforeTransfer
|
||||
);
|
||||
expect(jsonMetadataBeforeTransfer.name).to.eq(`@${MOCK_PROFILE_HANDLE}`);
|
||||
expect(jsonMetadataBeforeTransfer.description).to.eq(
|
||||
`@${MOCK_PROFILE_HANDLE} - Lens profile`
|
||||
);
|
||||
const expectedAttributesBeforeTransfer = [
|
||||
{ trait_type: 'id', value: `#${FIRST_PROFILE_ID.toString()}` },
|
||||
{ trait_type: 'followers', value: '0' },
|
||||
{ trait_type: 'owner', value: userAddress.toLowerCase() },
|
||||
{ trait_type: 'handle', value: `@${MOCK_PROFILE_HANDLE}` },
|
||||
];
|
||||
expect(jsonMetadataBeforeTransfer.attributes).to.eql(expectedAttributesBeforeTransfer);
|
||||
|
||||
await expect(lensHub.follow([FIRST_PROFILE_ID], [[]])).to.not.be.reverted;
|
||||
|
||||
const tokenURIAfterTransfer = await lensHub.tokenURI(FIRST_PROFILE_ID);
|
||||
const jsonMetadataAfterTransfer = await getJsonMetadataFromBase64TokenUri(
|
||||
tokenURIAfterTransfer
|
||||
);
|
||||
expect(jsonMetadataAfterTransfer.name).to.eq(`@${MOCK_PROFILE_HANDLE}`);
|
||||
expect(jsonMetadataAfterTransfer.description).to.eq(
|
||||
`@${MOCK_PROFILE_HANDLE} - Lens profile`
|
||||
);
|
||||
const expectedAttributesAfterTransfer = [
|
||||
{ trait_type: 'id', value: `#${FIRST_PROFILE_ID.toString()}` },
|
||||
{ trait_type: 'followers', value: '1' },
|
||||
{ trait_type: 'owner', value: userAddress.toLowerCase() },
|
||||
{ trait_type: 'handle', value: `@${MOCK_PROFILE_HANDLE}` },
|
||||
];
|
||||
expect(jsonMetadataAfterTransfer.attributes).to.eql(expectedAttributesAfterTransfer);
|
||||
});
|
||||
|
||||
it('User should set user two as a dispatcher on their profile, user two should set the profile URI', async function () {
|
||||
@@ -64,7 +200,10 @@ makeSuiteCleanRoom('Profile URI Functionality', function () {
|
||||
await expect(
|
||||
lensHub.connect(userTwo).setProfileImageURI(FIRST_PROFILE_ID, MOCK_URI)
|
||||
).to.not.be.reverted;
|
||||
expect(await lensHub.tokenURI(FIRST_PROFILE_ID)).to.eq(MOCK_URI);
|
||||
const tokenURI = await lensHub.tokenURI(FIRST_PROFILE_ID);
|
||||
expect(keccak256(toUtf8Bytes(tokenURI))).to.eq(
|
||||
'0xe1731460db6ce5fa9f3a9f2bd778a8af49e623dceb531df6b1a5c162b7c2d79a'
|
||||
);
|
||||
});
|
||||
|
||||
it('User should follow profile 1, user should change the follow NFT URI, URI is accurate before and after the change', async function () {
|
||||
@@ -297,7 +436,11 @@ makeSuiteCleanRoom('Profile URI Functionality', function () {
|
||||
MAX_UINT256
|
||||
);
|
||||
|
||||
const profileImageURIBefore = await lensHub.tokenURI(FIRST_PROFILE_ID);
|
||||
const tokenURIBefore = await lensHub.tokenURI(FIRST_PROFILE_ID);
|
||||
|
||||
expect(keccak256(toUtf8Bytes(tokenURIBefore))).to.eq(
|
||||
'0x6ed04aa8ab68b7c5201afb2f9655d8fd483559794ce933b7f2282549ca9e3dba'
|
||||
);
|
||||
|
||||
await expect(
|
||||
lensHub.setProfileImageURIWithSig({
|
||||
@@ -312,10 +455,14 @@ makeSuiteCleanRoom('Profile URI Functionality', function () {
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
|
||||
const profileImageURIAfter = await lensHub.tokenURI(FIRST_PROFILE_ID);
|
||||
const tokenURIAfter = await lensHub.tokenURI(FIRST_PROFILE_ID);
|
||||
|
||||
expect(profileImageURIBefore).to.eq(MOCK_PROFILE_URI);
|
||||
expect(profileImageURIAfter).to.eq(MOCK_URI);
|
||||
expect(MOCK_PROFILE_URI).to.not.eq(MOCK_URI);
|
||||
expect(tokenURIBefore).to.not.eq(tokenURIAfter);
|
||||
|
||||
expect(keccak256(toUtf8Bytes(tokenURIAfter))).to.eq(
|
||||
'0x2f93fe42168b386790c5615061bcd3c1d8aac1976bddca8cf57eb2bc525541ab'
|
||||
);
|
||||
});
|
||||
|
||||
it('TestWallet should set the follow NFT URI with sig', async function () {
|
||||
|
||||
@@ -180,7 +180,7 @@ makeSuiteCleanRoom('Fee Collect Module', function () {
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.ERC20_TRANSFER_EXCEEDS_ALLOWANCE);
|
||||
).to.be.revertedWith(ERRORS.ERC20_INSUFFICIENT_ALLOWANCE);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror without following the original profile', async function () {
|
||||
@@ -189,7 +189,7 @@ makeSuiteCleanRoom('Fee Collect Module', function () {
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
@@ -217,7 +217,7 @@ makeSuiteCleanRoom('Fee Collect Module', function () {
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
@@ -250,7 +250,7 @@ makeSuiteCleanRoom('Fee Collect Module', function () {
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
|
||||
@@ -216,7 +216,7 @@ makeSuiteCleanRoom('Limited Fee Collect Module', function () {
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.ERC20_TRANSFER_EXCEEDS_ALLOWANCE);
|
||||
).to.be.revertedWith(ERRORS.ERC20_INSUFFICIENT_ALLOWANCE);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror without following the original profile', async function () {
|
||||
|
||||
@@ -231,7 +231,7 @@ makeSuiteCleanRoom('Limited Timed Fee Collect Module', function () {
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.ERC20_TRANSFER_EXCEEDS_ALLOWANCE);
|
||||
).to.be.revertedWith(ERRORS.ERC20_INSUFFICIENT_ALLOWANCE);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror without following the original profile', async function () {
|
||||
@@ -240,7 +240,7 @@ makeSuiteCleanRoom('Limited Timed Fee Collect Module', function () {
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
@@ -271,7 +271,7 @@ makeSuiteCleanRoom('Limited Timed Fee Collect Module', function () {
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
@@ -307,7 +307,7 @@ makeSuiteCleanRoom('Limited Timed Fee Collect Module', function () {
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
@@ -340,7 +340,7 @@ makeSuiteCleanRoom('Limited Timed Fee Collect Module', function () {
|
||||
lensHub.connect(userTwo).createProfile({
|
||||
to: userTwoAddress,
|
||||
handle: 'usertwo',
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
imageURI: MOCK_PROFILE_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
|
||||
@@ -195,7 +195,7 @@ makeSuiteCleanRoom('Timed Fee Collect Module', function () {
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).collect(FIRST_PROFILE_ID, 1, data)
|
||||
).to.be.revertedWith(ERRORS.ERC20_TRANSFER_EXCEEDS_ALLOWANCE);
|
||||
).to.be.revertedWith(ERRORS.ERC20_INSUFFICIENT_ALLOWANCE);
|
||||
});
|
||||
|
||||
it('UserTwo should mirror the original post, fail to collect from their mirror without following the original profile', async function () {
|
||||
|
||||
@@ -139,7 +139,7 @@ makeSuiteCleanRoom('Fee Follow Module', function () {
|
||||
);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).follow([FIRST_PROFILE_ID], [data])
|
||||
).to.be.revertedWith(ERRORS.ERC20_TRANSFER_EXCEEDS_ALLOWANCE);
|
||||
).to.be.revertedWith(ERRORS.ERC20_INSUFFICIENT_ALLOWANCE);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import '@nomiclabs/hardhat-ethers';
|
||||
import { expect } from 'chai';
|
||||
import { keccak256, toUtf8Bytes } from 'ethers/lib/utils';
|
||||
import { UIDataProvider__factory } from '../../typechain-types';
|
||||
import { ZERO_ADDRESS } from '../helpers/constants';
|
||||
import { ERRORS } from '../helpers/errors';
|
||||
import { getJsonMetadataFromBase64TokenUri } from '../helpers/utils';
|
||||
import {
|
||||
approvalFollowModule,
|
||||
deployer,
|
||||
@@ -153,7 +155,20 @@ makeSuiteCleanRoom('Misc', function () {
|
||||
});
|
||||
|
||||
it('Profile tokenURI should return the accurate URI', async function () {
|
||||
expect(await lensHub.tokenURI(FIRST_PROFILE_ID)).to.eq(MOCK_PROFILE_URI);
|
||||
const tokenURI = await lensHub.tokenURI(FIRST_PROFILE_ID);
|
||||
const jsonMetadata = await getJsonMetadataFromBase64TokenUri(tokenURI);
|
||||
expect(jsonMetadata.name).to.eq(`@${MOCK_PROFILE_HANDLE}`);
|
||||
expect(jsonMetadata.description).to.eq(`@${MOCK_PROFILE_HANDLE} - Lens profile`);
|
||||
const expectedAttributes = [
|
||||
{ trait_type: 'id', value: `#${FIRST_PROFILE_ID.toString()}` },
|
||||
{ trait_type: 'followers', value: '0' },
|
||||
{ trait_type: 'owner', value: userAddress.toLowerCase() },
|
||||
{ trait_type: 'handle', value: `@${MOCK_PROFILE_HANDLE}` },
|
||||
];
|
||||
expect(jsonMetadata.attributes).to.eql(expectedAttributes);
|
||||
expect(keccak256(toUtf8Bytes(tokenURI))).to.eq(
|
||||
'0xa2e9967e825705ce8f38931f7d1f88fe63bef6f2f2c52715692f14d42d889b76'
|
||||
);
|
||||
});
|
||||
|
||||
it('Publication reference module getter should return the correct reference module (or zero in case of no reference module)', async function () {
|
||||
|
||||
Reference in New Issue
Block a user