Skip to main content

Module 2: Dasar-dasar Bahasa Move

Dalam tutorial ini, kita akan belajar bahasa Move step-by-step dari yang paling dasar. Setiap konsep akan dijelaskan satu per satu dengan contoh sederhana menggunakan data mahasiswa kampus.

Setup Awal​

1. Setup Sui CLI​

Sui CLI adalah tool untuk membuat dan menjalankan smart contract Move.

Langkah-langkah:

  1. Install Sui CLI dari https://docs.sui.io/build/install
  2. Buat wallet baru:
    sui client new-address ed25519
  3. Dapatkan testnet token:
    sui client faucet
  4. Cek balance:
    sui client balance

2. Setup Project Move​

  1. Buat folder project:

    mkdir kampus-move
    cd kampus-move
    sui move new kampus
  2. Struktur project:

    kampus/
    ├── Move.toml
    └── sources/
    └── kampus.move
  3. File Move.toml:

    [package]
    name = "kampus"
    version = "1.0.0"

    [dependencies]
    Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }

    [addresses]
    kampus = "0x0"
Siap!

Sekarang Anda sudah siap untuk belajar Move dan deploy ke Sui Network!

Move 101: Mengenal Tipe Data​

Mari kita mulai dengan memahami tipe data dasar di Move satu per satu.

1. Tipe Data String​

String digunakan untuk menyimpan teks.

module kampus::belajar_string {
use std::string::{Self, String};

// Struct untuk menyimpan nama mahasiswa
public struct Mahasiswa has key {
id: UID,
nama: String,
}

// Fungsi untuk membuat mahasiswa baru
public fun buat_mahasiswa(nama: String, ctx: &mut TxContext): Mahasiswa {
Mahasiswa {
id: object::new(ctx),
nama,
}
}

// Fungsi untuk mengubah nama
public fun ubah_nama(mahasiswa: &mut Mahasiswa, nama_baru: String) {
mahasiswa.nama = nama_baru;
}

// Fungsi untuk mendapatkan nama
public fun get_nama(mahasiswa: &Mahasiswa): String {
mahasiswa.nama
}
}

Penjelasan:

  • String = tipe data untuk menyimpan teks
  • public struct Mahasiswa has key = membuat struct yang bisa disimpan di blockchain
  • UID = unique identifier yang diperlukan untuk setiap object
  • &mut = reference yang bisa diubah

2. Tipe Data Number (u64)​

u64 digunakan untuk menyimpan angka positif.

module kampus::belajar_number {
// Struct untuk data mahasiswa dengan angka
public struct DataMahasiswa has key {
id: UID,
nim: u32,
umur: u8,
total_sks: u64,
}

// Fungsi untuk membuat data mahasiswa
public fun buat_data_mahasiswa(
nim: u32,
umur: u8,
ctx: &mut TxContext
): DataMahasiswa {
DataMahasiswa {
id: object::new(ctx),
nim,
umur,
total_sks: 0,
}
}

// Fungsi untuk menambah SKS
public fun tambah_sks(data: &mut DataMahasiswa, sks: u64) {
data.total_sks = data.total_sks + sks;
}

// Fungsi untuk menambah umur
public fun tambah_umur(data: &mut DataMahasiswa) {
data.umur = data.umur + 1;
}
}

Penjelasan:

  • u8 = angka dari 0 sampai 255
  • u32 = angka dari 0 sampai ~4 miliar
  • u64 = angka dari 0 sampai ~18 quintillion
  • data.total_sks + sks = operasi penjumlahan

3. Tipe Data Boolean​

Boolean hanya punya 2 nilai: true atau false.

