← Semua pembelajaran / Blockchain Nol → RWA
Fase 2 · Standar Token & Tokenisasi RWA

ERC-721 — NFT & aset unik

Sertifikat tanah, tiket bernomor kursi, karya seni digital — semuanya butuh representasi yang beda dari saldo token biasa. ERC-721 menambahkan satu konsep yang tidak ada di ERC-20: identitas per unit.

Intisari

  • ERC-721 adalah token non-fungible — tiap tokenId unik dan punya pemiliknya sendiri, dilacak lewat ownerOf(tokenId), bukan balanceOf saja.
  • safeTransferFrom memeriksa penerima (kalau kontrak) mengimplementasikan onERC721Received — mencegah NFT terkirim ke kontrak yang tidak siap menerimanya dan terkunci selamanya.
  • tokenURI(tokenId) menunjuk ke metadata (biasanya JSON di IPFS) — nama, gambar, atribut. Metadata tidak tersimpan on-chain karena terlalu mahal.
  • Cocok untuk representasi aset yang secara alami satuan: sertifikat kepemilikan tanah, tiket bernomor, sertifikat audit kustodian.
  • ERC721Enumerable menambah kemampuan mendaftar semua tokenId milik satu pemilik — tapi menambah biaya gas signifikan tiap mint/transfer; pertimbangkan apakah kamu benar-benar butuh on-chain, atau cukup diindeks di backend (Fase 3).

Beda konsep dari ERC-20

ERC-20ERC-721
Unit dasarJumlah (integer, bisa dipecah)tokenId (integer, tapi tidak bisa dipecah)
Kepemilikan dilacak lewatbalanceOf(address) → uint256ownerOf(tokenId) → address
Dua unit bisa identik?Ya, seluruhnya identikTidak — tiap tokenId berbeda
Analog dunia nyataSaldo rekeningNomor sertifikat/nomor kursi

Implementasi dasar

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract SertifikatTanah is ERC721, Ownable {
    uint256 private _idBerikutnya;

    constructor(address admin) ERC721("Sertifikat Tanah", "STANAH") Ownable(admin) {}

    function terbitkan(address pemilik, string memory metadataURI) external onlyOwner returns (uint256) {
        uint256 id = _idBerikutnya++;
        _safeMint(pemilik, id);
        _setTokenURI(id, metadataURI);   // perlu extension ERC721URIStorage
        return id;
    }
}

transferFrom vs safeTransferFrom

Selalu pakai safeTransferFrom, kecuali kamu tahu persis kenapa tidak. transferFrom polos akan memindahkan NFT ke kontrak apa pun tanpa pengecekan apakah kontrak itu tahu cara menerima NFT. Kalau kontrak tujuan tidak punya logika untuk memindahkannya lagi, NFT itu terkunci di sana selamanya — tidak ada mekanisme pemulihan. safeTransferFrom mewajibkan kontrak penerima mengimplementasikan onERC721Received yang mengonfirmasi ia siap menerima, sebelum transfer benar-benar terjadi.

Metadata: kenapa tidak disimpan on-chain

{
  "name": "Sertifikat Tanah #42",
  "description": "Bidang 500m2, Blok C, Desa Sukamaju",
  "image": "ipfs://bafybeigdyrzt.../gambar.png",
  "attributes": [
    { "trait_type": "Luas (m2)", "value": 500 },
    { "trait_type": "Zona", "value": "Pemukiman" }
  ]
}

tokenURI(id) mengembalikan tautan ke JSON ini, bukan isinya langsung. Menyimpan gambar atau deskripsi panjang di storage EVM akan berharga puluhan hingga ratusan juta gas — mustahil secara ekonomi. Pola standarnya: gambar dan metadata di IPFS (konten diverifikasi lewat hash, tidak bisa diubah diam-diam), hanya tautan pendeknya yang tersimpan on-chain.

Kapan ERC-721 adalah pilihan yang tepat untuk RWA

AsetStandar yang cocokAlasan
Emas batangan (bisa dipecah nilainya)ERC-20Fungible — 1 gram emas identik dengan 1 gram emas lain
Sertifikat tanah satu bidang spesifikERC-721Non-fungible — bidang A tidak bisa dipertukarkan dengan bidang B
Saham perusahaan (fungible per kelas)ERC-20 varian (lihat ERC-3643 berikutnya)Fungible dalam satu kelas saham, tapi butuh kepatuhan KYC

Latihan: deploy SertifikatTanah, terbitkan dua token ke dua alamat berbeda, lalu panggil tokenURI() untuk masing-masing dan buka JSON hasilnya (pakai data URI sederhana untuk latihan, bukan IPFS sungguhan). Verifikasi ownerOf(0) dan ownerOf(1) mengembalikan dua address yang berbeda.

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.