Deep EVM #3: Memahami Gas — Mengapa Kontrak Anda Mahal
Engineering Team
Apa Itu Gas dan Mengapa Ada?
Gas adalah unit pengukuran komputasi di Ethereum. Setiap operasi dalam EVM memerlukan sejumlah gas tertentu, dan pengguna membayar gas ini dengan ETH. Gas memecahkan dua masalah fundamental:
- Halting problem — Tanpa batas gas, smart contract bisa berjalan selamanya, membekukan seluruh jaringan.
- Penetapan harga sumber daya — Gas memastikan bahwa penggunaan CPU, storage, dan bandwidth jaringan dihargai secara adil.
Total biaya transaksi = gas_used * gas_price. Sejak EIP-1559, gas_price terdiri dari base_fee (dibakar) + priority_fee (ke validator).
Anatomi Biaya Gas Transaksi
Ketika Anda mengirim transaksi, biaya gas dipecah menjadi beberapa komponen:
1. Gas Intrinsik (21.000 minimum)
Setiap transaksi membayar setidaknya 21.000 gas hanya untuk eksis. Ini mencakup verifikasi tanda tangan, pembaruan nonce, dan biaya overhead jaringan.
2. Biaya Calldata
- 4 gas per byte zero
- 16 gas per byte non-zero
Ini mengapa teknik kompresi calldata penting. EIP-4844 (proto-danksharding) memperkenalkan blob space yang lebih murah untuk data rollup.
3. Biaya Eksekusi
Setiap opcode yang dieksekusi menambah biaya gas. Opcode aritmatika sederhana (ADD, SUB) memerlukan 3 gas. Operasi storage bisa memerlukan ribuan gas.
4. Biaya Ekspansi Memory
Memory EVM tumbuh secara dinamis, dan biaya tumbuh secara kuadratik:
biaya_memory(ukuran) = 3 * a + floor(a^2 / 512)
di mana a = ceil(ukuran / 32)
Untuk 1 KB memory: ~96 gas. Untuk 1 MB: ~3.145.728 gas. Pertumbuhan kuadratik ini mencegah kontrak menggunakan memory berlebihan.
EIP-1559: Mekanisme Biaya Base Fee
Sebelum EIP-1559, gas price ditentukan melalui lelang first-price — pengguna menebak berapa yang harus dibayar. EIP-1559 (London, Agustus 2021) memperkenalkan:
- Base fee — Ditetapkan secara algoritmik berdasarkan utilisasi block sebelumnya. Jika block sebelumnya penuh, base fee naik ~12.5%. Jika kosong, turun ~12.5%.
- Priority fee (tip) — Biaya tambahan langsung ke validator untuk memprioritaskan transaksi Anda.
- Max fee — Jumlah maksimum yang bersedia Anda bayar (base + priority).
// Biaya efektif per gas:
effective_gas_price = min(max_fee, base_fee + priority_fee)
// Yang dibakar:
burned = base_fee * gas_used
// Yang ke validator:
tip = (effective_gas_price - base_fee) * gas_used
Opcode Termahal
Berikut opcode yang paling berdampak pada biaya gas:
| Opcode | Biaya Gas | Penjelasan |
|---|---|---|
| SSTORE (cold, 0→1) | 22.100 | Menulis ke slot storage baru |
| SSTORE (cold, 1→1) | 5.000 | Mengubah slot storage yang ada |
| CREATE | 32.000 | Membuat kontrak baru |
| CREATE2 | 32.000 | Membuat kontrak dengan alamat deterministik |
| SLOAD (cold) | 2.100 | Membaca slot storage pertama kali |
| CALL (cold) | 2.600 | Memanggil kontrak lain pertama kali |
| LOG4 | 1.875+ | Menerbitkan event dengan 4 topik |
| EXTCODESIZE (cold) | 2.600 | Memeriksa ukuran kode kontrak |
Teknik Optimasi Gas
1. Caching Storage
Pola paling berdampak — cache pembacaan storage ke variabel lokal:
// Buruk: 2 SLOAD = 2200 gas (warm)
function buruk() external view returns (uint256) {
return balances[msg.sender] + balances[msg.sender];
}
// Baik: 1 SLOAD + 1 DUP = 103 gas
function baik() external view returns (uint256) {
uint256 bal = balances[msg.sender];
return bal + bal;
}
2. Short-Circuit dengan Assembly
// Solidity menghasilkan branching yang berat
require(a > 0 && b > 0 && c > 0);
// Yul: single-pass check
assembly {
if iszero(and(and(gt(a, 0), gt(b, 0)), gt(c, 0))) {
revert(0, 0)
}
}
3. Packed Storage
Gabungkan variabel kecil ke dalam satu slot:
// 3 slot storage (66.300 gas untuk menulis semua):
uint256 a; uint256 b; uint256 c;
// 1 slot storage (22.100 gas):
uint64 a; uint64 b; uint128 c;
4. Gunakan Errors Kustom
// String error: ~130+ byte calldata (mahal)
require(x > 0, "Value must be greater than zero");
// Kustom error: 4 byte selector (murah)
error ValueTooLow();
if (x == 0) revert ValueTooLow();
5. Unchecked Arithmetic
Ketika Anda yakin tidak ada overflow:
// Dengan pemeriksaan overflow (default Solidity 0.8+):
for (uint256 i = 0; i < length; i++) { ... }
// Tanpa pemeriksaan (hemat ~60 gas per iterasi):
for (uint256 i = 0; i < length; ) {
// ...
unchecked { ++i; }
}
Gas Refund
Beberapa operasi memberikan refund gas:
- SSTORE (nonzero → zero): Refund 4.800 gas
- Refund dibatasi pada 20% dari total gas yang digunakan (EIP-3529)
Ini berarti membersihkan storage masih menguntungkan, tetapi tidak sebanyak sebelum EIP-3529 yang membatasi refund dari 50% ke 20%.
Access List (EIP-2930)
Anda dapat menyertakan access list dalam transaksi untuk “memanaskan” alamat dan slot storage di muka:
{
"accessList": [
{
"address": "0x1234....",
"storageKeys": ["0x00", "0x01"]
}
]
}
Biaya: 2400 gas per alamat + 1900 per slot. Ini lebih murah daripada akses cold pertama (2600/2100), sehingga menghemat gas ketika Anda tahu slot mana yang akan diakses.
Profiling Gas dengan Foundry
Foundry menyediakan alat untuk menganalisis biaya gas:
# Gas report per fungsi:
forge test --gas-report
# Snapshot untuk perbandingan:
forge snapshot
forge snapshot --diff
# Tracing detail:
forge test -vvvv
Kesimpulan
Optimasi gas adalah campuran ilmu dan seni. Memahami biaya setiap opcode, menggunakan caching storage, mengemas variabel, dan memanfaatkan teknik assembly bisa mengurangi biaya gas secara dramatis. Untuk MEV bot dan kontrak frekuensi tinggi, setiap unit gas yang dihemat langsung diterjemahkan menjadi keuntungan.