How I Reduced My Solidity Smart Contract Size — Practical Guide

Farasat Ali
CoinsBench
Published in
4 min readJul 28, 2023

--

How I Reduced My Solidity Smart Contract Size — Practical Guide
How I Reduced My Solidity Smart Contract Size — Practical Guide

In the ever-evolving world of blockchain technology, Ethereum has emerged as a dominant platform, enabling developers to create decentralized applications and execute smart contracts. These self-executing contracts are at the core of Ethereum's capabilities, facilitating secure and transparent interactions between parties without intermediaries. However, there are some significant challenges that developers face and contract size optimization and transaction cost optimization are some to name.

To deploy a contract on Ethereum, the Max allowable size is 24KB which was introduced in EIP-170. This limitation is also observed in the Layer 2 solutions like Polygon, Arbitrium, etc. When optimizing a contract, developers face a trade-off: reducing the contract size may lead to increased transaction costs while on the other hand, optimizing transaction costs might elevate the deployment cost as the contract size increases.

Without further wasting any time let's start our practical guide :)

Following is the code I am considering which is less than the 24KB limit due to space constraints here.

Unoptimized Ownable Contract
Unoptimized Pausable Contract
Unoptimized Main Contract

1) Replace the Require() with Custom Error:

To decrease contract size, you can replace the require() with Custom Errors. We will put these errors in the if condition and invert the previous condition written in require().

Ownable Contract with Custom Error
Pausable Contract with Custom Error
Main Contract with Custom Error

As you can see here, we have decreased the contract size. Since this is an example contract, that’s why I have used fewer modifiers but the real-world code might contain a lot of modifiers which would result in large contract size saving.

2) Remove Any Strings in Structs

If you have any string field in the struct, remove it, as it takes up a lot of space when used in the struct.

Removing String Field from the struct

3) Move the extra calls to a Proxy Contract:

When we fetch data from the blockchain, we make calls to get data. We can move these calls to another proxy contract to reduce the size of the main contract.

To separate calls into the proxy contract, I have created a proxy contract, an interface, and an abstract contract.

The interface IMainContract contains the types for the main contract which are also required by both abstract and proxy contracts so separating these structs in an interface.

Interface providing structs for Main, Proxy, and Abstract Contracts

The abstract contract contains the function signatures of the main contract that would be called by the proxy contract.

Abstract Contract containing the Function Signatures of Main Contract

Finally, we have our Proxy Contract that contains all our calls:

Proxy Contract consists of calls to the Main Contract

Now we are left with our Main Contract which is very small in size i.e. ~ 8 KiB!

The Main Contract with Extra Calls shifted to the Proxy Contract

4) Use a Library for Custom Errors:

To further reduce our contract size we can move all of our Custom Errors to a library and create a single Custom Error with an indicator that will show what error has occurred.

In my case, I have very less Custom Errors so it did not do too much saving but when you have a lot of errors it will become handy.

ContractError Library containing Custom Error
Ownable Contract using ContractError Library
Pausable Contract using ContractError Library
Proxy Contract using ContractError Library

5) Optimizer:

Up until now, we have not turned on the optimization. Now we will see the effects of optimization with different numbers of the “runs” on the contract size.

Following is the comparison of contract sizes when compiled with different numbers of runs.

Comparison of Contract Sizes on different numbers of Runs

Conclusion:

In real-world scenarios, the effectiveness of these optimizations will depend on the complexity of the smart contract and its particular use case. I would also like to remind you again that there is a tradeoff between the contract size and transaction cost. Developers must find the right balance between contract size and transaction costs to ensure optimal performance and cost-effectiveness for their specific smart contract deployment.

--

--

Tech Savvy. ♥ Web3, Cloud, ML. Love to travel, explore cultures, and watch animes. Visit My Portfolio 👇 https://linktr.ee/faraasat