iT邦幫忙

2023 iThome 鐵人賽

DAY 15
0
Web 3

Web3 X 公共財系列 第 15

Day 15 - Registry.sol

  • 分享至 

  • xImage
  •  

摘要

Registry合約是Allo生態系的基本要件,使得創建和管理Profile 成為可能。
Profile 作為分散應用的基本實體,提供身份管理、訪問控制和互動功能。此合約配備了創建個Profile、更新元數據、管理所有權和處理資金恢復的機制。它利用外部庫、接口和內部函數,提供強大的功能,同時遵循安全和透明性。

流程圖

https://ithelp.ithome.com.tw/upload/images/20230930/20103331GSkEelZ26R.pnghttps://ithelp.ithome.com.tw/upload/images/20230930/201033313GW9JGxS0O.png
https://ithelp.ithome.com.tw/upload/images/20230930/20103331lsmv8Qds0n.png
https://ithelp.ithome.com.tw/upload/images/20230930/20103331elysoi7QKt.png
https://ithelp.ithome.com.tw/upload/images/20230930/20103331gqhS2uQhLQ.png

引用

  1. 外部引用: AccessControl and CREATE3
  2. Interface: IRegistry
import {Metadata} from "../libraries/Metadata.sol";

interface IRegistry {
    // Profile struct宣告
     struct Profile {
        bytes32 id;
        uint256 nonce;
        string name;
        Metadata metadata;
        address owner;
        address anchor;
    }
    //Events 
     event ProfileCreated(
     bytes32 indexed profileId, 
     uint256 nonce, string name, 
     Metadata metadata, 
     address owner, 
     address anchor
    );
    event ProfileNameUpdated(bytes32 indexed profileId, string name, address anchor);
    event ProfileMetadataUpdated(bytes32 indexed profileId, Metadata metadata);
    event ProfileOwnerUpdated(bytes32 indexed profileId, address owner);
    event ProfilePendingOwnerUpdated(bytes32 indexed profileId, address pendingOwner);
    
    //View Functions
    function getProfileById(bytes32 _profileId) external view returns (Profile memory profile);
    function getProfileByAnchor(address _anchor) external view returns (Profile memory profile);
    function isOwnerOrMemberOfProfile(bytes32 _profileId, address _account) external view
       returns (bool isOwnerOrMemberOfProfile);
    function isOwnerOfProfile(bytes32 _profileId, address _owner) external view returns (bool isOwnerOfProfile);
    function isMemberOfProfile(bytes32 _profileId, address _member) external view returns (bool isMemberOfProfile);
    
    //External/Public Functions
    function createProfile(
        uint256 _nonce,
        string memory _name,
        Metadata memory _metadata,
        address _owner,
        address[] memory _members
    ) external returns (bytes32 profileId);
    
    function updateProfileName(bytes32 _profileId, string memory _name) external returns (address anchor);
    function updateProfileMetadata(bytes32 _profileId, Metadata memory _metadata) external;
    function updateProfilePendingOwner(bytes32 _profileId, address _pendingOwner) external;
    function acceptProfileOwnership(bytes32 _profileId) external;
    function addMembers(bytes32 _profileId, address[] memory _members) external;
    function removeMembers(bytes32 _profileId, address[] memory _members) external;
    function recoverFunds(address _token, address _recipient) external;

}

實際合約

