From 02980ab21fac04618699781dfa072c6d8fb7dc53 Mon Sep 17 00:00:00 2001 From: D3v Date: Tue, 8 Feb 2022 03:55:48 +0100 Subject: [PATCH 01/32] Docker improvements Signed-off-by: D3v --- .dockerignore | 1 + .env.example | 9 +++++++++ Dockerfile | 18 +++++++++++++++++- README.md | 4 ++-- docker-compose.yml | 31 +++++++++++++++++-------------- docker-entrypoint.sh | 3 +++ 6 files changed, 49 insertions(+), 17 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 docker-entrypoint.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..40b878d --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..7544134 --- /dev/null +++ b/.env.example @@ -0,0 +1,9 @@ +MNEMONIC= +ETHERSCAN_KEY= +INFURA_KEY= +ETHERSCAN_NETWORK= +TENDERLY_PROJECT= +TENDERLY_USERNAME= +ALCHEMY_KEY= +TENDERLY_FORK_ID= +TENDERLY_HEAD_ID= diff --git a/Dockerfile b/Dockerfile index dc29e52..da730a9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,21 @@ +# syntax=docker/dockerfile:1.3 FROM ethereum/solc:0.8.7 as build-deps +FROM node:16 as build-packages + +COPY package*.json ./ +COPY tsconfig*.json ./ + +RUN npm ci --quiet + FROM node:16 + +WORKDIR /src + +COPY --from=build-deps /usr/bin/solc /usr/bin/solc +COPY --from=build-packages /node_modules /node_modules +COPY docker-entrypoint.sh /docker-entrypoint.sh + USER node -COPY --from=build-deps /usr/bin/solc /usr/bin/solc \ No newline at end of file + +ENTRYPOINT ["sh", "/docker-entrypoint.sh"] \ No newline at end of file diff --git a/README.md b/README.md index 3559b4d..03a334a 100644 --- a/README.md +++ b/README.md @@ -51,13 +51,13 @@ ETHERSCAN_KEY="YOUR ETHERSCAN API KEY HERE" With the environment file set up, you can move on to using Docker: ``` -$ sudo docker-compose up +$ sudo docker-compose up -d --build ``` And in another terminal: ``` -$ sudo docker-compose exec contracts-env bash +$ sudo docker-compose exec contracts-env /bin/bash ``` From there, have fun! diff --git a/docker-compose.yml b/docker-compose.yml index 7c94bcb..a64b9ed 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,22 +2,25 @@ version: '3.5' services: contracts-env: + security_opt: + - no-new-privileges + user: 1001:1001 env_file: - .env build: context: ./ - working_dir: /src - command: npm run run-env + stdin_open: true + tty: true volumes: - - ./:/src - - $HOME/.tenderly/config.yaml:/root/.tenderly/config.yaml - environment: - MNEMONIC: ${MNEMONIC} - ETHERSCAN_KEY: ${ETHERSCAN_KEY} - INFURA_KEY: ${INFURA_KEY} - ETHERSCAN_NETWORK: ${ETHERSCAN_NETWORK} - TENDERLY_PROJECT: ${TENDERLY_PROJECT} - TENDERLY_USERNAME: ${TENDERLY_USERNAME} - ALCHEMY_KEY: ${ALCHEMY_KEY} - TENDERLY_FORK_ID: ${TENDERLY_FORK_ID} - TENDERLY_HEAD_ID: ${TENDERLY_HEAD_ID} + - ./:/src:rw + - $HOME/.tenderly/config.yaml:/root/.tenderly/config.yaml:ro + #environment: + #- MNEMONIC= + #- ETHERSCAN_KEY= + #- INFURA_KEY= + #- ETHERSCAN_NETWORK= + #- TENDERLY_PROJECT= + #- TENDERLY_USERNAME= + #- ALCHEMY_KEY= + #- TENDERLY_FORK_ID= + #- TENDERLY_HEAD_ID= diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..c90d941 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +[ ! -d "/src/node_modules" ] && mv /node_modules /src/node_modules ; bash \ No newline at end of file From 251933c16f57adad2138204364b5dfbc47493142 Mon Sep 17 00:00:00 2001 From: D3v Date: Tue, 8 Feb 2022 03:57:29 +0100 Subject: [PATCH 02/32] Missing newline Signed-off-by: D3v --- .dockerignore | 2 +- docker-entrypoint.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.dockerignore b/.dockerignore index 40b878d..c2658d7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1 @@ -node_modules/ \ No newline at end of file +node_modules/ diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index c90d941..8b4f443 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -1,3 +1,3 @@ #!/bin/bash -[ ! -d "/src/node_modules" ] && mv /node_modules /src/node_modules ; bash \ No newline at end of file +[ ! -d "/src/node_modules" ] && mv /node_modules /src/node_modules ; bash From ff90e6961b25cf5057a7eb5651eeae40193fad37 Mon Sep 17 00:00:00 2001 From: Zer0dot Date: Mon, 14 Feb 2022 12:21:09 -0500 Subject: [PATCH 03/32] misc: Fixed missing brackets. --- test/hub/interactions/following.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/hub/interactions/following.spec.ts b/test/hub/interactions/following.spec.ts index 1d3471e..713b96a 100644 --- a/test/hub/interactions/following.spec.ts +++ b/test/hub/interactions/following.spec.ts @@ -119,7 +119,7 @@ makeSuiteCleanRoom('Following', function () { lensHub.followWithSig({ follower: testWallet.address, profileIds: [FIRST_PROFILE_ID], - datas: [], + datas: [[]], sig: { v, r, From a9aadfc27c13ac37b17b48766690b811e1c45f30 Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Wed, 16 Feb 2022 12:28:48 +0000 Subject: [PATCH 04/32] feat: extend UI data provider helper to include `getLatestDataByHandle` --- contracts/misc/UIDataProvider.sol | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/contracts/misc/UIDataProvider.sol b/contracts/misc/UIDataProvider.sol index ad3cf01..71cfcb7 100644 --- a/contracts/misc/UIDataProvider.sol +++ b/contracts/misc/UIDataProvider.sol @@ -29,16 +29,30 @@ contract UIDataProvider { HUB = hub; } - /** * @notice Returns the profile struct and latest publication struct associated with the passed * profile ID. * * @param profileId The profile ID to query. - * + * * @return A custom `LatestData` struct containing the `ProfileStruct` and the `PublicationStruct` queried. */ - function getLatestData(uint256 profileId) external view returns (LatestData memory) { + function getLatestDataByProfile(uint256 profileId) external view returns (LatestData memory) { + DataTypes.ProfileStruct memory profileStruct = HUB.getProfile(profileId); + uint256 pubCount = profileStruct.pubCount; + return LatestData(profileStruct, HUB.getPub(profileId, pubCount)); + } + + /** + * @notice Returns the profile struct and latest publication struct associated with the passed + * profile ID. + * + * @param handle The handle to query. + * + * @return A custom `LatestData` struct containing the `ProfileStruct` and the `PublicationStruct` queried. + */ + function getLatestDataByHandle(string memory handle) external view returns (LatestData memory) { + uint256 profileId = HUB.getProfileIdByHandle(handle); DataTypes.ProfileStruct memory profileStruct = HUB.getProfile(profileId); uint256 pubCount = profileStruct.pubCount; return LatestData(profileStruct, HUB.getPub(profileId, pubCount)); From b44bf9cf79edaa6f21fc25f45505c2a21dc23d2b Mon Sep 17 00:00:00 2001 From: zerosnacks Date: Fri, 18 Feb 2022 17:55:36 +0100 Subject: [PATCH 05/32] fix param name --- contracts/interfaces/ILensHub.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/interfaces/ILensHub.sol b/contracts/interfaces/ILensHub.sol index 232650d..531947c 100644 --- a/contracts/interfaces/ILensHub.sol +++ b/contracts/interfaces/ILensHub.sol @@ -46,9 +46,9 @@ interface ILensHub { * @notice Sets the protocol state to either a global pause, a publishing pause or an unpaused state. This function * can only be called by the governance address or the emergency admin address. * - * @param state The state to set, as a member of the ProtocolState enum. + * @param newState The state to set, as a member of the ProtocolState enum. */ - function setState(DataTypes.ProtocolState state) external; + function setState(DataTypes.ProtocolState newState) external; /** * @notice Adds or removes a profile creator from the whitelist. This function can only be called by the current From a302f969bdce8a5e4cd731750ec29ac5eb4e6ee6 Mon Sep 17 00:00:00 2001 From: yqrashawn Date: Sun, 20 Feb 2022 10:22:34 +0800 Subject: [PATCH 06/32] fix: minor typo in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3559b4d..7723ab2 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ This is a publication type that points back to another publication, whether it b This is a publication type that points to another publication, note that mirrors cannot, themselves, be mirrored (doing so instead mirrors the pointed content). Mirrors have no original content of its own. Akin to a "share" on traditional social media. Mirrors contain: 1. An empty URI, since they cannot have content associated with them. -2. An initialized pointer, contianing the profile ID and the publication ID of the mirrored publication. +2. An initialized pointer, containing the profile ID and the publication ID of the mirrored publication. ### Profile Interaction From 75927948eb6fb24ea834e1481ad042a607e9b492 Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Wed, 23 Feb 2022 11:31:35 +0000 Subject: [PATCH 07/32] fix: fix broken test and add missing test for new method --- test/other/misc.spec.ts | 51 ++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/test/other/misc.spec.ts b/test/other/misc.spec.ts index 93c7575..027c424 100644 --- a/test/other/misc.spec.ts +++ b/test/other/misc.spec.ts @@ -689,24 +689,43 @@ makeSuiteCleanRoom('Misc', function () { // Then, deploy the data provider const dataProvider = await new UIDataProvider__factory(deployer).deploy(lensHub.address); - // Lastly, validate the result from the data provider - const result = await dataProvider.getLatestData(FIRST_PROFILE_ID); - const pubStruct = result.publicationStruct; - const profileStruct = result.profileStruct; + // `getLatestDataByProfile`, validate the result from the data provider + const resultByProfileId = await dataProvider.getLatestDataByProfile(FIRST_PROFILE_ID); + const pubByProfileIdStruct = resultByProfileId.publicationStruct; + const profileByProfileIdStruct = resultByProfileId.profileStruct; - expect(profileStruct.pubCount).to.eq(2); - expect(profileStruct.followModule).to.eq(ZERO_ADDRESS); - expect(profileStruct.followNFT).to.eq(ZERO_ADDRESS); - expect(profileStruct.handle).to.eq(MOCK_PROFILE_HANDLE); - expect(profileStruct.imageURI).to.eq(MOCK_PROFILE_URI); - expect(profileStruct.followNFTURI).to.eq(MOCK_FOLLOW_NFT_URI); + expect(profileByProfileIdStruct.pubCount).to.eq(2); + expect(profileByProfileIdStruct.followModule).to.eq(ZERO_ADDRESS); + expect(profileByProfileIdStruct.followNFT).to.eq(ZERO_ADDRESS); + expect(profileByProfileIdStruct.handle).to.eq(MOCK_PROFILE_HANDLE); + expect(profileByProfileIdStruct.imageURI).to.eq(MOCK_PROFILE_URI); + expect(profileByProfileIdStruct.followNFTURI).to.eq(MOCK_FOLLOW_NFT_URI); - expect(pubStruct.profileIdPointed).to.eq(0); - expect(pubStruct.pubIdPointed).to.eq(0); - expect(pubStruct.contentURI).to.eq(secondURI); - expect(pubStruct.referenceModule).to.eq(ZERO_ADDRESS); - expect(pubStruct.collectModule).to.eq(emptyCollectModule.address); - expect(pubStruct.collectNFT).to.eq(ZERO_ADDRESS); + expect(pubByProfileIdStruct.profileIdPointed).to.eq(0); + expect(pubByProfileIdStruct.pubIdPointed).to.eq(0); + expect(pubByProfileIdStruct.contentURI).to.eq(secondURI); + expect(pubByProfileIdStruct.referenceModule).to.eq(ZERO_ADDRESS); + expect(pubByProfileIdStruct.collectModule).to.eq(emptyCollectModule.address); + expect(pubByProfileIdStruct.collectNFT).to.eq(ZERO_ADDRESS); + + // `getLatestDataByHandle`, validate the result from the data provider + const resultByHandle = await dataProvider.getLatestDataByHandle(MOCK_PROFILE_HANDLE); + const pubByHandleStruct = resultByHandle.publicationStruct; + const profileByHandleStruct = resultByHandle.profileStruct; + + expect(profileByHandleStruct.pubCount).to.eq(2); + expect(profileByHandleStruct.followModule).to.eq(ZERO_ADDRESS); + expect(profileByHandleStruct.followNFT).to.eq(ZERO_ADDRESS); + expect(profileByHandleStruct.handle).to.eq(MOCK_PROFILE_HANDLE); + expect(profileByHandleStruct.imageURI).to.eq(MOCK_PROFILE_URI); + expect(profileByHandleStruct.followNFTURI).to.eq(MOCK_FOLLOW_NFT_URI); + + expect(pubByHandleStruct.profileIdPointed).to.eq(0); + expect(pubByHandleStruct.pubIdPointed).to.eq(0); + expect(pubByHandleStruct.contentURI).to.eq(secondURI); + expect(pubByHandleStruct.referenceModule).to.eq(ZERO_ADDRESS); + expect(pubByHandleStruct.collectModule).to.eq(emptyCollectModule.address); + expect(pubByHandleStruct.collectNFT).to.eq(ZERO_ADDRESS); }); }); }); From 60d6b6dfdf414e1fe8b580cf56efeab493d7a259 Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Fri, 25 Feb 2022 12:36:34 +0000 Subject: [PATCH 08/32] feat: allow to set default profile --- contracts/core/LensHub.sol | 100 ++++++++++++++++++++++ contracts/core/storage/LensHubStorage.sol | 15 +++- contracts/interfaces/ILensHub.sol | 39 +++++++++ contracts/libraries/DataTypes.sol | 24 ++++++ contracts/libraries/Events.sol | 9 ++ 5 files changed, 186 insertions(+), 1 deletion(-) diff --git a/contracts/core/LensHub.sol b/contracts/core/LensHub.sol index 0302fc0..c37c6a7 100644 --- a/contracts/core/LensHub.sol +++ b/contracts/core/LensHub.sol @@ -156,6 +156,76 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat ); } + /// @inheritdoc ILensHub + function setDefaultProfile(uint256 profileId) external override whenNotPaused { + _setDefaultProfile(profileId, msg.sender); + } + + /// @inheritdoc ILensHub + function setDefaultProfileWithSig(DataTypes.SetDefaultProfileWithSigData calldata vars) + external + override + whenNotPaused + { + address owner = ownerOf(vars.profileId); + bytes32 digest; + unchecked { + digest = keccak256( + abi.encodePacked( + '\x19\x01', + _calculateDomainSeparator(), + keccak256( + abi.encode( + SET_DEFAULT_PROFILE_WITH_SIG_TYPEHASH, + vars.profileId, + sigNonces[owner]++, + vars.sig.deadline + ) + ) + ) + ); + } + + _validateRecoveredAddress(digest, owner, vars.sig); + + _setDefaultProfile(vars.profileId, owner); + } + + /// @inheritdoc ILensHub + function unsetDefaultProfile(uint256 profileId) external override whenNotPaused { + _unsetDefaultProfile(profileId, msg.sender); + } + + /// @inheritdoc ILensHub + function unsetDefaultProfileWithSig(DataTypes.UnsetDefaultProfileWithSigData calldata vars) + external + override + whenNotPaused + { + address owner = ownerOf(vars.profileId); + bytes32 digest; + unchecked { + digest = keccak256( + abi.encodePacked( + '\x19\x01', + _calculateDomainSeparator(), + keccak256( + abi.encode( + UNSET_DEFAULT_PROFILE_WITH_SIG_TYPEHASH, + vars.profileId, + sigNonces[owner]++, + vars.sig.deadline + ) + ) + ) + ); + } + + _validateRecoveredAddress(digest, owner, vars.sig); + + _setDefaultProfile(vars.profileId, owner); + } + /// @inheritdoc ILensHub function setFollowModule( uint256 profileId, @@ -673,6 +743,11 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat return _profileCreatorWhitelisted[profileCreator]; } + /// @inheritdoc ILensHub + function defaultProfile(address wallet) external view override returns (uint256) { + return _addressByDefaultProfile[wallet]; + } + /// @inheritdoc ILensHub function isFollowModuleWhitelisted(address followModule) external view override returns (bool) { return _followModuleWhitelisted[followModule]; @@ -875,6 +950,24 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat ); } + function _setDefaultProfile(uint256 profileId, address owner) internal { + _validateCallerIsProfileOwner(profileId, owner); + + _defaultProfileByAddress[profileId] = owner; + _addressByDefaultProfile[owner] = profileId; + + emit Events.DefaultProfileSet(profileId, owner, block.timestamp); + } + + function _unsetDefaultProfile(uint256 profileId, address owner) internal { + _validateCallerIsProfileOwner(profileId, owner); + + _defaultProfileByAddress[profileId] = address(0); + _addressByDefaultProfile[owner] = 0; + + emit Events.DefaultProfileSet(0, address(0), block.timestamp); + } + function _createComment(DataTypes.CommentData memory vars) internal { PublishingLogic.createComment( vars, @@ -934,6 +1027,9 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat if (_dispatcherByProfile[tokenId] != address(0)) { _setDispatcher(tokenId, address(0)); } + + _defaultProfileByAddress[tokenId] = address(0); + _addressByDefaultProfile[msg.sender] = 0; super._beforeTokenTransfer(from, to, tokenId); } @@ -946,6 +1042,10 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat if (msg.sender != ownerOf(profileId)) revert Errors.NotProfileOwner(); } + function _validateCallerIsProfileOwner(uint256 profileId, address wallet) internal view { + if (wallet != ownerOf(profileId)) revert Errors.NotProfileOwner(); + } + function _validateCallerIsGovernance() internal view { if (msg.sender != _governance) revert Errors.NotGovernance(); } diff --git a/contracts/core/storage/LensHubStorage.sol b/contracts/core/storage/LensHubStorage.sol index 82c3b23..97689ce 100644 --- a/contracts/core/storage/LensHubStorage.sol +++ b/contracts/core/storage/LensHubStorage.sol @@ -10,8 +10,18 @@ contract LensHubStorage { // keccak256( // 'CreateProfileWithSig(string handle,string uri,address followModule,bytes followModuleData,uint256 nonce,uint256 deadline)' // ); + bytes32 internal constant SET_DEFAULT_PROFILE_WITH_SIG_TYPEHASH = + 0x76a7f2df5a2c73b8b0bbf095e1efae3cc5fb722c9a4000d53c90aa1848421fd5; + // keccak256( + // 'setDefaultProfileWithSig(uint256 profileId,uint256 nonce,uint256 deadline)' + // ); + bytes32 internal constant UNSET_DEFAULT_PROFILE_WITH_SIG_TYPEHASH = + 0x2c0999aca14a6108b99d0483911154022cf8071dfe55ac3325ccd3e28e12a3c2; + // keccak256( + // 'unsetDefaultProfileWithSig(uint256 profileId,uint256 nonce,uint256 deadline)' + // ); bytes32 internal constant SET_FOLLOW_MODULE_WITH_SIG_TYPEHASH = - 0x6f3f6455a608af1cc57ef3e5c0a49deeb88bba264ec8865b798ff07358859d4b; + 0x5d91a73d4b313d08b27193d276cd37aadd617e55d521190e2f80dc2217a9066f; // keccak256( // 'SetFollowModuleWithSig(uint256 profileId,address followModule,bytes followModuleData,uint256 nonce,uint256 deadline)' // ); @@ -66,6 +76,9 @@ contract LensHubStorage { mapping(uint256 => DataTypes.ProfileStruct) internal _profileById; mapping(uint256 => mapping(uint256 => DataTypes.PublicationStruct)) internal _pubByIdByProfile; + mapping(uint256 => address) internal _defaultProfileByAddress; + mapping(address => uint256) internal _addressByDefaultProfile; + uint256 internal _profileCounter; address internal _governance; address internal _emergencyAdmin; diff --git a/contracts/interfaces/ILensHub.sol b/contracts/interfaces/ILensHub.sol index 531947c..1e4c6f8 100644 --- a/contracts/interfaces/ILensHub.sol +++ b/contracts/interfaces/ILensHub.sol @@ -99,6 +99,36 @@ interface ILensHub { */ function createProfile(DataTypes.CreateProfileData calldata vars) external; + /** + * @notice Sets a the mapping between wallet and its main profile identity + * + * @param profileId The token ID of the profile to set as the main profile identity + */ + function setDefaultProfile(uint256 profileId) external; + + /** + * @notice Sets a the mapping between wallet and its main profile identity via signature with the specified parameters. + * + * @param vars A SetDefaultProfileWithSigData struct, including the regular parameters and an EIP712Signature struct. + */ + function setDefaultProfileWithSig(DataTypes.SetDefaultProfileWithSigData calldata vars) + external; + + /** + * @notice remove the mapping between wallet and its main profile identity + * + * @param profileId The token ID of the profile to set as the main profile identity + */ + function unsetDefaultProfile(uint256 profileId) external; + + /** + * @notice Unsets a the mapping between wallet and its main profile identity via signature with the specified parameters. + * + * @param vars A UnsetDefaultProfileWithSigData struct, including the regular parameters and an EIP712Signature struct. + */ + function unsetDefaultProfileWithSig(DataTypes.UnsetDefaultProfileWithSigData calldata vars) + external; + /** * @notice Sets a profile's follow module, must be called by the profile owner. * @@ -294,6 +324,15 @@ interface ILensHub { */ function isProfileCreatorWhitelisted(address profileCreator) external view returns (bool); + /** + * @notice Returns default profile for a given wallet address + * + * @param wallet The address to find the default mapping + * + * @return A uint256 profile id will be 0 if not mapped + */ + function defaultProfile(address wallet) external view returns (uint256); + /** * @notice Returns whether or not a follow module is whitelisted. * diff --git a/contracts/libraries/DataTypes.sol b/contracts/libraries/DataTypes.sol index abf27dc..6090def 100644 --- a/contracts/libraries/DataTypes.sol +++ b/contracts/libraries/DataTypes.sol @@ -109,6 +109,30 @@ library DataTypes { string followNFTURI; } + /** + * @notice A struct containing the parameters required for the `setDefaultProfileWithSig()` function. Parameters are + * the same as the regular `setDefaultProfile()` function, with an added EIP712Signature. + * + * @param profileId The token ID of the profile which will be set as default + * @param sig The EIP712Signature struct containing the profile owner's signature. + */ + struct SetDefaultProfileWithSigData { + uint256 profileId; + EIP712Signature sig; + } + + /** + * @notice A struct containing the parameters required for the `unsetDefaultProfileWithSig()` function. Parameters are + * the same as the regular `unsetDefaultProfile()` function, with an added EIP712Signature. + * + * @param profileId The token ID of the profile which will be unset as default + * @param sig The EIP712Signature struct containing the profile owner's signature. + */ + struct UnsetDefaultProfileWithSigData { + uint256 profileId; + EIP712Signature sig; + } + /** * @notice A struct containing the parameters required for the `setFollowModuleWithSig()` function. Parameters are * the same as the regular `setFollowModule()` function, with an added EIP712Signature. diff --git a/contracts/libraries/Events.sol b/contracts/libraries/Events.sol index 71857bb..f5ebea9 100644 --- a/contracts/libraries/Events.sol +++ b/contracts/libraries/Events.sol @@ -138,6 +138,15 @@ library Events { uint256 timestamp ); + /** + * @dev Emitted when a a default profile is set for a wallet as its main identity + * + * @param profileId The token ID of the profile for which the default profile is being set. + * @param wallet The wallet which owns this profile + * @param timestamp The current block timestamp. + */ + event DefaultProfileSet(uint256 indexed profileId, address indexed wallet, uint256 timestamp); + /** * @dev Emitted when a dispatcher is set for a specific profile. * From fffbc2b635107f95aa5659bd1cd124876a405bfc Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Fri, 25 Feb 2022 12:40:34 +0000 Subject: [PATCH 09/32] use unset over set on the unset method --- contracts/core/LensHub.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/core/LensHub.sol b/contracts/core/LensHub.sol index c37c6a7..1f7503c 100644 --- a/contracts/core/LensHub.sol +++ b/contracts/core/LensHub.sol @@ -223,7 +223,7 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat _validateRecoveredAddress(digest, owner, vars.sig); - _setDefaultProfile(vars.profileId, owner); + _unsetDefaultProfile(vars.profileId, owner); } /// @inheritdoc ILensHub From f0196c1f098cf3f6b417fa867ed07a715b17f427 Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Fri, 25 Feb 2022 14:02:21 +0000 Subject: [PATCH 10/32] revert follow sig typehash --- contracts/core/storage/LensHubStorage.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/core/storage/LensHubStorage.sol b/contracts/core/storage/LensHubStorage.sol index 97689ce..3ab8681 100644 --- a/contracts/core/storage/LensHubStorage.sol +++ b/contracts/core/storage/LensHubStorage.sol @@ -21,7 +21,7 @@ contract LensHubStorage { // 'unsetDefaultProfileWithSig(uint256 profileId,uint256 nonce,uint256 deadline)' // ); bytes32 internal constant SET_FOLLOW_MODULE_WITH_SIG_TYPEHASH = - 0x5d91a73d4b313d08b27193d276cd37aadd617e55d521190e2f80dc2217a9066f; + 0x6f3f6455a608af1cc57ef3e5c0a49deeb88bba264ec8865b798ff07358859d4b; // keccak256( // 'SetFollowModuleWithSig(uint256 profileId,address followModule,bytes followModuleData,uint256 nonce,uint256 deadline)' // ); From 8ab8446b30be0be134fb6de800dc6cecd671aca3 Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Fri, 25 Feb 2022 14:11:27 +0000 Subject: [PATCH 11/32] feat: add a event for unset --- contracts/core/LensHub.sol | 2 +- contracts/libraries/Events.sol | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/contracts/core/LensHub.sol b/contracts/core/LensHub.sol index 1f7503c..eea2df0 100644 --- a/contracts/core/LensHub.sol +++ b/contracts/core/LensHub.sol @@ -965,7 +965,7 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat _defaultProfileByAddress[profileId] = address(0); _addressByDefaultProfile[owner] = 0; - emit Events.DefaultProfileSet(0, address(0), block.timestamp); + emit Events.DefaultProfileUnset(profileId, owner, block.timestamp); } function _createComment(DataTypes.CommentData memory vars) internal { diff --git a/contracts/libraries/Events.sol b/contracts/libraries/Events.sol index f5ebea9..a7163fb 100644 --- a/contracts/libraries/Events.sol +++ b/contracts/libraries/Events.sol @@ -147,6 +147,19 @@ library Events { */ event DefaultProfileSet(uint256 indexed profileId, address indexed wallet, uint256 timestamp); + /** + * @dev Emitted when a default profile is unset for a wallet as its main identity + * + * @param unsetProfileId The token ID of the profile for which the default profile is being unset from. + * @param wallet The wallet which owns this profile + * @param timestamp The current block timestamp. + */ + event DefaultProfileUnset( + uint256 indexed unsetProfileId, + address indexed wallet, + uint256 timestamp + ); + /** * @dev Emitted when a dispatcher is set for a specific profile. * From 402b7d7ff4dbcfac9c5d6580340bca744b575716 Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Fri, 25 Feb 2022 14:17:52 +0000 Subject: [PATCH 12/32] msg.sender > to --- contracts/core/LensHub.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/core/LensHub.sol b/contracts/core/LensHub.sol index eea2df0..da46e12 100644 --- a/contracts/core/LensHub.sol +++ b/contracts/core/LensHub.sol @@ -1029,7 +1029,7 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat } _defaultProfileByAddress[tokenId] = address(0); - _addressByDefaultProfile[msg.sender] = 0; + _addressByDefaultProfile[from] = 0; super._beforeTokenTransfer(from, to, tokenId); } From b9d37c586f7c9b16ed542930154709dd2e0ff88e Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Fri, 25 Feb 2022 14:19:27 +0000 Subject: [PATCH 13/32] only change mappings if not from dead address --- contracts/core/LensHub.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contracts/core/LensHub.sol b/contracts/core/LensHub.sol index da46e12..4538f77 100644 --- a/contracts/core/LensHub.sol +++ b/contracts/core/LensHub.sol @@ -1028,8 +1028,11 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat _setDispatcher(tokenId, address(0)); } - _defaultProfileByAddress[tokenId] = address(0); - _addressByDefaultProfile[from] = 0; + if (from != address(0)) { + _defaultProfileByAddress[tokenId] = address(0); + _addressByDefaultProfile[from] = 0; + } + super._beforeTokenTransfer(from, to, tokenId); } From 8e1a2b3739dfdfd46424c9b6a765f3d2d8159383 Mon Sep 17 00:00:00 2001 From: donosonaumczuk Date: Sun, 27 Feb 2022 18:56:43 -0300 Subject: [PATCH 14/32] CI github workflow added --- .github/workflows/ci.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1e1f777 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: Continuous Integration + +on: + push: + pull_request: + branches: [main] + +jobs: + ci: + runs-on: ubuntu-latest + env: + working-directory: '.' + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: '14.16.1' + - name: Install hardhat + run: npm install --save-dev hardhat + working-directory: ${{env.working-directory}} + - name: Install packages + run: npm install + working-directory: ${{env.working-directory}} + - name: Compile code + run: npx hardhat compile + working-directory: ${{env.working-directory}} + - name: Run tests + run: npx hardhat test + working-directory: ${{env.working-directory}} From 3048171599c9012561ff0a363afe0d7a302f3a94 Mon Sep 17 00:00:00 2001 From: donosonaumczuk Date: Sun, 27 Feb 2022 19:57:51 -0300 Subject: [PATCH 15/32] Node version updated to v16 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e1f777..992865e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: - node-version: '14.16.1' + node-version: '16' - name: Install hardhat run: npm install --save-dev hardhat working-directory: ${{env.working-directory}} From 0c0970a7b5f17c81b31db86071edf492c2553f15 Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Mon, 28 Feb 2022 16:18:48 +0000 Subject: [PATCH 16/32] fix: naming issues from PR comments --- contracts/core/LensHub.sol | 14 +++++++------- contracts/core/storage/LensHubStorage.sol | 4 ++-- contracts/interfaces/ILensHub.sol | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/contracts/core/LensHub.sol b/contracts/core/LensHub.sol index 4538f77..75d52f5 100644 --- a/contracts/core/LensHub.sol +++ b/contracts/core/LensHub.sol @@ -745,7 +745,7 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat /// @inheritdoc ILensHub function defaultProfile(address wallet) external view override returns (uint256) { - return _addressByDefaultProfile[wallet]; + return _addressToDefaultProfile[wallet]; } /// @inheritdoc ILensHub @@ -953,8 +953,8 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat function _setDefaultProfile(uint256 profileId, address owner) internal { _validateCallerIsProfileOwner(profileId, owner); - _defaultProfileByAddress[profileId] = owner; - _addressByDefaultProfile[owner] = profileId; + _defaultProfileToAddress[profileId] = owner; + _addressToDefaultProfile[owner] = profileId; emit Events.DefaultProfileSet(profileId, owner, block.timestamp); } @@ -962,8 +962,8 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat function _unsetDefaultProfile(uint256 profileId, address owner) internal { _validateCallerIsProfileOwner(profileId, owner); - _defaultProfileByAddress[profileId] = address(0); - _addressByDefaultProfile[owner] = 0; + _defaultProfileToAddress[profileId] = address(0); + _addressToDefaultProfile[owner] = 0; emit Events.DefaultProfileUnset(profileId, owner, block.timestamp); } @@ -1029,8 +1029,8 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat } if (from != address(0)) { - _defaultProfileByAddress[tokenId] = address(0); - _addressByDefaultProfile[from] = 0; + _defaultProfileToAddress[tokenId] = address(0); + _addressToDefaultProfile[from] = 0; } super._beforeTokenTransfer(from, to, tokenId); diff --git a/contracts/core/storage/LensHubStorage.sol b/contracts/core/storage/LensHubStorage.sol index 3ab8681..9df354a 100644 --- a/contracts/core/storage/LensHubStorage.sol +++ b/contracts/core/storage/LensHubStorage.sol @@ -76,8 +76,8 @@ contract LensHubStorage { mapping(uint256 => DataTypes.ProfileStruct) internal _profileById; mapping(uint256 => mapping(uint256 => DataTypes.PublicationStruct)) internal _pubByIdByProfile; - mapping(uint256 => address) internal _defaultProfileByAddress; - mapping(address => uint256) internal _addressByDefaultProfile; + mapping(uint256 => address) internal _defaultProfileToAddress; + mapping(address => uint256) internal _addressToDefaultProfile; uint256 internal _profileCounter; address internal _governance; diff --git a/contracts/interfaces/ILensHub.sol b/contracts/interfaces/ILensHub.sol index 1e4c6f8..ffb9dd2 100644 --- a/contracts/interfaces/ILensHub.sol +++ b/contracts/interfaces/ILensHub.sol @@ -100,14 +100,14 @@ interface ILensHub { function createProfile(DataTypes.CreateProfileData calldata vars) external; /** - * @notice Sets a the mapping between wallet and its main profile identity + * @notice Sets the mapping between wallet and its main profile identity * * @param profileId The token ID of the profile to set as the main profile identity */ function setDefaultProfile(uint256 profileId) external; /** - * @notice Sets a the mapping between wallet and its main profile identity via signature with the specified parameters. + * @notice Sets the mapping between wallet and its main profile identity via signature with the specified parameters. * * @param vars A SetDefaultProfileWithSigData struct, including the regular parameters and an EIP712Signature struct. */ @@ -122,7 +122,7 @@ interface ILensHub { function unsetDefaultProfile(uint256 profileId) external; /** - * @notice Unsets a the mapping between wallet and its main profile identity via signature with the specified parameters. + * @notice Unsets the mapping between wallet and its main profile identity via signature with the specified parameters. * * @param vars A UnsetDefaultProfileWithSigData struct, including the regular parameters and an EIP712Signature struct. */ From babe726f15c97918f939cbf7b6d1c0a082cdde67 Mon Sep 17 00:00:00 2001 From: donosonaumczuk Date: Mon, 28 Feb 2022 17:58:37 -0300 Subject: [PATCH 17/32] Change compile and test commands to use scripts which sets SKIP_LOAD --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 992865e..7c79a53 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,8 +22,8 @@ jobs: run: npm install working-directory: ${{env.working-directory}} - name: Compile code - run: npx hardhat compile + run: npm run compile working-directory: ${{env.working-directory}} - name: Run tests - run: npx hardhat test + run: npm run test working-directory: ${{env.working-directory}} From 4470a288e601aaf9f464c5b8af585aa9e2f856b8 Mon Sep 17 00:00:00 2001 From: donosonaumczuk Date: Mon, 28 Feb 2022 18:04:46 -0300 Subject: [PATCH 18/32] Run CI on pushes but only on main branch --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c79a53..f61a8ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,7 @@ name: Continuous Integration on: push: + branches: [main] pull_request: branches: [main] From c3d3f24abd19830198fc890e28a2a760c902d60c Mon Sep 17 00:00:00 2001 From: donosonaumczuk Date: Mon, 28 Feb 2022 18:15:23 -0300 Subject: [PATCH 19/32] Update workflow and job names --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f61a8ee..f1a088a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Continuous Integration +name: ci on: push: @@ -7,7 +7,7 @@ on: branches: [main] jobs: - ci: + compile_and_run_tests: runs-on: ubuntu-latest env: working-directory: '.' From 1d90218d8d4faa9608d509b9bdcd8aa1f493040f Mon Sep 17 00:00:00 2001 From: donosonaumczuk Date: Mon, 28 Feb 2022 18:46:12 -0300 Subject: [PATCH 20/32] Changed dependency installation command to 'npm ci' --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1a088a..60ff611 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: run: npm install --save-dev hardhat working-directory: ${{env.working-directory}} - name: Install packages - run: npm install + run: npm ci working-directory: ${{env.working-directory}} - name: Compile code run: npm run compile From fac119b9bc6d1f70cbfbb5fd8558b5f6d55a5635 Mon Sep 17 00:00:00 2001 From: donosonaumczuk Date: Tue, 1 Mar 2022 13:19:49 -0300 Subject: [PATCH 21/32] Remove redundant hardhat installation --- .github/workflows/ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 60ff611..d8959e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,10 +16,7 @@ jobs: - uses: actions/setup-node@v2 with: node-version: '16' - - name: Install hardhat - run: npm install --save-dev hardhat - working-directory: ${{env.working-directory}} - - name: Install packages + - name: Install dependencies run: npm ci working-directory: ${{env.working-directory}} - name: Compile code From fd15f76d8de3af8d23e4548286b2dd694c7b7ec1 Mon Sep 17 00:00:00 2001 From: donosonaumczuk Date: Tue, 1 Mar 2022 13:20:26 -0300 Subject: [PATCH 22/32] Remove redundant working directory env variable --- .github/workflows/ci.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d8959e9..ef459b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,8 +9,6 @@ on: jobs: compile_and_run_tests: runs-on: ubuntu-latest - env: - working-directory: '.' steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 @@ -18,10 +16,7 @@ jobs: node-version: '16' - name: Install dependencies run: npm ci - working-directory: ${{env.working-directory}} - name: Compile code run: npm run compile - working-directory: ${{env.working-directory}} - name: Run tests run: npm run test - working-directory: ${{env.working-directory}} From e70d126d22dd9a3421f5244f3a3a26cdc5e9cde0 Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Tue, 1 Mar 2022 17:00:06 +0000 Subject: [PATCH 23/32] test: starting default profile spec --- test/hub/profiles/default-profile.spec.ts | 52 +++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 test/hub/profiles/default-profile.spec.ts diff --git a/test/hub/profiles/default-profile.spec.ts b/test/hub/profiles/default-profile.spec.ts new file mode 100644 index 0000000..ff8f656 --- /dev/null +++ b/test/hub/profiles/default-profile.spec.ts @@ -0,0 +1,52 @@ +import '@nomiclabs/hardhat-ethers'; +import { expect } from 'chai'; +import { ZERO_ADDRESS } from '../../helpers/constants'; +import { ERRORS } from '../../helpers/errors'; +import { + FIRST_PROFILE_ID, + lensHub, + makeSuiteCleanRoom, + MOCK_FOLLOW_NFT_URI, + MOCK_PROFILE_HANDLE, + MOCK_PROFILE_URI, + userAddress, + userTwo, +} from '../../__setup.spec'; + +makeSuiteCleanRoom('Default profile Functionality', function () { + context('Generic', function () { + beforeEach(async function () { + await expect( + lensHub.createProfile({ + to: userAddress, + handle: MOCK_PROFILE_HANDLE, + imageURI: MOCK_PROFILE_URI, + followModule: ZERO_ADDRESS, + followModuleData: [], + followNFTURI: MOCK_FOLLOW_NFT_URI, + }) + ).to.not.be.reverted; + }); + + context('Negatives', function () { + it('UserTwo should fail to set the default profile on profile owned by user 1', async function () { + await expect( + lensHub.connect(userTwo).setDefaultProfile(FIRST_PROFILE_ID) + ).to.be.revertedWith(ERRORS.NOT_PROFILE_OWNER); + }); + + it('UserTwo should fail to change the default profile for profile one', async function () { + await expect( + lensHub.connect(userTwo).setDefaultProfile(FIRST_PROFILE_ID) + ).to.be.revertedWith(ERRORS.NOT_PROFILE_OWNER); + }); + }); + + context('Scenarios', function () { + it('User should set the default profile', async function () { + await expect(lensHub.setDefaultProfile(FIRST_PROFILE_ID)).to.not.be.reverted; + expect(await lensHub.defaultProfile(lensHub.address)).to.eq(FIRST_PROFILE_ID); + }); + }); + }); +}); From 0cae03a0b6af138fb75461f8cd8f097c2c69e322 Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Tue, 1 Mar 2022 17:09:25 +0000 Subject: [PATCH 24/32] remove unset methods to bring down contract size --- contracts/core/LensHub.sol | 49 ++--------------------- contracts/core/storage/LensHubStorage.sol | 5 --- contracts/interfaces/ILensHub.sol | 18 +-------- contracts/libraries/Events.sol | 13 ------ 4 files changed, 5 insertions(+), 80 deletions(-) diff --git a/contracts/core/LensHub.sol b/contracts/core/LensHub.sol index 75d52f5..f4debfb 100644 --- a/contracts/core/LensHub.sol +++ b/contracts/core/LensHub.sol @@ -157,8 +157,9 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat } /// @inheritdoc ILensHub - function setDefaultProfile(uint256 profileId) external override whenNotPaused { - _setDefaultProfile(profileId, msg.sender); + function setDefaultProfile(uint256 profileId, address owner) external override whenNotPaused { + _validateCallerIsProfileOwner(profileId, msg.sender); + _setDefaultProfile(profileId, owner); } /// @inheritdoc ILensHub @@ -191,41 +192,6 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat _setDefaultProfile(vars.profileId, owner); } - /// @inheritdoc ILensHub - function unsetDefaultProfile(uint256 profileId) external override whenNotPaused { - _unsetDefaultProfile(profileId, msg.sender); - } - - /// @inheritdoc ILensHub - function unsetDefaultProfileWithSig(DataTypes.UnsetDefaultProfileWithSigData calldata vars) - external - override - whenNotPaused - { - address owner = ownerOf(vars.profileId); - bytes32 digest; - unchecked { - digest = keccak256( - abi.encodePacked( - '\x19\x01', - _calculateDomainSeparator(), - keccak256( - abi.encode( - UNSET_DEFAULT_PROFILE_WITH_SIG_TYPEHASH, - vars.profileId, - sigNonces[owner]++, - vars.sig.deadline - ) - ) - ) - ); - } - - _validateRecoveredAddress(digest, owner, vars.sig); - - _unsetDefaultProfile(vars.profileId, owner); - } - /// @inheritdoc ILensHub function setFollowModule( uint256 profileId, @@ -959,15 +925,6 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat emit Events.DefaultProfileSet(profileId, owner, block.timestamp); } - function _unsetDefaultProfile(uint256 profileId, address owner) internal { - _validateCallerIsProfileOwner(profileId, owner); - - _defaultProfileToAddress[profileId] = address(0); - _addressToDefaultProfile[owner] = 0; - - emit Events.DefaultProfileUnset(profileId, owner, block.timestamp); - } - function _createComment(DataTypes.CommentData memory vars) internal { PublishingLogic.createComment( vars, diff --git a/contracts/core/storage/LensHubStorage.sol b/contracts/core/storage/LensHubStorage.sol index 9df354a..3b77c68 100644 --- a/contracts/core/storage/LensHubStorage.sol +++ b/contracts/core/storage/LensHubStorage.sol @@ -15,11 +15,6 @@ contract LensHubStorage { // keccak256( // 'setDefaultProfileWithSig(uint256 profileId,uint256 nonce,uint256 deadline)' // ); - bytes32 internal constant UNSET_DEFAULT_PROFILE_WITH_SIG_TYPEHASH = - 0x2c0999aca14a6108b99d0483911154022cf8071dfe55ac3325ccd3e28e12a3c2; - // keccak256( - // 'unsetDefaultProfileWithSig(uint256 profileId,uint256 nonce,uint256 deadline)' - // ); bytes32 internal constant SET_FOLLOW_MODULE_WITH_SIG_TYPEHASH = 0x6f3f6455a608af1cc57ef3e5c0a49deeb88bba264ec8865b798ff07358859d4b; // keccak256( diff --git a/contracts/interfaces/ILensHub.sol b/contracts/interfaces/ILensHub.sol index ffb9dd2..0b126a4 100644 --- a/contracts/interfaces/ILensHub.sol +++ b/contracts/interfaces/ILensHub.sol @@ -103,8 +103,9 @@ interface ILensHub { * @notice Sets the mapping between wallet and its main profile identity * * @param profileId The token ID of the profile to set as the main profile identity + * @param owner The address of the wallet which owns this profileId */ - function setDefaultProfile(uint256 profileId) external; + function setDefaultProfile(uint256 profileId, address owner) external; /** * @notice Sets the mapping between wallet and its main profile identity via signature with the specified parameters. @@ -114,21 +115,6 @@ interface ILensHub { function setDefaultProfileWithSig(DataTypes.SetDefaultProfileWithSigData calldata vars) external; - /** - * @notice remove the mapping between wallet and its main profile identity - * - * @param profileId The token ID of the profile to set as the main profile identity - */ - function unsetDefaultProfile(uint256 profileId) external; - - /** - * @notice Unsets the mapping between wallet and its main profile identity via signature with the specified parameters. - * - * @param vars A UnsetDefaultProfileWithSigData struct, including the regular parameters and an EIP712Signature struct. - */ - function unsetDefaultProfileWithSig(DataTypes.UnsetDefaultProfileWithSigData calldata vars) - external; - /** * @notice Sets a profile's follow module, must be called by the profile owner. * diff --git a/contracts/libraries/Events.sol b/contracts/libraries/Events.sol index a7163fb..f5ebea9 100644 --- a/contracts/libraries/Events.sol +++ b/contracts/libraries/Events.sol @@ -147,19 +147,6 @@ library Events { */ event DefaultProfileSet(uint256 indexed profileId, address indexed wallet, uint256 timestamp); - /** - * @dev Emitted when a default profile is unset for a wallet as its main identity - * - * @param unsetProfileId The token ID of the profile for which the default profile is being unset from. - * @param wallet The wallet which owns this profile - * @param timestamp The current block timestamp. - */ - event DefaultProfileUnset( - uint256 indexed unsetProfileId, - address indexed wallet, - uint256 timestamp - ); - /** * @dev Emitted when a dispatcher is set for a specific profile. * From fb8d93bb57c2937d89ad3092263b9d22a11f0f0c Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Tue, 1 Mar 2022 17:19:32 +0000 Subject: [PATCH 25/32] allow dispatcher to work with `setDefaultProfile` --- contracts/core/LensHub.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contracts/core/LensHub.sol b/contracts/core/LensHub.sol index f4debfb..b2c50c5 100644 --- a/contracts/core/LensHub.sol +++ b/contracts/core/LensHub.sol @@ -158,7 +158,7 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat /// @inheritdoc ILensHub function setDefaultProfile(uint256 profileId, address owner) external override whenNotPaused { - _validateCallerIsProfileOwner(profileId, msg.sender); + _validateCallerIsProfileOwnerOrDispatcher(profileId); _setDefaultProfile(profileId, owner); } @@ -917,7 +917,10 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat } function _setDefaultProfile(uint256 profileId, address owner) internal { - _validateCallerIsProfileOwner(profileId, owner); + // you should only be able to map this to the owner OR dead address + if (owner != address(0)) { + _validateCallerIsProfileOwner(profileId, owner); + } _defaultProfileToAddress[profileId] = owner; _addressToDefaultProfile[owner] = profileId; From c38c6d1ea08ec74170c77792954fcd6403ce6821 Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Tue, 1 Mar 2022 17:22:08 +0000 Subject: [PATCH 26/32] rename `owner` > `wallet` --- contracts/core/LensHub.sol | 16 ++++++++-------- contracts/interfaces/ILensHub.sol | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/contracts/core/LensHub.sol b/contracts/core/LensHub.sol index b2c50c5..17fe8e5 100644 --- a/contracts/core/LensHub.sol +++ b/contracts/core/LensHub.sol @@ -157,9 +157,9 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat } /// @inheritdoc ILensHub - function setDefaultProfile(uint256 profileId, address owner) external override whenNotPaused { + function setDefaultProfile(uint256 profileId, address wallet) external override whenNotPaused { _validateCallerIsProfileOwnerOrDispatcher(profileId); - _setDefaultProfile(profileId, owner); + _setDefaultProfile(profileId, wallet); } /// @inheritdoc ILensHub @@ -916,16 +916,16 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat ); } - function _setDefaultProfile(uint256 profileId, address owner) internal { + function _setDefaultProfile(uint256 profileId, address wallet) internal { // you should only be able to map this to the owner OR dead address - if (owner != address(0)) { - _validateCallerIsProfileOwner(profileId, owner); + if (wallet != address(0)) { + _validateCallerIsProfileOwner(profileId, wallet); } - _defaultProfileToAddress[profileId] = owner; - _addressToDefaultProfile[owner] = profileId; + _defaultProfileToAddress[profileId] = wallet; + _addressToDefaultProfile[wallet] = profileId; - emit Events.DefaultProfileSet(profileId, owner, block.timestamp); + emit Events.DefaultProfileSet(profileId, wallet, block.timestamp); } function _createComment(DataTypes.CommentData memory vars) internal { diff --git a/contracts/interfaces/ILensHub.sol b/contracts/interfaces/ILensHub.sol index 0b126a4..a93c4cf 100644 --- a/contracts/interfaces/ILensHub.sol +++ b/contracts/interfaces/ILensHub.sol @@ -103,9 +103,9 @@ interface ILensHub { * @notice Sets the mapping between wallet and its main profile identity * * @param profileId The token ID of the profile to set as the main profile identity - * @param owner The address of the wallet which owns this profileId + * @param wallet The address of the wallet which owns this profileId */ - function setDefaultProfile(uint256 profileId, address owner) external; + function setDefaultProfile(uint256 profileId, address wallet) external; /** * @notice Sets the mapping between wallet and its main profile identity via signature with the specified parameters. From e7649a04aea9b6b4616f1f78bfd981259812a15f Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Wed, 2 Mar 2022 10:48:07 +0000 Subject: [PATCH 27/32] test: add unit tests --- contracts/core/LensHub.sol | 18 ++- contracts/core/storage/LensHubStorage.sol | 4 +- contracts/interfaces/ILensHub.sol | 2 +- contracts/libraries/DataTypes.sol | 2 + test/helpers/utils.ts | 51 +++++-- test/hub/profiles/default-profile.spec.ts | 168 +++++++++++++++++++++- 6 files changed, 215 insertions(+), 30 deletions(-) diff --git a/contracts/core/LensHub.sol b/contracts/core/LensHub.sol index 17fe8e5..1b61b10 100644 --- a/contracts/core/LensHub.sol +++ b/contracts/core/LensHub.sol @@ -179,6 +179,7 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat abi.encode( SET_DEFAULT_PROFILE_WITH_SIG_TYPEHASH, vars.profileId, + vars.wallet, sigNonces[owner]++, vars.sig.deadline ) @@ -189,7 +190,7 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat _validateRecoveredAddress(digest, owner, vars.sig); - _setDefaultProfile(vars.profileId, owner); + _setDefaultProfile(vars.profileId, vars.wallet); } /// @inheritdoc ILensHub @@ -920,12 +921,17 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat // you should only be able to map this to the owner OR dead address if (wallet != address(0)) { _validateCallerIsProfileOwner(profileId, wallet); + _addressToDefaultProfile[wallet] = profileId; + _defaultProfileToAddress[profileId] = wallet; + + emit Events.DefaultProfileSet(profileId, wallet, block.timestamp); + } else { + // unset the default + _addressToDefaultProfile[ownerOf(profileId)] = 0; + _defaultProfileToAddress[profileId] = wallet; + + emit Events.DefaultProfileSet(0, wallet, block.timestamp); } - - _defaultProfileToAddress[profileId] = wallet; - _addressToDefaultProfile[wallet] = profileId; - - emit Events.DefaultProfileSet(profileId, wallet, block.timestamp); } function _createComment(DataTypes.CommentData memory vars) internal { diff --git a/contracts/core/storage/LensHubStorage.sol b/contracts/core/storage/LensHubStorage.sol index 3b77c68..cad63b5 100644 --- a/contracts/core/storage/LensHubStorage.sol +++ b/contracts/core/storage/LensHubStorage.sol @@ -11,9 +11,9 @@ contract LensHubStorage { // 'CreateProfileWithSig(string handle,string uri,address followModule,bytes followModuleData,uint256 nonce,uint256 deadline)' // ); bytes32 internal constant SET_DEFAULT_PROFILE_WITH_SIG_TYPEHASH = - 0x76a7f2df5a2c73b8b0bbf095e1efae3cc5fb722c9a4000d53c90aa1848421fd5; + 0xae4d0f1a57c80ed196993d814b14f19b25a688b09b9cb0467c33d76e022c216f; // keccak256( - // 'setDefaultProfileWithSig(uint256 profileId,uint256 nonce,uint256 deadline)' + // 'SetDefaultProfileWithSig(uint256 profileId,address wallet,uint256 nonce,uint256 deadline)' // ); bytes32 internal constant SET_FOLLOW_MODULE_WITH_SIG_TYPEHASH = 0x6f3f6455a608af1cc57ef3e5c0a49deeb88bba264ec8865b798ff07358859d4b; diff --git a/contracts/interfaces/ILensHub.sol b/contracts/interfaces/ILensHub.sol index a93c4cf..0bc26eb 100644 --- a/contracts/interfaces/ILensHub.sol +++ b/contracts/interfaces/ILensHub.sol @@ -103,7 +103,7 @@ interface ILensHub { * @notice Sets the mapping between wallet and its main profile identity * * @param profileId The token ID of the profile to set as the main profile identity - * @param wallet The address of the wallet which owns this profileId + * @param wallet The address of the wallet which is either the owner of the profile or address(0) */ function setDefaultProfile(uint256 profileId, address wallet) external; diff --git a/contracts/libraries/DataTypes.sol b/contracts/libraries/DataTypes.sol index 6090def..5692f6e 100644 --- a/contracts/libraries/DataTypes.sol +++ b/contracts/libraries/DataTypes.sol @@ -114,10 +114,12 @@ library DataTypes { * the same as the regular `setDefaultProfile()` function, with an added EIP712Signature. * * @param profileId The token ID of the profile which will be set as default + * @param wallet The address of the wallet which is either the owner of the profile or address(0) * @param sig The EIP712Signature struct containing the profile owner's signature. */ struct SetDefaultProfileWithSigData { uint256 profileId; + address wallet; EIP712Signature sig; } diff --git a/test/helpers/utils.ts b/test/helpers/utils.ts index fcfbbea..ac5ed86 100644 --- a/test/helpers/utils.ts +++ b/test/helpers/utils.ts @@ -1,22 +1,12 @@ +import { TransactionReceipt, TransactionResponse } from '@ethersproject/providers'; import '@nomiclabs/hardhat-ethers'; -import { - BigNumberish, - Bytes, - Event, - logger, - utils, - BigNumber, - Contract, - ContractReceipt, -} from 'ethers'; -import { TransactionReceipt } from '@ethersproject/providers'; +import { expect } from 'chai'; +import { BigNumber, BigNumberish, Bytes, Contract, logger, utils } from 'ethers'; import { hexlify, keccak256, RLP, toUtf8Bytes } from 'ethers/lib/utils'; -import { TransactionResponse } from '@ethersproject/providers'; import hre from 'hardhat'; import { LensHub__factory } from '../../typechain-types'; -import { lensHub, LENS_HUB_NFT_NAME, helper, testWallet, eventsLib } from '../__setup.spec'; +import { eventsLib, helper, lensHub, LENS_HUB_NFT_NAME, testWallet } from '../__setup.spec'; import { HARDHAT_CHAINID, MAX_UINT256 } from './constants'; -import { expect } from 'chai'; export enum ProtocolState { Unpaused, @@ -327,6 +317,16 @@ export async function getSetProfileImageURIWithSigParts( return await getSig(msgParams); } +export async function getSetDefaultProfileWithSigParts( + profileId: BigNumberish, + wallet: string, + nonce: number, + deadline: string +): Promise<{ v: number; r: string; s: string }> { + const msgParams = buildSetDefaultProfileWithSigParams(profileId, wallet, nonce, deadline); + return await getSig(msgParams); +} + export async function getSetFollowNFTURIWithSigParts( profileId: BigNumberish, followNFTURI: string, @@ -592,6 +592,29 @@ const buildSetProfileImageURIWithSigParams = ( }, }); +const buildSetDefaultProfileWithSigParams = ( + profileId: BigNumberish, + wallet: string, + nonce: number, + deadline: string +) => ({ + types: { + SetDefaultProfileWithSig: [ + { name: 'profileId', type: 'uint256' }, + { name: 'wallet', type: 'address' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + ], + }, + domain: domain(), + value: { + profileId: profileId, + wallet: wallet, + nonce: nonce, + deadline: deadline, + }, +}); + const buildSetFollowNFTURIWithSigParams = ( profileId: BigNumberish, followNFTURI: string, diff --git a/test/hub/profiles/default-profile.spec.ts b/test/hub/profiles/default-profile.spec.ts index ff8f656..5c7977f 100644 --- a/test/hub/profiles/default-profile.spec.ts +++ b/test/hub/profiles/default-profile.spec.ts @@ -1,7 +1,8 @@ import '@nomiclabs/hardhat-ethers'; import { expect } from 'chai'; -import { ZERO_ADDRESS } from '../../helpers/constants'; +import { MAX_UINT256, ZERO_ADDRESS } from '../../helpers/constants'; import { ERRORS } from '../../helpers/errors'; +import { cancelWithPermitForAll, getSetDefaultProfileWithSigParts } from '../../helpers/utils'; import { FIRST_PROFILE_ID, lensHub, @@ -9,8 +10,10 @@ import { MOCK_FOLLOW_NFT_URI, MOCK_PROFILE_HANDLE, MOCK_PROFILE_URI, + testWallet, userAddress, userTwo, + userTwoAddress, } from '../../__setup.spec'; makeSuiteCleanRoom('Default profile Functionality', function () { @@ -31,21 +34,172 @@ makeSuiteCleanRoom('Default profile Functionality', function () { context('Negatives', function () { it('UserTwo should fail to set the default profile on profile owned by user 1', async function () { await expect( - lensHub.connect(userTwo).setDefaultProfile(FIRST_PROFILE_ID) - ).to.be.revertedWith(ERRORS.NOT_PROFILE_OWNER); + lensHub.connect(userTwo).setDefaultProfile(FIRST_PROFILE_ID, userTwoAddress) + ).to.be.revertedWith(ERRORS.NOT_PROFILE_OWNER_OR_DISPATCHER); }); - it('UserTwo should fail to change the default profile for profile one', async function () { + it('UserOne should fail to change the default profile for address that doesnt own the profile', async function () { await expect( - lensHub.connect(userTwo).setDefaultProfile(FIRST_PROFILE_ID) + lensHub.setDefaultProfile(FIRST_PROFILE_ID, userTwoAddress) ).to.be.revertedWith(ERRORS.NOT_PROFILE_OWNER); }); }); context('Scenarios', function () { it('User should set the default profile', async function () { - await expect(lensHub.setDefaultProfile(FIRST_PROFILE_ID)).to.not.be.reverted; - expect(await lensHub.defaultProfile(lensHub.address)).to.eq(FIRST_PROFILE_ID); + await expect(lensHub.setDefaultProfile(FIRST_PROFILE_ID, userAddress)).to.not.be.reverted; + expect((await lensHub.defaultProfile(userAddress)).toNumber()).to.eq(FIRST_PROFILE_ID); + }); + + it('User should set the default profile and then be able to unset it', async function () { + await expect(lensHub.setDefaultProfile(FIRST_PROFILE_ID, userAddress)).to.not.be.reverted; + expect((await lensHub.defaultProfile(userAddress)).toNumber()).to.eq(FIRST_PROFILE_ID); + + await expect(lensHub.setDefaultProfile(FIRST_PROFILE_ID, ZERO_ADDRESS)).to.not.be.reverted; + expect((await lensHub.defaultProfile(userAddress)).toNumber()).to.eq(0); + }); + }); + }); + + context('Meta-tx', function () { + beforeEach(async function () { + await expect( + lensHub.connect(testWallet).createProfile({ + to: testWallet.address, + handle: MOCK_PROFILE_HANDLE, + imageURI: MOCK_PROFILE_URI, + followModule: ZERO_ADDRESS, + followModuleData: [], + followNFTURI: MOCK_FOLLOW_NFT_URI, + }) + ).to.not.be.reverted; + }); + + context('Negatives', function () { + it('TestWallet should fail to set default profile with sig with signature deadline mismatch', async function () { + const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber(); + const { v, r, s } = await getSetDefaultProfileWithSigParts( + FIRST_PROFILE_ID, + testWallet.address, + nonce, + '0' + ); + + await expect( + lensHub.setDefaultProfileWithSig({ + profileId: FIRST_PROFILE_ID, + wallet: testWallet.address, + sig: { + v, + r, + s, + deadline: MAX_UINT256, + }, + }) + ).to.be.revertedWith(ERRORS.SIGNATURE_INVALID); + }); + + it('TestWallet should fail to set default profile with sig with invalid deadline', async function () { + const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber(); + const { v, r, s } = await getSetDefaultProfileWithSigParts( + FIRST_PROFILE_ID, + testWallet.address, + nonce, + '0' + ); + + await expect( + lensHub.setDefaultProfileWithSig({ + profileId: FIRST_PROFILE_ID, + wallet: testWallet.address, + sig: { + v, + r, + s, + deadline: '0', + }, + }) + ).to.be.revertedWith(ERRORS.SIGNATURE_EXPIRED); + }); + + it('TestWallet should fail to set default profile with sig with invalid nonce', async function () { + const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber(); + const { v, r, s } = await getSetDefaultProfileWithSigParts( + FIRST_PROFILE_ID, + testWallet.address, + nonce + 1, + MAX_UINT256 + ); + + await expect( + lensHub.setDefaultProfileWithSig({ + profileId: FIRST_PROFILE_ID, + wallet: testWallet.address, + sig: { + v, + r, + s, + deadline: MAX_UINT256, + }, + }) + ).to.be.revertedWith(ERRORS.SIGNATURE_INVALID); + }); + + it('TestWallet should sign attempt to set default profile with sig, cancel with empty permitForAll, then fail to set default profile with sig', async function () { + const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber(); + const { v, r, s } = await getSetDefaultProfileWithSigParts( + FIRST_PROFILE_ID, + testWallet.address, + nonce, + MAX_UINT256 + ); + + await cancelWithPermitForAll(); + + await expect( + lensHub.setDefaultProfileWithSig({ + profileId: FIRST_PROFILE_ID, + wallet: testWallet.address, + sig: { + v, + r, + s, + deadline: MAX_UINT256, + }, + }) + ).to.be.revertedWith(ERRORS.SIGNATURE_INVALID); + }); + }); + + context('Scenarios', function () { + it('TestWallet should set the default profile with sig', async function () { + const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber(); + const { v, r, s } = await getSetDefaultProfileWithSigParts( + FIRST_PROFILE_ID, + testWallet.address, + nonce, + MAX_UINT256 + ); + + const defaultProfileBeforeUse = await lensHub.defaultProfile(testWallet.address); + + await expect( + lensHub.setDefaultProfileWithSig({ + profileId: FIRST_PROFILE_ID, + wallet: testWallet.address, + sig: { + v, + r, + s, + deadline: MAX_UINT256, + }, + }) + ).to.not.be.reverted; + + const defaultProfileAfter = await lensHub.defaultProfile(testWallet.address); + + expect(defaultProfileBeforeUse.toNumber()).to.eq(0); + expect(defaultProfileAfter.toNumber()).to.eq(FIRST_PROFILE_ID); }); }); }); From 87f9fe38a3596953209935297ed8fa7e729fe2c2 Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Wed, 2 Mar 2022 10:50:17 +0000 Subject: [PATCH 28/32] remove `UnsetDefaultProfileWithSigData` --- contracts/libraries/DataTypes.sol | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/contracts/libraries/DataTypes.sol b/contracts/libraries/DataTypes.sol index 5692f6e..7316215 100644 --- a/contracts/libraries/DataTypes.sol +++ b/contracts/libraries/DataTypes.sol @@ -123,18 +123,6 @@ library DataTypes { EIP712Signature sig; } - /** - * @notice A struct containing the parameters required for the `unsetDefaultProfileWithSig()` function. Parameters are - * the same as the regular `unsetDefaultProfile()` function, with an added EIP712Signature. - * - * @param profileId The token ID of the profile which will be unset as default - * @param sig The EIP712Signature struct containing the profile owner's signature. - */ - struct UnsetDefaultProfileWithSigData { - uint256 profileId; - EIP712Signature sig; - } - /** * @notice A struct containing the parameters required for the `setFollowModuleWithSig()` function. Parameters are * the same as the regular `setFollowModule()` function, with an added EIP712Signature. From 7b0966b242f3697489b2bc4e951af4f0e6f756bc Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Wed, 2 Mar 2022 13:00:47 +0000 Subject: [PATCH 29/32] fix storage naming --- contracts/core/LensHub.sol | 14 +++++++------- contracts/core/storage/LensHubStorage.sol | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/contracts/core/LensHub.sol b/contracts/core/LensHub.sol index 1b61b10..00aedb9 100644 --- a/contracts/core/LensHub.sol +++ b/contracts/core/LensHub.sol @@ -712,7 +712,7 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat /// @inheritdoc ILensHub function defaultProfile(address wallet) external view override returns (uint256) { - return _addressToDefaultProfile[wallet]; + return _defaultProfileByAddress[wallet]; } /// @inheritdoc ILensHub @@ -921,14 +921,14 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat // you should only be able to map this to the owner OR dead address if (wallet != address(0)) { _validateCallerIsProfileOwner(profileId, wallet); - _addressToDefaultProfile[wallet] = profileId; - _defaultProfileToAddress[profileId] = wallet; + _defaultProfileByAddress[wallet] = profileId; + _addressByDefaultProfile[profileId] = wallet; emit Events.DefaultProfileSet(profileId, wallet, block.timestamp); } else { // unset the default - _addressToDefaultProfile[ownerOf(profileId)] = 0; - _defaultProfileToAddress[profileId] = wallet; + _defaultProfileByAddress[ownerOf(profileId)] = 0; + _addressByDefaultProfile[profileId] = wallet; emit Events.DefaultProfileSet(0, wallet, block.timestamp); } @@ -995,8 +995,8 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat } if (from != address(0)) { - _defaultProfileToAddress[tokenId] = address(0); - _addressToDefaultProfile[from] = 0; + _addressByDefaultProfile[tokenId] = address(0); + _defaultProfileByAddress[from] = 0; } super._beforeTokenTransfer(from, to, tokenId); diff --git a/contracts/core/storage/LensHubStorage.sol b/contracts/core/storage/LensHubStorage.sol index cad63b5..9eb56cf 100644 --- a/contracts/core/storage/LensHubStorage.sol +++ b/contracts/core/storage/LensHubStorage.sol @@ -71,8 +71,8 @@ contract LensHubStorage { mapping(uint256 => DataTypes.ProfileStruct) internal _profileById; mapping(uint256 => mapping(uint256 => DataTypes.PublicationStruct)) internal _pubByIdByProfile; - mapping(uint256 => address) internal _defaultProfileToAddress; - mapping(address => uint256) internal _addressToDefaultProfile; + mapping(uint256 => address) internal _addressByDefaultProfile; + mapping(address => uint256) internal _defaultProfileByAddress; uint256 internal _profileCounter; address internal _governance; From ce392f53df6d6f039d63a14ce166621fd8e1fe57 Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Wed, 2 Mar 2022 13:07:08 +0000 Subject: [PATCH 30/32] `_validateCallerIsProfileOwner` > `_validateWalletIsProfileOwner` --- contracts/core/LensHub.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/core/LensHub.sol b/contracts/core/LensHub.sol index 00aedb9..64d8e73 100644 --- a/contracts/core/LensHub.sol +++ b/contracts/core/LensHub.sol @@ -920,7 +920,7 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat function _setDefaultProfile(uint256 profileId, address wallet) internal { // you should only be able to map this to the owner OR dead address if (wallet != address(0)) { - _validateCallerIsProfileOwner(profileId, wallet); + _validateWalletIsProfileOwner(profileId, wallet); _defaultProfileByAddress[wallet] = profileId; _addressByDefaultProfile[profileId] = wallet; @@ -1011,7 +1011,7 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat if (msg.sender != ownerOf(profileId)) revert Errors.NotProfileOwner(); } - function _validateCallerIsProfileOwner(uint256 profileId, address wallet) internal view { + function _validateWalletIsProfileOwner(uint256 profileId, address wallet) internal view { if (wallet != ownerOf(profileId)) revert Errors.NotProfileOwner(); } From bff5483ca2a37a0e2adb3b1417638a706f07b2cd Mon Sep 17 00:00:00 2001 From: Josh Stevens Date: Wed, 2 Mar 2022 16:17:52 +0000 Subject: [PATCH 31/32] add last bit of tests --- test/hub/profiles/default-profile.spec.ts | 146 ++++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/test/hub/profiles/default-profile.spec.ts b/test/hub/profiles/default-profile.spec.ts index 5c7977f..64147f8 100644 --- a/test/hub/profiles/default-profile.spec.ts +++ b/test/hub/profiles/default-profile.spec.ts @@ -58,6 +58,25 @@ makeSuiteCleanRoom('Default profile Functionality', function () { await expect(lensHub.setDefaultProfile(FIRST_PROFILE_ID, ZERO_ADDRESS)).to.not.be.reverted; expect((await lensHub.defaultProfile(userAddress)).toNumber()).to.eq(0); }); + + it('User should set the default profile and then be able to change it to another', async function () { + await expect(lensHub.setDefaultProfile(FIRST_PROFILE_ID, userAddress)).to.not.be.reverted; + expect((await lensHub.defaultProfile(userAddress)).toNumber()).to.eq(FIRST_PROFILE_ID); + + await expect( + lensHub.createProfile({ + to: userAddress, + handle: new Date().getTime().toString(), + imageURI: MOCK_PROFILE_URI, + followModule: ZERO_ADDRESS, + followModuleData: [], + followNFTURI: MOCK_FOLLOW_NFT_URI, + }) + ).to.not.be.reverted; + + await expect(lensHub.setDefaultProfile(2, userAddress)).to.not.be.reverted; + expect((await lensHub.defaultProfile(userAddress)).toNumber()).to.eq(2); + }); }); }); @@ -201,6 +220,133 @@ makeSuiteCleanRoom('Default profile Functionality', function () { expect(defaultProfileBeforeUse.toNumber()).to.eq(0); expect(defaultProfileAfter.toNumber()).to.eq(FIRST_PROFILE_ID); }); + + it('TestWallet should set the default profile with sig and then be able to unset it', async function () { + const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber(); + const { v, r, s } = await getSetDefaultProfileWithSigParts( + FIRST_PROFILE_ID, + testWallet.address, + nonce, + MAX_UINT256 + ); + + const defaultProfileBeforeUse = await lensHub.defaultProfile(testWallet.address); + + await expect( + lensHub.setDefaultProfileWithSig({ + profileId: FIRST_PROFILE_ID, + wallet: testWallet.address, + sig: { + v, + r, + s, + deadline: MAX_UINT256, + }, + }) + ).to.not.be.reverted; + + const defaultProfileAfter = await lensHub.defaultProfile(testWallet.address); + + expect(defaultProfileBeforeUse.toNumber()).to.eq(0); + expect(defaultProfileAfter.toNumber()).to.eq(FIRST_PROFILE_ID); + + const nonce2 = (await lensHub.sigNonces(testWallet.address)).toNumber(); + const signature2 = await getSetDefaultProfileWithSigParts( + FIRST_PROFILE_ID, + ZERO_ADDRESS, + nonce2, + MAX_UINT256 + ); + + const defaultProfileBeforeUse2 = await lensHub.defaultProfile(testWallet.address); + + await expect( + lensHub.setDefaultProfileWithSig({ + profileId: FIRST_PROFILE_ID, + wallet: ZERO_ADDRESS, + sig: { + v: signature2.v, + r: signature2.r, + s: signature2.s, + deadline: MAX_UINT256, + }, + }) + ).to.not.be.reverted; + + const defaultProfileAfter2 = await lensHub.defaultProfile(testWallet.address); + + expect(defaultProfileBeforeUse2.toNumber()).to.eq(1); + expect(defaultProfileAfter2.toNumber()).to.eq(0); + }); + + it('TestWallet should set the default profile and then be able to change it to another', async function () { + const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber(); + const { v, r, s } = await getSetDefaultProfileWithSigParts( + FIRST_PROFILE_ID, + testWallet.address, + nonce, + MAX_UINT256 + ); + + const defaultProfileBeforeUse = await lensHub.defaultProfile(testWallet.address); + + await expect( + lensHub.setDefaultProfileWithSig({ + profileId: FIRST_PROFILE_ID, + wallet: testWallet.address, + sig: { + v, + r, + s, + deadline: MAX_UINT256, + }, + }) + ).to.not.be.reverted; + + const defaultProfileAfter = await lensHub.defaultProfile(testWallet.address); + + expect(defaultProfileBeforeUse.toNumber()).to.eq(0); + expect(defaultProfileAfter.toNumber()).to.eq(FIRST_PROFILE_ID); + + await expect( + lensHub.createProfile({ + to: testWallet.address, + handle: new Date().getTime().toString(), + imageURI: MOCK_PROFILE_URI, + followModule: ZERO_ADDRESS, + followModuleData: [], + followNFTURI: MOCK_FOLLOW_NFT_URI, + }) + ).to.not.be.reverted; + + const nonce2 = (await lensHub.sigNonces(testWallet.address)).toNumber(); + const signature2 = await getSetDefaultProfileWithSigParts( + 2, + testWallet.address, + nonce2, + MAX_UINT256 + ); + + const defaultProfileBeforeUse2 = await lensHub.defaultProfile(testWallet.address); + + await expect( + lensHub.setDefaultProfileWithSig({ + profileId: 2, + wallet: testWallet.address, + sig: { + v: signature2.v, + r: signature2.r, + s: signature2.s, + deadline: MAX_UINT256, + }, + }) + ).to.not.be.reverted; + + const defaultProfileAfter2 = await lensHub.defaultProfile(testWallet.address); + + expect(defaultProfileBeforeUse2.toNumber()).to.eq(1); + expect(defaultProfileAfter2.toNumber()).to.eq(2); + }); }); }); }); From bf45fd19d2f625898ead96be7174fa61100d8f7a Mon Sep 17 00:00:00 2001 From: Vladimir Yumatov Date: Fri, 4 Mar 2022 21:08:39 +0300 Subject: [PATCH 32/32] Fix PR peding check problem (#43) --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef459b6..0543157 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,10 +1,13 @@ name: ci +concurrency: + group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}' + cancel-in-progress: true + on: push: branches: [main] pull_request: - branches: [main] jobs: compile_and_run_tests: