When deploying Ethereum smart contracts written in Solidity, transactions costs paid in gas can add up quickly. Finding ways to optimize gas usage is crucial for keeping fees lower. In this post we’ll explore 7 straightforward gas optimization techniques for Solidity developers.
1. Avoid Unnecessary Use of Storage
Writing data to storage is one of the most expensive operations in Solidity. Reduce gas costs by avoiding unnecessary writes. For example, move constant values to memory:
// Expensive
string public constant NAME = "MyContract";
// Cheaper
string memory NAME = "MyContract";
Also be careful about storing large pieces of data on-chain. Use alternative patterns like IPFS for larger data.
- Storing data costs 20,000 gas versus 3 gas for memory.
- Minimize storage by using memory for fixed constants and temporary values.
- Store large data like files off-chain (IPFS) and put hash on-chain.
2. Enable the ABI Encoder v2
The ABI encoder translates data types into the format stored on the blockchain. Enabling the newer v2 encoder saves gas:
pragma solidity ^0.8.0;
// Enable ABI encoder v2
pragma abicoder v2;
contract MyContract {
//...
}
- Newer encoder uses less gas for structs, arrays, function params.
- But may break older clients – test compatibility.
3. Avoid Loops and Complex Computations
Loops that iterate or compute over arrays and mappings are expensive:
// Very expensive loop!
for (uint i = 0; i < users.length; i++) {
// ...
}
Avoid loops when possible. If needed, limit iterations and computations inside them.
- Every iteration costs gas, so minimize loops.
- If needed, limit range and computations per loop.
- Map/filter/reduce patterns often cheaper than for loops.
4. Optimize Order of Expensive Ops
Some operations like SSTOREs are more expensive than SLOADs. Reorder statements to shift cheaper ops first:
mapping(address => uint) public balances;
function deposit() public payable {
// SSTORE then SLOAD
uint balance = balances[msg.sender];
balance += msg.value;
balances[msg.sender] = balance;
// SLOAD then SSTORE (cheaper)
uint balance = balances[msg.sender];
balances[msg.sender] = balance + msg.value;
}
- Do cheaper SLOADs before more expensive SSTOREs.
- Move static calls like address(this) out of loops.
- Be aware of the gas cost order – SLOAD < other ops < SSTORE.
5. Precompute Off-Chain When Possible
Computing values in advance outside the blockchain is cheaper than doing it inside smart contracts:
// On-chain computation
function verify(uint numA, uint numB) {
require(numA * numB < 1000);
}
// Precomputed off-chain
function verify(uint result) {
require(result < 1000);
}
- Do computations like hashes, signatures outside.
- Pass in precomputed values to save on-chain gas.
- Allows using faster languages like Python or Go.
6. Optimize Data Layout
Storing data contiguously in structs and arrays is cheaper than separate variables. Packed data costs less gas.
- Packed slots and contiguous storage is cheaper.
- Group commonly accessed data together.
- Order by size with smallest types first.
7. Use Events over Logs
For debugging, events are a cheaper way to record data than logs:
// More expensive
log("Deposit made");
// Cheaper
event Deposit(address indexed user, uint amount);
emit Deposit(msg.sender, msg.value);
- Events are 75% cheaper for logging data.
- Useful for tracing and debugging.
- Indexed params help filter event logs.
Conclusion
Following gas optimization best practices will allow you to deploy leaner and more efficient smart contracts! Let me know if you need any clarification or have additional questions.
Posted in Blockchain, Ethereum, Smart Contract