重要function

    function _checkOnlyProfileOwner(bytes32 _profileId) internal view {
        if (!_isOwnerOfProfile(_profileId, msg.sender)) revert UNAUTHORIZED();
    }
     function _isOwnerOfProfile(bytes32 _profileId, address _owner) internal view returns (bool) {
        return profilesById[_profileId].owner == _owner;
    }
    
    //最重要 createProfile
    function createProfile(
        uint256 _nonce,
        string memory _name,
        Metadata memory _metadata,
        address _owner,
        address[] memory _members
    ) external returns (bytes32) {
        // Generate a profile ID using a nonce and the msg.sender
        bytes32 profileId = _generateProfileId(_nonce);

        // Make sure the nonce is available
        if (profilesById[profileId].anchor != address(0)) revert NONCE_NOT_AVAILABLE();

        // Make sure the owner is not the zero address
        if (_owner == address(0)) revert ZERO_ADDRESS();

        // Create a new Profile instance, also generates the anchor address
        Profile memory profile = Profile({
            id: profileId,
            nonce: _nonce,
            name: _name,
            metadata: _metadata,
            owner: _owner,
            anchor: _generateAnchor(profileId, _name)
        });

        profilesById[profileId] = profile;
        anchorToProfileId[profile.anchor] = profileId;

        // Assign roles for the profile members
        uint256 memberLength = _members.length;
        for (uint256 i; i < memberLength;) {
            address member = _members[i];

            // Will revert if any of the addresses are a zero address
            if (member == address(0)) revert ZERO_ADDRESS();

            // Grant the role to the member and emit the event for each member
            _grantRole(profileId, member);
            unchecked {
                ++i;
            }
        }

        // Emit the event that the profile was created
        emit ProfileCreated(profileId, profile.nonce, profile.name, profile.metadata, profile.owner, profile.anchor);

        // Return the profile ID
        return profileId;
    }
    
    //同步產生anchor合約
    function _generateAnchor(bytes32 _profileId, string memory _name) internal returns (address anchor) {
        bytes32 salt = keccak256(abi.encodePacked(_profileId, _name));

        address preCalculatedAddress = CREATE3.getDeployed(salt);

        // check if the contract already exists and if the profileId matches
        if (preCalculatedAddress.code.length > 0) {
            if (Anchor(payable(preCalculatedAddress)).profileId() != _profileId) revert ANCHOR_ERROR();

            anchor = preCalculatedAddress;
        } else {
            // check if the contract has already been deployed by checking code size of address
            bytes memory creationCode = abi.encodePacked(type(Anchor).creationCode, abi.encode(_profileId));

            // Use CREATE3 to deploy the anchor contract
            anchor = CREATE3.deploy(salt, creationCode, 0);
        }
    }
    
    //ProfileID產生方式
    /// @notice Generates the 'profileId' based on msg.sender and nonce
    /// @dev Internal function used by 'createProfile()' to generate profileId.
    /// @param _nonce Nonce provided by the caller to generate 'profileId'
    /// @return 'profileId' The ID of the profile
    function _generateProfileId(uint256 _nonce) internal view returns (bytes32) {
        return keccak256(abi.encodePacked(_nonce, msg.sender));
    }
    
    //重要function
    function recoverFunds(address _token, address _recipient) external onlyRole(ALLO_OWNER) {
        if (_recipient == address(0)) revert ZERO_ADDRESS();

        uint256 amount = _token == NATIVE ? address(this).balance : ERC20(_token).balanceOf(address(this));
        _transferAmount(_token, _recipient, amount);
    }
    

補充

CREATE, CREATE2, CREATA3差異? 文章

  • 目的: 在合約執行過程中部署新合約。
  • CREATE: 新合約地址是根據 sender_address 和 sender_nonce 決定。因為合約 nonce 只在部署合約時會從 1 開始遞增,且此合約的地址也須固定,因此想讓新合約在多鏈上有相同地址是不太容易管理的。

address = keccak256(rlp([sender_address, sender_nonce]))[12:]

  • CREATE2: 新合約地址是根據 init_code 決定而非 sender_nonce,也就是新合約地址在編譯完成後就確定了,而非根據部署時的鏈上 sender_nonce 決定。
  • BUT: init_code 包含 constructor 的參數,因此當各鏈上需要給定不同 constructor參數時,新合約地址會不同。

address = keccak256(0xff + sender_address + salt + keccak256(init_code))[12:]

  • CREATE3:是新合約的地址只根據 sender_address 和 salt 決定。也就是就算個各鏈上新合約的 init_code 不同 (像是 constructor 參數),也能部署在相同位置。

Summary

CREATE3 結合 CREATE 和 CREATE2 的特性來減少生成合約地址的變因,從最初的 nonce 到 init_code 進而只剩下 sender_address和 salt。相對的是開發者需要更注意其中的細節,像是 constructor 中的邏輯、部署時不要用錯新合約 init_code 否則地址就會被佔用、多餘的部署成本 (CREATE3 Factory & CREATE2 Proxy) 以及較複雜的流程。

明天

預計補上這三個合約其他雜項solidity 包含: metadata.sol等位於libraries內的合約


上一篇
Day 14- Anchor.sol
下一篇
Day 16 - Allo libraries內及 Factory 的合約
系列文
Web3 X 公共財30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言