Membangun token standar di atas OpenZeppelin: ERC-20 dengan transfer, approve, transferFrom, dan decimals, ERC-721 untuk NFT dengan metadata dan royalty ERC-2981, serta ERC-1155 untuk multi-token yang fleksibel

Setelah di episode 10 kalian menulis test dengan Foundry dan Hardhat, pada episode ini kita membangun fondasi ekosistem: token standards. Hampir semua produk kripto — exchange, NFT marketplace, lending, stablecoin — beroperasi di atas token yang mematuhi standar tertentu.
Mengapa episode ini penting? Karena standar token adalah bahasa bersama ekosistem EVM. Jika kalian menerbitkan token ERC-20 yang benar, secara otomatis ia kompatibel dengan ratusan wallet, exchange, dan protokol DeFi tanpa kerja ekstra. Memahami standar juga berarti memahami cara menulis kontrak yang interoperable — keahlian inti seorang smart contract developer.
ERC-20 adalah standar token fungible — setiap unit identik dan bisa dibagi. Fungsi wajibnya:
| Fungsi | Fungsi |
|---|---|
totalSupply() | Total token yang beredar |
balanceOf(account) | Saldo sebuah alamat |
transfer(to, amount) | Kirim token ke alamat lain |
approve(spender, amount) | Beri izin ke spender memakai dana kita |
transferFrom(from, to, amount) | Pindahkan dana berdasarkan izin |
allowance(owner, spender) | Lihat sisa izin |
Model izin (allowance) adalah kunci ERC-20: untuk menarik token dari pengguna, protokol DeFi memanggil transferFrom, dan itu hanya berhasil jika pengguna sudah approve dulu.
Karena EVM tidak punya floating point, ERC-20 memakai integer dengan jumlah desimal. Standar umumnya 18 decimals:
uint256 public constant decimals = 18;Artinya 1 token = 10 ** 18 unit terkecil (wei-ekuivalen). Menampilkan saldo di frontend berarti membagi dengan 10 ** decimals.
Menulis ERC-20 dari nol adalah pekerjaan berisiko — puluhan detail edge case. Solusi standarnya adalah OpenZeppelin Contracts:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.36;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MyToken is ERC20 {
constructor(uint256 initialSupply) ERC20("MyToken", "MTK") {
_mint(msg.sender, initialSupply);
}
}Hanya ini — transfer, approve, transferFrom, dan balanceOf sudah ditangani library yang sudah di-audit luas. _mint di constructor mengeluarkan supply awal ke deployer.
ERC-721 mewakili aset unik — setiap token punya ID sendiri dan hanya dimiliki satu alamat dalam satu waktu. Fungsi wajibnya ownerOf(tokenId), safeTransferFrom, dan tokenURI (metadata).
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.36;
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {ERC721URIStorage} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
contract MyNFT is ERC721URIStorage {
uint256 private _nextTokenId;
constructor() ERC721("MyNFT", "MNFT") {}
function mint(address to, string memory uri) external returns (uint256) {
uint256 tokenId = _nextTokenId++;
_mint(to, tokenId);
_setTokenURI(tokenId, uri);
return tokenId;
}
}tokenURI menunjuk ke metadata JSON (di IPFS atau server) berisi nama, deskripsi, dan gambar NFT.
ERC-2981 menambahkan royalty — persentase dari penjualan sekunder yang otomatis dialihkan ke kreator:
import {ERC2981} from "@openzeppelin/contracts/token/common/ERC2981.sol";
contract MyNFT is ERC721, ERC2981 {
constructor() ERC721("MyNFT", "MNFT") {
_setDefaultRoyalty(msg.sender, 500); // 5% (basis poin)
}
function supportsInterface(bytes4 interfaceId)
public view override(ERC721, ERC2981) returns (bool)
{
return super.supportsInterface(interfaceId);
}
}Nilai royalty dinyatakan dalam basis poin: 500 = 5%, 10000 = 100%. Marketplace yang mendukung ERC-2981 membayar royalty secara otomatis.
ERC-1155 menggabungkan fungible dan non-fungible dalam satu kontrak — hemat biaya deploy dan gas:
safeBatchTransferFrom).Cocok untuk game (senjata + koin dalam satu kontrak), koleksi bertema, atau aplikasi yang butuh efisiensi.
Tip
Aturan praktis memilih: ERC-20 untuk uang/token fungible, ERC-721 untuk aset unik, ERC-1155 saat butuh banyak tipe token dalam satu kontrak. Dan selalu gunakan library ter-audit (OpenZeppelin) daripada menulis dari nol.
transfer tanpa pengecekan — tidak seperti native ETH, transfer ERC-20 mengembalikan bool; abaikan risiko kehilangan token.tokenURI menunjuk ke server yang mati, NFT tampil kosong; IPFS mengurangi risiko ini.safeTransferFrom — versi safe* mencegah token terkirim ke kontrak yang tidak bisa menerimanya.Inti yang harus dibawa pulang:
transfer/approve/transferFrom, decimals biasanya 18.tokenURI, royalty via ERC-2981.Di episode 12 selanjutnya kita akan membahas DeFi patterns: DEX, Vault & Staking — AMM ala Uniswap V2 dengan liquidity pool dan slippage control, vault dengan deposit/withdraw dan shares accounting, serta model bunga ala Compound. Sampai jumpa di episode 12!