Tekan ESC untuk menutup

Kerentanan Standar ERC: Panduan Arsitek Smart Contract

Halo bos! Kebetulan lu lagi baca tulisan ini, berarti lu antara emang lagi penasaran banget sama security smart contract, atau lu lagi pusing ngerancang arsitektur protokol DeFi baru dan agak ketar-ketir mikirin besok proyek lu bakal ludes jadi abu gara-gara celah bug yang sepele.

Gini bro, gua di dunia ginian udah lumayan sepuh. Udah ngelewatin masa-masa nge-hackathon, zaman ngulik nyari exploit modal makan pizza sama minum suplemen energi doang, sampe akhirnya sekarang bisa duduk di kursi CTO crypto exchange. Dan asal lu tahu aja, sebagian besar kasus hack parah yang pernah gua usut (dan beberapa sempat gua salahin pas lagi sesi audit), itu jebolnya bukan karena sistem kriptografinya yang rusak. Bukan juga karena compiler Solidity-nya yang error. Kejadian begituan murni karena developer kagak paham konsep dasar gimana behavior antar-standard ERC pas lagi saling berinteraksi di balik layar.

Kita sering mikir kalau standar itu aturan baku yang otomatis bikin aman. Padahal, petaka justru ngumpet di detail implementasi sama efek samping yang kagak kelihatan. Yuk kita bedah di mana para arsitek sering blunder, dan gimana caranya biar lu bisa nge-deploy sistem dengan tenang tanpa perlu begadang tiap malam.

1. Bahaya Tersembunyi ERC-20: Masalah Klasik yang Mematikan

Kelihatannya ERC-20 udah khatam dibahas di mana-mana. Apa sih yang bisa bikin zonk? Ya bisa semuanya, kalau lu asal gabungin token orang lain ke protokol lu tanpa ada validasi yang ketat.

Masalah Kagak Ada Return Value (The No-Return Dilemma)

Kalau ikut spek aslinya, fungsi transfer sama transferFrom itu wajib nge-return data berupa bool. Tapi realitanya, banyak token jadul yang market cap-nya gede (contoh nyata: USDT sama BNB di beberapa contract lawas mereka) kagak ngikutin aturan ini. Mereka kagak ngasih return value apa-apa kalau transaksinya sukses.

Misalkan contract lu ngarep ada balikan bool lewat interface standar kayak gini:

// Jangan pernah pakai cara ini kalau lu nge-handle sembarang token!
IERC20(token).transferFrom(msg.sender, address(this), amount);

Pas interaksi sama USDT, transaksi lu bakalan langsung ketolak alias revert. Kenapa? Soalnya EVM bakal nyari return value di dalam stack, tapi pas dicek ternyata zonk. Atau apesnya lagi, kalau lu kagak ngecek hasil return-nya sama sekali (pakai token.transfer(...) bukannya dibungkus dalam require(token.transfer(...))), ada beberapa token yang pas error malah nge-return value false bukannya nge-revert transaksi. Contract lu bakal tetap jalan lempeng seolah-olah kagak ada masalah. Hasilnya? User bisa manipulasi saldo mereka sendiri dari yang tadinya ghaib jadi ada.

Solusinya: Mulai sekarang lupain cara manggil langsung transfer sama transferFrom. Selalu pakai library SafeERC20 punya OpenZeppelin, terus pakai method safeTransfer / safeTransferFrom. Di balik layar, library ini bakal ngecek low-level return data dan bisa nge-handle contract yang cacat dengan bener.

Weird ERC-20 Tokens: Pas Standar Kagak Sesuai Ekspektasi

Ini ada tabel contekan ringkas soal token-token aneh yang kelakuannya kagak sesuai sama isi buku teks. Sebagai arsitek, lu wajib masukin variabel-variabel ini ke dalam logika liquidity pool lu.

