Сетка BlockFirst
Картинка BlockFirst
Link
Иконка копировать
Иконка скопированно

Solidity Gas Optimization Tips

This article provides a detailed overview of various techniques and recommendations for developing smart contracts in Solidity that help reduce gas consumption during transactions and decrease the overall bytecode size of contracts. The authors explain how to write code efficiently to improve its performance.

10.06.2025
Начинающий уровень
Автор BlockFirst
Share
Иконка поделиться
Link
Иконка копировать
Иконка скопировано
ARTICLE BELOW
Картинка
Превью BlockFirst

function setBoolean(
uint256 _packedBools,
uint256 _boolNumber,
bool _value
) public view returns(uint256) {
if (_value)
return _packedBools | uint256(1) << _boolNumber;
else
return _packedBools & ~(uint256(1) << _boolNumber);
}

require(balance >= amount);
//This check is redundant because the safemath subtract function used below already includes this check.balance = balance.sub(amount);

require(balance >= amount, "Insufficient balance"); //good
require(balance >= amount, "To whomsoever it may concern. I am writing this error message to let you know that the amount you are trying to transfer is unfortunately more than your current balance. Perhaps you made a typo or you are just trying to be a hacker boi. In any case, this transaction is going to revert. Please try again with a lower amount. Warm regards, EVM"; //bad

uint256 hello = 0; //bad, expensive
uint256 world; //good, cheap

(hello, world) = (world, hello)

Превью BlockFirst

Most of the general principles of good programming and optimization apply to Solidity as well, but there are some peculiarities, like those described above, that make optimizing Solidity code more difficult (but interesting). As you work with Solidity, you will learn more and more tricks. However, no matter how many tricks you use, you may still run into the 24KB bytecode size limit when creating complex code. You can split your contracts into multiple contracts using proxies or other techniques, but the limit is still an issue. If you'd like to see this limitation removed, leave your feedback on this GitHub Issue page.

Final thoughts

If you need to deploy multiple copies of the same contract, consider deploying one main contract with an implementation and multiple proxy contracts that delegate their logic to the main contract. This will allow the contracts to use the same logic but store different data.

Using proxy patterns for mass deployment

Within a smart contract, calling internal functions is cheaper than calling public functions. When calling a public function, all parameters are copied into memory and passed to the function again. When calling an internal function, references to the parameters are passed to the function without being copied back into memory. This saves gas, especially if the parameters are large.

Calling internal functions is cheaper

It is generally considered good practice to use small individual functions that perform a single task. However, in Solidity, using lots of small functions costs more gas and requires more bytecode. Complex large functions can complicate testing and auditing, so I don't recommend using them everywhere, but if you want to maximize your contract optimization, it can help.

Using fewer features can be helpful

In addition to being able to turn the optimizer on and off, the Solidity compiler (solc) allows you to configure the number of optimizer runs using the runs parameter. This parameter does not mean how many times the optimizer will run, but indicates how many times you expect to call functions in this smart contract. If the contract is intended for one-time use (for example, for vesting or blocking tokens), you can set runs to 1 - the compiler will create the minimum possible bytecode, but function calls may cost a bit more in gas. If the contract will be used frequently (for example, ERC20 token), it is recommended to set a large value of runs, for example, 1337 - the bytecode will be a bit larger, but function calls, such as transfer, will be cheaper.

Use the optimizer correctly

Storing data through events is much cheaper than through variables. However, data from events cannot be used within the blockchain itself. In addition, work is underway to remove old events, so in the future we may have to run our own nodes to access data from old events. Using events in this way may not be considered ethical, but who am I to judge. I won't tell anyone if you don't either - :)

Use events to store data that is not needed on the blockchain

Solidity provides the rather rare ability to exchange variable values on a single line. Use this instead of temporary variables, XOR operations or arithmetic functions to exchange values. Below is an example showing how to swap variable values:

Use one-line value exchanges

There is no need to check the same condition repeatedly in different places. The most common redundant checks are related to the use of the SafeMath library. This library itself checks for overflows and underflows, so you don't need to perform these checks manually.

Avoid repetitive checks

I managed to reduce the size of one of my contracts from 23.95 KB to 11.9 KB using this trick. You can check out a simple “magic” commit here. Pay attention to the DataStore.sol contract.

Internal functions, on the other hand, are not inlined but called as separate functions. This means their execution is slightly more expensive at runtime, but they significantly reduce redundant bytecode during deployment. Internal functions also help avoid the dreaded "Stack too deep" error, since variables created inside an internal function don’t use the limited stack of the original function, whereas variables created in modifiers share that same stack.

When you call a public function from a library, the bytecode of that function is not included in your contract. This allows you to place complex logic in libraries while keeping the contract size small. Note that library calls require gas and some bytecode. Library calls are made via delegatecall, which means that libraries have access to the same data and the same permissions as the contract. Therefore, using libraries is not advisable for simple tasks. It is also important to remember that the Solidity compiler (solc) inlines internal library functions. Inlining has its advantages but takes up space in the bytecode.

Use Libraries to Save Bytecode

Use short strings with error reasons. You can (and should) add error explanation strings along with require statements to make it easier to understand why a contract call reverted. However, such strings take up space in the bytecode after deployment. Each reason string occupies at least 32 bytes, so make sure your string fits within 32 bytes; otherwise, it will be more expensive.

Use short strings with error reasons

If a variable is not set or initialized, it is automatically assigned a default value (0, false, 0x0, etc., depending on the data type). If you explicitly initialize a variable with its default value, you are simply wasting gas.

There is no need to initialize variables with default values

Using this technique, you can store 256 boolean values in a single storage slot. If you pack booleans the usual way (for example, inside a struct), you can fit at most 32 boolean values in one slot. Use this method only when you need to store more than 32 boolean values.

To set or clear a boolean value, use:

In Solidity, boolean types (bool) are actually implemented as uint8, meaning they occupy 8 bits of storage. A boolean value can only be true or false, so technically only one bit is needed to store it. You can pack up to 256 boolean values into a single 256-bit word.
The simplest way is to use a uint256 variable and use each of its 256 bits to represent individual boolean values. To retrieve a specific boolean value from a uint256, you can use the following function:

Boolean values occupy 8 bits, even though only 1 bit is actually needed.

When you add a function modifier, the code of that function is inserted into the modifier at the placeholder _. This can be understood as “inlining” the function modifiers. In regular programming languages, inlining small pieces of code is usually more efficient and doesn’t have serious drawbacks, but Solidity is not a typical language. In Solidity, the maximum contract size is limited to 24 KB according to EIP-170. If the same code is inlined repeatedly, it increases the overall size, and this limit can be easily exceeded.

Function modifiers can be inefficient

Solidity is a unique language with many subtle quirks. Many things in Solidity work differently than in most other languages because it’s designed to run on the EVM, which has a limited set of features. A few months ago, I wrote a blog post with ten tips for saving gas in Solidity, and it received great feedback. Since then, I’ve gathered even more tips and tricks that I want to share with you. Here they are:

function getBoolean(uint256 _packedBools, uint256 _boolNumber)
public view returns(bool)
{
uint256 flag = (_packedBools >> _boolNumber) & uint256(1);
return (flag == 1 ? true : false);
}

Share
Иконка поделиться
Link
Иконка копировать
Иконка скопированно
Back to blog
Кнопка назад
Original article
кнопка вперед
Share
Иконка поделиться
Link
Иконка копировать
Иконка скопировано
Back to blog
Кнопка назад
Original article
кнопка вперед
сетка BlockFirst
сетка BlockFirst
сетка BlockFirst

For requests from users

hello@blockfirst.io

Icon mail

For business inquiries

business@blockfirst.io

Icon mail

Telegram for quick replies

Icon mail

company

Community

media

By signing up for the newsletter, you can be sure we won't spam you :)

News. Specials. Announcements

To top
© 2025-2026 BlockFirst. All rights reserved.
Сетка BlockFirst
hello@blockfirst.io
For commercial offers
Company
Telegram bot for quick replies
Кнопка копировать
Скопировано