Biaya Gas di jaringan utama Ethereum telah menjadi masalah besar, terutama ketika jaringan padat. Pada jam sibuk, pengguna sering kali harus membayar biaya transaksi yang sangat tinggi. Oleh karena itu, mengoptimalkan biaya Gas selama tahap pengembangan kontrak pintar sangat penting. Mengoptimalkan konsumsi Gas tidak hanya secara efektif mengurangi biaya transaksi, tetapi juga meningkatkan efisiensi transaksi, memberikan pengalaman penggunaan blockchain yang lebih ekonomis dan efisien bagi pengguna.

Artikel ini akan memberikan gambaran umum tentang mekanisme biaya Gas di Ethereum Virtual Machine (EVM), konsep inti terkait optimasi biaya Gas, serta praktik terbaik untuk mengoptimalkan biaya Gas saat mengembangkan kontrak pintar. Diharapkan melalui konten ini, dapat memberikan inspirasi dan bantuan praktis bagi pengembang, serta membantu pengguna umum untuk lebih memahami cara kerja biaya Gas EVM, bersama-sama menghadapi tantangan dalam ekosistem blockchain.

Pengantar mekanisme biaya Gas EVM

Di jaringan yang kompatibel dengan EVM, 'Gas' adalah istilah yang digunakan untuk mengukur kapasitas komputasi yang dibutuhkan untuk mengeksekusi operasi tertentu.

Gambar di bawah ini menunjukkan tata letak struktur EVM. Dalam gambar, konsumsi Gas dibagi menjadi tiga bagian: eksekusi operasi, panggilan pesan eksternal, serta membaca dan menulis memori dan penyimpanan.

Sumber: Situs resmi Ethereum [1]

Karena setiap eksekusi transaksi memerlukan sumber daya komputasi, biaya tertentu akan dikenakan untuk mencegah loop tak terbatas dan serangan penolakan layanan (DoS). Biaya yang diperlukan untuk menyelesaikan sebuah transaksi disebut sebagai 'biaya Gas'.

Sejak EIP-1559 (hard fork London) berlaku, biaya Gas dihitung dengan rumus berikut:

Biaya gas = unit gas yang digunakan * (biaya dasar + biaya prioritas)

Biaya dasar akan dihancurkan, dan biaya prioritas akan diberikan sebagai insentif, mendorong validator untuk menambahkan transaksi ke blockchain. Mengatur biaya prioritas yang lebih tinggi saat mengirim transaksi dapat meningkatkan kemungkinan transaksi dimasukkan ke dalam blok berikutnya. Ini mirip dengan 'tip' yang dibayarkan pengguna kepada validator.

1. Memahami optimasi Gas di EVM

Ketika mengompilasi kontrak pintar dengan Solidity, kontrak tersebut diubah menjadi serangkaian 'opcode', yaitu opcodes.

Setiap opcode (seperti membuat kontrak, melakukan panggilan pesan, mengakses penyimpanan akun, dan mengeksekusi operasi di mesin virtual) memiliki biaya konsumsi Gas yang diakui, dan biaya tersebut dicatat dalam yellow paper Ethereum [2].

Setelah beberapa modifikasi EIP, biaya Gas untuk beberapa opcode telah disesuaikan dan mungkin berbeda dari yang ada di yellow paper. Untuk informasi terbaru tentang biaya opcode, silakan lihat di sini [3].

2. Konsep dasar optimasi Gas

Inti dari optimasi Gas adalah memilih operasi yang lebih efisien biaya di blockchain EVM, dan menghindari operasi yang mahal biaya Gas.

Di EVM, operasi berikut memiliki biaya yang lebih rendah:

  • Membaca dan menulis variabel memori

  • Membaca variabel konstan dan tidak berubah

  • Membaca dan menulis variabel lokal

  • Membaca variabel calldata, seperti array dan struktur calldata

  • Panggilan fungsi internal

Operasi yang lebih mahal termasuk:

  • Membaca dan menulis variabel status dalam penyimpanan kontrak

  • Panggilan fungsi eksternal

  • Operasi iterasi

Praktik terbaik untuk mengoptimalkan biaya Gas EVM

Berdasarkan konsep dasar di atas, kami telah menyusun daftar praktik terbaik untuk mengoptimalkan biaya Gas bagi komunitas pengembang. Dengan mengikuti praktik-praktik ini, pengembang dapat mengurangi konsumsi biaya Gas kontrak pintar, menurunkan biaya transaksi, dan menciptakan aplikasi yang lebih efisien dan ramah pengguna.

1. Usahakan untuk mengurangi penggunaan penyimpanan