module kampus::belajar_boolean {
use std::string::String;

// Struct dengan boolean
public struct StatusMahasiswa has key {
id: UID,
nama: String,
aktif: bool,
lulus: bool,
}

// Fungsi untuk membuat status mahasiswa
public fun buat_status(nama: String, ctx: &mut TxContext): StatusMahasiswa {
StatusMahasiswa {
id: object::new(ctx),
nama,
aktif: true, // Default aktif
lulus: false, // Default belum lulus
}
}

// Fungsi untuk set lulus
public fun set_lulus(status: &mut StatusMahasiswa) {
status.lulus = true;
}

// Fungsi untuk nonaktifkan
public fun nonaktifkan(status: &mut StatusMahasiswa) {
status.aktif = false;
}

// Fungsi untuk cek apakah bisa wisuda
public fun bisa_wisuda(status: &StatusMahasiswa): bool {
status.aktif && status.lulus
}
}

Penjelasan:

  • bool = tipe data yang cuma bisa true atau false
  • status.aktif && status.lulus = operasi AND (dan)
  • && = kedua kondisi harus true

4. Gabungkan Semua Tipe Data​

Sekarang kita gabungkan semua tipe data dalam satu module sederhana:

module kampus::data_mahasiswa_lengkap {
use std::string::String;

// Struct lengkap mahasiswa
public struct Mahasiswa has key {
id: UID,
nama: String,
nim: u32,
jurusan: String,
umur: u8,
total_sks: u64,
aktif: bool,
lulus: bool,
}

// Fungsi untuk membuat mahasiswa baru
public fun daftar_mahasiswa(
nama: String,
nim: u32,
jurusan: String,
umur: u8,
ctx: &mut TxContext
): Mahasiswa {
Mahasiswa {
id: object::new(ctx),
nama,
nim,
jurusan,
umur,
total_sks: 0,
aktif: true,
lulus: false,
}
}

// Fungsi untuk ambil mata kuliah (tambah SKS)
public fun ambil_mata_kuliah(mhs: &mut Mahasiswa, sks: u64) {
mhs.total_sks = mhs.total_sks + sks;
}

// Fungsi untuk lulus
public fun set_lulus(mhs: &mut Mahasiswa) {
// Syarat lulus: minimal 144 SKS
if (mhs.total_sks >= 144) {
mhs.lulus = true;
}
}

// Fungsi untuk get info mahasiswa
public fun get_info(mhs: &Mahasiswa): (String, u32, u64, bool) {
(mhs.nama, mhs.nim, mhs.total_sks, mhs.lulus)
}
}

Cara menggunakan:

  1. Save kode di file sources/mahasiswa.move
  2. Compile: sui move build
  3. Publish: sui client publish --gas-budget 100000000

5. Mengenal Vector (Array)​

Vector adalah seperti list yang bisa menyimpan banyak data dengan tipe yang sama.

module kampus::belajar_vector {
use std::string::String;
use std::vector;

// Struct untuk menyimpan daftar mahasiswa
public struct DaftarMahasiswa has key {
id: UID,
nama_mahasiswa: vector<String>,
nim_mahasiswa: vector<u32>,
}

// Fungsi untuk membuat daftar kosong
public fun buat_daftar_kosong(ctx: &mut TxContext): DaftarMahasiswa {
DaftarMahasiswa {
id: object::new(ctx),
nama_mahasiswa: vector::empty<String>(),
nim_mahasiswa: vector::empty<u32>(),
}
}

// Fungsi untuk tambah mahasiswa ke daftar
public fun tambah_mahasiswa(
daftar: &mut DaftarMahasiswa,
nama: String,
nim: u32
) {
vector::push_back(&mut daftar.nama_mahasiswa, nama);
vector::push_back(&mut daftar.nim_mahasiswa, nim);
}

// Fungsi untuk menghitung jumlah mahasiswa
public fun jumlah_mahasiswa(daftar: &DaftarMahasiswa): u64 {
vector::length(&daftar.nama_mahasiswa)
}

// Fungsi untuk mendapatkan mahasiswa berdasarkan index
public fun get_mahasiswa_ke(daftar: &DaftarMahasiswa, index: u64): (String, u32) {
let nama = *vector::borrow(&daftar.nama_mahasiswa, index);
let nim = *vector::borrow(&daftar.nim_mahasiswa, index);
(nama, nim)
}

// Fungsi untuk cek apakah daftar kosong
public fun apakah_kosong(daftar: &DaftarMahasiswa): bool {
vector::is_empty(&daftar.nama_mahasiswa)
}
}

