EnumerableSet.js 7.7 KB

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