Tipe Token (Weird ERC-20)Sisi Anehnya di Mana?Bahayanya Apa Buat Arsitektur?
Deflationary / Fee-on-Transfer (misal: STA, PAXG)Ngepotong fee langsung pas proses transfer berjalan.Lu mikirnya contract nerima 100 token, padahal yang masuk ke balance cuma 99. Pencatatan likuiditas internal lu bisa berantakan dan bikin defisit token.
Upgradable Proxies (misal: USDC, USDT)Logika token bisa diubah kapan aja sama pihak admin.Fitur blacklist. Kalau alamat contract lu masuk daftar hitam, semua likuiditas bakal nyangkut di dalam dan kagak bakal bisa ditarik selamanya.
Rebasing Tokens (misal: AMPL)Balance di dalam wallet bisa berubah secara dinamis (supply naik-turun buat stabilin harga).Jumlah saldo di dalam contract bisa berkurang atau bertambah sendiri tanpa perlu ada eksekusi fungsi transfer sama sekali.

2. ERC-721 dan ERC-1155: Jebakan onERC721Received dan Serangan Reentrancy

Nah, ini dia topik favorit gua. Udah kagak kehitung berapa banyak platform NFT marketplace sama lending protocol yang rontok gara-gara fungsi safe transfer!

Pas lu manggil fungsi safeTransferFrom di ERC-721 atau ERC-1155, contract token bakal ngecek apakah si penerima itu berupa smart contract atau bukan. Kalau iya, dia bakal nge-trigger fungsi hook onERC721Received atau onERC1155Received di sisi penerima.

Tujuannya buat apa? Biar mastiin kalau contract penerima emang support buat nampung NFT, jadi tuh token kagak bakal nyangkut mati di sana.

Tapi liciknya di mana? Hook ini bakal mindahin kendali eksekusi ke external code yang kagak bisa lu kontrol di tengah-tengah transaksi, tepat sebelum lu sempet ngubah state internal di dalam contract lu sendiri!

Contoh Code Minting / Marketplace yang Rentan

Coba lu liat potongan code ini. Gua sengaja nulis bagian ini buat nunjukin error arsitektur yang paling sering terjadi, yaitu ngelanggar aturan pola Checks-Effects-Interactions.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
contract VulnerableNFCLending {
    // Nyimpen data jaminan: User => ID Token => Status Jaminan Aktif
    mapping(address => mapping(uint256 => bool)) public hasCollateral;
    IERC721 public nftToken;
    constructor(address _nft) {
        nftToken = IERC721(_nft);
    }
    // User mau minjem dana dengan jaminan NFT
    function depositCollateral(uint256 tokenId) external {
        // 1. Interactions: Transfer NFT ke dalam contract
        // Fungsi safeTransferFrom bakal nge-trigger hook onERC721Received di contract penerima? 
        // Bentar, di sini posisi KITA yang jadi penerima. Tapi kalau contract manggil fungsi safeMint... 
        // Oke stop, mari kita ubah konteksnya ke situasi pas kita ngelepas NFT atau pas attacker ngambil alih kendali.
        
        // Kita ubah skenarionya: Contract ngondisiin balik NFT (misal pas penarikan jaminan) 
        // atau ini berupa contract minting yang ngelakuin transfer duluan sebelum ngebaca update state.
    }
}

Biar lebih gampang dipahami, mending gua kasih liat contoh bersih dari contract minting yang punya celah bug. Anggaplah kita punya contract yang ngejagain biar satu wallet cuma bisa nge-mint 1 NFT gratisan.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract VulnerableMint is ERC721 {
    mapping(address => bool) public hasMinted;
    uint256 public currentTokenId;
    constructor() ERC721("DangerNFT", "DNFT") {}
    function freeMint() external {
        // Checks
        require(!hasMinted[msg.sender], "Brother, you already have one!");
        // Interactions (Di dalam _safeMint ada fungsi buat manggil external contract!)
        _safeMint(msg.sender, currentTokenId);
        currentTokenId++;
        // Effects (Update state-nya telat banget, keburu jebol duluan)
        hasMinted[msg.sender] = true;
    }
}

Nah, sekarang liat versi contract si attacker. Dia tinggal nangkep hook callback tadi, ngeliat status hasMinted di contract utama ternyata posisinya masih false, terus tinggal nge-spam manggil fungsi freeMint berulang kali sampe kuotanya ludes atau gas-nya habis.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IVulnerableMint {
    function freeMint() external;
}
contract Attacker {
    IVulnerableMint public target;
    uint256 count;
    constructor(address _target) {
        target = IVulnerableMint(_target);
    }
    function attack() external {
        target.freeMint();
    }
    // Fungsi hook pembawa petaka yang dipanggil otomatis sama standar ERC-721
    function onERC721Received(
        address,
        address,
        uint256,
        bytes calldata
    ) external returns (bytes4) {
        if (count < 5) {
            count++;
            // Reentrancy terjadi di sini! State internal si korban belum sempat berubah
            target.freeMint();
        }
        return this.onERC721Received.selector;
    }
}

