iT邦幫忙

2023 iThome 鐵人賽

DAY 23
0
Web 3

Web3 X 公共財系列 第 23

Day 23 - IAllowlist、AllowlistMinter、Murky及HypercertMinter.sol

  • 分享至 

  • xImage
  •  

IAllowlist

// This interface declares the required functionality for a hypercert token
// notice This interface does not specify the underlying token type (e.g. 721 or 1155)

interface IAllowlist {
    function isAllowedToClaim(
        bytes32[] calldata proof,
        uint256 tokenID,
        bytes32 leaf
    ) external view returns (bool isAllowed);
}

藉由merkle proof的方式與allowlist 互動+實作

AllowlistMinter

包含兩個Event
引入 oz-upgradeable/utils/cryptography/MerkleProofUpgradeable.sol 需再往上查verifyCalldata如何實作

event AllowlistCreated(uint256 tokenID, bytes32 root);
event LeafClaimed(uint256 tokenID, bytes32 leaf);

Merkle tree相關變數
mapping(uint256 => bytes32) internal merkleRoots;
查詢+proof => isAllowed = MerkleProofUpgradeable.verifyCalldata(proof, merkleRoots[claimID], leaf);

   function _createAllowlist(uint256 claimID, bytes32 merkleRoot, uint256 units) internal {
        if (merkleRoot == "" || units == 0) revert Errors.Invalid();
        if (merkleRoots[claimID] != "") revert Errors.DuplicateEntry();

        merkleRoots[claimID] = merkleRoot;
        maxUnits[claimID] = units;
        emit AllowlistCreated(claimID, merkleRoot);
    }
  function _processClaim(bytes32[] calldata proof, uint256 claimID, uint256 amount) internal {
        if (merkleRoots[claimID].length == 0) revert Errors.DoesNotExist();

        bytes32 leaf = _calculateLeaf(msg.sender, amount);

        if (hasBeenClaimed[claimID][leaf]) revert Errors.AlreadyClaimed();
        if (
            !MerkleProofUpgradeable.verifyCalldata(proof, merkleRoots[claimID], leaf) ||
            (minted[claimID] + amount) > maxUnits[claimID]
        ) revert Errors.Invalid();
        hasBeenClaimed[claimID][leaf] = true;

        emit LeafClaimed(claimID, leaf);
    }

    function _calculateLeaf(address account, uint256 amount) internal pure returns (bytes32 leaf) {
        leaf = keccak256(bytes.concat(keccak256(abi.encode(account, amount))));
    }

Murky 與 openzepelin-murkle proof

Murky contains contracts that can generate merkle roots and proofs. Murky also performs inclusion verification. Both XOR-based and a concatenation-based hashing are currently supported.

The root generation, proof generation, and verification functions are all fuzz tested (configured 5,000 runs by default) using arbitrary bytes32 arrays and uint leaves. There is also standardized testing and differential testing.
用共同的MerkleBase合約後,使用不同的HASHING FUNCTION(但都以Assembly書寫有點超出難度了)

    /**********************
    * PROOF VERIFICATION *
    **********************/
    
    function verifyProof(bytes32 root, bytes32[] memory proof, bytes32 valueToProve) external pure returns (bool) {
        // proof length must be less than max array size
        bytes32 rollingHash = valueToProve;
        uint256 length = proof.length;
        unchecked {
            for(uint i = 0; i < length; ++i){
                rollingHash = hashLeafPairs(rollingHash, proof[i]);
            }
        }
        return root == rollingHash;
    }

op寫法 單一x2(verify + process)x2 (X + calldata)-> Batch x4

function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
}

 

 /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     */
function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Calldata version of {processProof}
     */
     */
    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    /**
     * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
     */
    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }

HypercertMinter

Merkle proof僅再與allowlist有關之mint的驗證實作,藉由呼叫_processClaim function

function mintClaimFromAllowlist(
        address account,
        bytes32[] calldata proof,
        uint256 claimID,
        uint256 units
    ) external whenNotPaused {
        _processClaim(proof, claimID, units);
        _mintToken(account, claimID, units);
    }
 function batchMintClaimsFromAllowlists(
        address account,
        bytes32[][] calldata proofs,
        uint256[] calldata claimIDs,
        uint256[] calldata units
    ) external whenNotPaused {
        uint256 len = claimIDs.length;
        for (uint256 i; i < len; ) {
            _processClaim(proofs[i], claimIDs[i], units[i]);
            unchecked {
                ++i;
            }
        }
        _batchMintTokens(account, claimIDs, units);
    }

查詢merkle tree

solidity by example之介紹及影片介紹
Yes


上一篇
Day 22- 細看官方介紹、IHypercertToken及ERC1155使用
下一篇
Day 24 - testing練習
系列文
Web3 X 公共財30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言