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.
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.
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.
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
This is a pattern we intend to use more in order to avoid the usage of storage variables for customizing contract behavior.
ERC20 on GitHub.
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
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.
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.
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
MinimalForwarder is a utility contract that can assist during testing of a GSNv2-based system.
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.
Remember this is a beta release and there may be small breaking changes before the stable release.
- Check out the release branch on GitHub.
- Install from npm with
npm install @openzeppelin/contracts@next,
or the upgradeable version with
npm install @openzeppelin/contracts-upgradeable@next
- See the Contracts 4.0 Timeline.
Leave your comments below.