OpenZeppelin Contracts 4.0 Beta

Due to the new built-in overflow checks in Solidity 0.8, which mark the end of the SafeMath era, this upcoming release of OpenZeppelin Contracts is a new major version of the library.

We used this opportunity to include a few small breaking changes that we had in the backlog. We’re putting out a beta release to ask for feedback on these changes, and to ask if there are any other breaking changes that people think should be included.

If you want to support the project check out the notes below, try out the code, and let us know your thoughts!


These are the breaking changes that will be included in Contracts 4.0.

Reorganized Repository

The most visible change you will find is that some of the contracts were moved to different directories in the repository and npm package.

The reasoning behind this change was that the previous organization was not very good for discoverability of all of the features we offer, and in many cases could be confusing for new users learning about the project and how to use it.

Not all files were moved, but those that were will require users to change their import paths after upgrading to 4.0. In order to mitigate the impact of this breaking change, we’re also including a script that can be run with npx openzeppelin-contracts-migrate-imports after upgrading, that will automatically adjust import paths in Solidity files.

Check out the contracts directory on GitHub and navigate around.

Storage Optimizations

Given the current high gas prices and upcoming opcode repricing in the Berlin hard fork, we turned our attention to a few places where storage usage could be optimized or entirely removed. These changes result in cheaper deployment and lower runtime costs.

If there are any other optimizations we’ve missed, make sure to let us know so we can include them in the release.

ERC20 Decimals

The decimals number used to be kept in storage and was configurable through an internal _setDecimals function. Both the storage variable and internal function are now removed, and customization of a token’s decimals is now done by overriding the decimals function.

This is a pattern we intend to use more in order to avoid the usage of storage variables for customizing contract behavior.

Check out ERC20 on GitHub.

ERC165

Similarly, supported ERC165 interfaces were kept in storage in order to offer an internal _registerInterface function that made things simple to set up. We’ve now changed this so that storage is no longer used, and instead the functionality is implemented through Solidity overrides. Users are encouraged to do the same for any additional supported interfaces, but the old storage-based implementation is still available as ERC165Storage.

Check out ERC165 and ERC165Storage on GitHub.

ERC721 Enumerability and URIs

Our implementation of the ERC721 token (an NFT) was made enumerable by default in version 3.0.0, with no way to opt out of it. This was controversial because it adds to deployment and runtime costs. We’ve now removed enumerability from the base contract and made it an optional extension.

The metadata extension is still included in our base ERC721 contract, just as ERC20 metadata in our base ERC20 token. These extensions are technically optional but universally included in all tokens. We include them by default so that users don’t have to deal with more inheritance to get the basic requirements set up.

One part of the ERC721 metadata extension that was not well optimized was the fact that token URIs (of which there is one per token id of an NFT) were kept in storage individually. We’ve changed this so that the default behavior does not rely on storage. There is now an internal _baseURI() function that can be overriden to return a string. If this string is available, it will be concatenated with the token ID to generate the token URI. This default behavior can be entirely overriden if a different one is required.

Check out ERC721 and ERC721Enumerable on GitHub.

AccessControl Enumerability

We think AccessControl is a great choice for projects that need to grow beyond the Ownable pattern. It allows defining separate roles for the different tasks in a contract that require authorization, and it provides a lot of flexibility as to how those roles are managed, supporting a hierarchy of roles if necessary.

This contract used to come with enumerability built in (of addresses assigned to each role), with a cost that may have discouraged its use. We decided to make it simpler by removing the default of on-chain enumerability.

The information can still be obtained off-chain by consuming the log of events of a contract. If on-chain enumerability is required, there is a separate AccessControlEnumerable contract that implements it.

Check out AccessControl and AccessControlEnumerable.

Meta Transactions (GSN v2)

The OpenGSN team has been busy working on GSNv2, and GSNv1 was put in maintenance mode. Thus, we’ve removed our contracts for GSNv1 integration and begun adding support for GSNv2.

The new version is a more modular system and requires a lot less code. The core is in ERC2771Context, while MinimalForwarder is a utility contract that can assist during testing of a GSNv2-based system.


SafeMath

As I mentioned at the beginning, Solidity 0.8 marks the end of the SafeMath era, as overflow checks are now built into the compiler.

You will find that SafeMath is still in the repository, athough it is now just a wrapper over the built-in overflow checks. The library was kept to ease the transition, but we encourage users on Solidity 0.8 to remove SafeMath from their contracts and rely on the compiler checks exclusively. We may fully remove it from the repository in a future major release.

See the discussion at https://github.com/OpenZeppelin/openzeppelin-contracts/issues/2465.


Get Started

Remember this is a beta release and there may be small breaking changes before the stable release.

Leave your comments below.

4 Likes