Penjelasan:

  • vector<String> = vector yang berisi String
  • vector::empty<String>() = membuat vector kosong
  • vector::push_back() = menambah data ke vector
  • vector::length() = menghitung panjang vector
  • vector::borrow() = mengambil reference ke element vector

Move 102: Object Ownership dan Struct​

Sekarang kita belajar cara mengelompokkan data dengan Struct dan memahami ownership pattern di Sui.

1. Mengenal Struct dan Object Model​

Struct adalah cara untuk mengelompokkan beberapa data jadi satu. Di Sui, ada 3 jenis object berdasarkan ownership.

Owned Objects - Dimiliki Address Tertentu​

module kampus::owned_objects {
use std::string::String;

// Struct mahasiswa yang dimiliki oleh address tertentu
public struct KartuMahasiswa has key, store {
id: UID,
nama: String,
nim: u32,
jurusan: String,
tahun_masuk: u16,
}

// Fungsi untuk membuat kartu mahasiswa (owned object)
public fun daftar_mahasiswa(
nama: String,
nim: u32,
jurusan: String,
tahun_masuk: u16,
ctx: &mut TxContext
) {
let kartu = KartuMahasiswa {
id: object::new(ctx),
nama,
nim,
jurusan,
tahun_masuk,
};

// Transfer ke sender (mahasiswa yang mendaftar)
transfer::transfer(kartu, tx_context::sender(ctx));
}

// Mahasiswa bisa update data sendiri
public fun update_nama(kartu: &mut KartuMahasiswa, nama_baru: String) {
kartu.nama = nama_baru;
}

// Fungsi untuk transfer kartu ke orang lain
public fun transfer_kartu(kartu: KartuMahasiswa, penerima: address) {
transfer::transfer(kartu, penerima);
}
}

Penjelasan:

  • has key, store = object bisa disimpan di blockchain dan di dalam struct lain
  • transfer::transfer() = pindahkan ownership ke address lain
  • Hanya pemilik yang bisa mengakses object owned

Shared Objects - Bisa Diakses Siapa Saja​

module kampus::shared_objects {
use std::string::String;
use std::vector;

// Struct untuk registrasi kampus (shared object)
public struct RegistrasiKampus has key {
id: UID,
nama_kampus: String,
total_mahasiswa: u64,
daftar_nim: vector<u32>,
admin: address,
}

// Fungsi untuk membuat registrasi kampus (shared)
public fun buat_registrasi_kampus(
nama_kampus: String,
ctx: &mut TxContext
) {
let registrasi = RegistrasiKampus {
id: object::new(ctx),
nama_kampus,
total_mahasiswa: 0,
daftar_nim: vector::empty<u32>(),
admin: tx_context::sender(ctx),
};

// Share object agar semua orang bisa akses
transfer::share_object(registrasi);
}

// Siapa saja bisa lihat jumlah mahasiswa
public fun get_total_mahasiswa(registrasi: &RegistrasiKampus): u64 {
registrasi.total_mahasiswa
}

// Hanya admin yang bisa tambah mahasiswa
public fun tambah_mahasiswa_ke_registrasi(
registrasi: &mut RegistrasiKampus,
nim: u32,
ctx: &TxContext
) {
// Cek apakah yang memanggil adalah admin
assert!(registrasi.admin == tx_context::sender(ctx), 0);

// Tambah mahasiswa
registrasi.total_mahasiswa = registrasi.total_mahasiswa + 1;
vector::push_back(&mut registrasi.daftar_nim, nim);
}
}

Penjelasan:

  • transfer::share_object() = buat object bisa diakses semua orang
  • Shared object butuh consensus untuk diubah
  • Bisa ada authorization check di dalam fungsi

2. Capabilities Pattern​

Capabilities adalah cara memberikan permission dengan object ownership.