Di Solidity, Storage (penyimpanan) adalah sumber daya yang terbatas, yang konsumsi Gas-nya jauh lebih tinggi dibandingkan Memory (memori). Setiap kali kontrak pintar membaca atau menulis data dari penyimpanan, akan menghasilkan biaya Gas yang tinggi.

Menurut definisi dalam yellow paper Ethereum, biaya operasi penyimpanan lebih dari 100 kali lebih tinggi daripada operasi memori. Misalnya, instruksi OPcodesmload dan mstore hanya menghabiskan 3 unit Gas, sementara operasi penyimpanan seperti sload dan sstore bahkan dalam kondisi paling ideal, biayanya setidaknya memerlukan 100 unit.

Metode untuk membatasi penggunaan penyimpanan termasuk:

  • Menyimpan data non-permanen di memori

  • Kurangi frekuensi modifikasi penyimpanan: dengan menyimpan hasil menengah di memori, setelah semua perhitungan selesai, baru kemudian hasilnya dialokasikan ke variabel penyimpanan.

2. Pengemasan variabel

Jumlah slot penyimpanan yang digunakan dalam kontrak pintar dan cara pengembang menyatakan data sangat mempengaruhi konsumsi biaya Gas.

Compiler Solidity akan mengemas variabel penyimpanan yang berurutan selama proses kompilasi, dan menggunakan slot penyimpanan 32 byte sebagai unit dasar penyimpanan variabel. Pengemasan variabel adalah dengan cara pengaturan yang baik, sehingga beberapa variabel dapat muat dalam satu slot penyimpanan.

Di sisi kiri adalah cara implementasi yang kurang efisien, yang akan menghabiskan 3 slot penyimpanan; di sisi kanan adalah cara implementasi yang lebih efisien.

Dengan penyesuaian detail ini, pengembang dapat menghemat 20.000 unit Gas (menyimpan satu slot penyimpanan yang tidak digunakan memerlukan 20.000 Gas), tetapi sekarang hanya memerlukan dua slot penyimpanan.

Karena setiap slot penyimpanan mengkonsumsi Gas, pengemasan variabel mengoptimalkan penggunaan Gas dengan mengurangi jumlah slot penyimpanan yang diperlukan.

3. Optimalkan tipe data

Sebuah variabel dapat diwakili oleh berbagai tipe data, tetapi biaya operasi untuk berbagai tipe data juga berbeda. Memilih tipe data yang tepat membantu mengoptimalkan penggunaan Gas.

Misalnya, di Solidity, bilangan bulat dapat dibagi menjadi ukuran yang berbeda: uint8, uint16, uint32, dll. Karena EVM mengeksekusi operasi dalam unit 256 bit, menggunakan uint8 berarti EVM harus terlebih dahulu mengonversinya menjadi uint256, dan konversi ini akan menghabiskan Gas tambahan.

Kita dapat membandingkan biaya Gas antara uint8 dan uint256 melalui kode di gambar. Fungsi UseUint() menghabiskan 120.382 unit Gas, sementara fungsi UseUInt8() menghabiskan 166.111 unit Gas.

Secara terpisah, di sini menggunakan uint256 lebih murah dibandingkan uint8. Namun, jika menggunakan pengemasan variabel yang kami sarankan sebelumnya menjadi berbeda. Jika pengembang dapat mengemas empat variabel uint8 ke dalam satu slot penyimpanan, maka total biaya iterasi mereka akan lebih rendah dibandingkan dengan empat variabel uint256. Dengan cara ini, kontrak pintar dapat membaca dan menulis satu slot penyimpanan dan memasukkan empat variabel uint8 ke dalam memori/penyimpanan dalam satu operasi.

4. Gunakan variabel ukuran tetap sebagai pengganti variabel dinamis

Jika data dapat dikendalikan dalam 32 byte, disarankan menggunakan tipe data bytes32 sebagai pengganti bytes atau strings. Secara umum, variabel ukuran tetap menghabiskan Gas lebih sedikit dibandingkan variabel ukuran variabel. Jika panjang byte dapat dibatasi, sebaiknya pilih panjang minimum dari bytes1 hingga bytes32.

5. Pemetaan dan array

Daftar data di Solidity dapat diwakili oleh dua tipe data: array (Arrays) dan pemetaan (Mappings), tetapi sintaksis dan strukturnya sangat berbeda.

Pemetaan umumnya lebih efisien dan lebih murah dalam banyak kasus, tetapi array memiliki kemampuan untuk diiterasi dan mendukung pengemasan tipe data. Oleh karena itu, disarankan untuk menggunakan pemetaan saat mengelola daftar data, kecuali jika diperlukan untuk diiterasi atau dapat mengoptimalkan konsumsi Gas melalui pengemasan tipe data.

6. Gunakan calldata sebagai pengganti memory

Variabel yang dideklarasikan dalam parameter fungsi dapat disimpan dalam calldata atau memory. Perbedaan utama antara keduanya adalah, memory dapat dimodifikasi oleh fungsi, sedangkan calldata adalah tidak dapat diubah.

Ingat prinsip ini: jika parameter fungsi bersifat read-only, sebaiknya gunakan calldata daripada memory. Ini dapat menghindari operasi penyalinan yang tidak perlu dari calldata fungsi ke memory.

Contoh 1: Menggunakan memory

Ketika menggunakan kata kunci memory, nilai array akan disalin dari calldata yang dikodekan ke memory selama proses dekode ABI. Biaya eksekusi blok kode ini adalah 3.694 unit Gas.

Contoh 2: Menggunakan calldata

Saat membaca nilai langsung dari calldata, lewati operasi memory di tengah. Cara optimasi ini membuat biaya eksekusi turun menjadi hanya 2.413 unit Gas, dengan efisiensi Gas meningkat sebesar 35%.

7. Gunakan kata kunci Constant/Immutable sebisa mungkin

Variabel Constant/Immutable tidak akan disimpan di penyimpanan kontrak. Variabel ini dihitung pada saat kompilasi dan disimpan dalam bytecode kontrak. Oleh karena itu, biaya aksesnya jauh lebih rendah dibandingkan penyimpanan, disarankan untuk menggunakan kata kunci Constant atau Immutable sebisa mungkin.

8. Gunakan Unchecked saat memastikan tidak terjadi overflow / underflow

Ketika pengembang dapat memastikan bahwa operasi aritmetika tidak akan menyebabkan overflow atau underflow, mereka dapat menggunakan kata kunci unchecked yang diperkenalkan di Solidity v0.8.0, untuk menghindari pemeriksaan overflow atau underflow yang tidak perlu, sehingga menghemat biaya Gas.

Dalam gambar di bawah ini, dengan batasan kondisi i

Selain itu, compiler versi 0.8.0 dan yang lebih baru tidak lagi memerlukan penggunaan library SafeMath, karena compiler itu sendiri telah memiliki fitur perlindungan terhadap overflow dan underflow.

9. Optimasi modifier

Kode dari modifier disisipkan ke dalam fungsi yang telah dimodifikasi, dan setiap kali modifier digunakan, kodenya akan disalin. Ini akan meningkatkan ukuran bytecode dan meningkatkan konsumsi Gas. Berikut adalah cara untuk mengoptimalkan biaya Gas modifier:

Sebelum optimasi:

Setelah optimasi:

Dalam contoh ini, dengan merestrukturisasi logika menjadi fungsi internal _checkOwner(), memungkinkan penggunaan kembali fungsi internal tersebut dalam modifier, dapat mengurangi ukuran bytecode dan menurunkan biaya Gas.

10. Optimasi short-circuit

Untuk operator || dan &&, evaluasi logika akan terjadi dengan penilaian short-circuit, yaitu jika kondisi pertama sudah dapat menentukan hasil ekspresi logika, maka kondisi kedua tidak akan dievaluasi.

Untuk mengoptimalkan konsumsi Gas, sebaiknya letakkan kondisi dengan biaya komputasi yang lebih rendah di depan, sehingga memungkinkan untuk melewatkan perhitungan yang mahal.

Saran umum tambahan

1. Hapus kode yang tidak berguna

Jika terdapat fungsi atau variabel yang tidak digunakan dalam kontrak, disarankan untuk menghapusnya. Ini adalah cara paling langsung untuk mengurangi biaya penerapan kontrak dan menjaga ukuran kontrak tetap kecil.

Berikut adalah beberapa saran praktis:

Gunakan algoritma yang paling efisien untuk melakukan perhitungan. Jika hasil dari perhitungan tertentu digunakan langsung dalam kontrak, maka proses perhitungan yang berlebihan harus dihapus. Secara esensial, setiap perhitungan yang tidak digunakan harus dihapus.

Di Ethereum, pengembang dapat menerima imbalan Gas dengan melepaskan ruang penyimpanan. Jika suatu variabel tidak lagi diperlukan, harus menggunakan kata kunci delete untuk menghapusnya, atau mengatur nilainya ke nilai default.

Optimasi loop: hindari operasi loop yang mahal, gabungkan loop sebisa mungkin, dan pindahkan perhitungan berulang keluar dari tubuh loop.

2. Gunakan kontrak yang telah dikompilasi sebelumnya