Buset, udah sering banget gua nemu kasus ginian pas lagi sesi audit. Banyak dev mikirnya enteng: "Halah, ini kan cuma transfer NFT, bukan kirim native ETH, aman lah kagak bakal kenapa-napa." Kena reentrancy nangis lu, kelar lapak lu dimaling semua.

Golden Rule Buat Arsitek: Selalu update state di awal (hasMinted[msg.sender] = true;), baru abis itu lu bebas mau manggil method minting atau transfer apa aja. Dan satu lagi, inget ya, selalu pasang modifier nonReentrant dari OpenZeppelin di fungsi yang urusannya sama urusan transfer NFT.

Lanjut gas. Karena kita udah bongkar tuntas soal Reentrancy lewat hook, sekarang mari kita kulik isu yang lebih fresh dan subtle. Masalah ini jarang banget dipikirin pas awal ngerancang arsitektur—sampai akhirnya apes dan protokol apes kena exploit di production.

3. ERC-2612 (Permit): Phantom Approval dan Serangan Front-running

Standard ERC-2612 itu penyelamat banget di dunia Web3 berkat adanya fungsi permit. Fitur ini bikin user bisa ttd (sign) pesan offline (EIP-712) buat ngasih izin (approval) spending token, terus biaya gas-nya bisa dilempar ke relayer atau protokol itu sendiri. UX-nya naik kelas banget: dari yang tadinya kudu ribet dua kali transaksi (approve + transferFrom), sekarang user cukup sekali jalan.

Tapi, para architect sering khilaf soal gimana signature ini bekerja di balik layar, walhasil mereka bikin blunder fatal di logika smart contract-nya.

Front-running Signature (Signature Front-running)

Bayangin fungsi deposit klasik di smart contract yang pakai permit kayak gini:

function depositWithPermit(
    uint256 amount,
    uint256 deadline,
    uint8 v,
    bytes32 r,
    bytes32 s
) external {
    // Pertama, eksekusi permit pakai signature yang dikasih user
    IERC20Permit(token).permit(msg.sender, address(this), amount, deadline, v, r, s);
    
    // Abis itu, tarik tokennya
    IERC20(token).transferFrom(msg.sender, address(this), amount);
    
    // Mint poin internal / share pool
    _mintShares(msg.sender, amount);
}

Celahnya di mana? MEV bot mana pun yang lagi nyecan mempool publik pasti bisa ngeliat transaksi menggantung ini. Si bot tinggal comot signature valid (v, r, s) beserta parameter dari tx lu, bikin tx tandingan buat manggil token.permit(...) langsung ke contract token-nya, terus nge-front-run si user dengan pasang Gas Price yang lebih tinggi.

Transaksi si bot bakal masuk duluan. Signature sukses tereksekusi, dan allowance pun terpasang. Tepat setelah itu, transaksi user asli baru masuk ke chain. Tapi karena signature tadi udah hangus dipake bot, otomatis nonce si user di contract token udah naik! Panggilan permit di dalem fungsi depositWithPermit bakal langsung kena revert karena signature-nya udah ga valid lagi.

Efeknya? Transaksi user gagal total, gas terbuang sia-sia, dan UX-nya rusak parah. Lebih apes lagi kalau transaksi itu turns out adalah top-up margin krusial buat nyelametin posisi long yang ke-leverage; gara-gara ketahan delay ini, posisi user bisa langsung kena likuidasi massal.

Cara Ngamanin Arsitekturnya

Bungkus panggillan permit lu pake blok try/catch. Kalau semisal signature-nya udah kesenggol atau di-front-run duluan di jaringan, contract token-nya sebenernya udah dapet nilai allowance yang dibutuhin. Contract lu tinggal abaiin aja error duplikasi signature itu, lalu gas terus buat jalanin proses transferFrom.