module kampus::capabilities_pattern {
use std::string::String;

// Admin capability - hanya admin yang punya
public struct AdminCap has key, store {
id: UID,
}

// Dosen capability - hanya dosen yang punya
public struct DosenCap has key, store {
id: UID,
mata_kuliah: String,
}

// Struct untuk sistem nilai
public struct SistemNilai has key {
id: UID,
total_mahasiswa: u64,
}

// Struct untuk nilai mahasiswa
public struct NilaiMahasiswa has key, store {
id: UID,
nim: u32,
mata_kuliah: String,
nilai: u8,
dosen_pemberi: address,
}

// Init function - jalan sekali saat deploy
fun init(ctx: &mut TxContext) {
// Buat admin capability
let admin_cap = AdminCap {
id: object::new(ctx),
};

// Buat sistem nilai
let sistem = SistemNilai {
id: object::new(ctx),
total_mahasiswa: 0,
};

// Transfer admin cap ke deployer
transfer::transfer(admin_cap, tx_context::sender(ctx));

// Share sistem nilai
transfer::share_object(sistem);
}

// Hanya admin yang bisa buat dosen capability
public fun buat_dosen_cap(
_: &AdminCap, // Admin capability sebagai "kunci"
mata_kuliah: String,
dosen_address: address,
ctx: &mut TxContext
) {
let dosen_cap = DosenCap {
id: object::new(ctx),
mata_kuliah,
};

transfer::transfer(dosen_cap, dosen_address);
}

// Hanya dosen yang bisa kasih nilai
public fun beri_nilai(
dosen_cap: &DosenCap,
sistem: &mut SistemNilai,
nim: u32,
nilai: u8,
mahasiswa_address: address,
ctx: &mut TxContext
) {
// Validasi nilai
assert!(nilai <= 100, 1);

let nilai_obj = NilaiMahasiswa {
id: object::new(ctx),
nim,
mata_kuliah: dosen_cap.mata_kuliah,
nilai,
dosen_pemberi: tx_context::sender(ctx),
};

// Update sistem
sistem.total_mahasiswa = sistem.total_mahasiswa + 1;

// Transfer ke mahasiswa
transfer::transfer(nilai_obj, mahasiswa_address);
}
}

Penjelasan:

  • AdminCap, DosenCap = capabilities untuk different permissions
  • _: &AdminCap = parameter yang harus ada tapi tidak digunakan
  • fun init() = fungsi yang jalan sekali saat deploy contract
  • Capability pattern lebih aman daripada address-based permission

3. Fungsi dan Control Flow​

Mari pelajari cara membuat fungsi yang lebih baik dengan error handling:

