So, you heard the news: Solidity 0.6.0 is out. And it got some breaking changes! In this post & attached video I want to go through some of the changes hands-on.

Video Walkthrough:

Override and Virtual Keywords

Directly from the Solidity Docs: "Functions can now only be overridden when they are either marked with the virtual keyword or defined in an interface. [...] When overriding a function or modifier, the new keyword override must be used."

Does the binary change with the virtual keyword?

First thing interesting for me was: Can functions with the virtual keyword still be used normally, or does change something? I found the best way to give it a try is to compare the binary of two functions, one with and one without the new virtual keyword.

pragma solidity >=0.5.15 <0.7.0; contract A { uint someUint; function fun() public virtual { someUint = 5; } }

With and without the virtual keyword and the solidity compiler 0.6.0 in Remix selected, you get the following binaries:

6080604052348015600f57600080fd5b50607580601d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063946644cd14602d575b600080fd5b60336035565b005b600560008190555056fea2646970667358221220dfb7d0afd7e3583c74623d39f9241dfde8d6838f5a8bed5215f5922a6b65a6e564736f6c63430006000033 vs 6080604052348015600f57600080fd5b50607580601d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063946644cd14602d575b600080fd5b60336035565b005b600560008190555056fea2646970667358221220d35f5fa4ea349aa4c7b7dc017aee02405d53b8f6bc6cdcdba480a0786027674364736f6c63430006000033

I am not a low-level expert, but it tells me that there is at least something different. If you are an expert, reach out to me, I'd be interested what it exactly is.

What's the difference between Solidity 0.5.15 and 0.6.0 for overriding functions?

Next up, I was interested in the difference between overriding functions from multiple smart contracts between 0.5.15 and 0.6.0. Here is my Test-Smart Contract you can copy and paste in Remix:

