Teknik optimasi gas tingkat lanjut: calldata vs memory untuk parameter, assembly Yul di hot path, pola storage yang hemat akses, pengukuran gas dengan gas reporter Hardhat, dan pemeriksaan storage packing

Setelah di episode 18 kalian memahami audit dan best practice, pada episode ini kita mengasah keterampilan yang paling dihargai di tim DeFi: optimasi gas tingkat lanjut.
Mengapa episode ini penting? Di protokol yang dipakai ribuan user, selisih beberapa ribu gas per transaksi bisa berarti jutaan dolar biaya kumulatif per tahun. Optimasi gas juga sering menjadi pembeda saat kalian menawar audit atau menarik user. Episode 6 membahas fondasi storage; di sini kita naik level: calldata, assembly, dan tooling pengukuran.
external function menerima parameter reference types. Membacanya dari calldata tidak menyalin — jauh lebih murah daripada memory yang menyalin data:
// Mahal: menyalin seluruh array ke memory
function sumMemory(uint256[] memory data) external pure returns (uint256) {
uint256 total;
for (uint256 i; i < data.length; i++) total += data[i];
return total;
}
// Murah: baca langsung dari calldata
function sumCalldata(uint256[] calldata data) external pure returns (uint256) {
uint256 total;
for (uint256 i; i < data.length; i++) total += data[i];
return total;
}Aturan praktis: parameter external reference types selalu calldata kecuali kalian benar-benar ingin menyalin untuk dimodifikasi. Hal yang sama berlaku untuk bytes dan string.
Inline assembly (Yul) memungkinkan kontrol opcode langsung — kecepatan maksimal di fungsi yang dipanggil sangat sering. Contoh klasik: membaca nilai storage yang diketahui lokasinya.
uint256 private constant PRICE_SLOT = 0;
function getPrice() external view returns (uint256 price) {
assembly {
price := sload(PRICE_SLOT)
}
}Versi assembly lebih hemat gas daripada sload yang dihasilkan kompiler karena menghindari overhead tambahan. Contoh lain yang umum:
// Memisahkan high/low 128-bit dari satu slot
function unpack(uint256 packed) external pure returns (uint128 a, uint128 b) {
assembly {
a := shr(128, packed)
b := and(packed, 0xffffffffffffffffffffffffffffffff)
}
}Yul juga menjadi dasar solady — library optimasi gas yang sangat agresif (menggantikan banyak helper OpenZeppelin di proyek hemat-gas).
Warning
Assembly menonaktifkan banyak pengamanan kompiler — satu baris salah bisa berarti bug fatal yang lolos dari review otomatis. Gunakan hanya untuk hot path yang benar-benar terukur, sertakan komentar yang jelas, dan uji dengan fuzz sebelum production.
Menggabungkan pelajaran episode 6 dan 12:
function process() external {
uint256 balance = balances[msg.sender]; // 1 SLOAD
// pakai balance berkali-kali, bukan panggil balances[msg.sender] ulang
}Hindari menulis nilai yang sama — bandingkan dulu sebelum SSTORE.
Simpan data kecil di packed slot — gabungkan uint128/uint64/bool dalam satu slot (episode 6).
Pindahkan hitungan ke memory di loop — tulis storage sekali setelah loop selesai.
function batch(uint256[] calldata ids) external {
uint256 total;
for (uint256 i; i < ids.length; i++) total += ids[i]; // hitung di memory
totalAccumulated += total; // satu SSTORE
}Jangan menebak — ukur. hardhat-gas-reporter menampilkan biaya gas per fungsi di output test:
npm install --save-dev hardhat-gas-reporter
npx hardhat testOutput tabel menunjukkan gas rata-rata, minimal, dan maksimal per fungsi, plus estimasi biaya USD. Di Foundry, gunakan forge test --gas-report yang serupa.
Dengan angka nyata, optimasi kalian jadi terukur: bandingkan sebelum/sesudah refactor, dan ketahui persis berapa yang dihemat per transaksi.
Plugin seperti hardhat-storage-layout menampilkan layout slot kontrak — alat yang sempurna untuk mengecek packing:
npx hardhat storage-layoutOutput menunjukkan slot mana yang terisi dan apakah variabel kecil sudah ter-packing dengan baik. Di Foundry, kalian bisa memeriksa slot secara manual dengan forge inspect atau langsung cast storage di kontrak yang ter-deploy.
Tip
Urutan prioritas optimasi yang realistis: (1) kurangi jumlah SSTORE dan SLOAD, (2) gunakan calldata, (3) packing struct/state, (4) immutable/constant, lalu (5) assembly hanya untuk hot path terukur. Optimasi di tahap awal membawa dampak terbesar dengan risiko terkecil.
Inti yang harus dibawa pulang:
external reference types → calldata, bukan memory, untuk hemat salin.sload/shr/and langsung, tetapi pakai hanya yang terukur.forge test --gas-report; cek packing dengan storage-layout.Di episode 20 selanjutnya kita akan membahas Layer 2, Pectra & EVM Modern — perbandingan optimistic rollup (Optimism/Base) vs ZK rollup (zkSync, Polygon zkEVM), konsep bridge dan rollup, serta upgrade EVM Pectra, Fusaka, dan Amsterdam dengan EIP-7843 block.slotnum. Sampai jumpa di episode 20!