module kampus::fungsi_lengkap {
use std::string::String;
use std::vector;

// Error constants
const ENIM_INVALID: u64 = 0;
const ENILAI_INVALID: u64 = 1;
const ESKS_INVALID: u64 = 2;
const ENOT_AUTHORIZED: u64 = 3;

public struct MahasiswaProfile has key {
id: UID,
nama: String,
nim: u32,
total_sks: u64,
nilai_list: vector<u8>,
owner: address,
}

// Fungsi dengan validasi lengkap
public fun buat_profile(
nama: String,
nim: u32,
ctx: &mut TxContext
) {
// Validasi NIM (8 digit, dimulai 20)
assert!(nim >= 20000000 && nim <= 29999999, ENIM_INVALID);

// Validasi nama tidak kosong
assert!(string::length(&nama) > 0, 100);

let profile = MahasiswaProfile {
id: object::new(ctx),
nama,
nim,
total_sks: 0,
nilai_list: vector::empty<u8>(),
owner: tx_context::sender(ctx),
};

transfer::transfer(profile, tx_context::sender(ctx));
}

// Fungsi dengan authorization check
public fun tambah_nilai(
profile: &mut MahasiswaProfile,
nilai: u8,
sks: u64,
ctx: &TxContext
) {
// Cek ownership
assert!(profile.owner == tx_context::sender(ctx), ENOT_AUTHORIZED);

// Validasi nilai (0-100)
assert!(nilai <= 100, ENILAI_INVALID);

// Validasi SKS (1-6)
assert!(sks >= 1 && sks <= 6, ESKS_INVALID);

// Update data
vector::push_back(&mut profile.nilai_list, nilai);
profile.total_sks = profile.total_sks + sks;
}

// Fungsi dengan multiple return values dan control flow
public fun analisis_performa(profile: &MahasiswaProfile): (u64, bool, String) {
let jumlah_mk = vector::length(&profile.nilai_list);

// Return early jika belum ada nilai
if (jumlah_mk == 0) {
return (0, false, string::utf8(b"Belum ada nilai"))
};

// Hitung rata-rata nilai
let mut total_nilai = 0u64;
let mut i = 0;

while (i < jumlah_mk) {
let nilai = *vector::borrow(&profile.nilai_list, i);
total_nilai = total_nilai + (nilai as u64);
i = i + 1;
};

let rata_rata = total_nilai / jumlah_mk;

// Tentukan status dan pesan
let (bisa_lulus, pesan) = if (rata_rata >= 60 && profile.total_sks >= 144) {
(true, string::utf8(b"Bisa lulus"))
} else if (rata_rata < 60) {
(false, string::utf8(b"Nilai kurang"))
} else {
(false, string::utf8(b"SKS kurang"))
};

(rata_rata, bisa_lulus, pesan)
}

// Fungsi untuk cari nilai tertinggi
public fun cari_nilai_tertinggi(profile: &MahasiswaProfile): u8 {
let nilai_list = &profile.nilai_list;
let len = vector::length(nilai_list);

assert!(len > 0, 200); // Harus ada nilai

let mut max_nilai = *vector::borrow(nilai_list, 0);
let mut i = 1;

while (i < len) {
let nilai = *vector::borrow(nilai_list, i);
if (nilai > max_nilai) {
max_nilai = nilai;
};
i = i + 1;
};

max_nilai
}
}

Penjelasan Konsep Penting:

  • assert!(condition, error_code) = validasi dengan error code
  • as u64 = type casting untuk konversi tipe data
  • let mut = variabel yang bisa diubah
  • while loop untuk iterasi
  • Multiple return values dengan tuple

Move 103: Hands-On Project​

Mari buat project lengkap untuk sistem kampus sederhana.

Project Setup​

  1. Buat project baru:

    sui move new sistem_kampus_lengkap
    cd sistem_kampus_lengkap
  2. Edit Move.toml:

    [package]
    name = "sistem_kampus_lengkap"
    version = "1.0.0"

    [dependencies]
    Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }

    [addresses]
    sistem_kampus = "0x0"

Main Contract: sources/kampus.move​

