Optimize smart contract code for bytecode size


Recently when working on a set of smart contracts I ran into an issue, where several of them could not be deployed because they would overflow the block gas limit.

What are the costs of deploying a contract?

As per the Yellow Paper there is a 21000 gas Gtransaction fee paid on all transaction on the Ethereum network. To this we need to add 32000 Gcreate fee for a contract creation transaction. Finaly we pay for the transaction data size, that is for the bytecode size of the compiled contract binary, which is respectively:

  • Gtxdatazero costs of 4 gas per zero byte.
  • Gtxdatanonzero costs of 68 gas per non-zero byte.

This prompted me to add the support for to the optimizer to the lein-solc plugin, to ensure that they can be deployed free and clear.

Using the optimizer

Optimizer is supported since the version 1.0.2 of the plugin:

:plugins [[lein-solc "1.0.2"]]

To activate the optimizer for the contracts that rely on it you pass a map with contract filenames as keys and the parameters as values, for example:

:solc {:src-path "resources/contracts/src"
       :build-path "resources/contracts/build/"
       :contracts :all
       :solc-err-only true
       :wc true
       :optimize-runs {"MyContract.sol" 1}}

Choosing the parameter value

solc optimizer works with a linear trade-off between the size of the code (which in turn increases the deployment costs) and the runtime costs (costs of transacting with the contract). The parameter n can take any positive integer value between 0 and +infinity that represents this tradeoff. Simplifyng a bit we can say it represents how many times the contract will be run after it is deployed.

For example if the contract is to be invoked only once and then subsequently destroyed, a good value would be 1. But if the contract is going to get executed hundreds of times, than increasing that value (from its default of 200) to refelect that is a good idea.

Written on October 18, 2018