OpenZeppelin——EnumerableSet介绍

EnumerableSet 是 OpenZeppelin 提供的 Solidity 库中的一个合约,用于管理集合(Set)类型的数据结构。

核心功能介绍

EnumerableSet结构

  • _values:用于存储集合元素值,
  • _indexes:用于记录元素值与索引位置映射关系
struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position of the value in the `values` array, plus 1 because index 0
        // means a value is not in the set.
        mapping(bytes32 => uint256) _indexes;
    }

以下是常用函数和内部实现代码解析: 

add(Bytes32Set storage set, bytes32 value) internal returns (bool);

向集合中提及添加元素,如果集合中不存在该元素则添加成功,返回true,否则返回false。 

/**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

function remove(Bytes32Set storage set, bytes32 value) internal returns (bool); 

 从集合中移除某个元素,如存在移除的值删除后返回true,不存在则返回false。实现步骤为:第一步从_indexes映射中获取要移除的元素(valueA)和最后一个元素(valueB)的索引(indexA、indexB),第二步用_values数组中最后一个元素(valueB)去覆盖要移除的元素(valueA)完成值替换,在_indexes映射中修改元素(indexB)的索引为(indexA)完成索引替换,第三步从_values数组中删掉元素(valueA),_indexes映射中删掉valueA的映射关系(valueA——>indexA)。

/**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We read and store the value's index to prevent multiple reads from the same storage slot
        uint256 valueIndex = set._indexes[value];

        if (valueIndex != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 toDeleteIndex = valueIndex - 1;
            uint256 lastIndex = set._values.length - 1;

            if (lastIndex != toDeleteIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the last value to the index where the value to delete is
                set._values[toDeleteIndex] = lastValue;
                // Update the index for the moved value
                set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the index for the deleted slot
            delete set._indexes[value];

            return true;
        } else {
            return false;
        }
    }

 function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool);

查询集合中是否包含某个元素, 如有返回true,否则返回false。

/**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._indexes[value] != 0;
    }

function length(Bytes32Set storage set) internal view returns (uint256); 

 查询集合长度大小值。

/**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32); 

查询集合中某个索引对应的元素值。

function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

function values(Bytes32Set storage set) internal view returns (bytes32[] memory); 

查询集合存储的所有元素,返回数组。 

/**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

EnumerableSet支持Bytes32、Address、Uint256三种数据类型。Address、Uint256类型数据在会转化为Bytes32类型进行存储。

 // sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 // and `uint256` (`UintSet`) are supported.

// Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

// AddressSet

    struct AddressSet {
        Set _inner;
    }

// UintSet

    struct UintSet {
        Set _inner;
    }

 EnumerableSet优点介绍

**高效查找:** `EnumerableSet` 提供了对集合元素的高效查找。在集合中查找某个元素的时间复杂度是 O(1)。这使得在集合中快速检查某个元素是否存在,而不需要遍历整个集合。

**可迭代性:** 与普通的 Set 不同,`EnumerableSet` 具有可迭代的特性。你可以方便地遍历集合中的所有元素,而不需要知道元素的具体值。这对于某些应用场景,比如合约状态的查询和遍历,非常有用。

**可操作性:** 提供了丰富的集合操作,如添加、删除、清空等,这些操作都是高效的。这样,开发者可以方便地对集合进行操作而无需担心性能问题。

**与 Solidity 标准兼容:** `EnumerableSet` 是为了与 Solidity 标准兼容而设计的,特别是 ERC 标准。例如,ERC-20 标准要求实现 `balanceOf` 函数时返回的是一个集合,而 `EnumerableSet` 可以方便地满足这个要求。

**类型安全:** `EnumerableSet` 使用泛型,这意味着你可以在编译时检查集合中存储的元素类型,从而提高代码的类型安全性。

**扩展性:** 由于 `EnumerableSet` 提供了可迭代性和高效的集合操作,因此它可以用作更复杂数据结构的基础。例如,可以用它来构建图、映射等更高层次的数据结构。

总体而言,`EnumerableSet` 提供了一种方便、高效且可迭代的集合管理方式,适用于各种 Solidity 合约的开发场景。

使用方式

import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

contract GameDB {    
    using EnumerableSet for EnumerableSet.UintSet;

    mapping(uint256 => EnumerableSet.UintSet) private _heroLearnSkills;

    function learnSkill(uint256 tokenId, uint256 skillId) external {
        uint256[] memory skills = _heroLearnSkills[tokenId].values();
        if (_heroLearnSkills[tokenId].contains(skillId)) {
            //todo
        } else {
            _heroLearnSkills[tokenId].add(skillId);
            //todo
        }
    
    }
}

以上是关于EnumerableSet的介绍,欢迎批评指导!

  • 18
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
OpenZeppelin是一个开源的智能合约开发框架,它提供了一套工具和库,帮助开发者构建安全可靠的智能合约。它的入门套件包含了一些常用的工具和库,如React、OpenZeppelin CLI、OpenZeppelin Contracts、Truffle和Infura。 要开始使用OpenZeppelin,你可以按照以下步骤进行安装和配置: 1. 在你的项目中新建一个合约目录,并进入该目录。 2. 使用Truffle命令初始化一个Truffle项目,生成一个package.json文件,其中包含了一些默认的配置信息。 3. 使用npm命令安装openzeppelin-solidity库,该库包含了一些OpenZeppelin的智能合约和库文件,可以通过import语句进行引入。 4. 可以根据需要引入OpenZeppelin的具体合约文件,例如使用import语句引入Ownable.sol合约文件,该文件定义了一个拥有者权限的基础合约。 使用OpenZeppelin升级插件部署的合约具备可升级的特性,意味着你可以通过升级来修改合约的代码,同时保留原有合约的地址、状态和余额。这使得你可以在不中断原有功能的情况下,向项目中添加新功能或修复已上线版本中的错误。为了配置开发环境,你可以根据OpenZeppelin提供的文档和示例进行相应的配置工作。 总结来说,OpenZeppelin是一个智能合约开发框架,提供了一套工具和库,帮助开发者构建安全可靠的智能合约。它的入门套件包含了一些常用的工具和库,可以帮助你快速开始使用可升级的智能合约。同时,OpenZeppelin还提供了升级插件,使得合约可以进行升级,添加新功能或修复错误,而不中断原有合约的功能。配置开发环境可以参考OpenZeppelin的文档和示例。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [入门套件:一个OpenZeppelin入门套件,其中包含React,OpenZeppelin SDK和OpenZeppelin合同](https://download.csdn.net/download/weixin_42132354/15065420)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [区块链入门教程openzeppelin库详解](https://blog.csdn.net/u013288190/article/details/123769975)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [部署OpenZeppelin可升级合约](https://blog.csdn.net/watson2017/article/details/122745389)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tomggo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值