module sistem_kampus::kampus {
use std::string::{Self, String};
use std::vector;

// === ERROR CONSTANTS ===
const ENIM_INVALID: u64 = 0;
const ENOT_AUTHORIZED: u64 = 1;
const EVALUES_INVALID: u64 = 2;
const ENOT_FOUND: u64 = 3;

// === STRUCTS ===

// Admin capability
public struct AdminCap has key {
id: UID,
}

// Mahasiswa profile (owned object)
public struct MahasiswaProfile has key, store {
id: UID,
nama: String,
nim: u32,
jurusan: String,
semester: u8,
total_sks: u64,
ipk: u64, // IPK x 100 (contoh: 350 = 3.50)
aktif: bool,
owner: address,
}

// Registry kampus (shared object)
public struct KampusRegistry has key {
id: UID,
nama_kampus: String,
total_mahasiswa: u64,
daftar_nim: vector<u32>,
admin: address,
}

// Nilai mata kuliah (owned object)
public struct NilaiMataKuliah has key, store {
id: UID,
nim_mahasiswa: u32,
kode_mk: String,
nama_mk: String,
sks: u8,
nilai: u8,
semester: u8,
tahun: u16,
}

// === EVENTS ===
public struct MahasiswaRegistered has copy, drop {
nim: u32,
nama: String,
jurusan: String,
}

public struct NilaiAdded has copy, drop {
nim: u32,
mata_kuliah: String,
nilai: u8,
sks: u8,
}

// === INIT FUNCTION ===
fun init(ctx: &mut TxContext) {
// Buat admin capability
let admin_cap = AdminCap {
id: object::new(ctx),
};

// Buat kampus registry
let registry = KampusRegistry {
id: object::new(ctx),
nama_kampus: string::utf8(b"Universitas Blockchain Indonesia"),
total_mahasiswa: 0,
daftar_nim: vector::empty<u32>(),
admin: tx_context::sender(ctx),
};

// Transfer admin cap ke deployer
transfer::transfer(admin_cap, tx_context::sender(ctx));

// Share registry untuk akses publik
transfer::share_object(registry);
}

// === ADMIN FUNCTIONS ===

// Register mahasiswa baru (hanya admin)
public fun register_mahasiswa(
_: &AdminCap, // Admin capability sebagai authorization
registry: &mut KampusRegistry,
nama: String,
nim: u32,
jurusan: String,
mahasiswa_address: address,
ctx: &mut TxContext
) {
// Validasi NIM
assert!(nim >= 20000000 && nim <= 29999999, ENIM_INVALID);

// Buat profile mahasiswa
let profile = MahasiswaProfile {
id: object::new(ctx),
nama: nama,
nim,
jurusan: jurusan,
semester: 1,
total_sks: 0,
ipk: 0,
aktif: true,
owner: mahasiswa_address,
};

// Update registry
registry.total_mahasiswa = registry.total_mahasiswa + 1;
vector::push_back(&mut registry.daftar_nim, nim);

// Emit event
sui::event::emit(MahasiswaRegistered {
nim,
nama,
jurusan,
});

// Transfer profile ke mahasiswa
transfer::transfer(profile, mahasiswa_address);
}

// Berikan nilai (hanya admin)
public fun beri_nilai(
_: &AdminCap,
kode_mk: String,
nama_mk: String,
sks: u8,
nilai: u8,
semester: u8,
tahun: u16,
nim_mahasiswa: u32,
mahasiswa_address: address,
ctx: &mut TxContext
) {
// Validasi
assert!(nilai <= 100, EVALUES_INVALID);
assert!(sks >= 1 && sks <= 6, EVALUES_INVALID);

let nilai_mk = NilaiMataKuliah {
id: object::new(ctx),
nim_mahasiswa,
kode_mk: kode_mk,
nama_mk: nama_mk,
sks,
nilai,
semester,
tahun,
};

// Emit event
sui::event::emit(NilaiAdded {
nim: nim_mahasiswa,
mata_kuliah: nama_mk,
nilai,
sks,
});

// Transfer ke mahasiswa
transfer::transfer(nilai_mk, mahasiswa_address);
}

// === MAHASISWA FUNCTIONS ===

// Update profile sendiri
public fun update_semester(
profile: &mut MahasiswaProfile,
semester_baru: u8,
ctx: &TxContext
) {
assert!(profile.owner == tx_context::sender(ctx), ENOT_AUTHORIZED);
assert!(semester_baru >= profile.semester, EVALUES_INVALID);

profile.semester = semester_baru;
}

// Update IPK berdasarkan nilai yang dimiliki
public fun hitung_ipk(
profile: &mut MahasiswaProfile,
daftar_nilai: &vector<NilaiMataKuliah>,
ctx: &TxContext
) {
assert!(profile.owner == tx_context::sender(ctx), ENOT_AUTHORIZED);

let mut total_nilai_tertimbang = 0u64;
let mut total_sks = 0u64;
let mut i = 0;

let len = vector::length(daftar_nilai);
while (i < len) {
let nilai_mk = vector::borrow(daftar_nilai, i);

// Hanya hitung nilai untuk mahasiswa ini
if (nilai_mk.nim_mahasiswa == profile.nim) {
// Konversi nilai ke point (A=4, B=3, C=2, D=1, E=0)
let point = if (nilai_mk.nilai >= 80) 4
else if (nilai_mk.nilai >= 70) 3
else if (nilai_mk.nilai >= 60) 2
else if (nilai_mk.nilai >= 50) 1
else 0;

total_nilai_tertimbang = total_nilai_tertimbang + (point * (nilai_mk.sks as u64));
total_sks = total_sks + (nilai_mk.sks as u64);
};

i = i + 1;
};

// Update profile
profile.total_sks = total_sks;
if (total_sks > 0) {
profile.ipk = (total_nilai_tertimbang * 100) / total_sks; // IPK x 100
};
}

// === VIEW FUNCTIONS ===

// Get info mahasiswa
public fun get_mahasiswa_info(profile: &MahasiswaProfile): (String, u32, String, u8, u64, u64, bool) {
(
profile.nama,
profile.nim,
profile.jurusan,
profile.semester,
profile.total_sks,
profile.ipk,
profile.aktif
)
}

// Get info kampus
public fun get_kampus_info(registry: &KampusRegistry): (String, u64) {
(registry.nama_kampus, registry.total_mahasiswa)
}

// Cek syarat kelulusan
public fun cek_kelulusan(profile: &MahasiswaProfile): bool {
profile.total_sks >= 144 && profile.ipk >= 200 && profile.aktif
}

// Get detail nilai
public fun get_nilai_info(nilai: &NilaiMataKuliah): (u32, String, String, u8, u8) {
(
nilai.nim_mahasiswa,
nilai.kode_mk,
nilai.nama_mk,
nilai.sks,
nilai.nilai
)
}
}

