From 2521ef4a29f696aedad658860366ff15ea4ddabb Mon Sep 17 00:00:00 2001 From: vicnaum Date: Tue, 14 Nov 2023 09:26:06 +0100 Subject: [PATCH] misc: TokenURI contracts separated --- contracts/FollowNFT.sol | 4 +- contracts/base/LensGovernable.sol | 16 ++++ contracts/base/LensHubStorage.sol | 4 + contracts/base/LensProfiles.sol | 5 +- contracts/interfaces/IFollowTokenURI.sol | 11 +++ contracts/interfaces/IHandleTokenURI.sol | 11 +++ contracts/interfaces/ILensGovernable.sol | 30 +++++++ contracts/interfaces/ILensHandles.sol | 15 ++++ contracts/interfaces/IProfileTokenURI.sol | 7 ++ contracts/libraries/GovernanceLib.sol | 8 ++ contracts/libraries/StorageLib.sol | 26 ++++++ .../images => svgs}/Follow/FollowSVG.sol | 0 .../Handle/GintoNordFontSVG.sol | 0 .../images => svgs}/Handle/HandleSVG.sol | 0 .../images => svgs}/Profile/Body.sol | 0 .../Profile/Body/BodyHoodie.sol | 0 .../Profile/Body/BodyJacket.sol | 0 .../Profile/Body/BodyTShirt.sol | 0 .../Profile/Body/BodyTanktop.sol | 0 .../images => svgs}/Profile/Face.sol | 0 .../images => svgs}/Profile/Hands.sol | 0 .../images => svgs}/Profile/Head.sol | 0 .../images => svgs}/Profile/Headwear.sol | 0 .../Profile/Headwear/HeadwearBeanie.sol | 0 .../Profile/Headwear/HeadwearCrown.sol | 0 .../Profile/Headwear/HeadwearFloral.sol | 0 .../Profile/Headwear/HeadwearGlasses.sol | 0 .../Profile/Headwear/HeadwearHat.sol | 0 .../Profile/Headwear/HeadwearIcecream.sol | 0 .../Profile/Headwear/HeadwearLeafs.sol | 0 .../Profile/Headwear/HeadwearMushroom.sol | 0 .../Profile/Headwear/HeadwearNightcap.sol | 0 .../Profile/Headwear/HeadwearPartyhat.sol | 0 .../Profile/Headwear/HeadwearPlants.sol | 0 .../Profile/Headwear/HeadwearSparkles.sol | 0 .../images => svgs}/Profile/Helpers.sol | 0 .../images => svgs}/Profile/Legs.sol | 0 .../images => svgs}/Profile/Logo.sol | 0 .../images => svgs}/Profile/ProfileSVG.sol | 0 .../images => svgs}/Profile/Shoes.sol | 0 .../token-uris/FollowTokenURI.sol} | 7 +- .../token-uris/HandleTokenURI.sol} | 7 +- .../token-uris/ProfileTokenURI.sol} | 10 +-- contracts/namespaces/LensHandles.sol | 14 +++- script/{DeploySVG.s.sol => DeployCore.s.sol} | 2 +- script/DeployTokenURIs.s.sol | 81 +++++++++++++++++++ 46 files changed, 240 insertions(+), 18 deletions(-) create mode 100644 contracts/interfaces/IFollowTokenURI.sol create mode 100644 contracts/interfaces/IHandleTokenURI.sol create mode 100644 contracts/interfaces/IProfileTokenURI.sol rename contracts/libraries/{token-uris/images => svgs}/Follow/FollowSVG.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Handle/GintoNordFontSVG.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Handle/HandleSVG.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Body.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Body/BodyHoodie.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Body/BodyJacket.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Body/BodyTShirt.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Body/BodyTanktop.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Face.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Hands.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Head.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Headwear.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Headwear/HeadwearBeanie.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Headwear/HeadwearCrown.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Headwear/HeadwearFloral.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Headwear/HeadwearGlasses.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Headwear/HeadwearHat.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Headwear/HeadwearIcecream.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Headwear/HeadwearLeafs.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Headwear/HeadwearMushroom.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Headwear/HeadwearNightcap.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Headwear/HeadwearPartyhat.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Headwear/HeadwearPlants.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Headwear/HeadwearSparkles.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Helpers.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Legs.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Logo.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/ProfileSVG.sol (100%) rename contracts/libraries/{token-uris/images => svgs}/Profile/Shoes.sol (100%) rename contracts/{libraries/token-uris/FollowTokenURILib.sol => misc/token-uris/FollowTokenURI.sol} (87%) rename contracts/{libraries/token-uris/HandleTokenURILib.sol => misc/token-uris/HandleTokenURI.sol} (84%) rename contracts/{libraries/token-uris/ProfileTokenURILib.sol => misc/token-uris/ProfileTokenURI.sol} (80%) rename script/{DeploySVG.s.sol => DeployCore.s.sol} (99%) create mode 100644 script/DeployTokenURIs.s.sol diff --git a/contracts/FollowNFT.sol b/contracts/FollowNFT.sol index 7ae8d16..38f4119 100644 --- a/contracts/FollowNFT.sol +++ b/contracts/FollowNFT.sol @@ -13,8 +13,8 @@ import {ILensHub} from 'contracts/interfaces/ILensHub.sol'; import {LensBaseERC721} from 'contracts/base/LensBaseERC721.sol'; import {Strings} from '@openzeppelin/contracts/utils/Strings.sol'; import {StorageLib} from 'contracts/libraries/StorageLib.sol'; -import {FollowTokenURILib} from 'contracts/libraries/token-uris/FollowTokenURILib.sol'; import {Types} from 'contracts/libraries/constants/Types.sol'; +import {IFollowTokenURI} from 'contracts/interfaces/IFollowTokenURI.sol'; /** * @custom:upgradeable Beacon proxy. The beacon, responsible for returning the implementation address, is the LensHub. @@ -282,7 +282,7 @@ contract FollowNFT is HubRestricted, LensBaseERC721, ERC2981CollectionRoyalties, revert Errors.TokenDoesNotExist(); } return - FollowTokenURILib.getTokenURI( + IFollowTokenURI(ILensHub(HUB).getFollowTokenURIContract()).getTokenURI( followTokenId, _followedProfileId, _followDataByFollowTokenId[followTokenId].originalFollowTimestamp diff --git a/contracts/base/LensGovernable.sol b/contracts/base/LensGovernable.sol index ad7dd7c..04ae4e8 100644 --- a/contracts/base/LensGovernable.sol +++ b/contracts/base/LensGovernable.sol @@ -52,6 +52,14 @@ abstract contract LensGovernable is ILensGovernable { GovernanceLib.whitelistProfileCreator(profileCreator, whitelist); } + function setProfileTokenURIContract(address profileTokenURIContract) external override onlyGov { + GovernanceLib.setProfileTokenURIContract(profileTokenURIContract); + } + + function setFollowTokenURIContract(address followTokenURIContract) external override onlyGov { + GovernanceLib.setFollowTokenURIContract(followTokenURIContract); + } + /////////////////////////////////////////// /// EXTERNAL VIEW FUNCTIONS /// /////////////////////////////////////////// @@ -61,6 +69,14 @@ abstract contract LensGovernable is ILensGovernable { return StorageLib.getGovernance(); } + function getProfileTokenURIContract() external view override returns (address) { + return StorageLib.getProfileTokenURIContract(); + } + + function getFollowTokenURIContract() external view override returns (address) { + return StorageLib.getFollowTokenURIContract(); + } + /** * @notice Returns the current protocol state. * diff --git a/contracts/base/LensHubStorage.sol b/contracts/base/LensHubStorage.sol index 09ea01d..f78ab64 100644 --- a/contracts/base/LensHubStorage.sol +++ b/contracts/base/LensHubStorage.sol @@ -60,4 +60,8 @@ abstract contract LensHubStorage { mapping(address migrationAdmin => bool allowed) internal _migrationAdminWhitelisted; // Slot 29 Types.TreasuryData internal _treasuryData; // Slot 30 + + address internal _profileTokenURIContract; // Slot 31 + + address internal _followTokenURIContract; // Slot 32 } diff --git a/contracts/base/LensProfiles.sol b/contracts/base/LensProfiles.sol index 7173de2..9e547d1 100644 --- a/contracts/base/LensProfiles.sol +++ b/contracts/base/LensProfiles.sol @@ -11,8 +11,8 @@ import {IERC721Burnable} from 'contracts/interfaces/IERC721Burnable.sol'; import {LensBaseERC721} from 'contracts/base/LensBaseERC721.sol'; import {ProfileLib} from 'contracts/libraries/ProfileLib.sol'; import {StorageLib} from 'contracts/libraries/StorageLib.sol'; -import {ProfileTokenURILib} from 'contracts/libraries/token-uris/ProfileTokenURILib.sol'; import {ValidationLib} from 'contracts/libraries/ValidationLib.sol'; +import {IProfileTokenURI} from 'contracts/interfaces/IProfileTokenURI.sol'; import {ERC2981CollectionRoyalties} from 'contracts/base/ERC2981CollectionRoyalties.sol'; @@ -99,7 +99,8 @@ abstract contract LensProfiles is LensBaseERC721, ERC2981CollectionRoyalties, IL if (!_exists(tokenId)) { revert Errors.TokenDoesNotExist(); } - return ProfileTokenURILib.getTokenURI(tokenId); + uint256 mintTimestamp = StorageLib.getTokenData(tokenId).mintTimestamp; + return IProfileTokenURI(StorageLib.getProfileTokenURIContract()).getTokenURI(tokenId, mintTimestamp); } function approve(address to, uint256 tokenId) public override(LensBaseERC721, IERC721) { diff --git a/contracts/interfaces/IFollowTokenURI.sol b/contracts/interfaces/IFollowTokenURI.sol new file mode 100644 index 0000000..71f0049 --- /dev/null +++ b/contracts/interfaces/IFollowTokenURI.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.15; + +interface IFollowTokenURI { + function getTokenURI( + uint256 followTokenId, + uint256 followedProfileId, + uint256 originalFollowTimestamp + ) external pure returns (string memory); +} diff --git a/contracts/interfaces/IHandleTokenURI.sol b/contracts/interfaces/IHandleTokenURI.sol new file mode 100644 index 0000000..c2213cc --- /dev/null +++ b/contracts/interfaces/IHandleTokenURI.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.15; + +interface IHandleTokenURI { + function getTokenURI( + uint256 tokenId, + string memory localName, + string memory namespace + ) external view returns (string memory); +} diff --git a/contracts/interfaces/ILensGovernable.sol b/contracts/interfaces/ILensGovernable.sol index b30943f..79d808f 100644 --- a/contracts/interfaces/ILensGovernable.sol +++ b/contracts/interfaces/ILensGovernable.sol @@ -47,6 +47,22 @@ interface ILensGovernable { */ function whitelistProfileCreator(address profileCreator, bool whitelist) external; + /** + * @notice Sets the profile token URI contract. + * @custom:permissions Governance. + * + * @param profileTokenURIContract The profile token URI contract to set. + */ + function setProfileTokenURIContract(address profileTokenURIContract) external; + + /** + * @notice Sets the follow token URI contract. + * @custom:permissions Governance. + * + * @param followTokenURIContract The follow token URI contract to set. + */ + function setFollowTokenURIContract(address followTokenURIContract) external; + /** * @notice Sets the treasury address. * @custom:permissions Governance @@ -108,4 +124,18 @@ interface ILensGovernable { * @return tuple First, the treasury address, second, the treasury fee. */ function getTreasuryData() external view returns (address, uint16); + + /** + * @notice Gets the profile token URI contract. + * + * @return address The profile token URI contract. + */ + function getProfileTokenURIContract() external view returns (address); + + /** + * @notice Gets the follow token URI contract. + * + * @return address The follow token URI contract. + */ + function getFollowTokenURIContract() external view returns (address); } diff --git a/contracts/interfaces/ILensHandles.sol b/contracts/interfaces/ILensHandles.sol index 1315557..8942605 100644 --- a/contracts/interfaces/ILensHandles.sol +++ b/contracts/interfaces/ILensHandles.sol @@ -63,6 +63,21 @@ interface ILensHandles is IERC721 { */ function totalSupply() external view returns (uint256); + /** + * @notice Returns the HandleTokenURI contract address. + * + * @return address The HandleTokenURI contract address. + */ + function getHandleTokenURIContract() external view returns (address); + + /** + * @notice Sets the HandleTokenURI contract address. + * @custom:permissions Only LensHandles contract's owner + * + * @param handleTokenURIContract The HandleTokenURI contract address to set. + */ + function setHandleTokenURIContract(address handleTokenURIContract) external; + /** * @notice DANGER: Triggers disabling the profile protection mechanism for the msg.sender, which will allow * transfers or approvals over profiles held by it. diff --git a/contracts/interfaces/IProfileTokenURI.sol b/contracts/interfaces/IProfileTokenURI.sol new file mode 100644 index 0000000..877adad --- /dev/null +++ b/contracts/interfaces/IProfileTokenURI.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.15; + +interface IProfileTokenURI { + function getTokenURI(uint256 profileId, uint256 mintTimestamp) external pure returns (string memory); +} diff --git a/contracts/libraries/GovernanceLib.sol b/contracts/libraries/GovernanceLib.sol index 8ee516a..20e2bb7 100644 --- a/contracts/libraries/GovernanceLib.sol +++ b/contracts/libraries/GovernanceLib.sol @@ -100,4 +100,12 @@ library GovernanceLib { emit Events.TreasuryFeeSet(prevTreasuryFee, newTreasuryFee, block.timestamp); } + + function setProfileTokenURIContract(address profileTokenURIContract) external { + StorageLib.setProfileTokenURIContract(profileTokenURIContract); + } + + function setFollowTokenURIContract(address followTokenURIContract) external { + StorageLib.setFollowTokenURIContract(followTokenURIContract); + } } diff --git a/contracts/libraries/StorageLib.sol b/contracts/libraries/StorageLib.sol index 200340f..1d02ad1 100644 --- a/contracts/libraries/StorageLib.sol +++ b/contracts/libraries/StorageLib.sol @@ -43,6 +43,8 @@ library StorageLib { uint256 constant PROFILE_ROYALTIES_BPS_SLOT = 28; uint256 constant MIGRATION_ADMINS_WHITELISTED_MAPPING_SLOT = 29; uint256 constant TREASURY_DATA_SLOT = 30; + uint256 constant PROFILE_TOKEN_URI_CONTRACT_SLOT = 31; + uint256 constant FOLLOW_TOKEN_URI_CONTRACT_SLOT = 32; function getPublication( uint256 profileId, @@ -211,4 +213,28 @@ library StorageLib { _treasuryData.slot := TREASURY_DATA_SLOT } } + + function setProfileTokenURIContract(address profileTokenURIContract) internal { + assembly { + sstore(PROFILE_TOKEN_URI_CONTRACT_SLOT, profileTokenURIContract) + } + } + + function setFollowTokenURIContract(address followTokenURIContract) internal { + assembly { + sstore(FOLLOW_TOKEN_URI_CONTRACT_SLOT, followTokenURIContract) + } + } + + function getProfileTokenURIContract() internal view returns (address _profileTokenURIContract) { + assembly { + _profileTokenURIContract := sload(PROFILE_TOKEN_URI_CONTRACT_SLOT) + } + } + + function getFollowTokenURIContract() internal view returns (address _followTokenURIContract) { + assembly { + _followTokenURIContract := sload(FOLLOW_TOKEN_URI_CONTRACT_SLOT) + } + } } diff --git a/contracts/libraries/token-uris/images/Follow/FollowSVG.sol b/contracts/libraries/svgs/Follow/FollowSVG.sol similarity index 100% rename from contracts/libraries/token-uris/images/Follow/FollowSVG.sol rename to contracts/libraries/svgs/Follow/FollowSVG.sol diff --git a/contracts/libraries/token-uris/images/Handle/GintoNordFontSVG.sol b/contracts/libraries/svgs/Handle/GintoNordFontSVG.sol similarity index 100% rename from contracts/libraries/token-uris/images/Handle/GintoNordFontSVG.sol rename to contracts/libraries/svgs/Handle/GintoNordFontSVG.sol diff --git a/contracts/libraries/token-uris/images/Handle/HandleSVG.sol b/contracts/libraries/svgs/Handle/HandleSVG.sol similarity index 100% rename from contracts/libraries/token-uris/images/Handle/HandleSVG.sol rename to contracts/libraries/svgs/Handle/HandleSVG.sol diff --git a/contracts/libraries/token-uris/images/Profile/Body.sol b/contracts/libraries/svgs/Profile/Body.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Body.sol rename to contracts/libraries/svgs/Profile/Body.sol diff --git a/contracts/libraries/token-uris/images/Profile/Body/BodyHoodie.sol b/contracts/libraries/svgs/Profile/Body/BodyHoodie.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Body/BodyHoodie.sol rename to contracts/libraries/svgs/Profile/Body/BodyHoodie.sol diff --git a/contracts/libraries/token-uris/images/Profile/Body/BodyJacket.sol b/contracts/libraries/svgs/Profile/Body/BodyJacket.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Body/BodyJacket.sol rename to contracts/libraries/svgs/Profile/Body/BodyJacket.sol diff --git a/contracts/libraries/token-uris/images/Profile/Body/BodyTShirt.sol b/contracts/libraries/svgs/Profile/Body/BodyTShirt.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Body/BodyTShirt.sol rename to contracts/libraries/svgs/Profile/Body/BodyTShirt.sol diff --git a/contracts/libraries/token-uris/images/Profile/Body/BodyTanktop.sol b/contracts/libraries/svgs/Profile/Body/BodyTanktop.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Body/BodyTanktop.sol rename to contracts/libraries/svgs/Profile/Body/BodyTanktop.sol diff --git a/contracts/libraries/token-uris/images/Profile/Face.sol b/contracts/libraries/svgs/Profile/Face.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Face.sol rename to contracts/libraries/svgs/Profile/Face.sol diff --git a/contracts/libraries/token-uris/images/Profile/Hands.sol b/contracts/libraries/svgs/Profile/Hands.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Hands.sol rename to contracts/libraries/svgs/Profile/Hands.sol diff --git a/contracts/libraries/token-uris/images/Profile/Head.sol b/contracts/libraries/svgs/Profile/Head.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Head.sol rename to contracts/libraries/svgs/Profile/Head.sol diff --git a/contracts/libraries/token-uris/images/Profile/Headwear.sol b/contracts/libraries/svgs/Profile/Headwear.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Headwear.sol rename to contracts/libraries/svgs/Profile/Headwear.sol diff --git a/contracts/libraries/token-uris/images/Profile/Headwear/HeadwearBeanie.sol b/contracts/libraries/svgs/Profile/Headwear/HeadwearBeanie.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Headwear/HeadwearBeanie.sol rename to contracts/libraries/svgs/Profile/Headwear/HeadwearBeanie.sol diff --git a/contracts/libraries/token-uris/images/Profile/Headwear/HeadwearCrown.sol b/contracts/libraries/svgs/Profile/Headwear/HeadwearCrown.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Headwear/HeadwearCrown.sol rename to contracts/libraries/svgs/Profile/Headwear/HeadwearCrown.sol diff --git a/contracts/libraries/token-uris/images/Profile/Headwear/HeadwearFloral.sol b/contracts/libraries/svgs/Profile/Headwear/HeadwearFloral.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Headwear/HeadwearFloral.sol rename to contracts/libraries/svgs/Profile/Headwear/HeadwearFloral.sol diff --git a/contracts/libraries/token-uris/images/Profile/Headwear/HeadwearGlasses.sol b/contracts/libraries/svgs/Profile/Headwear/HeadwearGlasses.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Headwear/HeadwearGlasses.sol rename to contracts/libraries/svgs/Profile/Headwear/HeadwearGlasses.sol diff --git a/contracts/libraries/token-uris/images/Profile/Headwear/HeadwearHat.sol b/contracts/libraries/svgs/Profile/Headwear/HeadwearHat.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Headwear/HeadwearHat.sol rename to contracts/libraries/svgs/Profile/Headwear/HeadwearHat.sol diff --git a/contracts/libraries/token-uris/images/Profile/Headwear/HeadwearIcecream.sol b/contracts/libraries/svgs/Profile/Headwear/HeadwearIcecream.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Headwear/HeadwearIcecream.sol rename to contracts/libraries/svgs/Profile/Headwear/HeadwearIcecream.sol diff --git a/contracts/libraries/token-uris/images/Profile/Headwear/HeadwearLeafs.sol b/contracts/libraries/svgs/Profile/Headwear/HeadwearLeafs.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Headwear/HeadwearLeafs.sol rename to contracts/libraries/svgs/Profile/Headwear/HeadwearLeafs.sol diff --git a/contracts/libraries/token-uris/images/Profile/Headwear/HeadwearMushroom.sol b/contracts/libraries/svgs/Profile/Headwear/HeadwearMushroom.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Headwear/HeadwearMushroom.sol rename to contracts/libraries/svgs/Profile/Headwear/HeadwearMushroom.sol diff --git a/contracts/libraries/token-uris/images/Profile/Headwear/HeadwearNightcap.sol b/contracts/libraries/svgs/Profile/Headwear/HeadwearNightcap.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Headwear/HeadwearNightcap.sol rename to contracts/libraries/svgs/Profile/Headwear/HeadwearNightcap.sol diff --git a/contracts/libraries/token-uris/images/Profile/Headwear/HeadwearPartyhat.sol b/contracts/libraries/svgs/Profile/Headwear/HeadwearPartyhat.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Headwear/HeadwearPartyhat.sol rename to contracts/libraries/svgs/Profile/Headwear/HeadwearPartyhat.sol diff --git a/contracts/libraries/token-uris/images/Profile/Headwear/HeadwearPlants.sol b/contracts/libraries/svgs/Profile/Headwear/HeadwearPlants.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Headwear/HeadwearPlants.sol rename to contracts/libraries/svgs/Profile/Headwear/HeadwearPlants.sol diff --git a/contracts/libraries/token-uris/images/Profile/Headwear/HeadwearSparkles.sol b/contracts/libraries/svgs/Profile/Headwear/HeadwearSparkles.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Headwear/HeadwearSparkles.sol rename to contracts/libraries/svgs/Profile/Headwear/HeadwearSparkles.sol diff --git a/contracts/libraries/token-uris/images/Profile/Helpers.sol b/contracts/libraries/svgs/Profile/Helpers.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Helpers.sol rename to contracts/libraries/svgs/Profile/Helpers.sol diff --git a/contracts/libraries/token-uris/images/Profile/Legs.sol b/contracts/libraries/svgs/Profile/Legs.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Legs.sol rename to contracts/libraries/svgs/Profile/Legs.sol diff --git a/contracts/libraries/token-uris/images/Profile/Logo.sol b/contracts/libraries/svgs/Profile/Logo.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Logo.sol rename to contracts/libraries/svgs/Profile/Logo.sol diff --git a/contracts/libraries/token-uris/images/Profile/ProfileSVG.sol b/contracts/libraries/svgs/Profile/ProfileSVG.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/ProfileSVG.sol rename to contracts/libraries/svgs/Profile/ProfileSVG.sol diff --git a/contracts/libraries/token-uris/images/Profile/Shoes.sol b/contracts/libraries/svgs/Profile/Shoes.sol similarity index 100% rename from contracts/libraries/token-uris/images/Profile/Shoes.sol rename to contracts/libraries/svgs/Profile/Shoes.sol diff --git a/contracts/libraries/token-uris/FollowTokenURILib.sol b/contracts/misc/token-uris/FollowTokenURI.sol similarity index 87% rename from contracts/libraries/token-uris/FollowTokenURILib.sol rename to contracts/misc/token-uris/FollowTokenURI.sol index ac0d3e8..e93e7ea 100644 --- a/contracts/libraries/token-uris/FollowTokenURILib.sol +++ b/contracts/misc/token-uris/FollowTokenURI.sol @@ -4,9 +4,10 @@ pragma solidity ^0.8.15; import {Base64} from '@openzeppelin/contracts/utils/Base64.sol'; import {Strings} from '@openzeppelin/contracts/utils/Strings.sol'; -import {FollowSVG} from 'contracts/libraries/token-uris/images/Follow/FollowSVG.sol'; +import {FollowSVG} from 'contracts/libraries/svgs/Follow/FollowSVG.sol'; +import {IFollowTokenURI} from 'contracts/interfaces/IFollowTokenURI.sol'; -library FollowTokenURILib { +contract FollowTokenURI is IFollowTokenURI { using Strings for uint96; using Strings for uint256; @@ -14,7 +15,7 @@ library FollowTokenURILib { uint256 followTokenId, uint256 followedProfileId, uint256 originalFollowTimestamp - ) external pure returns (string memory) { + ) external pure override returns (string memory) { string memory followTokenIdAsString = followTokenId.toString(); string memory followedProfileIdAsString = followedProfileId.toString(); return diff --git a/contracts/libraries/token-uris/HandleTokenURILib.sol b/contracts/misc/token-uris/HandleTokenURI.sol similarity index 84% rename from contracts/libraries/token-uris/HandleTokenURILib.sol rename to contracts/misc/token-uris/HandleTokenURI.sol index 8fbf5ef..6eacf0f 100644 --- a/contracts/libraries/token-uris/HandleTokenURILib.sol +++ b/contracts/misc/token-uris/HandleTokenURI.sol @@ -4,16 +4,17 @@ pragma solidity ^0.8.15; import {Base64} from '@openzeppelin/contracts/utils/Base64.sol'; import {Strings} from '@openzeppelin/contracts/utils/Strings.sol'; -import {HandleSVG} from 'contracts/libraries/token-uris/images/Handle/HandleSVG.sol'; +import {HandleSVG} from 'contracts/libraries/svgs/Handle/HandleSVG.sol'; +import {IHandleTokenURI} from 'contracts/interfaces/IHandleTokenURI.sol'; -library HandleTokenURILib { +contract HandleTokenURI is IHandleTokenURI { using Strings for uint256; function getTokenURI( uint256 tokenId, string memory localName, string memory namespace - ) external pure returns (string memory) { + ) external pure override returns (string memory) { return string.concat( 'data:application/json;base64,', diff --git a/contracts/libraries/token-uris/ProfileTokenURILib.sol b/contracts/misc/token-uris/ProfileTokenURI.sol similarity index 80% rename from contracts/libraries/token-uris/ProfileTokenURILib.sol rename to contracts/misc/token-uris/ProfileTokenURI.sol index 845916a..52194fc 100644 --- a/contracts/libraries/token-uris/ProfileTokenURILib.sol +++ b/contracts/misc/token-uris/ProfileTokenURI.sol @@ -4,14 +4,14 @@ pragma solidity ^0.8.15; import {Base64} from '@openzeppelin/contracts/utils/Base64.sol'; import {Strings} from '@openzeppelin/contracts/utils/Strings.sol'; -import {StorageLib} from 'contracts/libraries/StorageLib.sol'; -import {ProfileSVG} from 'contracts/libraries/token-uris/images/Profile/ProfileSVG.sol'; +import {ProfileSVG} from 'contracts/libraries/svgs/Profile/ProfileSVG.sol'; +import {IProfileTokenURI} from 'contracts/interfaces/IProfileTokenURI.sol'; -library ProfileTokenURILib { +contract ProfileTokenURI is IProfileTokenURI { using Strings for uint96; using Strings for uint256; - function getTokenURI(uint256 profileId) external view returns (string memory) { + function getTokenURI(uint256 profileId, uint256 mintTimestamp) external pure override returns (string memory) { string memory profileIdAsString = profileId.toString(); return string( @@ -32,7 +32,7 @@ library ProfileTokenURILib { '"},{"trait_type":"DIGITS","value":"', bytes(profileIdAsString).length.toString(), '"},{"trait_type":"MINTED AT","value":"', - StorageLib.getTokenData(profileId).mintTimestamp.toString(), + mintTimestamp.toString(), '"}]}' ) ) diff --git a/contracts/namespaces/LensHandles.sol b/contracts/namespaces/LensHandles.sol index ff16106..51f109e 100644 --- a/contracts/namespaces/LensHandles.sol +++ b/contracts/namespaces/LensHandles.sol @@ -7,7 +7,7 @@ import {ImmutableOwnable} from 'contracts/misc/ImmutableOwnable.sol'; import {ILensHandles} from 'contracts/interfaces/ILensHandles.sol'; import {HandlesEvents} from 'contracts/namespaces/constants/Events.sol'; import {HandlesErrors} from 'contracts/namespaces/constants/Errors.sol'; -import {HandleTokenURILib} from 'contracts/libraries/token-uris/HandleTokenURILib.sol'; +import {IHandleTokenURI} from 'contracts/interfaces/IHandleTokenURI.sol'; import {ILensHub} from 'contracts/interfaces/ILensHub.sol'; import {Address} from '@openzeppelin/contracts/utils/Address.sol'; import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol'; @@ -44,6 +44,8 @@ contract LensHandles is ERC721, ERC2981CollectionRoyalties, ImmutableOwnable, IL mapping(uint256 tokenId => string localName) internal _localNames; + address internal _handleTokenURIContract; + modifier onlyOwnerOrWhitelistedProfileCreator() { if (msg.sender != OWNER && !ILensHub(LENS_HUB).isProfileCreatorWhitelisted(msg.sender)) { revert HandlesErrors.NotOwnerNorWhitelisted(); @@ -85,12 +87,20 @@ contract LensHandles is ERC721, ERC2981CollectionRoyalties, ImmutableOwnable, IL return _totalSupply; } + function setHandleTokenURIContract(address handleTokenURIContract) external override onlyOwner { + _handleTokenURIContract = handleTokenURIContract; + } + + function getHandleTokenURIContract() external view override returns (address) { + return _handleTokenURIContract; + } + /** * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) public view override returns (string memory) { _requireMinted(tokenId); - return HandleTokenURILib.getTokenURI(tokenId, _localNames[tokenId], NAMESPACE); + return IHandleTokenURI(_handleTokenURIContract).getTokenURI(tokenId, _localNames[tokenId], NAMESPACE); } /// @inheritdoc ILensHandles diff --git a/script/DeploySVG.s.sol b/script/DeployCore.s.sol similarity index 99% rename from script/DeploySVG.s.sol rename to script/DeployCore.s.sol index 5ac7980..730b527 100644 --- a/script/DeploySVG.s.sol +++ b/script/DeployCore.s.sol @@ -10,7 +10,7 @@ import {Types} from 'contracts/libraries/constants/Types.sol'; import {Governance} from 'contracts/misc/access/Governance.sol'; import {LensHandles} from 'contracts/namespaces/LensHandles.sol'; -contract DeploySVG is Script, ForkManagement { +contract DeployCore is Script, ForkManagement { using stdJson for string; struct LensAccount { diff --git a/script/DeployTokenURIs.s.sol b/script/DeployTokenURIs.s.sol new file mode 100644 index 0000000..ebde71e --- /dev/null +++ b/script/DeployTokenURIs.s.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {ForkManagement} from 'script/helpers/ForkManagement.sol'; +import 'forge-std/Script.sol'; +import {ProfileTokenURI} from 'contracts/misc/token-uris/ProfileTokenURI.sol'; +import {HandleTokenURI} from 'contracts/misc/token-uris/HandleTokenURI.sol'; +import {FollowTokenURI} from 'contracts/misc/token-uris/FollowTokenURI.sol'; + +contract DeployTokenURIs is Script, ForkManagement { + using stdJson for string; + + struct LensAccount { + uint256 ownerPk; + address owner; + uint256 profileId; + } + + LensAccount _deployer; + + string mnemonic; + + function saveContractAddress(string memory contractName, address deployedAddress) internal { + // console.log('Saving %s (%s) into addresses under %s environment', contractName, deployedAddress, targetEnv); + string[] memory inputs = new string[](5); + inputs[0] = 'node'; + inputs[1] = 'script/helpers/saveAddress.js'; + inputs[2] = targetEnv; + inputs[3] = contractName; + inputs[4] = vm.toString(deployedAddress); + // bytes memory res = + vm.ffi(inputs); + // string memory output = abi.decode(res, (string)); + // console.log(output); + } + + function loadPrivateKeys() internal { + if (isEnvSet('MNEMONIC')) { + mnemonic = vm.envString('MNEMONIC'); + } + + if (bytes(mnemonic).length == 0) { + revert('Missing mnemonic'); + } + + console.log('\n'); + + (_deployer.owner, _deployer.ownerPk) = deriveRememberKey(mnemonic, 0); + console.log('Deployer address: %s', address(_deployer.owner)); + + console.log('\n'); + + console.log('Current block:', block.number); + } + + function deploy() internal { + vm.startBroadcast(_deployer.ownerPk); + address profileTokenURI = address(new ProfileTokenURI()); + address handleTokenURI = address(new HandleTokenURI()); + address followTokenURI = address(new FollowTokenURI()); + vm.stopBroadcast(); + + console.log('\n'); + console.log('ProfileTokenURI address: %s', address(profileTokenURI)); + saveContractAddress('ProfileTokenURI', profileTokenURI); + console.log('HandleTokenURI address: %s', address(handleTokenURI)); + saveContractAddress('HandleTokenURI', handleTokenURI); + console.log('FollowTokenURI address: %s', address(followTokenURI)); + saveContractAddress('FollowTokenURI', followTokenURI); + console.log('\n'); + } + + function run(string memory targetEnv_) external { + targetEnv = targetEnv_; + loadJson(); + checkNetworkParams(); + loadBaseAddresses(); + loadPrivateKeys(); + deploy(); + } +}