try IERC20Permit(token).permit(msg.sender, address(this), amount, deadline, v, r, s) {} 
catch {
    // Kalau gagal—kemungkinan besar signature-nya udah di-front-run. 
    // Cek apakah allowance yang ada sekarang udah cukup buat eksekusi operasi.
    require(IERC20(token).allowance(msg.sender, address(this)) >= amount, "Permit failed and allowance insufficient");
}

Masalah "Phantom Permit"

Nah, kalau yang ini bener-bener asupan rasa sakit internal anak DeFi yang jarang banget dibahas di dokumentasi umum. Apa jadinya kalau protokol DeFi lu nge-support sembarang token (arbitrary tokens), terus ada user iseng manggil depositWithPermit pake token yang sebenernya KAGAK support ERC-2612?

Mungkin lu mikir: "Ya pasti transaksinya gagal lah, kan fungsi permit-nya emang ga ada di situ." Eits, belom tentu!

Kalau contract token tersebut punya fungsi bawaan fallback() atau receive() yang ga nge-revert transaksi pas dapet function selector antah-berantah (nemu ginian biasanya di beberapa arsitektur proxy atau token jadul kayak WETH versi lama), maka panggilan permit bakal keliatan sukses (balikin success = true), padahal aslinya kagak ada allowance yang ke-approve sama sekali.

Abis itu contract lu bakal lanjut ngejalanin transferFrom, yang ujung-ujungnya bakal mati kutu kecuali emang udah ada allowance jadul yang kesisa. Tapi, kalau celah ini ketemu sama logika code yang cara validasi allowance-nya agak nyeleneh lewat flow lain, siap-siap aja apes kena reked parah. Selalu pastiin kalau target tokennya emang beneran support IERC20Permit via ERC-165, atau mendingan lu ketat-ketatin sekalian pake whitelist token.

4. ERC-3156 (Flash Loans): Bahaya Manipulasi Saldo di Dalem Satu Transaksi Tunggal

Flash loan itu emang komponen yang sakti mandraguna, tapi dia ngerusak asumsi timing transaksi yang biasa dipake sama software architect konvensional. Cuma modal satu transaksi atomic, attacker bisa minjem duit jutaan dolar, ngacak-ngacak state, terus balikin modalnya lagi di detik yang sama.

Blunder arsitektur paling fatal di sini adalah ketika lu masih ngandelin fungsi balanceOf(address(this)) buat nyari formula harga share pool atau valuasi harga aset.

// BLUNDER ARSITEKTUR YANG SANGAT KATASTROFIK
function getSharePrice() public view returns (uint256) {
    // Harga share pool bergantung langsung sama saldo token contract saat ini
    return token.balanceOf(address(this)) / totalShares;
}

Kalau contract lu nge-allow flash-loan buat token yang sama ini, pas si peminjam narik dananya, saldo di contract bakal terjun bebas ke titik nadir mendekati nol. Jika di momen krusial itu (pas di dalem callback onFlashLoan) protokol lu nge-izinin operasi lain jalan—misal kayak eksekusi likuidasi atau bagi-bagi reward—maka kalkulasi harga saham/share lu bakal lari jauh alias terdistorsi parah.

Hacker minjem flash loan -> saldo pool langsung tiris -> harga share anjlok ke tanah -> hacker pake wallet sekunder buat borong share murah meriah -> flash loan dibalikin -> saldo pool balik normal -> hacker tinggal dump share tadi di harga wajar. Kelar idup lu, pool langsung dikuras abis.

Golden rule: Jangan pernah sekalipun bersandar pada balanceOf(address(this)) buat kalkulasi matematika atau ekonomi yang sifatnya krusial, terutama kalau saldo itu bisa dimanipulasi sementara tanpa ngerubah state logika utama sistem. Pake pendekatan internal accounting lewat state variable kayak uint256 internalReserve, yang nilainya cuma boleh berubah pas proses deposit dan withdraw resmi yang terkontrol.

Oke, mari kita masuk ke hal-hal yang benar-benar bikin gue—sebagai orang yang bertanggung jawab atas keamanan infrastruktur exchange—merinding disko. Kita bakal bahas standar-standar baru yang relatif segar, di mana "lubang jebakan"-nya belum banyak diinjak oleh kaki ratusan developer.

