Read vs write — call gratis vs transaksi berbayar
Ini pembagian paling mendasar saat backend berbicara ke smart contract — dan salah memperlakukan salah satunya seperti yang lain adalah sumber bug arsitektur paling umum bagi pendatang baru.
Intisari
- Read (fungsi
view/pure): dipanggil lewateth_call, tidak butuh gas, tidak butuh private key, tidak butuh menunggu block — hasilnya instan, seperti query database biasa. - Write (fungsi yang mengubah state): butuh transaksi yang ditandatangani private key, dikirim ke mempool, menunggu masuk block, dan berbayar gas.
- Read tidak pernah gagal karena "kehabisan gas" — ia mensimulasikan eksekusi secara lokal di node tanpa benar-benar mengubah apa pun.
- Write butuh nonce yang benar dan gas price yang memadai; salah satunya bisa membuat transaksi tertahan lama di mempool atau gagal total.
- Arsitektur backend yang sehat: read bisa dipanggil sinkron dari request HTTP biasa; write harus asinkron — kirim transaksi, simpan hash-nya, lalu proses terpisah yang memantau konfirmasinya (dibahas di Fase 5).
Dua jalur yang terlihat mirip, perilakunya sangat berbeda
Read (eth_call) | Write (eth_sendRawTransaction) | |
|---|---|---|
| Butuh private key? | Tidak | Ya — untuk menandatangani |
| Butuh gas/biaya? | Tidak | Ya |
| Waktu respons | Instan (milidetik) | Detik sampai menit — menunggu block |
| Bisa mengubah state? | Tidak, sekalipun fungsinya mencoba | Ya |
| Analog Web2 | SELECT ke read-replica | INSERT/UPDATE lewat antrean job |
Kenapa read bisa gratis: eth_call menjalankan bytecode fungsi secara lokal
di node yang kamu tanya, memakai state block terkini, lalu membuang hasil eksekusinya begitu selesai —
tidak pernah disiarkan ke jaringan, tidak pernah masuk block, tidak pernah butuh konsensus. Ini kenapa
fungsi view/pure di Solidity ditolak compiler kalau mencoba menulis state:
compiler tahu hasil tulisannya tidak akan pernah benar-benar tersimpan lewat jalur ini.
Read di web3j — sinkron, seperti query biasa
// Wrapper Java digenerate dari ABI kontrak (dibahas di materi berikutnya)
GoldToken kontrak = GoldToken.load(alamatKontrak, web3j, credentials, gasProvider);
BigInteger saldo = kontrak.balanceOf(alamatPengguna).send();
// .send() di sini tidak mengirim transaksi ke jaringan — nama method
// generik web3j untuk "eksekusi panggilan dan tunggu hasilnya", dipakai
// baik untuk call maupun transaction. Konteksnya (fungsi view atau bukan)
// yang menentukan mana yang sebenarnya terjadi di baliknya.
Write di web3j — asinkron secara alami
TransactionReceipt receipt = kontrak.mintGold(
alamatPengguna, BigInteger.valueOf(100), "bukti-brankas-001"
).send(); // BLOCKING sampai transaksi masuk block — bisa detik sampai menit
if (receipt.isStatusOK()) {
System.out.println("Sukses, block: " + receipt.getBlockNumber());
} else {
System.out.println("Transaksi masuk block tapi REVERT — gas tetap terpakai");
}
Jebakan arsitektur paling umum: memperlakukan write seperti call REST API biasa. Kalau
endpoint API-mu memanggil .send() pada fungsi write lalu langsung menunggu hasilnya secara
sinkron di dalam siklus request HTTP, pengguna akan menatap loading spinner selama puluhan detik
— dan kalau koneksi terputus di tengah jalan, kamu tidak tahu apakah transaksinya sukses, gagal, atau
masih tertahan di mempool. Pola yang benar: endpoint API menerima permintaan, mengirim transaksi, langsung
mengembalikan transactionHash ke klien, lalu proses terpisah (worker/queue) yang memantau
konfirmasinya. Detail penuhnya di Fase 5.
Nonce: kenapa write butuh koordinasi yang tidak dibutuhkan read
Ingat dari Fase 0: tiap transaksi butuh nonce yang urut per akun. Kalau backend-mu mengirim
banyak transaksi write dari satu akun service secara paralel tanpa mengelola nonce secara eksplisit, dua
request yang datang bersamaan bisa mendapat nonce yang sama — salah satunya pasti ditolak node. Ini
konsekuensi langsung yang tidak pernah muncul di jalur read, karena read tidak pernah menyentuh
nonce sama sekali.
Latihan: di kontrak GoldToken milikmu (Fase 2), panggil balanceOf()
lewat Remix — perhatikan tidak ada dialog konfirmasi MetaMask/gas yang muncul. Lalu panggil
mintGold() — perhatikan dialog konfirmasi transaksi muncul. Jelaskan sendiri kenapa
Remix (dan wallet mana pun) tahu persis kapan harus meminta konfirmasi dan kapan tidak, berdasarkan
keyword view/pure di definisi fungsinya.
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.