Exploring the new Solidity 0.8 Release

And how to upgrade your contracts to Solidity 0.8

We are getting closer to that Solidity 1.0 release (unless of course after 0.9 comes 0.10). Now Solidity 0.8 has been released only 5 months after the 0.7 release!

Let's explore how you can migrate your contracts today...

Baby Yoda Release

New features & how to use them

Let's look at the two big new features which are the integrated SafeMath and the new error handling.

1. Integrated SafeMath

SafeMath Meme

That's right, you don't need to import the Openzeppelin SafeMath anymore. Best of all, you don't need to do anything to activate the Solidity integrated SafeMath. Just write a + b and it will automatically revert on overflows.

You might see errors in tools like Remix as 0.8 is not fully supported yet. For example overflows don't give you the exact reason yet:

 transact to Solidity08.test errored: VM error: revert. revert 

But this should change in the future.

What if you actually want the code to be able to overflow? Or you are just way too concerned about gas costs?

You can deactivate it by wrapping it with an unchecked form like this:

contract Solidity08 {
    function test() external pure returns(uint256) {
        // with SafeMath
        // will revert

        uint256 x = 0;

        return x;
contract Solidity08 {
    function test() external pure returns(uint256) {
        // not using SafeMath
        // will return type(uint256).max

        uint256 x = 0;
        unchecked { x--; }

        return x;

2. Invalid Opcode replaced by Reverting

Up until now certain operations caused the execution of the INVALID opcode. The issue with this opcode is that it's consuming all remaining gas. This is obviously bad and just not necessary. Why would you waste the gas and donate it to the miners?

For more details, check out the difference between revert and assert here.

Now Solidity is using the revert opcode. To distinguish between regular reverts and these system reverts, Solidity prefixes the return data with an identifier:

  • Regular Revert Errors start with the first four bytes of keccak256(Error(string)) which equals 0x08c379a0
  • System Internal Errors start with the first four bytes of keccak256(Panic(uint256)) which equals 0x4e487b71

Panics come with an additional error identifier. Currently available panics are

  • 0x01: Using assert.
  • 0x11: SafeMath over-/under-flows.
  • 0x12: Divide by 0.
  • 0x21: Conversion into non-existent enum type.
  • 0x22: Incorrectly encoded storage byte array.
  • 0x31: pop() on an empty array.
  • 0x32: Index out of bounds exception.
  • 0x41: Allocating too much memory or creating a too large array.
  • 0x51: Calling a zero-initialized variable of internal function type.

For further details see the new Error handling section in the documentation here.

Revert with panic

Should you migrate already?

Before we see how to migrate, let's first discuss should you migrate?

Depending on the time you read this, the answer may differ. Right now it was only just released and proper support from tools doesn't really exist yet. On top it's not heavily tested and used yet. Expect potential bugs!

What does this mean for you?

Let's go through our checklist.

  • Has Solidity 0.8 been out for several months now, all tools support it and even Openzeppelin Contracts are migrated to 0.8?

    • Yes!
      • Then why are you even asking?

    • No!
      • Are you planning to release to mainnet in the short-term?
        • Yes!
          • Don't migrate yet.

        • No!
          • Are all the tools you want to use already imported and working for 0.8?
            • No!
              • Better wait for that support.

            • Yes!
              • Can you live without the Openzeppelin Contracts?
                • Yes!
                  • Go and use 0.8 already you early-adopter.

                • No!
                  • Consider migrating what you need yourself, or wait.

How to migrate to Solidity 0.8

Alright you said yes to the migration after going through the checklist? Let's see how this can be done...

The migration should in most case be pretty straight-forward. Only some cases where you do strange type conversions could become more difficult.

The changes you have to make for the migration include:

  • ABIEncoderV2 is now the default and automatically activated. The encoder wasn't experimental anymore since 0.6 but the 'pragma experimental' name was just kept for legacy reasons. Now you won't need to add the line anymore.
  • Remove any Openzeppelin SafeMath, you won't need it anymore.
  • Some type conversions might be required.
    • msg.sender and tx.origin are not of type payable by default: Change msg.sender.transfer to payable(msg.sender).transfer
    • Conversion from literals is only allowed if it fits within the given type, so uint256(-1) won't work anymore. Use type(uint256).max instead.
    • Casting types is restricted in some cases when changing the sign multiple times, because the order of casting might play a role for the result. You would now see an error like TypeError: Explicit type conversion not allowed from "int256" to "bytes32". Then manually cast it to a uint256 first.
  • Combine 
    • myContract.functionCall{gas: 10000}{value: 1 ether }()  into
    • myContract.functionCall{gas: 10000, value: 1 ether }()
  • Change x**y**z to (x**y)**z as the default order of execution changed.
  • Change type byte to bytes1

I've left a few details out, for the full changelog and break down of all changes in detail, check out the documentation here.

Markus Waas

Solidity Developer

More great blog posts from Markus Waas

© 2024 Solidity Dev Studio. All rights reserved.

This website is powered by Scrivito, the next generation React CMS.