mirror of
https://github.com/lens-protocol/core.git
synced 2026-04-22 03:02:03 -04:00
feat: First minimal handle implementation
Co-authored-by: Victor Naumik <vicnaum@gmail.com>
This commit is contained in:
85
contracts/namespaces/Handles.sol
Normal file
85
contracts/namespaces/Handles.sol
Normal file
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
|
||||
import {ERC721} from '@openzeppelin/contracts/token/ERC721/ERC721.sol';
|
||||
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
|
||||
|
||||
// TODO: Move to a Errors file
|
||||
library Errors {
|
||||
error NotHandleOwner();
|
||||
error NotProfileOwner();
|
||||
error NotHandleOrProfileOwner();
|
||||
}
|
||||
|
||||
// TODO: Move to a Events file
|
||||
library Events {
|
||||
event HandleLinked(uint256 handleId, uint256 profileId);
|
||||
event HandleUnlinked(uint256 handleId, uint256 profileId);
|
||||
}
|
||||
|
||||
contract Handles is ERC721, Ownable {
|
||||
address immutable LENS_HUB;
|
||||
|
||||
mapping(uint256 handleId => uint256 profileId) handleToProfile;
|
||||
mapping(uint256 profileId => uint256 handleId) profileToHandle;
|
||||
|
||||
modifier onlyHandleOwner(uint256 handleId, address transactionExecutor) {
|
||||
if (ownerOf(handleId) != transactionExecutor) {
|
||||
revert Errors.NotHandleOwner();
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyProfileOwner(uint256 profileId, address transactionExecutor) {
|
||||
if (IERC721(LENS_HUB).ownerOf(profileId) != transactionExecutor) {
|
||||
revert Errors.NotProfileOwner();
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyHandleOrProfileOwner(
|
||||
uint256 handleId,
|
||||
uint256 profileId,
|
||||
address transactionExecutor
|
||||
) {
|
||||
// The transaction executor must at least be the owner of either the handle or the profile.
|
||||
// Used for unlinking (so either the handle owner or the profile owner can unlink)
|
||||
if (ownerOf(handleId) != transactionExecutor && IERC721(LENS_HUB).ownerOf(profileId) != transactionExecutor) {
|
||||
revert Errors.NotHandleOrProfileOwner();
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
// NOTE: We don't need whitelisting yet as we use immutable constants for the first version.
|
||||
constructor(address lensHub) ERC721('Lens Canonical Handles', '.lens') {
|
||||
LENS_HUB = lensHub;
|
||||
}
|
||||
|
||||
function linkHandleWithProfile(
|
||||
uint256 handleId,
|
||||
uint256 profileId
|
||||
) external onlyProfileOwner(profileId, msg.sender) onlyHandleOwner(handleId, msg.sender) {
|
||||
handleToProfile[handleId] = profileId;
|
||||
profileToHandle[profileId] = handleId;
|
||||
emit Events.HandleLinked(handleId, profileId);
|
||||
}
|
||||
|
||||
function unlinkHandleFromProfile(
|
||||
uint256 handleId,
|
||||
uint256 profileId
|
||||
) external onlyHandleOrProfileOwner(handleId, profileId, msg.sender) {
|
||||
delete handleToProfile[handleId];
|
||||
delete profileToHandle[profileId];
|
||||
emit Events.HandleUnlinked(handleId, profileId);
|
||||
}
|
||||
|
||||
function resolveProfile(uint256 profileId) external view returns (uint256) {
|
||||
return profileToHandle[profileId];
|
||||
}
|
||||
|
||||
function resolveHandle(uint256 handleId) external view returns (uint256) {
|
||||
return handleToProfile[handleId];
|
||||
}
|
||||
}
|
||||
153
contracts/namespaces/Namespace.sol.bak
Normal file
153
contracts/namespaces/Namespace.sol.bak
Normal file
@@ -0,0 +1,153 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
pragma solidity ^0.8.19;
|
||||
|
||||
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
|
||||
|
||||
struct Token {
|
||||
address collection;
|
||||
uint96 _gap;
|
||||
uint256 id;
|
||||
}
|
||||
|
||||
// TODO: Move to a Errors file
|
||||
library Errors {
|
||||
error NotHandleOwner();
|
||||
error NotProfileOwner();
|
||||
error NotHandleOrProfileOwner();
|
||||
}
|
||||
|
||||
// TODO: Move to a Events file
|
||||
library Events {
|
||||
event HandleLinked(uint256 handleId, Token token);
|
||||
event HandleUnlinked(uint256 handleId, Token token);
|
||||
}
|
||||
|
||||
// TODO: Make upgradeable?
|
||||
contract NamespaceRegistry {
|
||||
address immutable LENS_HUB;
|
||||
address immutable NAMESPACE;
|
||||
|
||||
/// 1to1 mapping for now, can be replaced to support multiple handles per profile if using mappings
|
||||
/// NOTE: Using bytes32 _handleHash(Handle) and _profileHash(Profile) as keys because solidity doesn't support structs as keys.
|
||||
mapping (uint256 handleId => Token token) handleToToken;
|
||||
mapping (bytes32 token => uint256 handleId) profileToHandle;
|
||||
|
||||
modifier onlyHandleOwner(uint256 handleId, address transactionExecutor) {
|
||||
if (IERC721(handle.namespaceCollection).ownerOf(handle.handleId) != transactionExecutor) {
|
||||
revert Errors.NotHandleOwner();
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyProfileOwner(Profile memory profile, address transactionExecutor) {
|
||||
if (IERC721(profile.profileCollection).ownerOf(profile.profileId) != transactionExecutor) {
|
||||
revert Errors.NotProfileOwner();
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyHandleOrProfileOwner(Handle memory handle, Profile memory profile, address transactionExecutor) {
|
||||
// The transaction executor must be the owner of the handle or the profile (or both).
|
||||
if (!(IERC721(handle.namespace).ownerOf(handle.handleId) == transactionExecutor || IERC721(profile.lensHub).ownerOf(profile.profileId) == transactionExecutor)) {
|
||||
revert Errors.NotHandleOrProfileOwner();
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
// NOTE: We don't need whitelisting yet as we use immutable constants for the first version.
|
||||
constructor(address lensHub, address namespace) {
|
||||
LENS_HUB = lensHub;
|
||||
NAMESPACE = namespace;
|
||||
}
|
||||
|
||||
// NOTE: Simplified interfaces for the first version - Namespace and LensHub are constants
|
||||
// TODO: Custom logic for linking/unlinking handles and profiles (modules, with bytes passed)
|
||||
function linkHandleWithProfile(uint256 handleId, uint256 profileId) external {
|
||||
_linkHandleWithProfile(Handle({ namespace: NAMESPACE, handleId: handleId}), Profile({ lensHub: LENS_HUB, profileId: profileId}));
|
||||
}
|
||||
|
||||
function unlinkHandleFromProfile(uint256 handleId, uint256 profileId) external {
|
||||
_unlinkHandleFromProfile(Handle({ namespace: NAMESPACE, handleId: handleId}), Profile({ lensHub: LENS_HUB, profileId: profileId}));
|
||||
}
|
||||
|
||||
// TODO: Think of better name?
|
||||
// handleToProfile(handleId)?
|
||||
// resolveProfileByHandle(handleId)?
|
||||
function resolveProfile(uint256 handleId) external view returns (uint256) {
|
||||
return _resolveProfile(Handle({ namespace: NAMESPACE, handleId: handleId})).profileId;
|
||||
}
|
||||
|
||||
// TODO: Same here - think of better name?
|
||||
// profileToHandle(profileId)?
|
||||
// resolveHandleByProfile(profileId)?
|
||||
function resolveHandle(uint256 profileId) external view returns (uint256) {
|
||||
return _resolveHandle(Profile({ lensHub: LENS_HUB, profileId: profileId})).handleId;
|
||||
}
|
||||
|
||||
// Internal functions
|
||||
|
||||
function _resolveProfile(Handle memory handle) internal view returns (Profile storage) {
|
||||
return handleToProfile[_handleHash(handle)];
|
||||
}
|
||||
|
||||
function _resolveHandle(Profile memory profile) internal view returns (Handle storage) {
|
||||
return profileToHandle[_profileHash(profile)];
|
||||
}
|
||||
|
||||
function _linkHandleWithProfile(Handle memory handle, Profile memory profile) internal onlyProfileOwner(profile, msg.sender) onlyHandleOwner(handle, msg.sender) {
|
||||
handleToProfile[_handleHash(handle)] = profile;
|
||||
profileToHandle[_profileHash(profile)] = handle;
|
||||
emit Events.HandleLinked(handle, profile);
|
||||
}
|
||||
|
||||
function _unlinkHandleFromProfile(Handle memory handle, Profile memory profile) internal onlyHandleOrProfileOwner(handle, profile, msg.sender) {
|
||||
delete handleToProfile[_handleHash(handle)];
|
||||
delete profileToHandle[_profileHash(profile)];
|
||||
emit Events.HandleUnlinked(handle, profile);
|
||||
}
|
||||
|
||||
// Utility functions for mappings
|
||||
|
||||
function _handleHash(Handle memory handle) internal pure returns (bytes32) {
|
||||
return keccak256(abi.encodePacked(handle.namespace, handle.handleId));
|
||||
}
|
||||
|
||||
function _profileHash(Profile memory profile) internal pure returns (bytes32) {
|
||||
return keccak256(abi.encodePacked(profile.lensHub, profile.profileId));
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
[Upgradeable Beacon] => [Beacon Implementation]
|
||||
|
||||
^
|
||||
|
|
||||
|
||||
[Lens namespace collection - aka .lens handle collection - Proxy] // Storage 1
|
||||
|
||||
[Lens namespace collection - aka .lens handle collection - Proxy] // Storage 2
|
||||
|
||||
[Lens namespace collection - aka .lens handle collection - Proxy] // Storage 3
|
||||
|
||||
|
||||
---------------------------------------------
|
||||
|
||||
[Beacon] // Storage
|
||||
|
||||
|
|
||||
V
|
||||
|
||||
[Generic namespace collection impl]
|
||||
|
||||
-----
|
||||
|
||||
[TransparentProxy] // Storage
|
||||
|
|
||||
v
|
||||
[Lens namespace collection - aka .lens handle collection - Implementation]
|
||||
|
||||
|
||||
===============
|
||||
@@ -4,15 +4,8 @@ pragma solidity ^0.8.19;
|
||||
|
||||
import {IERC721} from '@openzeppelin/contracts/token/ERC721/IERC721.sol';
|
||||
|
||||
// TODO: Move to a Types
|
||||
struct Handle {
|
||||
address namespace;
|
||||
uint256 handleId;
|
||||
}
|
||||
|
||||
struct Profile {
|
||||
address lensHub;
|
||||
uint256 profileId;
|
||||
struct Namespace {
|
||||
|
||||
}
|
||||
|
||||
// TODO: Move to a Errors file
|
||||
@@ -36,18 +29,18 @@ contract NamespaceRegistry {
|
||||
|
||||
/// 1to1 mapping for now, can be replaced to support multiple handles per profile if using mappings
|
||||
/// NOTE: Using bytes32 _handleHash(Handle) and _profileHash(Profile) as keys because solidity doesn't support structs as keys.
|
||||
mapping (bytes32 handle => Profile profile) handleToProfile;
|
||||
mapping (bytes32 profile => Handle handle) profileToHandle;
|
||||
mapping (string namespace => address owner) handleToProfile;
|
||||
mapping (address => Handle handle) profileToHandle;
|
||||
|
||||
modifier onlyHandleOwner(Handle memory handle, address transactionExecutor) {
|
||||
if (IERC721(handle.namespace).ownerOf(handle.handleId) != transactionExecutor) {
|
||||
if (IERC721(handle.namespaceCollection).ownerOf(handle.handleId) != transactionExecutor) {
|
||||
revert Errors.NotHandleOwner();
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyProfileOwner(Profile memory profile, address transactionExecutor) {
|
||||
if (IERC721(profile.lensHub).ownerOf(profile.profileId) != transactionExecutor) {
|
||||
if (IERC721(profile.profileCollection).ownerOf(profile.profileId) != transactionExecutor) {
|
||||
revert Errors.NotProfileOwner();
|
||||
}
|
||||
_;
|
||||
Reference in New Issue
Block a user