Solidity 0.8.31 Release Announcement
We are excited to announce the release of the Solidity Compiler v0.8.31!
This version of the compiler brings support for the new EVM features introduced by the Fusaka network upgrade, extends the functionality of storage layout specifiers and deprecates the first batch of features scheduled for removal in the 0.9.0 breaking release. We are also adding official ARM Linux builds.
Notable Features and Changes
Fusaka Support
osaka by Default
With Fusaka scheduled to go on the mainnet, we are making it the default target for the compiler. As always, you can still select an older version with --evm-version/settings.evmVersion.
CLZ Opcode
This release of the Solidity Compiler includes support for the CLZ opcode (EIP-7939). The feature was already available in our recent pre-release.
The opcode counts the number of leading zero bits in a 256-bit word. The operation is a basic building block useful for bit manipulation, compression algorithms and some data structures.
In terms of the current compiler implementation, the applications for this opcode are limited, but we are exploring it potential for future optimisations. The place where its benefits will be fully realized is the application layer. Libraries such as solady have many uses for it and it will be able to replace existing utilities such as Math.clz() in OpenZeppelin.
Changes to the Release Process
Linux ARM Releases
Starting with this release, we will be providing official Linux binaries for the ARM architecture. We expect this to be useful not only to developers using Linux as their main operating system, but also users of other systems on ARM hardware running the compiler binary in Linux containers, e.g., using Docker.
Note that ARM support in the compiler is not a new thing - we have already been providing binaries for ARM-based macs and it was always possible to build the compiler from source on Linux on ARM. What we are introducing here is the integration of those binaries into our CI and release infrastructure. While unofficial ARM builds have been available for a while from third-party sources, now you can obtain them from the same source as all the other official compiler binaries. This also means that they go through our full testing process, including checks that the produced bytecode and metadata are identical between all supported platforms.
Introducing Pre-releases
Until now, we only offered full releases of the compiler and nightly builds. With v0.8.31-pre.1 we shared back in October, we introduced a more lightweight process to bridge the gap and let us share experimental features earlier. The pre-release already included some of the features we are presenting here, such as the support for the CLZ opcode.
Check out the Twitter announcement for more details!
Discontinuation of PPA Releases
On every release, we provide compiler binaries via multiple channels, some of which are not widely used. To reduce maintenance burden, we have been planning to discontinue some of them. As the first step, with this release we are officially discontinuing our Ubuntu PPA as a binary distribution channel.
We intend to keep the Docker releases for at least one more release, but they will most likely be discontinued as well if we determine that the usage is marginal. Note, however, that they were migrated from DockerHub to Github's container registry and new binaries will also appear there.
Constants in Storage Layout Specifiers
Version 0.8.31 of the Solidity Compiler further extends the features of storage layout specifiers. It is now possible to use constant variables in the base slot expression:
uint constant OFFSET = 0x10 + 1; contract C layout at 0xAAAA + OFFSET { uint[3] x; // Occupies slots 0xAABB..0xAABD }
Please keep in mind that compile-time evaluation capabilities in Solidity are still very limited and only cover arithmetic expressions involving rational number literals. These calculations are always performed in unlimited precision, so introducing values that have a finite-precision integer type into the expression (through explicit type conversions, use of some builtins, use of ternary operator, etc) will result in a compilation error. This limitation extends to the expressions used to initialize constants.
We are planning to extend the range of allowed expressions a bit further in upcoming releases, in particular by defining special builtins for common expressions to sidestep these limitations, but anything beyond that will require adding a more robust compile-time evaluation system to the language.
Feature Deprecations
The 0.9.0 breaking release will be all about dropping old baggage and making the compiler leaner. In preparation for that we are starting to introduce deprecation warnings for features, which we will be removing. For now these include:
Removal of <address>.send() and <address>.transfer() Functions.
These functions were originally introduced to allow ether transfers without letting the callee perform any complex actions. As the EVM does not offer any straightforward way to only transfer ether without also executing the target contract, some gas always needs to be forwarded, but these functions limit it to a hard-coded stipend of 2300 gas. This used to be just enough to read from storage and emit events, but not to perform reentrant calls and write to storage. Unfortunately, this came with an implicit assumption that opcode pricing will never change, which quickly turned out not to be the case.
Nowadays their use is widely considered an anti-pattern and <address>.call() with empty payload is the recommended alternative. Please keep in mind that this does not provide any reentrancy protection, because by default the amount of gas forwarded to the callee is not limited. If you were relying on send() and transfer() specifically for this property, you should consider a different mechanism, such as a reentrancy guard in transient storage or restructuring your code to use the checks-effects-interactions pattern.
Simple ether transfers are still useful, but there is no way for the compiler to introduce such a mechanism in a future-proof way. This needs a solution at the EVM level, such as the EIP-5920: PAY opcode, which could be exposed as <address>.pay() if/when it becomes available.
Removal of ABI Coder V1.
ABI coder v2 was introduced as an experimental feature all the way back in 0.4.19. At first it had to be explicitly enabled using the ABI coder pragma (which back then was an experimental pragma). We started considering it stable in the 0.7 release cycle and with 0.8.0 it became the default.
v2 is a drop-in replacement for v1 and provides all of its features while adding extra capabilities: support for structs, support for arbitrarily nested arrays and more extensive input validations. The rewrite was necessary to make the ABI coder usable with the IR pipeline. ABI coder v1 is an option only for the legacy evmasm pipeline. In fact, when compiling with --via-ir the compiler ignores the pragma and always uses v2. v2 was implemented in Yul from the very beginning and is usable with both.
ABI coder v1 is completely obsolete by now and will eventually be removed.
Removal of Virtual Modifiers
Just like ordinary functions, modifiers can be declared virtual and overridden in derived contracts. We generally found this feature to be suprising and poorly understood by users, mainly serving as a source of tricky corner cases in the compiler implementation. Since inheritance is already a source of a lot of unnecessary complexity in the language and virtual modifiers are not an essential feature (note that we are not removing the ability to invoke virtual functions from within modifiers), we decided to remove them.
Removal of Contract Comparison Operators
Since variables of contract types are essentially just a thin abstraction around address, historically the language allowed operations that make sense for addresses to be performed on them as well. In fact, until 0.5.0, all address members were also available on contract types.
This has changed with the push for more explicit conversions and nowadays there is a much clearer distinction between addresses and contract types. However, one vestige of the old system is the fact that you can use comparison operators on contracts. And not only == and !=. An inequality such as c < d is still a valid expression when c and d represent contracts. It may not be clear what exactly is being compared here and, since contracts are syntactically quite similar to structs, it is not entirely unreasonable for the user to assume that the result is somehow determined by the contract's members. In 0.9.0 we will start requiring an explicit conversion to address for such operations to make semantics clear.
Removal of memory-safe-assembly Special Comment
Inline assembly allows code that can use memory in a way incompatible with the Solidity's memory model. Because of that, certain memory optimizations are, by default, globally disabled in the presence of any inline assembly block that contains a memory operation or assigns to a Solidity variable in memory.
To side-step this problem, the language allows the programmer to declare that an assembly block is in fact perfectly safe, using the memory-safe annotation:
assembly ("memory-safe") { ... }
However, since it cannot be used on versions released before it was introduced, we also provided an alternative, backwards-compatible temporary mechanism for annotating such blocks using a special Natspec comment:
/// @solidity memory-safe-assembly assembly { ... }
Since 0.9.0 will be not be backwards-compatible with previous versions anyway, we will be removing this alternative mechanism.
Full Changelog
Language Features
- Custom Storage Layout: Allow using constant state variables in the base slot expression.
- DocString Parser: Warn about deprecation of inline assembly special comment memory-safe-assembly.
- Syntax Checker: Warn about deprecation of ABI coder v1.
- Syntax Checker: Warn about deprecation of virtual modifiers.
- Type Checker: Warn about deprecation of send and transfer functions on instances of address.
- Type Checker: Warn about deprecation of comparisons between variables of contract types.
- Yul: Introduce builtin clz(x) for counting the number of leading zero bits in a 256-bit word.
Compiler Features
- ethdebug: Experimental support for instructions and source locations under EOF.
- EVM: Set default EVM Version to osaka.
Bugfixes
- Assembler: Fix not using a fixed-width type for IDs being assigned to subassemblies nested more than one level away, resulting in inconsistent --asm-json output between target architectures.
- Yul Optimizer: Fix edge case in which invalid Yul code is produced by ExpressionSimplifier due to expressions being substituted that contain out-of-scope variables.
Build System:
- Enable Linux arm64 binaries for testing and releases.
- Ubuntu PPA Packages: Discontinue the PPA as a binary distribution channel.
- Update minimum version requirements of Boost to 1.83.0 for non-windows builds and of GCC and Clang to 13.3 and 18.1.3, respectively. Fixes infinite recursion on boost::rational comparison affecting compiler binaries built with GCC<14.0 and Boost<1.75.
How to Install/Upgrade?
To upgrade to the latest version of the Solidity Compiler, please follow the installation instructions available in our documentation. You can download the new version of Solidity here: v0.8.31.
If you want to build from the source code, do not use the source archives generated automatically by GitHub. Instead, use the solidity_0.8.31.tar.gz source tarball or check out the v0.8.31 tag via git.
And last but not least, we would like to give a big thank you to all the contributors who helped make this release possible!