5. ERC-4337 (Account Abstraction): Jebakan di Level Batch Transaction dan Paymaster

Account Abstraction itu keren banget, gak perlu didebat lagi. Kita beralih dari EOAs (dompet biasa) ke smart contract yang berfungsi sebagai wallet user. Gak ada lagi cerita ribet simpan seed phrase, sekarang bisa pakai social recovery dan bayar gas fee pakai stablecoin lewat Paymaster.

Tapi dari sudut pandang arsitek protokol yang mau integrasi dengan ERC-4337, ini justru membuka jurang baru bagi berbagai vector attack yang spesifik banget.

Celah Keamanan Signature pada validateUserOp

Di ERC-4337, titik krusial dari kustomisasi validasi wallet ada pada method validateUserOp. Fungsi ini bertugas memeriksa signature transaksi dan mengembalikan status khusus.

// Contoh simpel logika validasi di dalam smart wallet
function validateUserOp(
    UserOperation calldata userOp,
    bytes32 userOpHash,
    uint256 missingAccountFunds
) external returns (uint256 validationData) {
    // Eror kritis: Masa kita percaya begitu saja pada panggilan dari sembarang (ANY) address?
    // Harusnya Bundler memanggil ini lewat EntryPoint. Tapi kalau kita lupa check...
    require(msg.sender == entryPoint, "Only EntryPoint can trigger validation");
    
    // Validasi signature internal
    if (_verifySignature(userOp, userOpHash)) {
        // Kembalikan nilai 0 jika validasi sukses
        return 0; 
    }
    
    // Kembalikan SIG_VALIDATION_FAILED (biasanya nilai 1) jika eror
    return 1; 
}

Bentar, lo paham gak celahnya di mana? Sesuai spesifikasi ERC-4337, kalau validasi signature gagal, fungsi ini TIDAK boleh melakukan revert. Dia harus mengembalikan nilai kembalian ter-packing khusus (error constant), supaya EntryPoint (kontrak utama bertindak sebagai dirigen) tahu kalau transaksi itu gak valid. Dengan begitu, gas fee gak bakal didebit dari wallet user, dan operasi tersebut langsung di-drop di level Bundler.

Kalau lo sebagai arsitek iseng masukin require(isValid, "Invalid signature"); karena kebiasaan, bersiaplah dapet zonk. Waktu transaksi dikirim massal dalam satu paket (batch), satu saja transaksi yang gagal gara-gara kena hard revert bakal ngerusak dan nge-brick seluruh paket tersebut di sisi Bundler. Ujung-ujungnya, wallet atau Paymaster lo bakal di-blacklist (banned) sama para bundler, dan user lo gak bakal bisa kirim transaksi sama sekali. Logika validasi harus bersifat atomik dan patuh total pada aturan matematika return value ERC-4337, bukan pakai pola Solidity klasik.

Serangan Gas Drain pada Paymaster

Kalau protokol DeFi lo bertindak sebagai Paymaster (misalnya lo nalangin gas fee buat user supaya mereka bisa trading tanpa komisi), lo wajib mengisolasi tahap validasi ini seketat mungkin dari dunia luar.

Di dalam kontrak Paymaster ada method bernama validatePaymasterUserOp. Di dalam method ini, lo **dilarang keras** menggunakan dynamic state yang bisa berubah nilainya antara waktu simulasi transaksi oleh bundler dengan waktu transaksi tersebut masuk ke dalam blok. Contohnya, jangan sekali-kali memanggil price oracle (seperti Chainlink) langsung di dalam validasi paymaster cuma buat hitung berapa token yang harus dipotong dari user untuk bayar gas.

Kenapa? Attacker bisa kirim transaksi, dan saat disimulasikan nilai oracle kelihatan normal (validasi lolos). Tapi tepat sebelum transaksi masuk ke blok, si hacker memanipulasi harga oracle tersebut (pakai flash loan atau fast trade). Validasi di dalam blok akhirnya bakal gagal (revert), tapi bundler telanjur memproses transaksi itu dan ngebakar gas fee. Alhasil, dana gas bakal tersedot dari Paymaster lo, sedangkan si hacker gak bayar apa-apa. Saldo gas lo bakal ludes diperas cuma dalam hitungan jam.

