Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ jobs:
run: npm run test:inheritance
- name: Check pragma consistency between files
run: npm run test:pragma
- name: Check procedurally generated contracts are up-to-date
run: npm run test:generation

coverage:
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## XX-XX-XXXX

- `EnumerableSetExtended` and `EnumerableMapExtended`: Extensions of the `EnumerableSet` and `EnumerableMap` libraries with more types, including non-value types.

## 03-04-2025

- `PaymasterERC20`: Extension of `PaymasterCore` that sponsors user operations against payment in ERC-20 tokens.
Expand Down
7 changes: 7 additions & 0 deletions contracts/utils/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Miscellaneous contracts and libraries containing utility functions you can use t
* {ERC7739}: An abstract contract to validate signatures following the rehashing scheme from `ERC7739Utils`.
* {ERC7739Utils}: Utilities library that implements a defensive rehashing mechanism to prevent replayability of smart contract signatures based on ERC-7739.
* {SignerECDSA}, {SignerP256}, {SignerRSA}: Implementations of an {AbstractSigner} with specific signature validation algorithms.
* {EnumerableSetExtended} and {EnumerableMapExtended}: Extensions of the `EnumerableSet` and `EnumerableMap` libraries with more types, including non-value types.
* {Masks}: Library to handle `bytes32` masks.

== Cryptography
Expand All @@ -27,6 +28,12 @@ Miscellaneous contracts and libraries containing utility functions you can use t

{{SignerRSA}}

== Structs

{{EnumerableSetExtended}}

{{EnumerableMapExtended}}

== Libraries

{{Masks}}
249 changes: 249 additions & 0 deletions contracts/utils/structs/EnumerableMapExtended.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
// SPDX-License-Identifier: MIT
// This file was procedurally generated from scripts/generate/templates/EnumerableMapExtended.js.

pragma solidity ^0.8.20;

import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {EnumerableSetExtended} from "./EnumerableSetExtended.sol";

/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Note: Extensions of openzeppelin/contracts/utils/struct/EnumerableMap.sol.
*/
library EnumerableMapExtended {
using EnumerableSet for *;
using EnumerableSetExtended for *;

/**
* @dev Query for a nonexistent map key.
*/
error EnumerableMapNonexistentBytesKey(bytes key);

struct BytesToUintMap {
// Storage of keys
EnumerableSetExtended.BytesSet _keys;
mapping(bytes key => uint256) _values;
}

/**
* @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(BytesToUintMap storage map, bytes memory key, uint256 value) internal returns (bool) {
map._values[key] = value;
return map._keys.add(key);
}

/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(BytesToUintMap storage map, bytes memory key) internal returns (bool) {
delete map._values[key];
return map._keys.remove(key);
}

/**
* @dev Removes all the entries from a map. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the map grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(BytesToUintMap storage map) internal {
uint256 len = length(map);
for (uint256 i = 0; i < len; ++i) {
delete map._values[map._keys.at(i)];
}
map._keys.clear();
}

/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(BytesToUintMap storage map, bytes memory key) internal view returns (bool) {
return map._keys.contains(key);
}

/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function length(BytesToUintMap storage map) internal view returns (uint256) {
return map._keys.length();
}

/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(BytesToUintMap storage map, uint256 index) internal view returns (bytes memory key, uint256 value) {
key = map._keys.at(index);
value = map._values[key];
}

/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(BytesToUintMap storage map, bytes memory key) internal view returns (bool exists, uint256 value) {
value = map._values[key];
exists = value != uint256(0) || contains(map, key);
}

/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(BytesToUintMap storage map, bytes memory key) internal view returns (uint256 value) {
bool exists;
(exists, value) = tryGet(map, key);
if (!exists) {
revert EnumerableMapNonexistentBytesKey(key);
}
}

/**
* @dev Return the an array containing all the keys
*
* 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(BytesToUintMap storage map) internal view returns (bytes[] memory) {
return map._keys.values();
}

/**
* @dev Query for a nonexistent map key.
*/
error EnumerableMapNonexistentStringKey(string key);

struct StringToStringMap {
// Storage of keys
EnumerableSetExtended.StringSet _keys;
mapping(string key => string) _values;
}

/**
* @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(StringToStringMap storage map, string memory key, string memory value) internal returns (bool) {
map._values[key] = value;
return map._keys.add(key);
}

/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(StringToStringMap storage map, string memory key) internal returns (bool) {
delete map._values[key];
return map._keys.remove(key);
}

/**
* @dev Removes all the entries from a map. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the map grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(StringToStringMap storage map) internal {
uint256 len = length(map);
for (uint256 i = 0; i < len; ++i) {
delete map._values[map._keys.at(i)];
}
map._keys.clear();
}

/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(StringToStringMap storage map, string memory key) internal view returns (bool) {
return map._keys.contains(key);
}

/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function length(StringToStringMap storage map) internal view returns (uint256) {
return map._keys.length();
}

/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(
StringToStringMap storage map,
uint256 index
) internal view returns (string memory key, string memory value) {
key = map._keys.at(index);
value = map._values[key];
}

/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(
StringToStringMap storage map,
string memory key
) internal view returns (bool exists, string memory value) {
value = map._values[key];
exists = bytes(value).length != 0 || contains(map, key);
}

/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(StringToStringMap storage map, string memory key) internal view returns (string memory value) {
bool exists;
(exists, value) = tryGet(map, key);
if (!exists) {
revert EnumerableMapNonexistentStringKey(key);
}
}

/**
* @dev Return the an array containing all the keys
*
* 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 map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(StringToStringMap storage map) internal view returns (string[] memory) {
return map._keys.values();
}
}
Loading
Loading