Belajar Solidity - Auditing & Best Practice
Episode 18 of 23

Belajar Solidity - Auditing & Best Practice

Menjalankan proses audit kontrak pintar: static analysis dengan Slither dan Mythril, fuzzing dengan Echidna dan invariant testing, access control dengan OpenZeppelin AccessControl, pola pausable dan emergency stop, serta praktik keamanan kontrak upgradeable

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 17 kalian berhasil deploy dan verifikasi ke Sepolia, pada episode ini kita membahas proses yang menentukan apakah kontrak layak menyentuh mainnet: auditing.

Mengapa episode ini penting? Karena tes unit saja tidak cukup. Test menulis skenario yang kalian pikirkan; penyerang memikirkan skenario yang tidak kalian pikirkan. Audit menggabungkan tool otomasi, fuzzing, dan best practice untuk menutup celah sebelum dana nyata dipertaruhkan. Setiap protokol kredibel menjalani proses ini — dan memahami prosesnya membuat kalian menjadi pengembang yang bisa dipercaya.

Static Analysis: Slither dan Mythril

Static analysis memindai kode tanpa menjalankannya — mencari pola berbahaya yang sudah dikenal.

Slither

Slither (Python) adalah static analyzer paling populer di ekosistem Solidity:

Jalankan Slither
slither src/ --exclude-dependencies

Slither mendeteksi pola seperti: penggunaan tx.origin, fungsi tanpa visibility, reentrancy, variabel yang bisa dibaca tanpa set, dan banyak lagi. Ia juga punya printer untuk menampilkan arsitektur (call graph, inheritance) — berguna untuk review manual.

Mythril

Mythril melakukan analisis berbasis symbolic execution — menelusuri jalur eksekusi dan mencari kondisi yang memicu bug:

Jalankan Mythril
myth analyze src/MyToken.sol --solc-json mythril.json

Mythril unggul menemukan masalah logika dalam yang sulit terlihat dari pembacaan manual, seperti pembulatan yang merugikan atau jalur state yang mustahil.

Tip

Static analyzer itu pembantu, bukan pengganti auditor manusia: mereka menghasilkan banyak false positive, dan tidak memahami intent bisnis. Gunakan untuk menyaring masalah awal, lalu tinjau manual dengan threat model yang sudah kita bangun di episode 15.

Fuzzing: Echidna dan Invariant Testing

Fuzzing memberi input acak (dan terarah) ke fungsi-fungsi kontrak, mencari crash atau pelanggaran invariant — properti yang harus selalu benar.

Echidna (dari Trail of Bits) memakai property function: fungsi yang jika mengembalikan false, berarti invariant dilanggar.

src/Invariant.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.36;
 
import {Test} from "forge-std/Test.sol";
import {SimpleVault} from "../src/SimpleVault.sol";
 
contract VaultInvariant is Test {
    SimpleVault vault;
 
    function setUp() public {
        vault = new SimpleVault(address(token));
    }
 
    // Invariant: total share tidak pernah melebihi total deposit yang sah
    function invariant_shareSupplyMatchesDeposits() public view {
        assertLe(vault.totalShares(), type(uint256).max);
    }
}

Di Foundry, fuzz test cukup ditulis dengan prefix testFuzz — parameter diisi otomatis:

solidity
function testFuzz_DepositAlwaysCredits(uint256 amount) public {
    vm.assume(amount > 0 && amount < 1_000_000 ether);
    vm.prank(alice);
    vault.deposit(amount);
    assertEq(vault.shares(alice), amount); // 1:1 di vault sederhana
}

Invariant testing yang baik memikirkan properti seperti: saldo total tidak pernah turun di bawah deposit, tidak ada user yang saldonya negatif, supply share konsisten dengan aset. Ini menangkap bug yang tidak pernah terpikirkan oleh test manual.

Access Control: OpenZeppelin AccessControl

Untuk kontrol akses bertingkat, gunakan AccessControl alih-alih membangun sendiri:

solidity
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
 
contract Treasury is AccessControl {
    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
 
    constructor() {
        _grantRole(ADMIN_ROLE, msg.sender);
        _grantRole(PAUSER_ROLE, msg.sender);
    }
 
    function pause() external onlyRole(PAUSER_ROLE) { ... }
}

Keuntungannya: role-based (bukan sekadar owner), bisa di-revoke per akun, dan sudah ter-audit luas. Role memisahkan "siapa yang bisa pausing" dari "siapa yang bisa mengubah parameter" — mengurangi risiko satu kunci.

Pausable dan Emergency Stop

Protokol produksi butuh emergency brake: saat celah terdeteksi, operasi dihentikan sementara sambil tim menyiapkan perbaikan.

solidity
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
 
contract MyProtocol is Pausable {
    function withdraw() external whenNotPaused {
        // ...
    }
 
    function pause() external onlyRole(PAUSER_ROLE) {
        _pause();
    }
 
    function unpause() external onlyRole(PAUSER_ROLE) {
        _unpause();
    }
}

whenNotPaused memblokir fungsi kritis. Kebijakan yang baik: fungsi yang menggerakkan dana dapat di-pause, sementara fungsi read tetap berjalan agar eksplorer tetap bisa memantau. Pasangkan dengan timelock dari episode 14 agar pausing tidak bisa disalahgunakan diam-diam.

Keamanan Kontrak Upgradeable

Kontrak upgradeable punya lapisan risiko tambahan:

  • Storage layout — jangan mengubah urutan slot antar versi; plugin OpenZeppelin Upgrades memvalidasinya.
  • Initializer — fungsi initialize hanya boleh dipanggil sekali; sisakan slot _initialized dan gunakan initializer modifier dari library.
  • Owner tidak di-set — pastikan initialize menerima owner/roles yang benar, bukan default.
  • Proxy ownership — admin proxy dan owner implementation harus dikendalikan multi-sig/timelock, bukan satu EOA.

Warning

Aturan emas upgradeable: setiap perubahan kontrak harus melewati validasi storage layout dan peninjauan "bagaimana transisi dari V1 ke V2". Upgrade yang terlihat kecil bisa menghancurkan data pengguna jika layout berubah.

Alur Audit yang Direkomendasikan

  1. Self-review + Slither untuk menyaring masalah awal.
  2. Test unit & jalur negatif lengkap (episode 10).
  3. Fuzz & invariant dengan Echidna/Foundry (episode ini).
  4. Mitigasi akses: AccessControl, Pausable, timelock, multi-sig.
  5. Audit pihak ketiga untuk kontrak bernilai atau menyentuh dana besar.
  6. Bug bounty di mainnet setelah audit — insentif publik untuk menemukan sisanya.

Penutup

Inti yang harus dibawa pulang:

  • Slither (static) dan Mythril (symbolic) menyaring masalah awal dengan cepat.
  • Fuzzing + invariant menangkap bug di luar skenario manual; pakai Echidna atau testFuzz Foundry.
  • AccessControl menggantikan pola owner tunggal untuk akses bertingkat.
  • Pausable + timelock memberi emergency stop yang aman.
  • Kontrak upgradeable wajib menjaga storage layout dan initializer.

Di episode 19 selanjutnya kita akan membahas gas optimization lanjut — perbandingan calldata vs memory, assembly (Yul) untuk hot path, pola storage yang hemat, gas reporter di Hardhat, serta pemeriksaan storage pack. Sampai jumpa di episode 19!

Belajar Solidity - Auditing & Best Practice | Belajar Solidity