Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add clear function to Enumerable{Set,Map} #5486

Merged
merged 12 commits into from
Feb 10, 2025
44 changes: 43 additions & 1 deletion contracts/utils/structs/EnumerableSet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pragma solidity ^0.8.20;
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
* - Elements are enumerated and cleared in O(n). No guarantees are made on the ordering.
Amxx marked this conversation as resolved.
Show resolved Hide resolved
*
* ```solidity
* contract Example {
Expand Down Expand Up @@ -114,6 +114,18 @@ library EnumerableSet {
}
}

/**
* @dev Removes all the values from a set. 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 set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function _clear(Set storage set) private {
for (uint256 i = _length(set); i > 0; --i) {
_remove(set, _at(set, i - 1));
}
arr00 marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* @dev Returns true if the value is in the set. O(1).
*/
Expand Down Expand Up @@ -180,6 +192,16 @@ library EnumerableSet {
return _remove(set._inner, value);
}

/**
* @dev Removes all the values from a set. 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 set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(Bytes32Set storage set) internal {
_clear(set._inner);
}

/**
* @dev Returns true if the value is in the set. O(1).
*/
Expand Down Expand Up @@ -253,6 +275,16 @@ library EnumerableSet {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}

/**
* @dev Removes all the values from a set. 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 set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(AddressSet storage set) internal {
_clear(set._inner);
}

/**
* @dev Returns true if the value is in the set. O(1).
*/
Expand Down Expand Up @@ -326,6 +358,16 @@ library EnumerableSet {
return _remove(set._inner, bytes32(value));
}

/**
* @dev Removes all the values from a set. 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 set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(UintSet storage set) internal {
_clear(set._inner);
}

/**
* @dev Returns true if the value is in the set. O(1).
*/
Expand Down
24 changes: 23 additions & 1 deletion scripts/generate/templates/EnumerableSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pragma solidity ^0.8.20;
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
* - Elements are enumerated and cleared in O(n). No guarantees are made on the ordering.
*
* \`\`\`solidity
* contract Example {
Expand Down Expand Up @@ -117,6 +117,18 @@ function _remove(Set storage set, bytes32 value) private returns (bool) {
}
}

/**
* @dev Removes all the values from a set. 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 set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function _clear(Set storage set) private {
for (uint256 i = _length(set); i > 0; --i) {
_remove(set, _at(set, i - 1));
}
arr00 marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* @dev Returns true if the value is in the set. O(1).
*/
Expand Down Expand Up @@ -185,6 +197,16 @@ function remove(${name} storage set, ${type} value) internal returns (bool) {
return _remove(set._inner, ${toBytes32(type, 'value')});
}

/**
* @dev Removes all the values from a set. 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 set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(${name} storage set) internal {
_clear(set._inner);
}

/**
* @dev Returns true if the value is in the set. O(1).
*/
Expand Down
35 changes: 35 additions & 0 deletions test/utils/structs/EnumerableSet.behavior.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,41 @@ function shouldBehaveLikeSet() {
expect(await this.methods.contains(this.valueB)).to.be.false;
});
});

describe('clear', function () {
it('clears a single item', async function () {
await this.methods.add(this.valueA);
await expect(this.methods.length()).to.eventually.equal(1);

await this.methods.clear();

await expect(this.methods.length()).to.eventually.equal(0);
});

it('clears multiple items', async function () {
await this.methods.add(this.valueA);
await this.methods.add(this.valueB);
await this.methods.add(this.valueC);
await expect(this.methods.length()).to.eventually.equal(3);

await this.methods.clear();

await expect(this.methods.length()).to.eventually.equal(0);
});

it('does not revert on empty set', async function () {
await this.methods.clear();
});

it('clear then add value', async function () {
await this.methods.add(this.valueA);
await this.methods.add(this.valueB);
await this.methods.clear();

await this.methods.add(this.valueA);
await expectMembersMatch(this.methods, [this.valueA]);
});
});
}

module.exports = {
Expand Down
1 change: 1 addition & 0 deletions test/utils/structs/EnumerableSet.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ async function fixture() {
methods: getMethods(mock, {
add: `$add(uint256,${type})`,
remove: `$remove(uint256,${type})`,
clear: `$clear_EnumerableSet_${name}(uint256)`,
contains: `$contains(uint256,${type})`,
length: `$length_EnumerableSet_${name}(uint256)`,
at: `$at_EnumerableSet_${name}(uint256,uint256)`,
Expand Down
Loading