pragma solidity >=0.5.15 <=0.6.0; /** * https://solidity.readthedocs.io/en/v0.6.0/060-breaking-changes.html#explicitness-requirements * Functions can now only be overridden when they are either marked with the virtual keyword or * defined in an interface. Functions without implementation outside an interface have to be marked virtual. * When overriding a function or modifier, the new keyword override must be used. When overriding a function * or modifier defined in multiple parallel bases, all bases must be listed in parentheses after the keyword * like so: override(Base1, Base2). * * */ /** * Works in Solidity 0.5.15 * * * contract A { event MyEvent(string _myString); function funA() public { emit MyEvent("from A"); } } contract B { function funA() public { //does nothing } } contract C is B,A { function funA() public { emit MyEvent("from B"); super.funA(); } } /**/ /** * works in 0.6.0 * */ contract A { event MyEvent(string _myString); function funA() public virtual { emit MyEvent("from A"); } } contract B { function funA() public virtual { //does nothing } } contract C is A,B { function funA() public override(B,A) { emit MyEvent("from B"); super.funA(); } } /** * */

Firstly, I have 3 smart contracts. One "A" which emits an event. One Smart Contract "B" which has the same function name "funA" as contract "A" but does nothing. One Smart Contract "C" which uses A and B as Base Smart Contracts.

The first thing you will notice is the new virtual and override keyword in solidity 0.6.0. But how about the order of the override? Does it have any impact?

The answer is: no, it doesn't.

How to increase and decrease the Array-Length in Solidity 0.6.0?

With the new Solidity version it's not allowed to modify the length of the array anymore. You have to use the new member functions push and pop.

My test smart contract to copy,paste and follow along in Remix:

pragma solidity >=0.5.15 <=0.6.0; /** * https://solidity.readthedocs.io/en/v0.6.0/060-breaking-changes.html#explicitness-requirements * Member-access to length of arrays is now always read-only, even for storage arrays. It is no * longer possible to resize storage arrays assigning a new value to their length. * Use push(), push(value) or pop() instead, or assign a full array, which will of * course overwrite existing content. The reason behind this is to prevent storage * collisions by gigantic storage arrays. * * */ /** * works in 0.5.15 * * contract MyContract { uint[] public myUintArray; function add(uint _num) public { myUintArray.length++; myUintArray[myUintArray.length - 1] = _num; } function removeElement() public { myUintArray.length--; } } /* */ /** * works in 0.5.15 AND 0.6.0 * */ contract MyContract { uint[] public myUintArray; function add(uint _num) public { myUintArray.push(_num); } function removeElement() public { myUintArray.pop(); } } /* */

There is nothing much to say. push and pop already existed before and works fine. If you smart contract uses these methods already, you're safe!

State Variable Shadowing in Solidity 0.6.0 is disallowed!

Next up is the state variable shadowing I want to try. From the solidity docs: "State variable shadowing is now disallowed. A derived contract can only declare a state variable x , if there is no visible state variable with the same name in any of its bases."

Here is my smart contract to follow along:

pragma solidity >=0.5.15 <=0.6.0; /** * https://solidity.readthedocs.io/en/v0.6.0/060-breaking-changes.html#explicitness-requirements * State variable shadowing is now disallowed. * A derived contract can only declare a state variable x, * if there is no visible state variable with the same name in any of its bases. */ /** * works in 0.5.14 * * contract A { uint x = 123; } contract B is A { uint x = 234; } /**/ /** * works in 0.6.0 * */ contract A { uint private x = 123; } contract B is A { uint x = 234; } /**/

As soon as the state variable is not visible, you are fine to do what you always did.

Unnamed Fallback function becomes "fallback" and "receive" in Solidity 0.6.0.

I welcome this change a lot. I think it brings some clarity to how these work from the outside and leaves more wiggle-room in terms of handling accidental function calls without overspending ether on pure receiving funds.

Let's have a look at this smart contract to follow along:

pragma solidity ^0.6.0; /** * New Fallback Function and Receive function * for 0.6.0 only * */ contract A { event SomeEvent(address _addr, uint _amount); /** * Will be called when (fallback) is used in Remix */ receive() external payable { emit SomeEvent(msg.sender, msg.value); } /** * Will be called when msg.data is not empty or when receive() doesn't exist * * If not payable => revert-style error on msg.value not empty * */ fallback () external { } }

It was confusing at the beginning, since in Remix you just have one "(fallback)" function to call after the smart contract is deployed. But it works as follows:

Send purely some funds: receive() is called Send msg.data without funds: fallback works just fine and doesn't have to be payable Send msg.data with funds: fallback must be payable in addition, otherwise it will trigger an error

Try/Catch in Solidity 0.6.0 Hands-On Example

I was surprised to see this feature coming up. I would've liked to see some more effort towards guaranteed atomic transactions, but instead it's going the other way round. Definitely there is some strategy behind that will reveal itself over the next months (maybe Eth2 cross-shard transactions?)

Anyways, here is my source code to follow along and try it yourself:

pragma solidity ^0.6.0; contract ContractA { function funARequireFailure() public pure { require(false, "This is an error String"); } function funBRevertFailure() public pure { revert("Error from Contract A"); } function funCAssertFailure() public pure { assert(false); } } contract B { ContractA instA; event Error(string _reason); event LowLevelError(bytes _reason); constructor() public { instA = new ContractA(); } function testRequireTryCatch() public returns(bool) { try instA.funCAssertFailure() { return true; } catch Error(string memory reason) { // This is executed in case // revert was called inside getData // and a reason string was provided. emit Error(reason); return false; } catch (bytes memory lowLevelData) { // This is executed in case revert() was used // or there was a failing assertion, division // by zero, etc. inside getData. emit LowLevelError(lowLevelData); return false; } } }

You can try and see which branches of the "catch" statement it will hit. The example is mostly out of the solidity docs over here: https://solidity.readthedocs.io/en/v0.6.0/control-structures.html#try-catch

If you like to get a more in-depth look at solidity, web3, truffle and all the other tools, then checkout my course here:

And don't forget to sign up for my newsletter and subscribe to my YouTube Channel.

Thanks for reading until the end, here is a 🎂for you.