EnumerableMap.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. const format = require('../format-lines');
  2. const { fromBytes32, toBytes32 } = require('./conversion');
  3. const { TYPES } = require('./EnumerableMap.opts');
  4. const header = `\
  5. pragma solidity ^0.8.20;
  6. import {EnumerableSet} from "./EnumerableSet.sol";
  7. /**
  8. * @dev Library for managing an enumerable variant of Solidity's
  9. * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[\`mapping\`]
  10. * type.
  11. *
  12. * Maps have the following properties:
  13. *
  14. * - Entries are added, removed, and checked for existence in constant time
  15. * (O(1)).
  16. * - Entries are enumerated in O(n). No guarantees are made on the ordering.
  17. *
  18. * \`\`\`solidity
  19. * contract Example {
  20. * // Add the library methods
  21. * using EnumerableMap for EnumerableMap.UintToAddressMap;
  22. *
  23. * // Declare a set state variable
  24. * EnumerableMap.UintToAddressMap private myMap;
  25. * }
  26. * \`\`\`
  27. *
  28. * The following map types are supported:
  29. *
  30. * - \`uint256 -> address\` (\`UintToAddressMap\`) since v3.0.0
  31. * - \`address -> uint256\` (\`AddressToUintMap\`) since v4.6.0
  32. * - \`bytes32 -> bytes32\` (\`Bytes32ToBytes32Map\`) since v4.6.0
  33. * - \`uint256 -> uint256\` (\`UintToUintMap\`) since v4.7.0
  34. * - \`bytes32 -> uint256\` (\`Bytes32ToUintMap\`) since v4.7.0
  35. * - \`uint256 -> bytes32\` (\`UintToBytes32Map\`) since v5.1.0
  36. * - \`address -> address\` (\`AddressToAddressMap\`) since v5.1.0
  37. * - \`address -> bytes32\` (\`AddressToBytes32Map\`) since v5.1.0
  38. * - \`bytes32 -> address\` (\`Bytes32ToAddressMap\`) since v5.1.0
  39. *
  40. * [WARNING]
  41. * ====
  42. * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
  43. * unusable.
  44. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
  45. *
  46. * In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
  47. * array of EnumerableMap.
  48. * ====
  49. */
  50. `;
  51. const defaultMap = `\
  52. // To implement this library for multiple types with as little code repetition as possible, we write it in
  53. // terms of a generic Map type with bytes32 keys and values. The Map implementation uses private functions,
  54. // and user-facing implementations such as \`UintToAddressMap\` are just wrappers around the underlying Map.
  55. // This means that we can only create new EnumerableMaps for types that fit in bytes32.
  56. /**
  57. * @dev Query for a nonexistent map key.
  58. */
  59. error EnumerableMapNonexistentKey(bytes32 key);
  60. struct Bytes32ToBytes32Map {
  61. // Storage of keys
  62. EnumerableSet.Bytes32Set _keys;
  63. mapping(bytes32 key => bytes32) _values;
  64. }
  65. /**
  66. * @dev Adds a key-value pair to a map, or updates the value for an existing
  67. * key. O(1).
  68. *
  69. * Returns true if the key was added to the map, that is if it was not
  70. * already present.
  71. */
  72. function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) {
  73. map._values[key] = value;
  74. return map._keys.add(key);
  75. }
  76. /**
  77. * @dev Removes a key-value pair from a map. O(1).
  78. *
  79. * Returns true if the key was removed from the map, that is if it was present.
  80. */
  81. function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
  82. delete map._values[key];
  83. return map._keys.remove(key);
  84. }
  85. /**
  86. * @dev Returns true if the key is in the map. O(1).
  87. */
  88. function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
  89. return map._keys.contains(key);
  90. }
  91. /**
  92. * @dev Returns the number of key-value pairs in the map. O(1).
  93. */
  94. function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
  95. return map._keys.length();
  96. }
  97. /**
  98. * @dev Returns the key-value pair stored at position \`index\` in the map. O(1).
  99. *
  100. * Note that there are no guarantees on the ordering of entries inside the
  101. * array, and it may change when more entries are added or removed.
  102. *
  103. * Requirements:
  104. *
  105. * - \`index\` must be strictly less than {length}.
  106. */
  107. function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32 key, bytes32 value) {
  108. bytes32 atKey = map._keys.at(index);
  109. return (atKey, map._values[atKey]);
  110. }
  111. /**
  112. * @dev Tries to returns the value associated with \`key\`. O(1).
  113. * Does not revert if \`key\` is not in the map.
  114. */
  115. function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool exists, bytes32 value) {
  116. bytes32 val = map._values[key];
  117. if (val == bytes32(0)) {
  118. return (contains(map, key), bytes32(0));
  119. } else {
  120. return (true, val);
  121. }
  122. }
  123. /**
  124. * @dev Returns the value associated with \`key\`. O(1).
  125. *
  126. * Requirements:
  127. *
  128. * - \`key\` must be in the map.
  129. */
  130. function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
  131. bytes32 value = map._values[key];
  132. if (value == 0 && !contains(map, key)) {
  133. revert EnumerableMapNonexistentKey(key);
  134. }
  135. return value;
  136. }
  137. /**
  138. * @dev Return the an array containing all the keys
  139. *
  140. * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
  141. * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
  142. * this function has an unbounded cost, and using it as part of a state-changing function may render the function
  143. * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
  144. */
  145. function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) {
  146. return map._keys.values();
  147. }
  148. `;
  149. const customMap = ({ name, keyType, valueType }) => `\
  150. // ${name}
  151. struct ${name} {
  152. Bytes32ToBytes32Map _inner;
  153. }
  154. /**
  155. * @dev Adds a key-value pair to a map, or updates the value for an existing
  156. * key. O(1).
  157. *
  158. * Returns true if the key was added to the map, that is if it was not
  159. * already present.
  160. */
  161. function set(${name} storage map, ${keyType} key, ${valueType} value) internal returns (bool) {
  162. return set(map._inner, ${toBytes32(keyType, 'key')}, ${toBytes32(valueType, 'value')});
  163. }
  164. /**
  165. * @dev Removes a value from a map. O(1).
  166. *
  167. * Returns true if the key was removed from the map, that is if it was present.
  168. */
  169. function remove(${name} storage map, ${keyType} key) internal returns (bool) {
  170. return remove(map._inner, ${toBytes32(keyType, 'key')});
  171. }
  172. /**
  173. * @dev Returns true if the key is in the map. O(1).
  174. */
  175. function contains(${name} storage map, ${keyType} key) internal view returns (bool) {
  176. return contains(map._inner, ${toBytes32(keyType, 'key')});
  177. }
  178. /**
  179. * @dev Returns the number of elements in the map. O(1).
  180. */
  181. function length(${name} storage map) internal view returns (uint256) {
  182. return length(map._inner);
  183. }
  184. /**
  185. * @dev Returns the element stored at position \`index\` in the map. O(1).
  186. * Note that there are no guarantees on the ordering of values inside the
  187. * array, and it may change when more values are added or removed.
  188. *
  189. * Requirements:
  190. *
  191. * - \`index\` must be strictly less than {length}.
  192. */
  193. function at(${name} storage map, uint256 index) internal view returns (${keyType} key, ${valueType} value) {
  194. (bytes32 atKey, bytes32 val) = at(map._inner, index);
  195. return (${fromBytes32(keyType, 'atKey')}, ${fromBytes32(valueType, 'val')});
  196. }
  197. /**
  198. * @dev Tries to returns the value associated with \`key\`. O(1).
  199. * Does not revert if \`key\` is not in the map.
  200. */
  201. function tryGet(${name} storage map, ${keyType} key) internal view returns (bool exists, ${valueType} value) {
  202. (bool success, bytes32 val) = tryGet(map._inner, ${toBytes32(keyType, 'key')});
  203. return (success, ${fromBytes32(valueType, 'val')});
  204. }
  205. /**
  206. * @dev Returns the value associated with \`key\`. O(1).
  207. *
  208. * Requirements:
  209. *
  210. * - \`key\` must be in the map.
  211. */
  212. function get(${name} storage map, ${keyType} key) internal view returns (${valueType}) {
  213. return ${fromBytes32(valueType, `get(map._inner, ${toBytes32(keyType, 'key')})`)};
  214. }
  215. /**
  216. * @dev Return the an array containing all the keys
  217. *
  218. * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
  219. * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
  220. * this function has an unbounded cost, and using it as part of a state-changing function may render the function
  221. * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
  222. */
  223. function keys(${name} storage map) internal view returns (${keyType}[] memory) {
  224. bytes32[] memory store = keys(map._inner);
  225. ${keyType}[] memory result;
  226. assembly ("memory-safe") {
  227. result := store
  228. }
  229. return result;
  230. }
  231. `;
  232. // GENERATE
  233. module.exports = format(
  234. header.trimEnd(),
  235. 'library EnumerableMap {',
  236. format(
  237. [].concat(
  238. 'using EnumerableSet for EnumerableSet.Bytes32Set;',
  239. '',
  240. defaultMap,
  241. TYPES.map(details => customMap(details)),
  242. ),
  243. ).trimEnd(),
  244. '}',
  245. );