Membedah layout storage Solidity berbasis slot 32-byte, teknik packing untuk menempatkan beberapa variabel dalam satu slot, strategi menghindari SSTORE yang mahal, serta kapan memakai immutable dan constant untuk menekan biaya gas

Setelah di episode 5 kalian menguasai fungsi, modifier, dan visibility, pada episode ini kita membahas topik yang paling berpengaruh pada biaya: bagaimana EVM menyimpan state dan bagaimana memilih tipe data agar murah.
Mengapa episode ini penting? Karena di blockchain, efisiensi = uang. Dua kontrak dengan fungsi identik bisa berbeda biaya deploy maupun biaya transaksi berkali-kali lipat hanya karena cara state variable disusun. Di dunia kerja, optimasi gas adalah pembeda antara kontrak yang layak dipakai dan kontrak yang terlalu mahal untuk dioperasikan.
Storage adalah array besar dari slot berukuran 32 byte yang berurutan. State variable ditempatkan secara berurutan mulai dari slot 0, dan packing otomatis terjadi: variabel-variabel kecil yang totalnya masih 32 byte akan digabung dalam satu slot.
uint256 public a; // slot 0 (full 32 byte)
address public b; // slot 1 (20 byte)
bool public c; // slot 1 (1 byte) -> menumpuk dengan b
uint128 public d; // slot 1 (16 byte) -> masih muat di slot 1Urutan di atas menempatkan 4 variabel hanya dalam 2 slot. Bandingkan jika ditulis serampangan:
bool public c; // slot 0
address public b; // slot 0 (muat)
uint256 public a; // slot 1 (full)
uint128 public d; // slot 2 (setengah terisi)Hasilnya 3 slot — lebih boros. Setiap slot terisi (SSTORE) saat deploy membayar biaya, jadi packing yang baik menghemat biaya deploy dan pembacaan.
Packing yang disengaja paling terlihat pada struct:
struct TokenInfo {
uint128 supply; // 16 byte
uint128 maxSupply; // 16 byte -> satu slot dengan supply
address owner; // 20 byte
bool paused; // 1 byte -> satu slot dengan owner
}TokenInfo memakan 2 slot berkat urutan yang rapi. Jika bool paused diletakkan di antara uint128 dan address, hasilnya bisa 3 slot. Aturannya: kelompokkan variabel berukuran kecil berdekatan agar muat dalam satu slot, dan utamakan uint256/besar di slot sendiri.
Tip
Kompiler memberi peringatan "Unused variable" atau rekomendasi layout lewat optimizer; di toolchain modern (Hardhat/Foundry), kalian juga bisa memakai plugin untuk menampilkan storage layout aktual kontrak kalian dan mengecek jumlah slot yang dipakai.
mapping dan array dinamis tidak menyimpan isinya di slot berurutan:
keccak256(key . slotIndex) untuk setiap key. Artinya isi mapping tidak bisa di-enumerasi — konsisten dengan apa yang kita bahas di episode 4.keccak256(slotIndex).Implikasi praktis: mapping itu murah untuk lookup acak, tapi jika kalian butuh urutan atau iterasi, simpan array terpisah.
Menulis storage (SSTORE) adalah operasi termahal. Beberapa strategi klasik:
immutable dan constant — nilai yang ditentukan sekali dan tak berubah bisa disimpan langsung di bytecode (constant) atau di-deploy sekali (immutable), tanpa memakan slot.uint256 public constant MAX_SUPPLY = 1_000_000;
address public immutable deployer;
constructor() {
deployer = msg.sender;
}MAX_SUPPLY dimasukkan ke bytecode, dan deployer di-set sekali saat deploy — keduanya tidak memakai slot storage, sehingga getter-nya murah dan deploy lebih ringan.
function update(uint256 _value) external {
uint256 current = stored; // 1x SLOAD
if (current == _value) return; // skip SSTORE jika sama
stored = _value; // 1x SSTORE
}string/bytes dinamis tanpa perlu — data dinamis disimpan terpisah dan lebih mahal dibaca.Inti yang harus dibawa pulang:
struct/state variable agar packing efisien — hemat deploy dan akses.mapping tersebar via keccak256, tidak iterable; array fixed-size disimpan inline.immutable/constant.Di episode 7 selanjutnya kita akan membahas inheritance, abstract contract, dan interface — mewarisi logika dengan is, menulis kontrak abstrak, dan berinteraksi dengan kontrak lain lewat interface seperti ERC-20 dan ERC-721. Pastikan kalian nyaman dengan layout storage, karena pola ini dipakai di hampir semua kontrak produksi!