Browse Source

Add uint to uint enumerable map (#3338)

ashhanai 3 years ago
parent
commit
c4f76cfa15

+ 1 - 0
CHANGELOG.md

@@ -7,6 +7,7 @@
  * `CrossChainEnabledPolygonChild`: replace the `require` statement with the custom error `NotCrossChainCall`. ([#3380](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3380))
  * `ERC20FlashMint`: Add customizable flash fee receiver. ([#3327](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3327))
  * `Strings`: add a new overloaded function `toHexString` that converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. ([#3403](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3403))
+ * `EnumerableMap`: add new `UintToUintMap` map type. ([#3338](https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3338))
 
 ## 4.6.0 (2022-04-26)
 

+ 43 - 0
contracts/mocks/EnumerableMapMock.sol

@@ -131,3 +131,46 @@ contract Bytes32ToBytes32MapMock {
         return _map.get(key, errorMessage);
     }
 }
+
+// UintToUintMap
+contract UintToUintMapMock {
+    using EnumerableMap for EnumerableMap.UintToUintMap;
+
+    event OperationResult(bool result);
+
+    EnumerableMap.UintToUintMap private _map;
+
+    function contains(uint256 key) public view returns (bool) {
+        return _map.contains(key);
+    }
+
+    function set(uint256 key, uint256 value) public {
+        bool result = _map.set(key, value);
+        emit OperationResult(result);
+    }
+
+    function remove(uint256 key) public {
+        bool result = _map.remove(key);
+        emit OperationResult(result);
+    }
+
+    function length() public view returns (uint256) {
+        return _map.length();
+    }
+
+    function at(uint256 index) public view returns (uint256 key, uint256 value) {
+        return _map.at(index);
+    }
+
+    function tryGet(uint256 key) public view returns (bool, uint256) {
+        return _map.tryGet(key);
+    }
+
+    function get(uint256 key) public view returns (uint256) {
+        return _map.get(key);
+    }
+
+    function getWithMessage(uint256 key, string calldata errorMessage) public view returns (uint256) {
+        return _map.get(key, errorMessage);
+    }
+}

+ 93 - 2
contracts/utils/structs/EnumerableMap.sol

@@ -31,6 +31,7 @@ import "./EnumerableSet.sol";
  * - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
  * - `address -> uint256` (`AddressToUintMap`) since v4.6.0
  * - `bytes32 -> bytes32` (`Bytes32ToBytes32`) since v4.6.0
+ * - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
  *
  * [WARNING]
  * ====
@@ -155,6 +156,98 @@ library EnumerableMap {
         return value;
     }
 
+    // UintToUintMap
+
+    struct UintToUintMap {
+        Bytes32ToBytes32Map _inner;
+    }
+
+    /**
+     * @dev Adds a key-value pair to a map, or updates the value for an existing
+     * key. O(1).
+     *
+     * Returns true if the key was added to the map, that is if it was not
+     * already present.
+     */
+    function set(
+        UintToUintMap storage map,
+        uint256 key,
+        uint256 value
+    ) internal returns (bool) {
+        return set(map._inner, bytes32(key), bytes32(value));
+    }
+
+    /**
+     * @dev Removes a value from a set. O(1).
+     *
+     * Returns true if the key was removed from the map, that is if it was present.
+     */
+    function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
+        return remove(map._inner, bytes32(key));
+    }
+
+    /**
+     * @dev Returns true if the key is in the map. O(1).
+     */
+    function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
+        return contains(map._inner, bytes32(key));
+    }
+
+    /**
+     * @dev Returns the number of elements in the map. O(1).
+     */
+    function length(UintToUintMap storage map) internal view returns (uint256) {
+        return length(map._inner);
+    }
+
+    /**
+     * @dev Returns the element 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(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) {
+        (bytes32 key, bytes32 value) = at(map._inner, index);
+        return (uint256(key), uint256(value));
+    }
+
+    /**
+     * @dev Tries to returns the value associated with `key`.  O(1).
+     * Does not revert if `key` is not in the map.
+     */
+    function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) {
+        (bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
+        return (success, uint256(value));
+    }
+
+    /**
+     * @dev Returns the value associated with `key`.  O(1).
+     *
+     * Requirements:
+     *
+     * - `key` must be in the map.
+     */
+    function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
+        return uint256(get(map._inner, bytes32(key)));
+    }
+
+    /**
+     * @dev Same as {get}, with a custom error message when `key` is not in the map.
+     *
+     * CAUTION: This function is deprecated because it requires allocating memory for the error
+     * message unnecessarily. For custom revert reasons use {tryGet}.
+     */
+    function get(
+        UintToUintMap storage map,
+        uint256 key,
+        string memory errorMessage
+    ) internal view returns (uint256) {
+        return uint256(get(map._inner, bytes32(key), errorMessage));
+    }
+
     // UintToAddressMap
 
     struct UintToAddressMap {
@@ -310,8 +403,6 @@ library EnumerableMap {
     /**
      * @dev Tries to returns the value associated with `key`.  O(1).
      * Does not revert if `key` is not in the map.
-     *
-     * _Available since v3.4._
      */
     function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) {
         (bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));

+ 14 - 0
test/utils/structs/EnumerableMap.test.js

@@ -3,6 +3,7 @@ const { BN, constants } = require('@openzeppelin/test-helpers');
 const AddressToUintMapMock = artifacts.require('AddressToUintMapMock');
 const UintToAddressMapMock = artifacts.require('UintToAddressMapMock');
 const Bytes32ToBytes32MapMock = artifacts.require('Bytes32ToBytes32MapMock');
+const UintToUintMapMock = artifacts.require('UintToUintMapMock');
 
 const { shouldBehaveLikeMap } = require('./EnumerableMap.behavior');
 
@@ -55,4 +56,17 @@ contract('EnumerableMap', function (accounts) {
       constants.ZERO_BYTES32,
     );
   });
+
+  // UintToUintMap
+  describe('UintToUintMap', function () {
+    beforeEach(async function () {
+      this.map = await UintToUintMapMock.new();
+    });
+
+    shouldBehaveLikeMap(
+      [ keyA, keyB, keyC ],
+      [ keyA, keyB, keyC ].map(k => k.add(new BN('1332'))),
+      new BN('0'),
+    );
+  });
 });