mirror of
https://github.com/lens-protocol/core.git
synced 2026-01-10 22:58:08 -05:00
feat: Modifications required for migration of Profiles
Co-authored-by: Alan <donosonaumczuk@gmail.com>
This commit is contained in:
@@ -22,6 +22,10 @@ import {StorageLib} from 'contracts/libraries/StorageLib.sol';
|
||||
import {FollowLib} from 'contracts/libraries/FollowLib.sol';
|
||||
import {CollectLib} from 'contracts/libraries/CollectLib.sol';
|
||||
|
||||
///////////////////////////////////// Migration imports ////////////////////////////////////
|
||||
import {LensHandles} from 'contracts/misc/namespaces/LensHandles.sol';
|
||||
import {TokenHandleRegistry} from 'contracts/misc/namespaces/TokenHandleRegistry.sol';
|
||||
|
||||
/**
|
||||
* @title LensHub
|
||||
* @author Lens Protocol
|
||||
@@ -41,6 +45,13 @@ contract LensHub is LensBaseERC721, VersionedInitializable, LensMultiState, Lens
|
||||
address internal immutable FOLLOW_NFT_IMPL;
|
||||
address internal immutable COLLECT_NFT_IMPL;
|
||||
|
||||
///////////////////////////////////// Migration constants ////////////////////////////////////
|
||||
uint256 internal constant LENS_PROTOCOL_PROFILE_ID = 1;
|
||||
address internal immutable migrator;
|
||||
LensHandles internal immutable lensHandles;
|
||||
TokenHandleRegistry internal immutable tokenHandleRegistry;
|
||||
///////////////////////////////// End of migration constants /////////////////////////////////
|
||||
|
||||
/**
|
||||
* @dev This modifier reverts if the caller is not the configured governance address.
|
||||
*/
|
||||
@@ -55,11 +66,20 @@ contract LensHub is LensBaseERC721, VersionedInitializable, LensMultiState, Lens
|
||||
* @param followNFTImpl The follow NFT implementation address.
|
||||
* @param collectNFTImpl The collect NFT implementation address.
|
||||
*/
|
||||
constructor(address followNFTImpl, address collectNFTImpl) {
|
||||
constructor(
|
||||
address followNFTImpl,
|
||||
address collectNFTImpl,
|
||||
address migratorAddress,
|
||||
address lensHandlesAddress,
|
||||
address tokenHandleRegistryAddress
|
||||
) {
|
||||
if (followNFTImpl == address(0)) revert Errors.InitParamsInvalid();
|
||||
if (collectNFTImpl == address(0)) revert Errors.InitParamsInvalid();
|
||||
FOLLOW_NFT_IMPL = followNFTImpl;
|
||||
COLLECT_NFT_IMPL = collectNFTImpl;
|
||||
migrator = migratorAddress;
|
||||
lensHandles = LensHandles(lensHandlesAddress);
|
||||
tokenHandleRegistry = TokenHandleRegistry(tokenHandleRegistryAddress);
|
||||
}
|
||||
|
||||
/// @inheritdoc ILensHub
|
||||
@@ -120,6 +140,49 @@ contract LensHub is LensBaseERC721, VersionedInitializable, LensMultiState, Lens
|
||||
emit Events.CollectModuleWhitelisted(collectModule, whitelist, block.timestamp);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
/// V1->V2 MIGRATION FUNCTIONS ///
|
||||
///////////////////////////////////////////
|
||||
|
||||
function migrateProfile(uint256 profileId, bytes32 handleHash) external {
|
||||
require(msg.sender == migrator, 'Only migrator');
|
||||
delete _profileById[profileId].handleDeprecated;
|
||||
delete _profileIdByHandleHash[handleHash];
|
||||
}
|
||||
|
||||
event ProfileMigrated(uint256 profileId, address profileDestination, string handle, uint256 handleId);
|
||||
|
||||
function _migrateProfilePublic(uint256 profileId) internal {
|
||||
address profileOwner = StorageLib.getTokenData(profileId).owner;
|
||||
if (profileOwner != address(0)) {
|
||||
string memory handle = _profileById[profileId].handleDeprecated;
|
||||
bytes32 handleHash = keccak256(bytes(handle));
|
||||
// "lensprotocol" is the only edge case without .lens suffix:
|
||||
if (profileId != LENS_PROTOCOL_PROFILE_ID) {
|
||||
assembly {
|
||||
let handle_length := mload(handle)
|
||||
mstore(handle, sub(handle_length, 5)) // Cut 5 chars (.lens) from the end
|
||||
}
|
||||
}
|
||||
delete _profileById[profileId].handleDeprecated;
|
||||
delete _profileIdByHandleHash[handleHash];
|
||||
|
||||
uint256 handleId = lensHandles.mintHandle(profileOwner, handle);
|
||||
tokenHandleRegistry.migrationLinkHandleWithToken(handleId, profileId);
|
||||
emit ProfileMigrated(profileId, profileOwner, handle, handleId);
|
||||
}
|
||||
}
|
||||
|
||||
function batchMigrateProfiles(uint256[] calldata profileIds) external {
|
||||
for (uint256 i = 0; i < profileIds.length; i++) {
|
||||
_migrateProfilePublic(profileIds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
/// END OF V1->V2 MIGRATION FUNCTIONS ///
|
||||
///////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////
|
||||
/// PROFILE OWNER FUNCTIONS ///
|
||||
///////////////////////////////////////////
|
||||
|
||||
@@ -74,8 +74,8 @@ library Types {
|
||||
* @param signer The address of the signer.
|
||||
* @param v The signature's recovery parameter.
|
||||
* @param r The signature's r parameter.
|
||||
* @param s The signature's s parameter
|
||||
* @param deadline The signature's deadline
|
||||
* @param s The signature's s parameter.
|
||||
* @param deadline The signature's deadline.
|
||||
*/
|
||||
struct EIP712Signature {
|
||||
address signer;
|
||||
@@ -90,8 +90,8 @@ library Types {
|
||||
*
|
||||
* @param pubCount The number of publications made to this profile.
|
||||
* @param followModule The address of the current follow module in use by this profile, can be empty.
|
||||
* @param followNFT The address of the followNFT associated with this profile, can be empty..
|
||||
* @param handleDeprecated The deprecated handle slot, no longer used. .
|
||||
* @param followNFT The address of the followNFT associated with this profile, can be empty.
|
||||
* @param handleDeprecated The deprecated handle slot, no longer used.
|
||||
* @param imageURI The URI to be used for the profile's image.
|
||||
* @param followNFTURI The URI to be used for the follow NFT.
|
||||
*/
|
||||
|
||||
54
contracts/misc/ProfileMigration.sol
Normal file
54
contracts/misc/ProfileMigration.sol
Normal file
@@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
|
||||
import {LensHub} from 'contracts/LensHub.sol';
|
||||
import {LensHandles} from 'contracts/misc/namespaces/LensHandles.sol';
|
||||
import {TokenHandleRegistry} from 'contracts/misc/namespaces/TokenHandleRegistry.sol';
|
||||
|
||||
contract ProfileMigration is Ownable {
|
||||
LensHub public immutable lensHub;
|
||||
LensHandles public immutable lensHandles;
|
||||
TokenHandleRegistry public immutable tokenHandleRegistry;
|
||||
|
||||
event ProfileMigrated(uint256 profileId, address profileDestination, string handle, uint256 handleId);
|
||||
|
||||
constructor(
|
||||
address ownerAddress,
|
||||
address lensHubAddress,
|
||||
address lensHandlesAddress,
|
||||
address tokenHandleRegistryAddress
|
||||
) {
|
||||
Ownable._transferOwnership(ownerAddress);
|
||||
lensHub = LensHub(lensHubAddress);
|
||||
lensHandles = LensHandles(lensHandlesAddress);
|
||||
tokenHandleRegistry = TokenHandleRegistry(tokenHandleRegistryAddress);
|
||||
}
|
||||
|
||||
struct ProfileMigrationData {
|
||||
uint256 profileId;
|
||||
address profileDestination;
|
||||
string handle;
|
||||
bytes32 handleHash;
|
||||
}
|
||||
|
||||
// TODO: Assume we pause everything - creating, transfer, etc.
|
||||
function _migrateProfile(ProfileMigrationData calldata profileMigrationData) internal {
|
||||
lensHub.migrateProfile(profileMigrationData.profileId, profileMigrationData.handleHash);
|
||||
uint256 handleId = lensHandles.mintHandle(profileMigrationData.profileDestination, profileMigrationData.handle);
|
||||
tokenHandleRegistry.migrationLinkHandleWithToken(handleId, profileMigrationData.profileId);
|
||||
emit ProfileMigrated(
|
||||
profileMigrationData.profileId,
|
||||
profileMigrationData.profileDestination,
|
||||
profileMigrationData.handle,
|
||||
handleId
|
||||
);
|
||||
}
|
||||
|
||||
function batchMigrateProfiles(ProfileMigrationData[] calldata profileMigrationDatas) external onlyOwner {
|
||||
for (uint256 i = 0; i < profileMigrationDatas.length; i++) {
|
||||
_migrateProfile(profileMigrationDatas[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
contracts/misc/migrations/ImmutableOwnable.sol
Normal file
30
contracts/misc/migrations/ImmutableOwnable.sol
Normal file
@@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
contract ImmutableOwnable {
|
||||
address immutable OWNER;
|
||||
address immutable LENS_HUB;
|
||||
|
||||
error OnlyOwner();
|
||||
error OnlyOwnerOrHub();
|
||||
|
||||
modifier onlyOwner() {
|
||||
if (msg.sender != OWNER) {
|
||||
revert OnlyOwner();
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyOwnerOrHub() {
|
||||
if (msg.sender != OWNER && msg.sender != LENS_HUB) {
|
||||
revert OnlyOwnerOrHub();
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
constructor(address owner, address lensHub) {
|
||||
OWNER = owner;
|
||||
LENS_HUB = lensHub;
|
||||
}
|
||||
}
|
||||
@@ -3,27 +3,21 @@
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import {ERC721} from '@openzeppelin/contracts/token/ERC721/ERC721.sol';
|
||||
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
|
||||
import {VersionedInitializable} from 'contracts/base/upgradeability/VersionedInitializable.sol';
|
||||
import {ImmutableOwnable} from 'contracts/misc/migrations/ImmutableOwnable.sol';
|
||||
|
||||
library Events {
|
||||
event HandleMinted(string handle, string namespace, uint256 handleId, address to);
|
||||
}
|
||||
|
||||
// TODO list:
|
||||
// 1. Code a contract that can batch-mint those handles
|
||||
// 2. Code a contract that can batch-link handles to profiles
|
||||
|
||||
contract LensHandles is ERC721, Ownable, VersionedInitializable {
|
||||
contract LensHandles is ERC721, VersionedInitializable, ImmutableOwnable {
|
||||
// Constant for upgradeability purposes, see VersionedInitializable. Do not confuse with EIP-712 revision number.
|
||||
uint256 internal constant REVISION = 1;
|
||||
|
||||
string constant NAMESPACE = 'lens';
|
||||
bytes32 constant NAMESPACE_HASH = keccak256(bytes(NAMESPACE));
|
||||
|
||||
constructor(address owner) ERC721('', '') {
|
||||
Ownable._transferOwnership(owner);
|
||||
}
|
||||
constructor(address owner, address lensHub) ERC721('', '') ImmutableOwnable(owner, lensHub) {}
|
||||
|
||||
function name() public pure override returns (string memory) {
|
||||
return string.concat(symbol(), ' Handles');
|
||||
@@ -33,9 +27,7 @@ contract LensHandles is ERC721, Ownable, VersionedInitializable {
|
||||
return string.concat('.', NAMESPACE);
|
||||
}
|
||||
|
||||
function initialize(address owner) external initializer {
|
||||
Ownable._transferOwnership(owner);
|
||||
}
|
||||
function initialize() external initializer {}
|
||||
|
||||
/**
|
||||
* @notice Mints a handle in the given namespace.
|
||||
@@ -45,7 +37,7 @@ contract LensHandles is ERC721, Ownable, VersionedInitializable {
|
||||
* @param to The address where the handle is being minted to.
|
||||
* @param localName The local name of the handle.
|
||||
*/
|
||||
function mintHandle(address to, string calldata localName) external onlyOwner returns (uint256) {
|
||||
function mintHandle(address to, string calldata localName) external onlyOwnerOrHub returns (uint256) {
|
||||
bytes32 localNameHash = keccak256(bytes(localName));
|
||||
bytes32 handleHash = keccak256(abi.encodePacked(localNameHash, NAMESPACE_HASH));
|
||||
uint256 handleId = uint256(handleHash);
|
||||
@@ -43,6 +43,9 @@ contract TokenHandleRegistry is VersionedInitializable {
|
||||
address immutable LENS_HUB;
|
||||
address immutable LENS_HANDLES;
|
||||
|
||||
// Migration constants
|
||||
address immutable migrator;
|
||||
|
||||
/// 1to1 mapping for now, can be replaced to support multiple handles per token if using mappings
|
||||
/// NOTE: Using bytes32 _handleHash(Handle) and _tokenHash(Token) as keys because solidity doesn't support structs as keys.
|
||||
mapping(bytes32 handle => Token token) handleToToken;
|
||||
@@ -78,13 +81,24 @@ contract TokenHandleRegistry is VersionedInitializable {
|
||||
}
|
||||
|
||||
// NOTE: We don't need whitelisting yet as we use immutable constants for the first version.
|
||||
constructor(address lensHub, address lensHandles) {
|
||||
constructor(address lensHub, address lensHandles, address migratorAddress) {
|
||||
LENS_HUB = lensHub;
|
||||
LENS_HANDLES = lensHandles;
|
||||
migrator = migratorAddress;
|
||||
}
|
||||
|
||||
function initialize() external initializer {}
|
||||
|
||||
// V1->V2 Migration function
|
||||
function migrationLinkHandleWithToken(uint256 handleId, uint256 tokenId) external {
|
||||
require(msg.sender == migrator, 'Only migrator');
|
||||
Handle memory handle = Handle({collection: LENS_HANDLES, id: handleId});
|
||||
Token memory token = Token({collection: LENS_HUB, id: tokenId});
|
||||
handleToToken[_handleHash(handle)] = token;
|
||||
tokenToHandle[_tokenHash(token)] = handle;
|
||||
emit Events.HandleLinked(handle, token);
|
||||
}
|
||||
|
||||
// NOTE: Simplified interfaces for the first version - Namespace and LensHub are constants
|
||||
// TODO: Custom logic for linking/unlinking handles and tokens (modules, with bytes passed)
|
||||
function linkHandleWithToken(uint256 handleId, uint256 tokenId) external {
|
||||
|
||||
@@ -39,7 +39,7 @@ contract EventTest is BaseTest {
|
||||
hubProxyAddr = predictContractAddress(deployer, 3);
|
||||
|
||||
// Deploy implementation contracts.
|
||||
hubImpl = new LensHub(followNFTAddr, collectNFTAddr);
|
||||
hubImpl = new LensHub(followNFTAddr, collectNFTAddr, address(0), address(0), address(0));
|
||||
followNFT = new FollowNFT(hubProxyAddr);
|
||||
collectNFT = new CollectNFT(hubProxyAddr);
|
||||
|
||||
|
||||
@@ -168,7 +168,7 @@ contract TestSetup is Test, ForkManagement, ArrayHelpers {
|
||||
hubProxyAddr = computeCreateAddress(deployer, 3);
|
||||
|
||||
// Deploy implementation contracts.
|
||||
hubImpl = new LensHub(followNFTAddr, collectNFTAddr);
|
||||
hubImpl = new LensHub(followNFTAddr, collectNFTAddr, address(0), address(0), address(0));
|
||||
followNFT = new FollowNFT(hubProxyAddr);
|
||||
collectNFT = new CollectNFT(hubProxyAddr);
|
||||
|
||||
|
||||
@@ -355,7 +355,7 @@ contract UpgradeForkTest is BaseTest {
|
||||
address collectNFTAddr = computeCreateAddress(deployer, 2);
|
||||
|
||||
// Deploy implementation contracts.
|
||||
hubImpl = new LensHub(followNFTAddr, collectNFTAddr);
|
||||
hubImpl = new LensHub(followNFTAddr, collectNFTAddr, address(0), address(0), address(0));
|
||||
followNFT = new FollowNFT(hubProxyAddr);
|
||||
collectNFT = new CollectNFT(hubProxyAddr);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user