6. ERC-4626 (Tokenized Vaults): Front-running pada Deposit Pertama (Inflation Attack)

ERC-4626 adalah standar emas untuk tokenized vault (staking, yield pool, lending). Standar ini menyatukan method seperti deposit, mint, withdraw, dan redeem. Ini jenius banget, karena sekarang yield aggregator sekelas Yearn bisa langsung integrasi ke pool baru mana pun cuma dalam waktu 5 menit.

Tapi, di balik desain matematis standar ini, tersimpan bom waktu yang dikenal sebagai **Inflation Attack** (Serangan Inflasi Aset). Serangan ini mengincar pool-pool yang baru saja di-deploy di jaringan dan saldo totalnya masih nol jidat.

Mekanisme Serangan

Rumus buat menghitung jumlah bagian (shares) yang didapat user saat mereka melakukan deposit aset (assets) biasanya kayak gini:

$$\text{shares} = \frac{\text{assets} \times \text{totalShares}}{\text{totalAssets}}$$

Kalau pool-nya masih kosong melompong (totalShares == 0), secara default nilainya adalah shares == assets. Berarti rasionya pas 1 banding 1.

Sekarang perhatikan trik sulap si hacker:

  • User jujur mengirim transaksi deposit sebesar 1000 USDC ke pool ERC-4626 yang bener-bener baru dan kosong.
  • Hacker melihat transaksi ini di mempool dan langsung melakukan front-run (pasang gas fee lebih tinggi). Dia cuma deposit sebesar 1 wei USDC ke pool tersebut. Pool bakal men-minting tepat 1 wei shares untuk si hacker. Sekarang posisinya: totalShares = 1, totalAssets = 1.
  • Lalu, masih dalam satu transaksi atomik yang sama, si hacker melakukan transfer langsung (lewat fungsi transfer ERC20 biasa, bukan lewat fungsi deposit) dalam jumlah raksasa—misalnya 10.000 USDC—langsung ke alamat kontrak vault tersebut.
  • Apa yang terjadi dengan logika matematika pool-nya? Sekarang totalShares tetap 1, tapi totalAssets melonjak jadi 10.001 USDC (karena transfer langsung tadi menginflasi saldo kontrak tanpa mencetak shares baru). Harga untuk 1 shares langsung terbang ke bulan.
  • Terakhir, transaksi si user jujur sebesar 1000 USDC baru tereksekusi. Kontrakt bakal menghitung jatah shares pakai rumus tadi:

    $$\text{shares} = \frac{1000 \times 1}{10\,001} = 0$$

    Gara-gara sifat pembulatan ke bawah (integer division) di Solidity, user tersebut dapet 0 shares! Tapi sialnya, uang 1000 USDC miliknya tetap sukses masuk dan pindah ke saldo pool.

  • Hacker tinggal panggil fungsi withdraw untuk satu-satunya shares milik dia (1 wei shares) dan nge-rug seluruh isi pool: menarik kembali 10.000 USDC miliknya, 1 wei modal awalnya, plus bonus 1000 USDC milik korban yang kecolongan.

Solusi Arsitektur: Untuk membentengi kode lo dari serangan ini, ada dua jalan ninja. Pertama—saat bikin pool, paksa minting "dead shares" ke zero address (kunci 1000 wei shares pertama di sana, mirip seperti trik yang dipakai di Uniswap V2). Kedua—gunakan library OpenZeppelin versi terbaru yang sudah dilengkapi proteksi via virtual offset (virtual assets dan virtual shares). Fitur ini menjaga agar angka penyebut pada pecahan gak bakal berubah jadi nol atau satu saat ada yang mencoba memanipulasi saldo.

Dengar, mari kita naik satu level lagi. Kita sudah bahas token, NFT, permit, dan vault secara spesifik. Tapi bagaimana cara menyatukan semuanya ke dalam satu arsitektur tanpa jadi gila saat proses integrasinya?

Saat kamu mendesain sistem skala besar—seperti yield aggregator atau bridge cross-chain—kamu harus bekerja dengan semua standar ini sekaligus. Di sinilah muncul efek sinergi kerentanan, di mana dua fitur yang aman secara terpisah bisa menciptakan lubang fatal saat digabungkan.

