EVM — arsitektur & model gas
Kalau kamu pernah paham kenapa JVM membuat kode Java 'write once, run anywhere', EVM membuat lompatan konseptual yang sama untuk smart contract — dengan satu tambahan yang tidak ada di JVM: setiap instruksi ada harganya.
Intisari
- EVM adalah mesin virtual berbasis stack, terisolasi sepenuhnya dari sistem operasi node — mirip JVM, tapi setiap node di jaringan menjalankan bytecode yang sama dan harus mendapat hasil yang identik.
- Solidity dikompilasi ke bytecode EVM, persis seperti Java dikompilasi ke bytecode JVM.
- Storage (permanen, on-chain) sangat mahal; Memory (sementara, selama eksekusi) jauh lebih murah — ini keputusan desain yang membentuk cara kamu menulis Solidity.
- Tiap opcode (ADD, SLOAD, SSTORE, dst.) punya biaya gas tetap — inilah yang membuat biaya transaksi bisa diperkirakan sebelum dikirim.
- EVM adalah deterministik total: tidak ada angka acak, waktu sistem, atau I/O eksternal langsung — semua input harus datang dari data block itu sendiri, supaya semua node menghasilkan state akhir yang sama persis.
Kenapa butuh mesin virtual sama sekali
Kalau smart contract dijalankan langsung sebagai native code di setiap node, tidak ada jaminan node dengan CPU/OS berbeda akan menghasilkan hasil yang identik — padahal itulah syarat mutlak konsensus. EVM menyelesaikannya seperti JVM menyelesaikan masalah portabilitas: definisikan satu instruction set abstrak, jalankan semuanya di dalam sandbox yang sama, di semua node, apa pun perangkat kerasnya.
| Konsep | JVM | EVM |
|---|---|---|
| Source | Java, Kotlin | Solidity, Vyper |
| Kompilasi ke | Java bytecode (.class) | EVM bytecode |
| Dijalankan di | Satu JVM per proses | Setiap node, hasil harus identik |
| Model eksekusi | Stack-based | Stack-based |
| Sumber daya | Heap, dibatasi -Xmx | Gas, dibatasi gasLimit |
| Kalau kehabisan sumber daya | OutOfMemoryError | Transaksi revert, gas tetap hangus |
Storage vs Memory vs Calldata
Perbedaan yang paling sering membingungkan pendatang, dan langsung berdampak ke biaya nyata dalam rupiah:
| Storage | Memory | Calldata | |
|---|---|---|---|
| Analog | Disk (permanen) | RAM (sementara) | Parameter read-only |
| Bertahan setelah fungsi selesai? | Ya | Tidak, dihapus | Tidak, dihapus |
| Biaya menulis pertama kali | ~20.000 gas / slot 32 byte | ~3 gas / kata | Termurah — tidak bisa ditulis |
| Dipakai untuk | State variable kontrak | Variabel lokal, array sementara | Parameter fungsi external |
Ini bukan detail teknis remeh — ini keputusan arsitektur. Menulis satu uint256 baru
ke storage berharga ~20.000 gas. Membaca dan mengubah array besar di storage di dalam loop bisa membuat
satu transaksi menghabiskan jutaan gas dan gagal karena melebihi block gas limit. Developer Solidity yang
berpengalaman berpikir "berapa slot storage yang tersentuh", bukan sekadar "berapa baris kode".
Gas: unit komputasi yang bisa diperkirakan
Setiap opcode EVM (penjumlahan, baca storage, panggil kontrak lain, dst.) punya biaya gas tetap yang
ditentukan protokol. Total gas satu transaksi adalah jumlah biaya semua opcode yang dieksekusi. Ini yang
membuat wallet bisa menampilkan estimasi biaya sebelum kamu konfirmasi — bukan tebakan, tapi
simulasi eksekusi yang benar-benar dijalankan (eth_estimateGas) di node.
| Operasi | Kira-kira biaya (gas) |
|---|---|
| Transfer ETH sederhana | 21.000 |
| Penjumlahan/pengurangan sederhana | 3 |
Baca satu slot storage (SLOAD) | ~2.100 (kalau belum pernah diakses di transaksi ini) |
Tulis storage dari nol ke nilai (SSTORE) | ~20.000 |
| Deploy kontrak baru | 32.000 + biaya per byte bytecode |
Determinisme total — tidak ada "kira-kira"
EVM tidak punya akses ke jam sistem yang sesungguhnya, generator angka acak sungguhan, atau panggilan
jaringan keluar. Semua yang terlihat seperti itu (mis. block.timestamp) sebenarnya adalah data
yang sudah disepakati dan tercatat di block header — bukan dibaca langsung dari OS node.
Kalau tidak, dua node yang mengeksekusi transaksi yang sama pada waktu yang sedikit berbeda bisa menghasilkan
state akhir yang berbeda, dan konsensus akan runtuh.
Konsekuensi bagi kamu sebagai developer: jangan pernah menganggap block.timestamp
sebagai sumber keacakan (miner/validator punya kontrol kecil atasnya) dan jangan berasumsi smart contract
bisa "menunggu" atau "polling" sesuatu di luar rantai. Data eksternal harus masuk lewat oracle —
disebut singkat di Fase 2 saat membahas arsitektur tokenisasi RWA, karena harga emas atau status audit
kustodian jelas bukan data yang ada di dalam EVM.
Latihan: di Remix, deploy kontrak kosong lalu satu kontrak dengan satu uint256 public
state variable. Panggil fungsi yang mengisinya untuk pertama kali, catat gas terpakai. Panggil lagi dengan
nilai lain (mengubah slot yang sudah terisi, bukan dari nol), bandingkan gas-nya — dan jelaskan sendiri
kenapa keduanya berbeda berdasarkan tabel storage di atas.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.