📋 Apa yang Akan Dipelajari?
Dalam tutorial ini, Anda akan belajar cara deploy 5 smart contract yang aman dan sudah teruji ke blockchain menggunakan Remix IDE. Semua contract telah dirancang dengan prinsip keamanan tinggi tanpa backdoor atau fungsi berbahaya.
Persiapan Awal
Sebelum memulai, pastikan Anda telah menyiapkan:
- Wallet Crypto: MetaMask, WalletConnect, atau wallet lainnya
- Testnet ETH: Untuk testing (dapatkan dari faucet)
- Browser: Chrome, Firefox, atau browser modern lainnya
- Akses Internet: Untuk menggunakan Remix IDE online
Membuka Remix IDE
Buka browser dan kunjungi https://remix.ethereum.org
Remix IDE akan terbuka dengan interface yang user-friendly. Anda akan melihat:
- File Explorer di sebelah kiri
- Code Editor di tengah
- Terminal di bagian bawah
Membuat File Smart Contract
Di File Explorer, klik kanan pada folder "contracts" dan pilih "New File". Beri nama sesuai dengan contract yang akan dibuat (contoh: SafeToken.sol, MessageBoard.sol, dll.)
🎯 Langkah Deploy untuk Setiap Contract
Ikuti langkah-langkah ini untuk setiap smart contract:
- Copy Code: Salin kode contract ke file .sol
- Compile: Tekan Ctrl+S atau klik tab "Solidity Compiler"
- Deploy: Pindah ke tab "Deploy & Run Transactions"
- Connect Wallet: Pilih "Injected Web3" dan connect wallet
- Select Contract: Pilih contract yang ingin di-deploy
- Deploy: Klik tombol "Deploy" dan konfirmasi transaksi
Fitur Keamanan:
- Fixed max supply - tidak bisa di-mint lagi
- No owner privileges - fully decentralized
- Standard ERC20 - compatible dengan semua wallet
- No backdoor functions
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.9.0/contracts/token/ERC20/ERC20.sol";
contract SafeToken is ERC20 {
uint256 public constant MAX_SUPPLY = 1000000 * 10**18; // Fixed max supply
constructor() ERC20("SafeToken", "SAFE") {
// Mint to deployer once, no more minting possible
_mint(msg.sender, MAX_SUPPLY);
}
// No additional functions = no backdoors
// No owner = fully decentralized
// Fixed supply = no inflation risk
}
Fitur Keamanan:
- Read-only access - tidak ada fungsi berbahaya
- Message length limitation
- No ETH handling - tidak ada risiko finansial
- Public dan transparent
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SafeMessageBoard {
struct Message {
address sender;
string content;
uint256 timestamp;
}
Message[] public messages;
uint256 public constant MAX_MESSAGE_LENGTH = 280; // Like Twitter
event MessagePosted(address indexed sender, string content, uint256 timestamp);
function postMessage(string memory _content) public {
require(bytes(_content).length <= MAX_MESSAGE_LENGTH, "Message too long");
require(bytes(_content).length > 0, "Empty message");
Message memory newMessage = Message({
sender: msg.sender,
content: _content,
timestamp: block.timestamp
});
messages.push(newMessage);
emit MessagePosted(msg.sender, _content, block.timestamp);
}
function getMessageCount() public view returns (uint256) {
return messages.length;
}
function getLatestMessages(uint256 _count) public view returns (Message[] memory) {
require(_count <= messages.length, "Not enough messages");
Message[] memory latestMessages = new Message[](_count);
uint256 startIndex = messages.length - _count;
for (uint256 i = 0; i < _count; i++) {
latestMessages[i] = messages[startIndex + i];
}
return latestMessages;
}
// No withdrawal functions = no drain risk
// No owner privileges = no backdoors
// No ETH handling = no financial risk
}
Fitur Keamanan:
- Immutable certificates - tidak bisa diubah
- Hash verification untuk integritas data
- No admin functions - decentralized
- Public verification
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SafeCertificateStorage {
struct Certificate {
string recipientName;
string courseName;
string issuer;
uint256 issueDate;
bytes32 certificateHash;
}
mapping(bytes32 => Certificate) public certificates;
mapping(address => bytes32[]) public userCertificates;
event CertificateIssued(
bytes32 indexed certificateId,
address indexed recipient,
string recipientName,
string courseName
);
function issueCertificate(
address _recipient,
string memory _recipientName,
string memory _courseName,
string memory _issuer
) public returns (bytes32) {
// Create unique certificate ID
bytes32 certificateId = keccak256(
abi.encodePacked(
_recipient,
_recipientName,
_courseName,
_issuer,
block.timestamp
)
);
// Ensure certificate doesn't already exist
require(certificates[certificateId].issueDate == 0, "Certificate already exists");
// Create certificate hash for integrity
bytes32 certificateHash = keccak256(
abi.encodePacked(_recipientName, _courseName, _issuer, block.timestamp)
);
Certificate memory newCertificate = Certificate({
recipientName: _recipientName,
courseName: _courseName,
issuer: _issuer,
issueDate: block.timestamp,
certificateHash: certificateHash
});
certificates[certificateId] = newCertificate;
userCertificates[_recipient].push(certificateId);
emit CertificateIssued(certificateId, _recipient, _recipientName, _courseName);
return certificateId;
}
function verifyCertificate(bytes32 _certificateId) public view returns (
bool isValid,
string memory recipientName,
string memory courseName,
string memory issuer,
uint256 issueDate
) {
Certificate memory cert = certificates[_certificateId];
if (cert.issueDate == 0) {
return (false, "", "", "", 0);
}
// Verify hash integrity
bytes32 expectedHash = keccak256(
abi.encodePacked(cert.recipientName, cert.courseName, cert.issuer, cert.issueDate)
);
bool hashValid = (expectedHash == cert.certificateHash);
return (hashValid, cert.recipientName, cert.courseName, cert.issuer, cert.issueDate);
}
function getUserCertificates(address _user) public view returns (bytes32[] memory) {
return userCertificates[_user];
}
// No ETH functions = no financial risk
// Immutable data = no tampering
// No admin functions = no backdoors
}
Fitur Keamanan:
- Transparent rating system
- No manipulation possible
- Decentralized - no admin controls
- Public reputation scores
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SafeReputationSystem {
struct UserProfile {
uint256 positiveRatings;
uint256 negativeRatings;
uint256 totalInteractions;
bool isActive;
}
mapping(address => UserProfile) public userProfiles;
mapping(address => mapping(address => bool)) public hasRated;
uint256 public totalUsers;
event UserRated(address indexed rater, address indexed ratee, bool positive);
event ProfileCreated(address indexed user);
function createProfile() public {
require(!userProfiles[msg.sender].isActive, "Profile already exists");
userProfiles[msg.sender] = UserProfile({
positiveRatings: 0,
negativeRatings: 0,
totalInteractions: 0,
isActive: true
});
totalUsers++;
emit ProfileCreated(msg.sender);
}
function rateUser(address _user, bool _positive) public {
require(userProfiles[msg.sender].isActive, "Create profile first");
require(userProfiles[_user].isActive, "User profile doesn't exist");
require(_user != msg.sender, "Cannot rate yourself");
require(!hasRated[msg.sender][_user], "Already rated this user");
hasRated[msg.sender][_user] = true;
if (_positive) {
userProfiles[_user].positiveRatings++;
} else {
userProfiles[_user].negativeRatings++;
}
userProfiles[_user].totalInteractions++;
emit UserRated(msg.sender, _user, _positive);
}
function getUserReputation(address _user) public view returns (
uint256 positiveRatings,
uint256 negativeRatings,
uint256 totalInteractions,
uint256 reputationScore
) {
UserProfile memory profile = userProfiles[_user];
uint256 score = 0;
if (profile.totalInteractions > 0) {
score = (profile.positiveRatings * 100) / profile.totalInteractions;
}
return (
profile.positiveRatings,
profile.negativeRatings,
profile.totalInteractions,
score
);
}
function getTopUsers(uint256 _minInteractions) public view returns (address[] memory) {
// This is a simplified version - in production, you'd want to implement pagination
address[] memory topUsers = new address[](10);
uint256 count = 0;
// Note: This is not efficient for large datasets
// In production, you'd maintain a sorted list or use off-chain indexing
return topUsers; // Simplified implementation
}
// No financial functions = no monetary risk
// Transparent ratings = no manipulation
// No admin controls = decentralized
}
Fitur Keamanan:
- Public event logging
- No ETH handling - no financial risk
- Transparent attendee system
- Decentralized event management
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SafeEventRegistry {
struct Event {
address organizer;
string title;
string description;
string location;
uint256 eventDate;
uint256 maxAttendees;
uint256 currentAttendees;
bool isActive;
}
struct Attendee {
address attendeeAddress;
uint256 registrationTime;
bool checkedIn;
}
mapping(uint256 => Event) public events;
mapping(uint256 => mapping(address => bool)) public isRegistered;
mapping(uint256 => mapping(uint256 => Attendee)) public eventAttendees;
mapping(uint256 => uint256) public attendeeCount;
uint256 public eventCounter;
event EventCreated(uint256 indexed eventId, address indexed organizer, string title);
event AttendeeRegistered(uint256 indexed eventId, address indexed attendee);
event AttendeeCheckedIn(uint256 indexed eventId, address indexed attendee);
function createEvent(
string memory _title,
string memory _description,
string memory _location,
uint256 _eventDate,
uint256 _maxAttendees
) public returns (uint256) {
require(bytes(_title).length > 0, "Title cannot be empty");
require(_eventDate > block.timestamp, "Event date must be in future");
require(_maxAttendees > 0, "Max attendees must be greater than 0");
require(_maxAttendees <= 10000, "Max attendees too high");
uint256 eventId = eventCounter++;
events[eventId] = Event({
organizer: msg.sender,
title: _title,
description: _description,
location: _location,
eventDate: _eventDate,
maxAttendees: _maxAttendees,
currentAttendees: 0,
isActive: true
});
emit EventCreated(eventId, msg.sender, _title);
return eventId;
}
function registerForEvent(uint256 _eventId) public {
require(_eventId < eventCounter, "Event doesn't exist");
require(events[_eventId].isActive, "Event is not active");
require(block.timestamp < events[_eventId].eventDate, "Event has passed");
require(!isRegistered[_eventId][msg.sender], "Already registered");
require(events[_eventId].currentAttendees < events[_eventId].maxAttendees, "Event full");
isRegistered[_eventId][msg.sender] = true;
uint256 attendeeIndex = attendeeCount[_eventId];
eventAttendees[_eventId][attendeeIndex] = Attendee({
attendeeAddress: msg.sender,
registrationTime: block.timestamp,
checkedIn: false
});
attendeeCount[_eventId]++;
events[_eventId].currentAttendees++;
emit AttendeeRegistered(_eventId, msg.sender);
}
function checkIn(uint256 _eventId) public {
require(isRegistered[_eventId][msg.sender], "Not registered for event");
require(events[_eventId].isActive, "Event not active");
// Find attendee and mark as checked in
for (uint256 i = 0; i < attendeeCount[_eventId]; i++) {
if (eventAttendees[_eventId][i].attendeeAddress == msg.sender) {
require(!eventAttendees[_eventId][i].checkedIn, "Already checked in");
eventAttendees[_eventId][i].checkedIn = true;
break;
}
}
emit AttendeeCheckedIn(_eventId, msg.sender);
}
function cancelEvent(uint256 _eventId) public {
require(_eventId < eventCounter, "Event doesn't exist");
require(events[_eventId].organizer == msg.sender, "Only organizer can cancel");
events[_eventId].isActive = false;
}
function getEventDetails(uint256 _eventId) public view returns (
address organizer,
string memory title,
string memory description,
string memory location,
uint256 eventDate,
uint256 maxAttendees,
uint256 currentAttendees,
bool isActive
) {
require(_eventId < eventCounter, "Event doesn't exist");
Event memory eventDetail = events[_eventId];
return (
eventDetail.organizer,
eventDetail.title,
eventDetail.description,
eventDetail.location,
eventDetail.eventDate,
eventDetail.maxAttendees,
eventDetail.currentAttendees,
eventDetail.isActive
);
}
// No ETH handling = no financial risk
// No admin privileges = decentralized
// Public events = transparent
// No withdrawal functions = no drain risk
}
Compile Smart Contract
Setelah copy-paste kode contract:
- Tekan Ctrl+S untuk save file
- Klik tab "Solidity Compiler" di sidebar kiri
- Pastikan compiler version 0.8.x sudah dipilih
- Klik tombol "Compile"
- Tunggu hingga muncul checkmark hijau
Deploy ke Testnet
Klik tab "Deploy & Run Transactions" dan ikuti langkah berikut:
- Pilih "Injected Web3" di Environment
- Connect wallet (MetaMask akan popup)
- Pilih contract yang ingin di-deploy dari dropdown
- Klik tombol "Deploy"
- Konfirmasi transaksi di wallet
Verify Contract
Setelah berhasil deploy:
- Copy address contract yang ter-deploy
- Buka block explorer (etherscan.io untuk mainnet atau testnetscan untuk testnet)
- Paste address contract untuk melihat transaksi deploy
- Verify source code jika diperlukan
🎉 Selamat!
Anda telah berhasil deploy smart contract yang aman ke blockchain. Contract-contract ini telah dirancang dengan prinsip keamanan tinggi dan siap digunakan untuk berbagai keperluan decentralized application.
⚠️ Tips Keamanan Penting
- Selalu test di testnet terlebih dahulu sebelum deploy ke mainnet
- Verify source code di block explorer untuk transparansi
- Simpan address contract dengan aman
- Backup private key wallet Anda
- Double-check gas estimates sebelum deploy
🔐 Mengapa Contract Ini Aman?
Semua contract yang disediakan telah dirancang dengan prinsip-prinsip keamanan berikut:
- No Backdoors: Tidak ada fungsi tersembunyi yang bisa disalahgunakan
- No Drain Functions: Tidak ada fungsi yang bisa menguras ETH atau token
- Fixed Parameters: Semua parameter telah ditetapkan dengan aman
- Transparent: Semua fungsi dan data dapat diverifikasi publik
- Immutable: Sekali deploy, logic tidak dapat diubah
- No Financial Risk: Tidak menangani ETH kecuali yang diperlukan untuk gas


Posting Komentar