π§ Why Gas Optimization Matters
Every operation in Ethereum consumes gas, a unit that represents computational cost. Efficient contracts cost users less to interact with, improve UX, and help your dApp scale. Plus, gas optimization often aligns with better logic and cleaner code.
In this blog, we'll walk through:
- β’Common gas inefficiencies and how to fix them
- β’Tools like Foundry for gas benchmarking
- β’Real Solidity code examples
- β’Practical optimization patterns
βοΈ Setup: Foundry for Gas Benchmarking
We'll use Foundry, a powerful tool for Ethereum development, to benchmark gas usage. Install it with:
curl -L https://foundry.paradigm.xyz | bash
foundryup
bashCreate a new project:
forge init GasOptimization
cd GasOptimization
bashβ οΈ Common Gas Pitfalls and Fixes
- β’π Avoid Unbounded Loops
// β Bad
function sum(uint[] memory arr) public pure returns (uint) {
uint total;
for (uint i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}
solidityWhy it's bad: The loop depends on input size. Gas costs become unbounded.
β Fix: Avoid loops for storage/memory writes. Offload to frontends or batch with limits.
- β’π¦ Use uint256 Over Smaller Types
// β Bad
uint8 a = 1;
uint8 b = 2;
solidityWhy it's bad: EVM reads in 32-byte words. Mixing types causes inefficient packing.
β Fix: Use uint256 everywhere. Itβs the EVMβs native word size and avoids packing issues.
uint256 a = 1;
uint256 b = 2;
solidity- β’πΎ Pack Storage Variables
// β Unpacked
uint256 a;
bool b;
uint256 c;
solidityWhy it's bad: Each storage slot costs 20,000 gas. Unpacked variables waste space.
β Fix: Pack variables into fewer slots. Use structs or arrays to group related data.
// β
Packed
unint256 a;
unint256 b;
bool c;
solidity- β’ποΈ Use calldata for External Function Parameters
// β Memory
function process(uint[] memory data) external {}
// β
Calldata
function process(uint[] calldata data) external {}
soliditycalldata is cheaper for external functions since it avoids memory allocation.
- β’π§ Short-Circuit Booleans
// β Evaluates both
if (checkA() && checkB()) {}
// β
Short-circuits if checkA() is false
if (!checkA()) return;
if (checkB()) {}
solidityπ§ͺ Benchmarking With Foundry
Letβs create a simple test to compare gas.
Example Contract
// SPDX-License-Identifier: MIT
// src/GasExample.sol
pragma solidity ^0.8.24;
contract GasExample {
// Inefficient
function sumMemory(uint[] memory arr) external pure returns (uint total) {
for (uint i = 0; i < arr.length; i++) {
total += arr[i];
}
}
// Efficient (calldata)
function sumCalldata(uint[] calldata arr) external pure returns (uint total) {
for (uint i = 0; i < arr.length; i++) {
total += arr[i];
}
}
}
solidityTest File
// SPDX-License-Identifier: MIT
// test/GasExample.t.sol
pragma solidity ^0.8.24;
import "forge-std/Test.sol";
import "../src/GasExample.sol";
contract GasExampleTest is Test {
GasExample gasExample;
function setUp() public {
gasExample = new GasExample();
}
function testGasSumMemory() public view {
uint[] memory arr = new uint[](5);
for (uint i = 0; i < 5; i++) {
arr[i] = i;
}
uint result = gasExample.sumMemory(arr);
// Expected result: 0 + 1 + 2 + 3 + 4 = 10
assertEq(result, 10);
}
function testGasSumCalldata() public view {
uint[] memory arr = new uint[](5);
for (uint i = 0; i < 5; i++) {
arr[i] = i;
}
uint result = gasExample.sumCalldata(arr);
// Expected result: 0 + 1 + 2 + 3 + 4 = 10
assertEq(result, 10);
}
}
solidityRun Benchmark
forge test --gas-report
bashYou will see gas usage for each function. Compare the results to see the impact of optimizations.
| Function Name | Max |
|----------------------|-------|
| testGasSumCalldata | 2226 |
| testGasSumMemory | 3699 |
calldata saved us over 1,000 gas compared to memory!
β General Optimization Checklist
- β’Use the latest Solidity version for optimizations
- β’Avoid unbounded loops and recursion
- β’Use calldata for external function parameters
- β’Pack storage variables efficiently
- β’Short-circuit boolean expressions
- β’Minimize state changes in functions
- β’Leverage libraries like OpenZeppelin for common patterns
π§ Final Thoughts
Gas optimization isn't about micro-obsessing β itβs about knowing where it matters. If your contract will be called millions of times or touches expensive opcodes, optimize. Otherwise, prioritize readability and security first.
π Further Reading
- β’https://ethereum.org/en/developers/docs/gas/
- β’https://www.rareskills.io/post/gas-optimization
- β’Foundry Documentation for Testing and Benchmarking