Solidity, the Young Adult
Solidity is one of the oldest EVM-compilable languages around and indeed the winner of the EVM race, probably only akin in survivability to LLL, who finds itself being used in only very select places (like the Vyper compiler, for example) nowadays.
Created by Christian Reitwiessner, Alex Beregszaszi and Gavin Wood in the beginning of 2015, the language borrowed from the C++, JavaScript, and Python worlds and developer adoption early indicated that the language was shaping up to be the best candidate to work in the externally-triggered, resource-bounded world of EVM smart contracts.
Solidity used to be a toddler. Fearless in its decisions, with safety far from its mind, it was versatile and friendly enough to capture the most significant share of early developers in the Ethersphere. But as time went by, growing pains appeared, and what was once a useful tool for the eager first few was no longer a safe tool for the next group.
Solidity is now a young adult! π It learned from its relatives’ glories and mistakes, and its future brings a lot of exciting new features, constructs, and syntax.
In this blog series, we’ll be peeking into that future, starting with what’s coming sooner and then extending our reach far into the future. We want these to not only inform but hopefully spark interesting conversations around the best syntax and use cases.
EXTCODEHASH instead of EXTCODESIZE
available in v0.5.13 - feature was dropped
Under the hood, Solidity is going to use the EXTCODEHASH
opcode instead of the EXTCODESIZE
one for every external call to other contracts that is present in the code.
This means that your codebase will save an extra 300 gas per existing external call! π That’s a lot! π
On another note, this new, post-Constantinople, opcode enables an exciting distinction between empty-code accounts (i.e., contracts still being instantiated, self-destructed ones and externally-owned accounts) and uninstantiated accounts (i.e., accounts that don’t yet exist in the state trie), so maybe we will see some new syntax or design patterns arise around this capability.
For further reading, check out:
- https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol#L18
- https://eips.ethereum.org/EIPS/eip-1052
Update:
From the Solidity team:
EXTCODEHASH
is available as an opcode, but is not yet used for the protection against calling empty contracts, because comparing against the empty code hash is a bit tricky.
The reason for this is that the empty code hash is a non-compressible 32 byte constant, which costs 8576 gas to deploy. So the costs only amortize after 28 calls (see ethereum/solidity#4834 for details). Most contracts will make more than 28 calls in their lifetime, but sometimes, you also need your contract to be small to fit the block gas limit at all to get deployed.
There is, however, another option to compute the empty hash discussed in https://github.com/ethereum/solidity/issues/4834.
The above prose shows the reason why we have not implemented the feature yet and it is not part of v0.5.13.
From Consensys Diligence:
This feature was completely dropped from the roadmap very recently since the price of EXTCODEHASH
will actually be rectified in EIP-1884 to match the 700 gas of the BALANCE
opcode, which also is stored in the account, in the state trie.
For more information on the decision please refer to https://github.com/ethereum/solidity/issues/4834.
Allow getting the address of linked libraries
available in v0.5.13
Until now there was no easy way (read almost impossible) to get an address of a library linked to a compiled smart contract in Solidity, now there is!
You will be able to access this information at runtime through address(LibraryName)
and implement your own delegatecall
s to the library directly.
Again, on another note, this enables some runtime fun. We will now be able to, for example, compare libraries’ addresses across a large and complex codebase (possibly useful with versioned sections or modules deployed at different times) for consistency at runtime.
For further reading, check out:
Abstract contracts should now be explicitly marked so
available in v0.6.0
Every abstract contract (i.e., a contract that does not implement all of its functions) should now be explicitly marked so with a preceding abstract
keyword before its name.
Taking the example from the updated Solidity docs themselves:
pragma solidity >=0.4.0 <0.7.0;
abstract contract Feline {
function utterance() public returns (bytes32);
}
contract Cat is Feline {
function utterance() public override returns (bytes32) {
return "miaow";
}
}
As you might already know, abstract contracts differentiate from interfaces by implementing some but not all the declared methods. Therefore, abstract contracts are meant to be used as building blocks of your codebase (e.g., you’re consuming some standard EIP implementation that leaves some methods for you to customize). Interfaces, on the other hand, just act as a guide for the compiler to be able to reason about how to communicate with contracts external to yours.
An example use case for distinguishing both would be an auction contract that imports interfaces for different tokens and inherits from an auction implementation that still asks you to build custom methods for handling transfers.
For further reading, check out:
Update from the Solidity team:
You have to mark contracts that do not implement all functions as abstract
, but you can also mark contracts that implement all functions as abstract
.
Introducing the override keyword
available in v0.6.0
Now, whenever a function or modifier is overridden in a derived contract, you must explicitly state it with the override keyword.
From Solidity’s changelog:
Language Feature: 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)
An example of a compilable contract in v0.6.0 would be:
pragma solidity >=0.5.0 <0.7.0;
contract Base
{
function foo() public {}
}
contract Inherited is Base
{
// In versions <0.6.0 the compiler would accept this function declaration:
// function foo() public {}
//
// But in >0.6.0 you will have to explicitly state your overriding desires!
function foo() public override {}
}
This is a significant step forward in increased readability for Solidity codebases. Since shadowing problems arise mostly in sizable and complex codebases, having the developer forcefully state their intentions in the early stages of code development prevents problems from surfacing later in the development cycle (and make the auditors’ life easier π).
For further reading, check out:
Update from the Solidity team:
override
will come together with its counterpart keyword virtual
.
Any function that can be overridden has to be marked virtual
and any function that overrides has to be marked as override
.
If a function is overriding but also meant to be overriden it has to be marked virtual override
.
Example:
contract A { function f() public pure virtual {} }
contract B is A { function f() public pure virtual override {} }
contract C is B { function f() public pure override {} }
All of these exciting changes will be here sooner than you know it, so it is always a good practice to keep refactoring your codebase in short periods of time (mainly to bring gas costs down and new built-in security features) or starting to future-proof them if they haven’t been released yet. New language features have security implications that should be thought through before an official release. If you find yourself looking for advice in small diffs like these, contact the Diligence team for a 1-day review. π
We are more than happy to discuss the attack surface of syntax changes with you! πͺ
Stay tuned for the next episode where, among others, we will bring some discussion-heavy topics like:
- Allow structs and
using ... for
in global scope - Imbued SafeMath
- …
DISCLAIMER: Some of these functionalities might not be fully implemented yet and might be subject to spec changes. Please be mindful of this when reasoning about features touched here that are slated for versions far out into the future.
Errata: an error slipped way during the review of the post. In the EXTCODEHASH instead of EXTCODESIZE
section it was stated that distinction was possible between empty contracts and EOAs when that is not the case. The distinction made possible is between empty and uninstantiated accounts.
Addendum: after the release of the blog post we were in touch with Christian Reitwiessner from the Solidity team to correct minor imperfections and provide timely updates on each of the above issues. The last article update was performed on 2019-11-19.
Thinking about smart contract security? We can provide training, ongoing advice, and smart contract auditing. Contact us.