Browse Source

Add BytesSet (#2395)

Co-authored-by: Francisco Giordano <frangio.1@gmail.com>
Nicolás Venturo 5 years ago
parent
commit
fcdf8f4618

+ 1 - 0
CHANGELOG.md

@@ -4,6 +4,7 @@
 
  * `Address`: added `functionStaticCall` and `functionDelegateCall`, similar to the existing `functionCall`. ([#2333](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2333))
  * `TimelockController`: added a contract to augment access control schemes with a delay. ([#2364](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2364))
+ * `EnumerableSet`: added `BytesSet`, for sets of `bytes32`. ([#2395](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/2395))
 
 ## 3.2.0 (2020-09-10)
 

+ 32 - 1
contracts/mocks/EnumerableSetMock.sol

@@ -4,6 +4,37 @@ pragma solidity ^0.6.0;
 
 import "../utils/EnumerableSet.sol";
 
+// Bytes32Set
+contract EnumerableBytes32SetMock {
+    using EnumerableSet for EnumerableSet.Bytes32Set;
+
+    event OperationResult(bool result);
+
+    EnumerableSet.Bytes32Set private _set;
+
+    function contains(bytes32 value) public view returns (bool) {
+        return _set.contains(value);
+    }
+
+    function add(bytes32 value) public {
+        bool result = _set.add(value);
+        emit OperationResult(result);
+    }
+
+    function remove(bytes32 value) public {
+        bool result = _set.remove(value);
+        emit OperationResult(result);
+    }
+
+    function length() public view returns (uint256) {
+        return _set.length();
+    }
+
+    function at(uint256 index) public view returns (bytes32) {
+        return _set.at(index);
+    }
+}
+
 // AddressSet
 contract EnumerableAddressSetMock {
     using EnumerableSet for EnumerableSet.AddressSet;
@@ -64,4 +95,4 @@ contract EnumerableUintSetMock {
     function at(uint256 index) public view returns (uint256) {
         return _set.at(index);
     }
-}
+}

+ 56 - 2
contracts/utils/EnumerableSet.sol

@@ -23,8 +23,8 @@ pragma solidity ^0.6.0;
  * }
  * ```
  *
- * As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
- * (`UintSet`) are supported.
+ * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
+ * and `uint256` (`UintSet`) are supported.
  */
 library EnumerableSet {
     // To implement this library for multiple types with as little code
@@ -132,6 +132,60 @@ library EnumerableSet {
         return set._values[index];
     }
 
+    // Bytes32Set
+
+    struct Bytes32Set {
+        Set _inner;
+    }
+
+    /**
+     * @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(Bytes32Set storage set, bytes32 value) internal returns (bool) {
+        return _add(set._inner, value);
+    }
+
+    /**
+     * @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(Bytes32Set storage set, bytes32 value) internal returns (bool) {
+        return _remove(set._inner, value);
+    }
+
+    /**
+     * @dev Returns true if the value is in the set. O(1).
+     */
+    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
+        return _contains(set._inner, value);
+    }
+
+    /**
+     * @dev Returns the number of values in the set. O(1).
+     */
+    function length(Bytes32Set storage set) internal view returns (uint256) {
+        return _length(set._inner);
+    }
+
+   /**
+    * @dev Returns the value stored at position `index` in the set. O(1).
+    *
+    * Note that there are no guarantees on the ordering of values inside the
+    * array, and it may change when more values are added or removed.
+    *
+    * Requirements:
+    *
+    * - `index` must be strictly less than {length}.
+    */
+    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
+        return _at(set._inner, index);
+    }
+
     // AddressSet
 
     struct AddressSet {

+ 15 - 1
test/utils/EnumerableSet.test.js

@@ -1,14 +1,28 @@
 const { BN } = require('@openzeppelin/test-helpers');
 
+const EnumerableBytes32SetMock = artifacts.require('EnumerableBytes32SetMock');
 const EnumerableAddressSetMock = artifacts.require('EnumerableAddressSetMock');
 const EnumerableUintSetMock = artifacts.require('EnumerableUintSetMock');
 
 const { shouldBehaveLikeSet } = require('./EnumerableSet.behavior');
 
 contract('EnumerableSet', function (accounts) {
+  // Bytes32Set
+  describe('EnumerableBytes32Set', function () {
+    const bytesA = '0xdeadbeef'.padEnd(66, '0');
+    const bytesB = '0x0123456789'.padEnd(66, '0');
+    const bytesC = '0x42424242'.padEnd(66, '0');
+
+    beforeEach(async function () {
+      this.set = await EnumerableBytes32SetMock.new();
+    });
+
+    shouldBehaveLikeSet(bytesA, bytesB, bytesC);
+  });
+
   // AddressSet
   describe('EnumerableAddressSet', function () {
-    const [ accountA, accountB, accountC ] = accounts;
+    const [accountA, accountB, accountC] = accounts;
 
     beforeEach(async function () {
       this.set = await EnumerableAddressSetMock.new();