7. Matriks Risiko Arsitektur dalam Desain Sistem

Biar kamu punya gambaran jelas, saya buatkan tabel ini. Jadikan ini checklist buat sesi Architecture Review kamu berikutnya. Simpan di Notion atau langsung cetak saja.

Standar ERCAncaman Tersembunyi UtamaDampak pada LogikaCara Mitigasi di Level Desain
ERC-20Return value hilang / Transfer tidak standarTransaksi gagal (revert) atau error terlewat begitu sajaWajib gunakan SafeERC20 (OpenZeppelin).
ERC-20 (Weird)Fee-on-Transfer / Perubahan saldo (Rebase)Selisih antara pembukuan internal sistem dengan saldo asli di kontrakHitung selisih saldo balanceAfter - balanceBefore alih-alih percaya pada argumen amount.
ERC-721 / 1155Pembajakan kontrol lewat hook onERC...ReceivedReentrancy sebelum state internal diperbaruiPatuh ketat pada pola Checks-Effects-Interactions + modifikator nonReentrant.
ERC-2612Frontrunning signature di mempoolDenial of Service (DoS) bagi pengguna yang sahBungkus panggilan permit di dalam blok try/catch.
ERC-3156Pengurasan likuiditas sementara (Flash Loan)Manipulasi harga spot yang bergantung pada balanceOfGunakan variabel akumulasi internal (internal reserves) daripada saldo langsung.
ERC-4337Hard revert saat validasi batchKontrak/wallet kena ban di sisi bundlerReturn error constant ajaib alih-alih me-revert transaksi dengan require.
ERC-4626Inflation Attack (Eksploitasi deposit pertama)Pembulatan share ke nol, dana depositor pertama dicuriMint "dead shares" ke address(0) saat inisialisasi atau pakai virtual offset.

8. Curhatan & Aturan Emas Arsitektur Aman

Asal tahu saja, selama tiga tahun duduk di kursi CTO, saya belajar satu hal. Kode paling aman adalah kode yang tidak ditulis. Semakin rumit diagram arsitektur kamu, semakin banyak koneksi tersembunyi di dalamnya, dan semakin besar peluang ada jenius di hackathon yang menemukan celah yang tidak terpikirkan olehmu saat sedang ngopi pagi.

Kalau saya harus kasih tiga aturan saja yang bisa menyelamatkan proyek kamu dari headline Rekt News, inilah dia:

  • Jangan pernah percaya kontrak eksternal. Meskipun itu token paling populer di dunia. Besok adminnya bisa update proxy, tambah blacklist, dan sistem kamu langsung macet total. Tulis kode dengan asumsi bahwa token eksternal itu aktor paling jahat dan tidak terprediksi di jaringan.
  • Update state dulu, baru transfer. Saya tidak akan bosan mengulangi ini. Ini dasar yang diajarkan di hari pertama kursus smart contract yang benar, tapi orang-orang masih saja keras kepala mengirim token sebelum mengupdate angka di mapping mereka. Kamu ambil atau kasih hak akses di dalam kontrak, commit ke blockchain, dan baru di langkah terakhir panggil transfer, safeMint, atau call eksternal.
  • Isolasi matematika dari saldo eksternal. Saldo kontrak kamu di EVM itu publik dan gampang banget dimanipulasi. Siapa saja bisa kirim jutaan dolar lewat flash loan atau paksa kontrak kamu terima ETH lewat selfdestruct. Kalau logika reward atau harga share kamu bergantung pada berapa banyak token yang tersimpan di alamat kontrak saat itu, kamu sudah kalah. Pembukuan internal kamu harus diisolasi seperti kokpit pilot.

Kurasa itu saja poin-poin krusial dari standar yang bisa menggagalkan usaha tim developer hebat kalau arsiteknya tidak sigap memberikan perlindungan di tempat yang tepat.

Oleg Filatov

As the Chief Technology Officer at EXMON Exchange, I focus on building secure, scalable crypto infrastructure and developing systems that protect user assets and privacy.

With over 15 years in cybersecurity, blockchain, and DevOps, I specialize in smart contract analysis, threat modeling, and secure system architecture.

At EXMON Academy, I share practical insights from real-world...

...

Sampaikan pemikiran Anda

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *