Gas efficiency of 0.8.1

Hey,

I was wondering if there are any recommendations/ changes on how to write contracts so that they use minimal gas (without having to resort to all the hacks with assembly :wink: ).

I am in the progress of migrating the Gnosis Safe contracts to a newer compiler from 0.5.17 and saw that it requires >5% (90k for an ERC20 transfer via the Safe to 96k) additional gas if I upgrade to 0.8.1.

If I upgrade to 0.7.6 I see that the gas usage is actually reduced compared to 0.5.17.

So the primary gas increase comes from the abi v2 encoder. If I switch back to v1 I only need 1% more gas … I am still surprised that this is the case (even if I am turning on the optimizer).

Additionally it seems that the contract size itself is also bigger with 0.8.1 compared to 0.7.6. With 0.8.1 I had to remove code to stay withing the size limit.

I only did the mininmal changes required to get the contract compiling with the corresponding version. If there are recommended changes to avoid some of that gas overhead I would be very much interested.

Cheers
Richard

Note: Here some simple gas tests run with hardhat:

We also saw similar numbers when upgrading an ABI encoder v2 contract from v0.7.5 → v0.8.0 a while back. Specifically, the running gas costs increased by 1.5% (PR here for those that are interested).

Additionally it seems that the contract size itself is also bigger with 0.8.1 compared to 0.7.6.

I don’t find this too surprising. Off the top of my head, new Panic(uint256) reverts probably require additional code that wasn’t there before.

1 Like

0.8.0 also adds checked arithmetic. We are currently making some improvements to the optimizer. If you want, you can try the nightly version of Solidity which already has a simple inliner that was able to save a lot of gas in our tests. I would be very eager to know if this makes a difference for you.

It doesn’t seem that easy to use the nightly builds with hardhat :grimacing:

Ah that’s a shame! You can also just wait another hour…

1 Like

So with 0.8.2 and the optimizer enabled we are more or less on the same level as with 0.7.6

4 Likes

I realized that for 0.7.6 it also increases the bytecode size. This forces you to enable the optimizer. While this is probably not a show stopper, this is also not optimal. It would be nice to increase the contract size limit :frowning: (I know that this is not a Solidity issues :wink: )

Current compiler versions are written in a way that assume that the optimizer is always activated for deployment. I know that this activates more code paths that might contain bugs, but it also has a safety component: It allows you to write smart contract code in safer or more descriptive but more expensive ways which are then reduced to the same code by the optimizer. At least that is the theory :slight_smile:

The Yul intermediate language and the new optimizer were also written in a way that keep the Yul code readable and thus auditable even after optimization.

Makes sense, and might be worth checking out for the Safe. Stupid question: how do I get the intermediate language? If I use ir or irOptimized I get an error that modifier are not implemented. Is this because I am using 0.7.6?

Edit: got it to work, just had to inline the modifier manually :slight_smile:

Edit2: Is yul used already under the hood, or is solidity by default still compiled directly to bytecode?

For using the IR please always use the latest version. I think with 0.7.6/0.8.0 we achieved rather high feature completeness for language constructs, but some edge cases still may be missing. Anything before that is rather lacking. Can you try with 0.8.2 and if you still get modifiers are not implemented open an issue please?

If you use --experimental-via-ir that switches the entire pipeline over to the IR and outputs such as --bin will be going through the IR. Whereas the --ir and --ir-optimized settings just run the first few steps outputting the Yul code, but they do not compile it to the EVM. So --ir --bin would give you EVM bytecode through the legacy pipeline, while --experimental-via-ir --bin would give you EVM bytecde through the new pipeline.

1 Like

You are correct, 0.8.2 can handle modifiers :slight_smile:

So I used viaIR in the settings and it errors with Variable var_module_1207 is 4 slot(s) too deep inside the stack.. Should I open an issue for that? If you are interested I can setup a buildstep that uses this option to try building the latest Safe version with 0.8.2 and run the tests and benchmarks (if it can build it).

Oh actually forgot to mention that you must use --optimize with it :grimacing:

If it fails with the --optimize setting please open an issue with an example code. We are tracking an older version of gnosis-safe failures here: Support compiling GnosisSafe with the IR · Issue #9351 · ethereum/solidity · GitHub. Perhaps you can link your new branch there?

I will setup the build steps and share it here (I will probably merge the 0.8.2 compatible version this week to the development branch). With the optimizer enabled it still fails (Variable param_15 is 2 slot(s) too deep inside the stack)

Edit: I will add the information also to the github issue once available :wink:

1 Like

Added it to our github flow: Add codesize command to benchmarks · gnosis/safe-contracts@5bfa9bb · GitHub (Once Closes #223 remove unnecessary code by rmeissner · Pull Request #275 · gnosis/safe-contracts · GitHub is merged this will always be available for the latest version) If you are interested in any other config let me know and I will add it :wink: