Due to the new built-in overflow checks in Solidity 0.8, which mark the end of the SafeMath
era, this 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.
If you’ve seen the beta announcement, you should be familiar with most of these items.
Solidity 0.8
This release only supports Solidity 0.8. Optimizations and new features will not be backported to previous Solidity versions.
We hope that future Solidity releases will allow us to support them with backwards compatible changes.
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.
Breaking Changes
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.
Note that these optimizations required signicant breaking changes and will not be backported to older Solidity verisons.
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.
The previous behavior, storing URIs in storage, is now available in the extension ERC721URIStorage
.
Check out ERC721
, ERC721Enumerable
, and ERC721URIStorage
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.
What’s Next?
🧙
We have a really exciting tool coming up in the next few days, that will allow to quickly bootstrap a project using OpenZeppelin Contracts. We think this will be useful for developers of all levels, but we’re specially focused on helping those that are new to Ethereum. This will be a way to quickly get started without spending hours fighting a new language to get something to compile. Stay tuned!
Governance
There has recently been a lot of interest in on-chain governance. Many projects are building their governance by incrementally modifying Compound’s Governor Alpha contract, so we’re looking into providing a set of contracts designed to fill this need.
If you’re thinking about adding on-chain governance to your project and this sounds like something you would use, we’d love to talk to you and collaborate! We want to better understand the needs of users in the ecosystem and how we could best contribute something valuable to this space.
More Features
A new and lighter proxy pattern based on UUPS proxies, to become the new default in our Upgrades Plugins for Hardhat and Truffle.
An ERC20 variant with flash minting, as specified in ERC3156.
Get Started
Install from npm with npm install @openzeppelin/contracts
Alternatively, install the upgradeable version with npm install @openzeppelin/contracts-upgradeable
.
Finally, if you are looking for an easy way to manage and automate your smart contract operations after your contracts have been deployed, you can learn more about OpenZeppelin Defender and sign up for a free account.