misc: TokenURI contracts separated

This commit is contained in:
vicnaum
2023-11-14 09:26:06 +01:00
committed by donosonaumczuk
parent 1e925dde1f
commit 2521ef4a29
46 changed files with 240 additions and 18 deletions

View File

@@ -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

View File

@@ -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.
*

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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)
}
}
}

View File

@@ -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

View File

@@ -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,',

View File

@@ -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(),
'"}]}'
)
)

View File

@@ -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

View File

@@ -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 {

View File

@@ -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();
}
}