Kontrak yang telah dikompilasi sebelumnya menyediakan fungsi perpustakaan yang kompleks, seperti enkripsi dan operasi hash. Karena kode tidak dijalankan di EVM, tetapi dijalankan secara lokal di node klien, maka Gas yang dibutuhkan lebih sedikit. Menggunakan kontrak yang telah dikompilasi sebelumnya dapat menghemat Gas dengan mengurangi beban kerja komputasi yang diperlukan untuk mengeksekusi kontrak pintar.

Contoh kontrak yang telah dikompilasi sebelumnya termasuk algoritma tanda tangan digital kurva elips (ECDSA) dan algoritma hash SHA2-256. Dengan menggunakan kontrak yang telah dikompilasi sebelumnya ini dalam kontrak pintar, pengembang dapat mengurangi biaya Gas dan meningkatkan efisiensi aplikasi.

Untuk daftar lengkap kontrak yang telah dikompilasi sebelumnya yang didukung oleh jaringan Ethereum, silakan lihat di sini [4].

3. Gunakan kode assembly inline

Assembly inline (in-line assembly) memungkinkan pengembang untuk menulis kode tingkat rendah yang efisien yang dapat dieksekusi langsung oleh EVM, tanpa perlu menggunakan opcode Solidity yang mahal. Assembly inline juga memungkinkan kontrol yang lebih tepat atas penggunaan memori dan penyimpanan, sehingga lebih lanjut mengurangi biaya Gas. Selain itu, assembly inline dapat melakukan beberapa operasi kompleks yang sulit dicapai hanya dengan menggunakan Solidity, memberikan lebih banyak fleksibilitas untuk mengoptimalkan konsumsi Gas.

Berikut adalah contoh kode yang menghemat Gas menggunakan assembly inline:

Dari gambar di atas, dapat dilihat bahwa dibandingkan dengan kasus penggunaan standar, kasus penggunaan kedua yang menggunakan teknik assembly inline memiliki efisiensi Gas yang lebih tinggi.

Namun, menggunakan assembly inline juga dapat membawa risiko dan rentan terhadap kesalahan. Oleh karena itu, harus digunakan dengan hati-hati dan hanya oleh pengembang yang berpengalaman.

4. Gunakan solusi Layer 2

Menggunakan solusi Layer 2 dapat mengurangi jumlah data yang perlu disimpan dan dihitung di jaringan utama Ethereum.

Solusi Layer 2 seperti rollups, sidechains, dan state channels dapat mengalihkan pemrosesan transaksi dari rantai utama Ethereum, memungkinkan transaksi yang lebih cepat dan lebih murah.

Dengan menggabungkan sejumlah besar transaksi, solusi ini mengurangi jumlah transaksi di blockchain, sehingga mengurangi biaya Gas. Menggunakan solusi Layer 2 juga dapat meningkatkan skalabilitas Ethereum, memungkinkan lebih banyak pengguna dan aplikasi untuk berpartisipasi dalam jaringan tanpa menyebabkan kemacetan karena kelebihan beban jaringan.

5. Gunakan alat dan pustaka optimasi

Ada beberapa alat optimasi yang tersedia, seperti optimizer solc, optimizer build Truffle, dan compiler Solidity Remix.

Alat-alat ini dapat membantu meminimalkan ukuran bytecode, menghapus kode yang tidak berguna, dan mengurangi jumlah operasi yang diperlukan untuk mengeksekusi kontrak pintar. Dengan menggabungkan pustaka optimasi Gas lainnya seperti 'solmate', pengembang dapat secara efektif mengurangi biaya Gas dan meningkatkan efisiensi kontrak pintar.

Kesimpulan

Mengoptimalkan konsumsi Gas adalah langkah penting bagi pengembang, yang tidak hanya dapat meminimalkan biaya transaksi tetapi juga meningkatkan efisiensi kontrak pintar di jaringan kompatibel EVM. Dengan memprioritaskan eksekusi operasi yang hemat biaya, mengurangi penggunaan penyimpanan, memanfaatkan assembly inline, dan mengikuti praktik terbaik lain yang dibahas dalam artikel ini, pengembang dapat secara efektif mengurangi konsumsi Gas kontrak.

Namun, harus diperhatikan bahwa selama proses optimasi, pengembang harus berhati-hati untuk menghindari memasukkan celah keamanan. Dalam proses mengoptimalkan kode dan mengurangi konsumsi Gas, tidak boleh mengorbankan keamanan inheren dari kontrak pintar.

[1] :https://ethereum.org/en/developers/docs/gas/

[2] :https://ethereum.github.io/yellowpaper/paper.pdf

[3] :https://www.evm.codes/

[4] :https://www.evm.codes/precompiled