Smart Contract Real World Asset (RWA): Properti Fraksional di Arbitrum
๐ฏ Apa yang Akan Kita Pelajariโ
Dalam panduan praktis ini, Anda akan mempelajari:
- Konsep Dasar Web3 & Real World Asset (RWA): Perbedaan mendasar antara platform investasi properti terpusat (Web2) vs tokenisasi kepemilikan aset nyata di atas blockchain (Web3).
- Fase Persiapan: Menyiapkan dompet MetaMask dengan jaringan Arbitrum Sepolia dan menghubungkannya ke Remix IDE.
- 11 Fondasi Solidity Esensial: Memahami konsep inti yang digunakan di smart contract RWA dunia nyata:
string: Representasi data properti (nama, dokumen legal).uint256&bool: Kuantitas unit saham fraksional dan status penawaran investasi.publicvsprivate: Visibilitas data aset publik vs data internal kontrak.view&pure: Pemanggilan data gratis tanpa gas fee.mapping: Pencatatan kepemilikan fraksi investor secara terdistribusi.msg.sender: Autentikasi dompet investor langsung dari tanda tangan kriptografi.block.timestamp: Penegakan aturan batas waktu dan cooldown transaksi.payable&msg.value: Penerimaan transfer mata uang kripto asli (ETH) dan membaca nilainya dalam satuan Wei.require(): Validasi kuota fraksi dan verifikasi kecukupan pembayaran ETH.event&emit: Pencatatan log transaksi on-chain untuk sinkronisasi antarmuka dApp secara real-time.constructor& Hak Akses Pengelola (owner): Pengaturan awal aset dan hak akses manajer properti.
- Tantangan Proyek Utama (
FractionalProperty.sol): Membangun smart contract lengkap untuk tokenisasi kepemilikan vila fisik (Bali Sunset Villa #01). - Praktik Langsung (Compile, Deploy, Test & Verify): Mengompilasi kode di Remix, men-deploy ke Arbitrum Sepolia Testnet, menguji fungsi investasi secara interaktif, dan memverifikasi sertifikatnya di Arbiscan.
๐ข Tinjauan Kasus: Tokenisasi Properti Fraksional (RWA)โ
Bayangkan sebuah properti fisik bernilai tinggi di dunia nyata, misalnya sebuah vila eksklusif: "Bali Sunset Villa #01"1.
Secara konvensional, membeli properti utuh membutuhkan modal yang sangat besar dan proses birokrasi legal yang memakan waktu berbulan-bulan. Melalui teknologi Real World Asset (RWA) di Arbitrum, hak kepemilikan properti tersebut difraksionalisasi (fractionalized) menjadi unit-unit kecil yang dapat dimiliki oleh siapa saja secara terdesentralisasi:
- Properti dibagi menjadi 1.000 unit fraksi kepemilikan.
- Setiap fraksi dijual seharga 0.001 ETH.
- Investor dapat membeli 1 fraksi atau lebih dan kepemilikannya tercatat secara permanen di blockchain.
- Tautan bukti sertifikat fisik (dokumen legal/BPN) disimpan secara transparan di dalam kontrak.
Perbandingan Masalah: Mengapa Platform Web2 Tidak Cukup?โ
Jika kita membangun platform investasi fraksional ini menggunakan JavaScript dan database tersentralisasi (Web2 biasa):
class PropertyPlatform {
fractionBalances = {}; // userId => jumlah lembar fraksi
availableFractions = 1000;
// Di Web2, identitas investor dan pencatatan saham bergantung pada server admin
buyFraction(userId, amount) {
if (this.availableFractions >= amount) {
this.fractionBalances[userId] = (this.fractionBalances[userId] || 0) + amount;
this.availableFractions -= amount;
return true;
}
return false;
}
}
- Manipulasi Kepemilikan (Double Selling): Admin database internal dapat diam-diam mengubah saldo kepemilikan atau mencetak lembar fraksi melebihi batas fisik properti tanpa audit independen.
- Ketiadaan Bukti Kriptografis Permanen: Jika perusahaan platform properti bangkrut atau server cloud dimatikan, seluruh catatan kepemilikan investor lenyap.
- Risiko Pemalsuan Identitas: Identitas investor hanya berupa string
userIddi database yang rentan dibobol atau dipalsukan. - Ketergantungan Perantara (Middleman Friction): Transaksi antar-investor lambat, mahal, dan tidak dapat dipindahkan secara bebas ke dompet pribadi.
Di Web3 (Arbitrum), kepemilikan fraksi properti dijamin oleh Smart Contract Solidity. Setiap pembelian diverifikasi dengan tanda tangan digital dompet investor, aturan suplai tidak dapat dimanipulasi (immutable), dan biaya transaksi di Arbitrum hanya sepersekian sen dolar dengan konfirmasi instan (~250ms).
๐ ๏ธ Fase Persiapan: Remix IDE & MetaMaskโ
Sebelum mulai menulis kode smart contract RWA, mari kita siapkan development environment kita terlebih dahulu:
1. Persiapan Dompet MetaMask & Jaringan Arbitrum Sepoliaโ
- Instalasi: Pasang ekstensi browser MetaMask dari situs resmi https://metamask.io.
- Buat Dompet: Buat akun wallet baru. Catat dan simpan Secret Recovery Phrase (Seed Phrase) Anda di tempat yang aman dan rahasia!
- Tambahkan Jaringan Arbitrum Sepolia:
- Buka MetaMask > Klik menu pemilih jaringan di pojok kiri atas > Klik Add Network > Pilih Add a network manually.
- Masukkan parameter berikut:
- Network Name:
Arbitrum Sepolia - New RPC URL:
https://sepolia-rollup.arbitrum.io/rpc - Chain ID:
421614 - Currency Symbol:
ETH - Block Explorer URL:
https://sepolia.arbiscan.io/
- Network Name:
- Klik Save dan beralihlah ke jaringan Arbitrum Sepolia.
- Dapatkan Testnet ETH Gratis:
- Dapatkan testnet ETH gratis melalui Alchemy Arbitrum Sepolia Faucet atau Chainlink Faucets.
- Atau, klaim Sepolia ETH di Layer 1 melalui sepoliafaucet.com lalu kirim (bridge) ke Arbitrum Sepolia lewat Arbitrum Official Bridge.
2. Buka & Siapkan Remix IDEโ
- Buka tab baru di browser Anda dan kunjungi https://remix.ethereum.org.
- Di panel navigasi sebelah kiri, klik ikon File Explorer (ikon folder).
- Lingkungan coding online Anda sudah siap digunakan tanpa perlu instalasi perangkat lunak tambahan apa pun di komputer!
3. Hubungkan Remix ke MetaMaskโ
- Di panel bilah kiri Remix, klik menu Deploy & Run Transactions (ikon logo Ethereum dengan tanda panah).
- Pada dropdown opsi ENVIRONMENT, ubah dari
Remix VMmenjadi Injected Provider - MetaMask. - Jendela pop-up MetaMask akan otomatis muncul meminta izin koneksi. Pilih akun dompet Anda, lalu klik Next โก๏ธ Connect.
- Perhatikan keterangan di bawah opsi Environment: pastikan tertera Chain ID
421614(Arbitrum Sepolia) dan saldo ETH Anda telah terbaca. - Selesai! Ekosistem coding dan dompet Web3 Anda kini telah saling terhubung.
๐ Fondasi Solidity: 11 Konsep Kunci Sebelum Masuk ke Kontrak RWAโ
Sebelum kita menyusun smart contract FractionalProperty.sol, mari kita pelajari 11 konsep inti Solidity berikut secara berurutan. Setiap konsep di bawah ini menyediakan satu file smart contract mandiri (.sol) yang dapat langsung Anda salin ke Remix IDE, di-deploy, dan diuji tombolnya secara interaktif:
1. Tipe Data String & Struktur Dasar Kontrakโ
Apa itu: Setiap smart contract diawali dengan lisensi open-source (SPDX-License-Identifier) dan deklarasi versi compiler (pragma solidity). Tipe data string digunakan untuk menyimpan teks (UTF-8), seperti nama properti atau dokumen legal.
Buat file LearnString.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract LearnString {
// Menyimpan nama aset properti fisik
string public propertyName;
// Constructor mengatur nama awal properti saat deploy
constructor() {
propertyName = "Bali Sunset Villa #01";
}
// Fungsi untuk memperbarui nama properti jika ada renovasi/rebranding
function updatePropertyName(string memory _newName) public {
propertyName = _newName;
}
}
Coba di Remix:
- Deploy contract
LearnString. - Di panel kiri bawah (Deployed Contracts), klik tombol biru
propertyNameโก๏ธ Muncul teks"Bali Sunset Villa #01". - Masukkan teks
"Bali Luxury Beachfront Villa"pada kolom fungsiupdatePropertyNameโก๏ธ Klik tombol oranyeupdatePropertyName. - Klik tombol biru
propertyNamelagi โก๏ธ Sekarang nilainya berubah menjadi"Bali Luxury Beachfront Villa"!
2. Tipe Data Numerik (uint256) & Boolean (bool)โ
Apa itu: uint256 menyimpan bilangan bulat positif 256-bit (dari 0 hingga 2^256 - 1), ideal untuk mencatat kuantitas fraksi saham properti. Sedangkan bool menyimpan nilai logika (true atau false) untuk menentukan status penawaran investasi (offering status).
Buat file LearnNumber.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract LearnNumber {
// Sisa fraksi properti yang tersedia untuk dibeli
uint256 public availableFractions;
// Status apakah periode penjualan investasi sedang dibuka
bool public isOfferingOpen;
constructor() {
availableFractions = 1000;
isOfferingOpen = true;
}
// Menambah kuota fraksi jika properti melakukan ekspansi
function addFractions(uint256 _amount) public {
availableFractions += _amount;
}
// Mengubah saklar buka/tutup penawaran investasi
function toggleOffering() public {
isOfferingOpen = !isOfferingOpen;
}
}
Coba di Remix:
- Deploy contract
LearnNumber. - Klik tombol biru
availableFractionsโก๏ธ Lihat angka1000. - Klik tombol biru
isOfferingOpenโก๏ธ Lihat nilaitrue. - Masukkan angka
250pada kolomaddFractionsโก๏ธ Klik tombol oranyeaddFractions. - Klik
availableFractionslagi โก๏ธ Sekarang nilainya menjadi1250! - Klik tombol oranye
toggleOfferingโก๏ธ Klik lagiisOfferingOpenโก๏ธ Sekarang bernilaifalse!
3. Visibilitas Fungsi dan Variabel: public dan privateโ
Apa itu: Di Solidity, setiap fungsi dan variabel state memiliki tingkat akses (visibility):
public: Dapat diakses dan dibaca oleh siapa saja dari luar blockchain (investor/dApp) maupun dari dalam kontrak itu sendiri.private: Hanya dapat dibaca dan dieksekusi oleh kode internal smart contract. Informasi sensitif (seperti kode brankas fisik) tidak dapat dipanggil langsung oleh dompet luar.
Buat file LearnVisibility.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract LearnVisibility {
// Data publik yang boleh diakses seluruh calon investor
string public publicLegalNotice = "Sertifikat Tanah Hak Milik No. 882/Canggu";
// Data privat: Hanya digunakan untuk audit internal pengelola
string private internalVaultCode = "VAULT-SEC-9912";
// Fungsi publik untuk memverifikasi keabsahan data privat secara terkontrol
function verifyVaultCode() public view returns (string memory) {
return getInternalCode();
}
// Fungsi private pembantu yang hanya bisa dipanggil dari dalam kontrak
function getInternalCode() private view returns (string memory) {
return internalVaultCode;
}
}
Coba di Remix:
- Deploy contract
LearnVisibility. - Perhatikan tombol yang muncul di panel Deployed Contracts:
- Tombol biru
publicLegalNoticedanverifyVaultCodemuncul karena berstatuspublic. - Variabel
internalVaultCodedan fungsigetInternalCodeTIDAK muncul sebagai tombol karena berstatusprivate!
- Tombol biru
- Klik tombol biru
verifyVaultCodeโก๏ธ Muncul teks"VAULT-SEC-9912". Ini membuktikan fungsi internal kontrak dapat mengakses data privat secara aman.
4. State Mutability (view & pure) serta Nilai Balik Fungsiโ
Apa itu: Fungsi yang hanya membaca data dari storage tanpa mengubah state ditandai dengan view. Pemanggilan fungsi view bersifat 100% bebas gas (gas-free) dan tidak memicu popup MetaMask. Fungsi pure melakukan kalkulasi murni tanpa membaca storage blockchain. Fungsi yang mengubah state memerlukan transaksi dan gas fee.
Buat file LearnFunctions.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract LearnFunctions {
uint256 public totalFractionsSold = 0;
uint256 public constant FRACTION_PRICE = 0.001 ether;
// 1. Fungsi Pengubah State: Memotong kuota & mengubah state (Butuh Gas)
function recordSale(uint256 _amount) public {
totalFractionsSold += _amount;
}
// 2. Fungsi 'view': HANYA MEMBACA data (100% GRATIS, Tanpa Gas Fee)
function getTotalSold() public view returns (uint256) {
return totalFractionsSold;
}
// 3. Fungsi 'pure': Menghitung estimasi biaya investasi sebelum transaksi
function calculateTotalCost(uint256 _amount) public pure returns (uint256) {
return _amount * FRACTION_PRICE;
}
}
Coba di Remix:
- Deploy contract
LearnFunctions. - Perhatikan perbedaan warna tombol:
- Tombol Biru (
getTotalSold,calculateTotalCost,FRACTION_PRICE): Fungsiview/pureโก๏ธ Klik instan bebas biaya gas. - Tombol Oranye (
recordSale): Fungsi pengubah state โก๏ธ Membutuhkan tanda tangan transaksi.
- Tombol Biru (
- Masukkan angka
5padacalculateTotalCostโก๏ธ Klik tombol biru โก๏ธ Muncul5000000000000000(yaitu0.005 ETHdalam Wei). - Masukkan angka
5pada tombol oranyerecordSaleโก๏ธ Klik tombol โก๏ธ Lalu klikgetTotalSoldโก๏ธ Sekarang bernilai5!
5. Struktur Data Mapping (Key-Value Storage)โ
Apa itu: mapping(keyType => valueType) adalah buku besar (ledger) tabel hash pada blockchain yang memetakan alamat dompet (address) ke jumlah fraksi saham kepemilikan (uint256).
Buat file LearnMapping.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract LearnMapping {
// Buku besar kepemilikan: Alamat Investor => Jumlah Fraksi
mapping(address => uint256) public fractionBalances;
// Menetapkan kepemilikan fraksi ke alamat investor tertentu
function assignFractions(address _investor, uint256 _shares) public {
fractionBalances[_investor] += _shares;
}
// Membaca kepemilikan investor tertentu
function getInvestorFractions(address _investor) public view returns (uint256) {
return fractionBalances[_investor];
}
}
Coba di Remix:
- Deploy contract
LearnMapping. - Salin alamat akun Anda dari bagian atas panel Remix (dropdown ACCOUNT).
- Masukkan alamat dompet Anda dan angka
10pada kolomassignFractions(format:0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 10). - Klik tombol oranye
assignFractions. - Masukkan alamat dompet Anda ke kolom biru
getInvestorFractionsโก๏ธ Muncul angka10!
6. Global Variable msg.sender (Autentikasi Pemanggil)โ
Apa itu: Di Web2, pengguna mengirim username/password yang rentan diretas. Di Solidity, msg.sender adalah variabel global bawaan EVM yang membaca alamat dompet penandatangan transaksi secara otomatis via kriptografi. Identitas pemanggil tidak bisa dipalsukan oleh siapa pun!
Buat file LearnMsgSender.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract LearnMsgSender {
// Mencatat siapa investor terakhir yang berinvestasi
address public lastInvestor;
mapping(address => uint256) public myFractions;
// Investor mengklaim fraksi tanpa perlu menginput alamat dompetnya secara manual
function investMySelf(uint256 _shares) public {
lastInvestor = msg.sender; // msg.sender otomatis membaca dompet Anda!
myFractions[msg.sender] += _shares;
}
}
Coba di Remix:
- Deploy contract
LearnMsgSender. - Masukkan angka
3pada kolom oranyeinvestMySelfโก๏ธ Klik tombol. - Klik tombol biru
lastInvestorโก๏ธ Alamat dompet Anda langsung tercatat tanpa Anda pernah mengetiknya! - Masukkan alamat Anda pada
myFractionsโก๏ธ Muncul nilai3!
7. Global Variable block.timestamp & Time Unitsโ
Apa itu: block.timestamp mengembalikan waktu standar UNIX saat blok tersebut divalidasi oleh Sequencer Arbitrum. Fitur ini krusial untuk membuat periode jeda (cooldown) guna melindungi sistem dari bot, atau menetapkan tenggat waktu penawaran investasi.
Buat file LearnTimestamp.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract LearnTimestamp {
mapping(address => uint256) public lastInvestmentTime;
uint256 public constant COOLDOWN_DURATION = 15 seconds;
// Investor hanya boleh melakukan transaksi setiap 15 detik sekali
function recordInvestment() public {
require(
block.timestamp >= lastInvestmentTime[msg.sender] + COOLDOWN_DURATION,
"Harap tunggu 15 detik sebelum melakukan investasi lagi!"
);
lastInvestmentTime[msg.sender] = block.timestamp;
}
// Melihat sisa waktu cooldown untuk investor
function checkCooldown(address _investor) public view returns (uint256) {
if (block.timestamp >= lastInvestmentTime[_investor] + COOLDOWN_DURATION) {
return 0; // Cooldown sudah selesai, siap transaksi
}
return (lastInvestmentTime[_investor] + COOLDOWN_DURATION) - block.timestamp;
}
}
Coba di Remix:
- Deploy contract
LearnTimestamp. - Klik tombol oranye
recordInvestmentโก๏ธ Transaksi sukses. - Langsung klik lagi tombol
recordInvestmentdalam 15 detik โก๏ธ Transaksi seketika gagal (revert) dengan pesan error cooldown! - Tunggu 15 detik, lalu klik lagi โก๏ธ Transaksi kembali sukses!
8. Transaksi Finansial: Kata Kunci payable & Nilai msg.valueโ
Apa itu: Secara bawaan, smart contract di Ethereum/Arbitrum akan menolak (revert) setiap transaksi yang mencoba mentransfer koin ETH jika fungsi tersebut tidak memiliki pengubah khusus payable. Variabel global msg.value membaca jumlah koin ETH (dalam satuan terkecil, yaitu Wei, di mana 1 ETH = 10^18 Wei) yang dikirimkan oleh investor bersama transaksi tersebut.
Buat file LearnPayable.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract LearnPayable {
// Total dana ETH yang terkumpul di dalam kontrak (dalam satuan Wei)
uint256 public totalFundsReceived;
// Kata kunci 'payable' mengizinkan fungsi menerima kiriman dana ETH asli
function invest() public payable {
// msg.value otomatis membaca berapa banyak ETH yang ditransfer oleh pemanggil
totalFundsReceived += msg.value;
}
// Membaca saldo ETH yang tersimpan di brankas smart contract ini secara langsung
function getContractBalance() public view returns (uint256) {
return address(this).balance;
}
}
Coba di Remix:
- Deploy contract
LearnPayable. - Perhatikan tombol fungsi di panel Deployed Contracts: Tombol
investberwarna merah tua/burgundy (bukan oranye biasa), menandakan bahwa fungsi ini adalah fungsipayableyang dapat menerima transfer uang kripto! - Di panel atas Remix (bagian VALUE):
- Masukkan angka
2. - Ubah satuan dari
WeimenjadiFinney(setara dengan0.002 Ether).
- Masukkan angka
- Klik tombol merah
investโก๏ธ Setujui transaksi di MetaMask. - Klik tombol biru
getContractBalanceโก๏ธ Muncul nilai2000000000000000(angka 18 desimal Wei dari 0.002 ETH). Dana berhasil masuk ke saldo smart contract!
9. Validasi Kondisi & Error Handling: require()โ
Apa itu: require(kondisi, "pesan error") adalah penjaga gerbang (guard clause) di Solidity. Jika kondisi bernilai false, seluruh eksekusi transaksi langsung dibatalkan (revert), seluruh perubahan state dikembalikan ke kondisi semula, dan sisa gas fee dikembalikan ke dompet pengguna. Di smart contract RWA, require() digunakan untuk memastikan sisa kuota fraksi mencukupi dan pembayaran ETH yang dikirimkan (msg.value) sesuai dengan harga.
Buat file LearnRequire.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract LearnRequire {
uint256 public availableFractions = 50;
uint256 public constant FRACTION_PRICE = 0.001 ether;
function purchaseFractions(uint256 _amount) public payable {
// 1. Validasi kuantitas minimal
require(_amount > 0, "Jumlah pembelian harus lebih dari 0!");
// 2. Validasi ketersediaan stok fraksi
require(_amount <= availableFractions, "Sisa kuota fraksi properti tidak cukup!");
// 3. Validasi pembayaran ETH investor
uint256 totalCost = _amount * FRACTION_PRICE;
require(msg.value >= totalCost, "Pembayaran ETH yang dikirim kurang!");
availableFractions -= _amount;
}
}
Coba di Remix:
- Deploy contract
LearnRequire. - Masukkan angka
10pada kolompurchaseFractions, isi kolom VALUE dengan0.01 Ether(atau10 Finney) โก๏ธ Sukses! NilaiavailableFractionsberkurang jadi40. - Masukkan angka
0padapurchaseFractionsโก๏ธ Gagal dengan pesan:"Jumlah pembelian harus lebih dari 0!". - Masukkan angka
5padapurchaseFractions, tapi kosongkan kolom VALUE (0 Ether) โก๏ธ Gagal dengan pesan:"Pembayaran ETH yang dikirim kurang!". - Masukkan angka
100padapurchaseFractionsโก๏ธ Gagal dengan pesan:"Sisa kuota fraksi properti tidak cukup!".
10. Event Logging & Emit: Komunikasi State ke Frontendโ
Apa itu: Blockchain tidak memiliki sistem webhook REST API tradisional. Untuk memberitahu frontend dApp saat ada transaksi baru, smart contract memancarkan event menggunakan perintah emit. Frontend React dapat mendengarkan event ini secara real-time untuk memperbarui tampilan UI tanpa reload halaman!
Buat file LearnEvent.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract LearnEvent {
// Deklarasi event: parameter 'indexed' mempermudah pencarian log oleh dApp
event FractionPurchased(
address indexed investor,
uint256 amount,
uint256 timestamp
);
function buy(uint256 _shares) public {
// Pancarkan log event ke blockchain
emit FractionPurchased(msg.sender, _shares, block.timestamp);
}
}
Coba di Remix:
- Deploy contract
LearnEvent. - Masukkan angka
5padabuyโก๏ธ Klik tombol oranye. - Buka konsol log di bagian bawah layar Remix, klik tanda panah pada baris transaksi terbaru.
- Perhatikan bagian logs: Terdapat data event
FractionPurchasedlengkap dengan alamatinvestor, jumlahamount: 5, dantimestamp!
11. Constructor & Kontrol Hak Akses (Owner)โ
Apa itu: constructor() adalah fungsi inisialisasi yang hanya dieksekusi satu kali saat kontrak pertama kali dideploy. Kita menyematkan alamat msg.sender deployer ke dalam variabel owner, lalu membuat proteksi agar fitur manajerial (seperti penarikan dana investasi) hanya dapat dijalankan oleh pemilik properti sah.
Buat file LearnOwner.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract LearnOwner {
address public owner;
uint256 public totalInvestmentQuota;
// Modifier untuk membatasi akses khusus owner
modifier onlyOwner() {
require(msg.sender == owner, "Akses ditolak: Anda bukan pengelola properti!");
_;
}
// Constructor menetapkan owner awal
constructor(uint256 _initialQuota) {
owner = msg.sender;
totalInvestmentQuota = _initialQuota;
}
// Fungsi manajerial: Hanya bisa dieksekusi oleh owner
function expandQuota(uint256 _extraQuota) public onlyOwner {
totalInvestmentQuota += _extraQuota;
}
}
Coba di Remix:
- Masukkan angka
1000di samping tombol Deploy โก๏ธ Klik Deploy. - Klik tombol biru
ownerโก๏ธ Muncul alamat dompet Anda. - Masukkan angka
500padaexpandQuotaโก๏ธ Transaksi sukses!totalInvestmentQuotabertambah jadi1500. - (Uji Keamanan): Beralih ke akun lain di dropdown ACCOUNT Remix, lalu klik
expandQuotaโก๏ธ Transaksi ditolak dengan pesan:"Akses ditolak: Anda bukan pengelola properti!".
๐ Tantangan Proyek Utama: Membangun FractionalProperty.solโ
Setelah menguasai 11 fondasi Solidity di atas, saatnya menyatukannya ke dalam satu smart contract produksi: Platform Investasi Properti Fraksional (Real World Asset)!
Kontrak ini mengintegrasikan seluruh elemen:
- Penyimpanan identitas aset fisik & tautan sertifikat legal on-chain (
propertyDocumentURI). - Pembayaran pembelian fraksi menggunakan mata uang kripto asli (
payableETH di Arbitrum). - Pencatatan saldo kepemilikan investor yang aman dan transparan.
- Penarikan modal investasi khusus pemilik aset (
onlyOwner). - Notifikasi live ke antarmuka web melalui Events.
๐ฆ Langkah Praktis: Menyiapkan Dokumen Legalitas Fisik ke IPFS via Pinataโ
Sebelum men-deploy smart contract RWA, aset properti fisik harus ditautkan dengan dokumen bukti legalitas aslinya (misalnya sertifikat tanah BPN atau surat kepemilikan villa).
Karena menyimpan file fisik besar (seperti PDF berukuran 2โ5 MB) langsung ke dalam blockchain Arbitrum membutuhkan biaya gas yang sangat mahal, standar industri Web3 menggunakan IPFS (InterPlanetary File System):
-
Mengapa Menggunakan IPFS untuk RWA?
- Content-Addressed Storage: Berbeda dengan URL web biasa yang bisa mati atau diubah diam-diam oleh pemilik server, file di IPFS diidentifikasi berdasarkan isi kontennya melalui hash kriptografis unik yang disebut CID (Content Identifier).
- Immutable & Transparan: Jika dokumen sertifikat tanah diubah atau dipalsukan satu karakter saja, nilai hash CID-nya akan berubah total. Smart contract menyimpan hash CID ini sebagai garansi permanen bahwa dokumen legal tidak dapat dimanipulasi.
-
3 Langkah Cepat Mengunggah File ke IPFS (Gratis via Pinata):
- Langkah 1: Daftar Akun Gratis di layanan IPFS pinning terpopuler Pinata.cloud (atau alternatif seperti Filebase).
- Langkah 2: Upload File โก๏ธ Buka dashboard Pinata, klik tombol Upload โก๏ธ Pilih File โก๏ธ Unggah dokumen sampel Anda (misalnya file PDF dummy sertifikat tanah atau foto properti villa).
- Langkah 3: Salin Nilai CID โก๏ธ Setelah selesai, salin string CID yang tertera di tabel file Anda (contoh format:
QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco).
-
Format Standar URI Web3: Rangkai CID yang Anda dapatkan dengan protokol
ipfs://:ipfs://QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco/sertifikat-tanah.pdfTautan hash inilah yang dimasukkan ke variabel
propertyDocumentURIdi bawah ini sebelum Anda menekan tombol deploy di Remix IDE!
Jika Anda atau peserta workshop tidak ingin repot mendaftar akun dan mengunggah file mandiri ke Pinata/IPFS saat sesi berlangsung, Anda dapat langsung menggunakan CID dokumen sertifikat legalitas resmi yang telah kami siapkan:
bafybeiexwukp7b44s42dk7fjybeduq4teqganfsmndrpxmr6im32meru3i
Atau dalam format protokol Web3:
ipfs://bafybeiexwukp7b44s42dk7fjybeduq4teqganfsmndrpxmr6im32meru3i
Nilai ini sudah terpasang langsung secara default di dalam constructor smart contract FractionalProperty.sol di bawah ini, sehingga peserta dapat langsung men-deploy kontrak dan menguji coba integrasi frontend tanpa hambatan!
Kode Lengkap Smart Contract:โ
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title FractionalProperty (Bali Sunset Villa #01)
* @notice Smart Contract Real World Asset (RWA) untuk tokenisasi properti fraksional di Arbitrum Sepolia.
*/
contract FractionalProperty {
// ==========================================
// 1. DATA ASET & STATE VARIABLES
// ==========================================
string public propertyName;
string public propertySymbol;
// Tautan permanen ke dokumen sertifikat tanah legal fisik di IPFS
string public propertyDocumentURI;
// Alamat dompet pengelola aset / developer properti
address public owner;
// Harga per fraksi properti (0.001 ETH)
uint256 public fractionPrice;
// Total fraksi yang diterbitkan
uint256 public totalFractions;
// Sisa fraksi yang masih tersedia untuk dibeli
uint256 public availableFractions;
// Buku besar kepemilikan: Alamat Investor => Jumlah Fraksi Dimiliki
mapping(address => uint256) public fractionBalances;
// Waktu transaksi terakhir investor (untuk anti-spam / cooldown)
mapping(address => uint256) public lastInvestmentTime;
uint256 public constant COOLDOWN_PERIOD = 10 seconds;
// ==========================================
// 2. EVENTS (Logging untuk Frontend dApp)
// ==========================================
event FractionPurchased(
address indexed investor,
uint256 amount,
uint256 totalCost,
uint256 remainingFractions,
uint256 timestamp
);
event FractionsRestocked(
uint256 additionalFractions,
uint256 newAvailableFractions,
uint256 timestamp
);
event FundsWithdrawn(
address indexed owner,
uint256 amount,
uint256 timestamp
);
// ==========================================
// 3. MODIFIERS
// ==========================================
modifier onlyOwner() {
require(msg.sender == owner, "Akses ditolak: Hanya pengelola properti yang berhak!");
_;
}
// ==========================================
// 4. CONSTRUCTOR
// ==========================================
constructor() {
propertyName = "Bali Sunset Villa #01";
propertySymbol = "VILLA-BALI-01";
// CID resmi dokumen properti workshop (peserta dapat langsung pakai tanpa ribet upload mandiri ke IPFS)
propertyDocumentURI = "ipfs://bafybeiexwukp7b44s42dk7fjybeduq4teqganfsmndrpxmr6im32meru3i";
owner = msg.sender;
fractionPrice = 0.001 ether; // 0.001 ETH per lembar fraksi
totalFractions = 1000;
availableFractions = 1000;
}
// ==========================================
// 5. CORE FUNCTIONS (Investasi & Beli Saham)
// ==========================================
/**
* @notice Membeli sejumlah fraksi kepemilikan properti dengan mengirimkan ETH.
* @param _amount Jumlah unit fraksi yang ingin dibeli.
*/
function buyFractions(uint256 _amount) public payable {
// Validasi 1: Kuantitas harus valid
require(_amount > 0, "Jumlah pembelian fraksi harus lebih dari 0!");
// Validasi 2: Sisa fraksi harus mencukupi
require(_amount <= availableFractions, "Sisa kuota fraksi properti tidak mencukupi!");
// Validasi 3: Nilai pembayaran ETH harus tepat atau lebih
uint256 totalCost = _amount * fractionPrice;
require(msg.value >= totalCost, "Pembayaran ETH yang dikirim kurang!");
// Validasi 4: Cooldown antar transaksi untuk proteksi anti-spam
require(
block.timestamp >= lastInvestmentTime[msg.sender] + COOLDOWN_PERIOD,
"Harap tunggu periode cooldown selesai sebelum melakukan investasi lagi!"
);
// Update State
lastInvestmentTime[msg.sender] = block.timestamp;
availableFractions -= _amount;
fractionBalances[msg.sender] += _amount;
// Pancarkan event agar antarmuka web dApp langsung ter-update live
emit FractionPurchased(
msg.sender,
_amount,
totalCost,
availableFractions,
block.timestamp
);
// Kembalikan kelebihan pembayaran ETH jika investor mengirim lebih
if (msg.value > totalCost) {
payable(msg.sender).transfer(msg.value - totalCost);
}
}
// ==========================================
// 6. VIEW FUNCTIONS (Pembacaan Data Gratis)
// ==========================================
/**
* @notice Membaca jumlah fraksi yang dimiliki oleh alamat pemanggil saat ini.
*/
function getMyFractions() public view returns (uint256) {
return fractionBalances[msg.sender];
}
/**
* @notice Membaca jumlah fraksi yang dimiliki oleh alamat investor tertentu.
*/
function getInvestorFractions(address _investor) public view returns (uint256) {
return fractionBalances[_investor];
}
// ==========================================
// 7. OWNER FUNCTIONS (Manajemen Properti)
// ==========================================
/**
* @notice Menambah kuota fraksi properti (Hanya untuk pengelola aset).
*/
function restockFractions(uint256 _additionalFractions) public onlyOwner {
require(_additionalFractions > 0, "Jumlah penambahan harus lebih dari 0!");
availableFractions += _additionalFractions;
totalFractions += _additionalFractions;
emit FractionsRestocked(_additionalFractions, availableFractions, block.timestamp);
}
/**
* @notice Menarik dana ETH hasil penjualan investasi untuk renovasi/operasional properti fisik.
*/
function withdrawFunds() public onlyOwner {
uint256 contractBalance = address(this).balance;
require(contractBalance > 0, "Tidak ada saldo ETH yang dapat ditarik!");
payable(owner).transfer(contractBalance);
emit FundsWithdrawn(owner, contractBalance, block.timestamp);
}
}
๐งช Praktik Langsung: Kompilasi, Deploy & Uji di Arbitrum Sepoliaโ
Setelah memahami smart contract FractionalProperty.sol, ikuti langkah-langkah praktis berikut di Remix IDE:
1. Kompilasi Kontrakโ
- Di panel bilah kiri Remix, buka tab Solidity compiler (ikon kedua).
- Pastikan compiler version sesuai (misal:
0.8.20). - Klik tombol biru Compile FractionalProperty.sol hingga muncul tanda centang hijau.
2. Deploy ke Arbitrum Sepoliaโ
- Pastikan ekstensi MetaMask Anda aktif di jaringan Arbitrum Sepolia (Chain ID:
421614). - Buka tab Deploy & Run Transactions (ikon ketiga di bilah navigasi kiri Remix).
- Pada dropdown ENVIRONMENT, pilih Injected Provider - MetaMask.
- Klik tombol oranye Deploy.
- Setujui transaksi di jendela pop-up MetaMask (biaya gas dipotong dari saldo Arbitrum Sepolia ETH Anda).
3. Uji Fungsionalitas Interaktifโ
Setelah transaksi berhasil dikonfirmasi oleh Sequencer Arbitrum (~250ms), buka menu dropdown kontrak Anda di bagian bawah panel kiri:
- Cek Informasi Aset:
- Klik tombol biru
propertyNameโก๏ธ Muncul"Bali Sunset Villa #01". - Klik tombol biru
propertyDocumentURIโก๏ธ Muncul tautan sertifikat IPFS. - Klik tombol biru
availableFractionsโก๏ธ Muncul angka1000.
- Klik tombol biru
- Uji Pembelian Fraksi (
buyFractions):- Di bagian atas panel Remix, pada kolom VALUE, masukkan angka
10dan ubah satuan dariWeimenjadiFinney(atau0.01 Ether). - Pada kolom fungsi oranye
buyFractions, masukkan angka10(membeli 10 fraksi seharga 0.01 ETH). - Klik tombol
buyFractionsโก๏ธ Konfirmasi di MetaMask. - Klik tombol biru
getMyFractionsโก๏ธ Nilai kepemilikan Anda bertambah menjadi10! - Klik
availableFractionsโก๏ธ Kuota sisa berkurang dari1000menjadi990!
- Di bagian atas panel Remix, pada kolom VALUE, masukkan angka
- Uji Validasi Cooldown:
- Segera klik tombol
buyFractionssekali lagi tanpa menunggu. - Transaksi seketika gagal (revert) dengan pesan:
"Harap tunggu periode cooldown selesai sebelum melakukan investasi lagi!".
- Segera klik tombol
- Uji Penarikan Dana Pengelola (
withdrawFunds):- Klik tombol oranye
withdrawFundsmenggunakan akun deployer (owner). - Dana ETH hasil penjualan fraksi berhasil ditransfer langsung ke dompet Anda!
- Klik tombol oranye
4. Verifikasi di Block Explorer (Arbiscan)โ
Setelah smart contract Anda berhasil di-deploy ke Arbitrum Sepolia, kontrak tersebut memiliki identitas unik berupa Contract Address (CA). Block explorer resmi Arbiscan Sepolia adalah sarana audit terbuka untuk memverifikasi seluruh kebenaran aset secara on-chain:
Langkah 1: Salin Contract Address (CA)โ
- Di panel kiri bawah Remix IDE, pada bagian Deployed Contracts, cari entri kontrak
FRACTIONALPROPERTY. - Klik ikon salin (๐) di samping nama kontrak untuk meng-copy alamat kontrak (contoh format:
0x7b12C9B...d34A).
Langkah 2: Buka Contract Address di Arbiscanโ
- Buka peramban dan kunjungi sepolia.arbiscan.io.
- Tempelkan (paste) Contract Address (CA) Anda di bilah pencarian utama, lalu tekan Enter.
- Di halaman ikhtisar kontrak, Anda dapat melihat:
- Contract Creator: Alamat dompet pengelola properti (deployer/owner) dan hash transaksi pembuatan kontrak (
Contract Creation). - Balance: Saldo ETH yang terkumpul dari hasil penjualan fraksi.
- Transactions: Daftar seluruh transaksi yang memanggil fungsi
buyFractions,restockFractions, danwithdrawFunds.
- Contract Creator: Alamat dompet pengelola properti (deployer/owner) dan hash transaksi pembuatan kontrak (
Langkah 3: Verifikasi Source Code (Verify & Publish)โ
Agar tab "Read Contract" dan "Write Contract" terbuka penuh di Arbiscan:
- Klik tab Contract di Arbiscan โก๏ธ Klik tautan biru Verify and Publish.
- Pilih opsi:
- Compiler Type: Solidity (Single file)
- Compiler Version:
v0.8.20+commit.a1b79de6(sesuaikan dengan compiler di Remix) - Open Source License Type: MIT License (MIT)
- Klik Continue, lalu tempelkan seluruh kode sumber
FractionalProperty.solke kolom kode. - Klik Verify and Publish. Arbiscan akan menampilkan centang hijau:
Contract Source Code Verified.
Langkah 4: Eksplorasi Tab "Read Contract" (Audit Dokumen & Kepemilikan)โ
Setelah terverifikasi, klik tab Contract โก๏ธ Read Contract:
- Klik fungsi
propertyName: Mengembalikan nilai string"Bali Sunset Villa #01". - Klik fungsi
propertyDocumentURI: Mengembalikan URI"ipfs://bafybeiexwukp7b44s42dk7fjybeduq4teqganfsmndrpxmr6im32meru3i". - Klik fungsi
availableFractions: Mengembalikan sisa kuota yang tersedia secara real-time. - Klik fungsi
fractionPrice: Mengembalikan angka1000000000000000(setara 0.001 ETH).
Langkah 5: Eksplorasi Tab "Events" (Audit Transaksi & Transparansi)โ
Klik tab Events di Arbiscan:
- Setiap kali transaksi
buyFractionsterjadi, eventFractionPurchasedtercatat permanen di blockchain Arbitrum lengkap dengan alamat dompet pembeli, jumlah kuota saham yang dibeli, dan sisa fraksi yang ada saat itu.
<img> Biasa di HTML?"Pertanyaan ini sering muncul dari pengembang Web2. Jawabannya adalah sangat berbeda secara fundamental:
- Prasasti Abadi vs Mading Kantor:
- Menaruh gambar/dokumen di HTML biasa (
<img src="server.com/villa.png">) ibarat menempelkan fotokopi sertifikat tanah di mading kantor swasta. Admin server cloud bisa diam-diam mengganti file tersebut kapan saja. - Di Web3 RWA, sidik jari digital (hash IPFS
bafybeiexwukp7b44s42dk7fjybeduq4teqganfsmndrpxmr6im32meru3i) dipahat permanen di ledger blockchain Arbitrum. Dokumen fisik yang diwakilinya tidak bisa ditukar sepihak dengan sertifikat properti lain karena hash kriptografisnya diikat mati di smart contract.
- Menaruh gambar/dokumen di HTML biasa (
- Kebenaran Mutlak Ada di Smart Contract, Bukan di Website:
- Frontend React hanyalah "kaca pembesar" atau antarmuka visual untuk membaca apa yang tertulis di smart contract on-chain.
- Jika besok server frontend mati, bangkrut, atau di-hack, hak kepemilikan dan bukti dokumen legalitas aset villa tetap hidup permanen. Siapapun di dunia dapat langsung memverifikasinya melalui Arbiscan cukup dengan bermodalkan Contract Address (CA)!
๐๏ธ Membedah Arsitektur RWA: Web2 vs Web3โ
| Aspek | Platform Properti Web2 | Smart Contract RWA (Arbitrum Sepolia) |
|---|---|---|
| Pencatatan Kepemilikan | Tabel database SQL internal perusahaan. | Buku besar terdistribusi mapping on-chain yang immutable. |
| Bukti Legal Fisik | File PDF di server cloud privat yang bisa diubah. | Hash dokumen sertifikat tanah permanen di IPFS & Smart Contract. |
| Verifikasi Investor | Username & Password yang rentan disusupi. | Tanda tangan kriptografis dari wallet Web3 (msg.sender). |
| Penyelesaian Transaksi | Berhari-hari via transfer bank konvensional. | Sub-detik (~250ms) di jaringan Arbitrum. |
| Kedaulatan Investor | Jika platform tutup, bukti kepemilikan hilang. | Investor memegang hak kepemilikan permanen di dompet pribadi. |
๐ Ringkasanโ
Selamat! Anda telah menguasai fondasi penuh smart contract Real World Asset (RWA):
- Memahami bagaimana aset properti fisik dapat difraksionalisasi secara aman di blockchain.
- Mengamankan hak kepemilikan investor secara deterministik menggunakan
msg.senderdanmapping. - Menerapkan validasi kuota dan pembayaran aman menggunakan
require()danpayable. - Menautkan dokumen legal aset fisik ke smart contract on-chain.
- Melakukan deployment dan uji coba langsung di testnet resmi Arbitrum Sepolia.
๐ Sumber Belajar Lanjutanโ
- Arbitrum Docs - Quickstart Solidity Remix: Sumber dokumentasi asli panduan quickstart dApp Arbitrum.
- Stylus Quickstart: Panduan resmi menulis smart contract berkinerja tinggi menggunakan bahasa pemrograman Rust di Arbitrum.
- Arbitrum Solidity References: Referensi fungsi dan integrasi EVM Arbitrum.
- Dokumentasi Resmi Arbitrum: Panduan lengkap ekosistem Rollups, Orbit, dan Stylus.
๐ Catatan Kaki (Footnotes)โ
Footnotesโ
-
Tokenisasi Real World Asset (RWA) adalah salah satu inovasi terdepan di ekosistem Web3, memungkinkan aset fisik bernilai besar (seperti properti, emas, komoditas, dan obligasi) diperdagangkan dalam satuan fraksi mikro dengan likuiditas global dan transparansi audit 24/7 di jaringan Layer-2 Arbitrum. โฉ