Testing dan Deploy​

  1. Build project:

    sui move build
  2. Publish ke testnet:

    sui client publish --gas-budget 100000000
  3. Interaksi dengan contract:

    # Cek objects yang dimiliki
    sui client objects

    # Register mahasiswa (sebagai admin)
    sui client call \
    --package <PACKAGE_ID> \
    --module kampus \
    --function register_mahasiswa \
    --args <ADMIN_CAP_ID> <REGISTRY_ID> "Budi Santoso" 20230001 "Teknik Informatika" <STUDENT_ADDRESS> \
    --gas-budget 10000000

    # Lihat info kampus
    sui client call \
    --package <PACKAGE_ID> \
    --module kampus \
    --function get_kampus_info \
    --args <REGISTRY_ID> \
    --gas-budget 1000000

📝 Rangkuman Module 2​

Yang Telah Dipelajari:​

  1. Setup Development: Sui CLI dan project Move
  2. Tipe Data Dasar: String, numbers (u8, u32, u64), boolean
  3. Vector: Array dinamis untuk koleksi data
  4. Object Model: Owned, shared objects dan ownership patterns
  5. Struct: Pengelompokan data dengan abilities
  6. Capabilities Pattern: Permission control dengan object ownership
  7. Fungsi: Public functions, parameters, return values
  8. Error Handling: Assert dan error constants
  9. Events: Logging aktivitas di blockchain
  10. Hands-On: Complete campus management system

Konsep Kunci Move:​

  • Resource Safety: Object ownership yang jelas dan aman
  • Linear Types: Tidak ada copy, hanya move atau borrow
  • Object-Centric: Semua data adalah object dengan UID
  • Capability-Based Security: Permission melalui object ownership
  • Type Safety: Strong typing untuk mencegah error

Best Practices:​

  • Selalu validasi input dengan assert!
  • Gunakan error constants untuk error yang jelas
  • Implement proper authorization checks
  • Emit events untuk monitoring
  • Use descriptive struct dan function names

Next Steps:​

Di Module 3, kita akan membangun Counter Contract yang lebih advanced dengan:

  • State management yang kompleks
  • Advanced testing patterns
  • Gas optimization
  • Production deployment strategies

🎯 Selamat! Anda telah menguasai dasar-dasar bahasa Move!

👉 Lanjut ke Module 3: Building a Token System →