Understanding EnumerableSet in Solidity: Mechanics and Pitfalls

When using an EnumerableSet in Solidity, understanding its mechanics and potential pitfalls is very important for secure and effective smart contract development. An EnumerableSet is similar to an array that ensures all elements are unique; it doesn't allow for duplicates.

When you attempt to add an element to the set, it will only be added if it's not already present in the set. This characteristic makes EnumerableSet particularly useful for managing collections of unique items, such as a list of addresses or tokens.

However, a common oversight in many implementations of EnumerableSet is the handling of operation results.

Specifically, both add and remove operations in an EnumerableSet return a boolean value indicating their success (true) or failure (false).

This design means that the operations are non-reverting by default. In other words, if an add operation fails (because the item already exists), or if a remove operation fails (because the item doesn't exist), the transaction doesn't automatically revert.

This behavior (of not strictly requiring the success) can lead to vulnerabilities. For instance, if a contract's logic depends on the successful addition of an element to the set, and this operation fails without causing the transaction to revert, the contract might proceed in an inconsistent state. This could be particularly problematic in scenarios where the integrity of the set's elements is critical for the contract's logic, such as when managing permissions or assets.

The Solution

To mitigate this risk and ensure that your contract behaves as expected, it's essential to enforce the success of these operations. This can be achieved by using Solidity's require statement to assert the success of add and remove operations.

Here's an example of how you might implement this: