EnumerableSet.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. const format = require('../format-lines');
  2. const { fromBytes32, toBytes32 } = require('./conversion');
  3. const TYPES = [
  4. { name: 'Bytes32Set', type: 'bytes32' },
  5. { name: 'AddressSet', type: 'address' },
  6. { name: 'UintSet', type: 'uint256' },
  7. ];
  8. /* eslint-disable max-len */
  9. const header = `\
  10. pragma solidity ^0.8.20;
  11. /**
  12. * @dev Library for managing
  13. * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
  14. * types.
  15. *
  16. * Sets have the following properties:
  17. *
  18. * - Elements are added, removed, and checked for existence in constant time
  19. * (O(1)).
  20. * - Elements are enumerated in O(n). No guarantees are made on the ordering.
  21. *
  22. * \`\`\`solidity
  23. * contract Example {
  24. * // Add the library methods
  25. * using EnumerableSet for EnumerableSet.AddressSet;
  26. *
  27. * // Declare a set state variable
  28. * EnumerableSet.AddressSet private mySet;
  29. * }
  30. * \`\`\`
  31. *
  32. * As of v3.3.0, sets of type \`bytes32\` (\`Bytes32Set\`), \`address\` (\`AddressSet\`)
  33. * and \`uint256\` (\`UintSet\`) are supported.
  34. *
  35. * [WARNING]
  36. * ====
  37. * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
  38. * unusable.
  39. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
  40. *
  41. * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
  42. * array of EnumerableSet.
  43. * ====
  44. */
  45. `;
  46. /* eslint-enable max-len */
  47. const defaultSet = () => `\
  48. // To implement this library for multiple types with as little code
  49. // repetition as possible, we write it in terms of a generic Set type with
  50. // bytes32 values.
  51. // The Set implementation uses private functions, and user-facing
  52. // implementations (such as AddressSet) are just wrappers around the
  53. // underlying Set.
  54. // This means that we can only create new EnumerableSets for types that fit
  55. // in bytes32.
  56. struct Set {
  57. // Storage of set values
  58. bytes32[] _values;
  59. // Position of the value in the \`values\` array, plus 1 because index 0
  60. // means a value is not in the set.
  61. mapping(bytes32 value => uint256) _indexes;
  62. }
  63. /**
  64. * @dev Add a value to a set. O(1).
  65. *
  66. * Returns true if the value was added to the set, that is if it was not
  67. * already present.
  68. */
  69. function _add(Set storage set, bytes32 value) private returns (bool) {
  70. if (!_contains(set, value)) {
  71. set._values.push(value);
  72. // The value is stored at length-1, but we add 1 to all indexes
  73. // and use 0 as a sentinel value
  74. set._indexes[value] = set._values.length;
  75. return true;
  76. } else {
  77. return false;
  78. }
  79. }
  80. /**
  81. * @dev Removes a value from a set. O(1).
  82. *
  83. * Returns true if the value was removed from the set, that is if it was
  84. * present.
  85. */
  86. function _remove(Set storage set, bytes32 value) private returns (bool) {
  87. // We read and store the value's index to prevent multiple reads from the same storage slot
  88. uint256 valueIndex = set._indexes[value];
  89. if (valueIndex != 0) {
  90. // Equivalent to contains(set, value)
  91. // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
  92. // the array, and then remove the last element (sometimes called as 'swap and pop').
  93. // This modifies the order of the array, as noted in {at}.
  94. uint256 toDeleteIndex = valueIndex - 1;
  95. uint256 lastIndex = set._values.length - 1;
  96. if (lastIndex != toDeleteIndex) {
  97. bytes32 lastValue = set._values[lastIndex];
  98. // Move the last value to the index where the value to delete is
  99. set._values[toDeleteIndex] = lastValue;
  100. // Update the index for the moved value
  101. set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
  102. }
  103. // Delete the slot where the moved value was stored
  104. set._values.pop();
  105. // Delete the index for the deleted slot
  106. delete set._indexes[value];
  107. return true;
  108. } else {
  109. return false;
  110. }
  111. }
  112. /**
  113. * @dev Returns true if the value is in the set. O(1).
  114. */
  115. function _contains(Set storage set, bytes32 value) private view returns (bool) {
  116. return set._indexes[value] != 0;
  117. }
  118. /**
  119. * @dev Returns the number of values on the set. O(1).
  120. */
  121. function _length(Set storage set) private view returns (uint256) {
  122. return set._values.length;
  123. }
  124. /**
  125. * @dev Returns the value stored at position \`index\` in the set. O(1).
  126. *
  127. * Note that there are no guarantees on the ordering of values inside the
  128. * array, and it may change when more values are added or removed.
  129. *
  130. * Requirements:
  131. *
  132. * - \`index\` must be strictly less than {length}.
  133. */
  134. function _at(Set storage set, uint256 index) private view returns (bytes32) {
  135. return set._values[index];
  136. }
  137. /**
  138. * @dev Return the entire set in an array
  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 set grows to a point where copying to memory consumes too much gas to fit in a block.
  144. */
  145. function _values(Set storage set) private view returns (bytes32[] memory) {
  146. return set._values;
  147. }
  148. `;
  149. const customSet = ({ name, type }) => `\
  150. // ${name}
  151. struct ${name} {
  152. Set _inner;
  153. }
  154. /**
  155. * @dev Add a value to a set. O(1).
  156. *
  157. * Returns true if the value was added to the set, that is if it was not
  158. * already present.
  159. */
  160. function add(${name} storage set, ${type} value) internal returns (bool) {
  161. return _add(set._inner, ${toBytes32(type, 'value')});
  162. }
  163. /**
  164. * @dev Removes a value from a set. O(1).
  165. *
  166. * Returns true if the value was removed from the set, that is if it was
  167. * present.
  168. */
  169. function remove(${name} storage set, ${type} value) internal returns (bool) {
  170. return _remove(set._inner, ${toBytes32(type, 'value')});
  171. }
  172. /**
  173. * @dev Returns true if the value is in the set. O(1).
  174. */
  175. function contains(${name} storage set, ${type} value) internal view returns (bool) {
  176. return _contains(set._inner, ${toBytes32(type, 'value')});
  177. }
  178. /**
  179. * @dev Returns the number of values in the set. O(1).
  180. */
  181. function length(${name} storage set) internal view returns (uint256) {
  182. return _length(set._inner);
  183. }
  184. /**
  185. * @dev Returns the value stored at position \`index\` in the set. O(1).
  186. *
  187. * Note that there are no guarantees on the ordering of values inside the
  188. * array, and it may change when more values are added or removed.
  189. *
  190. * Requirements:
  191. *
  192. * - \`index\` must be strictly less than {length}.
  193. */
  194. function at(${name} storage set, uint256 index) internal view returns (${type}) {
  195. return ${fromBytes32(type, '_at(set._inner, index)')};
  196. }
  197. /**
  198. * @dev Return the entire set in an array
  199. *
  200. * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
  201. * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
  202. * this function has an unbounded cost, and using it as part of a state-changing function may render the function
  203. * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
  204. */
  205. function values(${name} storage set) internal view returns (${type}[] memory) {
  206. bytes32[] memory store = _values(set._inner);
  207. ${type}[] memory result;
  208. /// @solidity memory-safe-assembly
  209. assembly {
  210. result := store
  211. }
  212. return result;
  213. }
  214. `;
  215. // GENERATE
  216. module.exports = format(
  217. header.trimEnd(),
  218. 'library EnumerableSet {',
  219. [defaultSet(), TYPES.map(details => customSet(details).trimEnd()).join('\n\n')],
  220. '}',
  221. );