SOLIDITY v0.8.20 COMPLIANT
Solidity Cheatsheet
Referensi lengkap arsitektur kode Solidity (Storage, Memory, Calldata, Visibility, Modifier) beserta 10 Smart Contract produksi dengan bedah fungsi baris per baris.
⚡ Kamus Inti Bahasa Pemrograman Solidity
1. Lokasi Memori (Data Locations)
storage: Disimpan permanen di blockchain State Trie. Sangat mahal gas. Default untuk state variables.memory: Bersifat sementara, hilang saat eksekusi fungsi selesai. Dapat dimodifikasi (mutable).calldata: Bersifat sementara, read-only (immutable), paling murah gas, digunakan untuk parameter fungsi eksternal.
2. Visibilitas Fungsi & Variabel
public: Dapat dipanggil dari dalam kontrak dan dari luar (transaksi EOA). Otomatis membuat getter function.external: Hanya dapat dipanggil dari luar kontrak atau melaluithis.f(). Paling efisien untuk parameter besar.internal: Hanya dapat diakses dari dalam kontrak saat ini dan kontrak turunan (inheritance).private: Hanya dapat diakses langsung oleh kontrak yang mendeklarasikannya saja.
3. Tipe Nilai & Khusus
address(20-byte account),address payable(bisa menerima ETH via transfer/send).mapping(key => value): Hash table terdistribusi tanpa ukuran panjang tetap.view: Hanya membaca status blockchain tanpa memodifikasi. Gratis jika dipanggil offline.pure: Tidak membaca maupun mengubah status blockchain sama sekali.payable: Mengizinkan fungsi menerima pembayaran koin Ether (msg.value).
4. Error Handling Modern (v0.8+)
require(kondisi, "Pesan"): Revert jika kondisi false, refund sisa gas.revert CustomError(): Cara paling hemat gas daripada require string error!assert(kondisi): Digunakan untuk mendeteksi bug internal (seharusnya tidak pernah bernilai false).
📜 10 Implementasi Smart Contract Siap Uji
01
1. Simple Token (Ledger Saldo Dasar)
Konsep: Mapping, Custom Modifiers, Transfer Event
Solidity ^0.8.20
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SimpleToken {
string public name = "CyberToken";
string public symbol = "CYBER";
uint8 public decimals = 18;
uint256 public totalSupply;
address public owner;
mapping(address => uint256) public balanceOf;
event Transfer(address indexed from, address indexed to, uint256 value);
event Mint(address indexed to, uint256 value);
modifier onlyOwner() {
require(msg.sender == owner, "Only owner authorized");
_;
}
constructor(uint256 _initialSupply) {
owner = msg.sender;
totalSupply = _initialSupply * 10 ** decimals;
balanceOf[msg.sender] = totalSupply;
}
function transfer(address _to, uint256 _value) public returns (bool success) {
require(_to != address(0), "Invalid receiver");
require(balanceOf[msg.sender] >= _value, "Insufficient balance");
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}
function mint(address _to, uint256 _value) public onlyOwner {
totalSupply += _value;
balanceOf[_to] += _value;
emit Mint(_to, _value);
}
}
Penjelasan Fungsi:
constructor: Menetapkan pembuat kontrak sebagaiownerdan mengalokasikan seluruh pasokan awal ke dompet owner.modifier onlyOwner: Memeriksa apakah pemanggil adalah owner sebelum menjalankan fungsi yang dilindungi.transfer(): Mengurangi saldo pengirim dan menambah saldo penerima secara aman serta memancarkan eventTransferuntuk dicatat node indexer.
02
2. Voting Contract (Pemungutan Suara Terdesentralisasi)
Konsep: Structs, Arrays, State Updates, Anti-Double-Vote
Solidity ^0.8.20
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract VotingSystem {
struct Candidate {
uint256 id;
string name;
uint256 voteCount;
}
address public admin;
Candidate[] public candidates;
mapping(address => bool) public hasVoted;
bool public votingOpen = true;
event VoteCasted(address indexed voter, uint256 candidateId);
constructor(string[] memory _candidateNames) {
admin = msg.sender;
for (uint256 i = 0; i < _candidateNames.length; i++) {
candidates.push(Candidate({id: i, name: _candidateNames[i], voteCount: 0}));
}
}
function vote(uint256 _candidateId) external {
require(votingOpen, "Voting closed");
require(!hasVoted[msg.sender], "Already voted");
require(_candidateId < candidates.length, "Invalid candidate id");
hasVoted[msg.sender] = true;
candidates[_candidateId].voteCount++;
emit VoteCasted(msg.sender, _candidateId);
}
function getWinner() external view returns (string memory winnerName, uint256 highestVotes) {
uint256 winningVote = 0;
uint256 winningId = 0;
for (uint256 i = 0; i < candidates.length; i++) {
if (candidates[i].voteCount > winningVote) {
winningVote = candidates[i].voteCount;
winningId = i;
}
}
return (candidates[winningId].name, winningVote);
}
}
Penjelasan Fungsi:
hasVoted mapping: Menjamin asas jujur pemilu di mana satu address hanya diizinkan memilih tepat 1 kali.vote(): Menerapkan pengecekan integritas sebelum memperbarui state kandidat dan menandai voter.getWinner(): Fungsiviewmurni tanpa biaya gas transaksi untuk menghitung pemenang terbanyak.
03
3. Crowdfunding (Penggalangan Dana & Refund Otomatis)
Konsep: Payable, Deadline Block Timestamp, Checks-Effects-Interactions
Solidity ^0.8.20
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Crowdfunding {
address payable public beneficiary;
uint256 public goal;
uint256 public deadline;
uint256 public totalRaised;
mapping(address => uint256) public contributions;
bool public fundingClaimed;
constructor(address payable _beneficiary, uint256 _goalEth, uint256 _durationHours) {
beneficiary = _beneficiary;
goal = _goalEth * 1 ether;
deadline = block.timestamp + (_durationHours * 1 hours);
}
function contribute() external payable {
require(block.timestamp < deadline, "Campaign expired");
require(msg.value > 0, "Donation must be > 0");
contributions[msg.sender] += msg.value;
totalRaised += msg.value;
}
function withdrawFunds() external {
require(block.timestamp >= deadline, "Campaign still active");
require(totalRaised >= goal, "Goal not reached");
require(!fundingClaimed, "Funds already claimed");
fundingClaimed = true;
beneficiary.transfer(totalRaised);
}
function claimRefund() external {
require(block.timestamp >= deadline, "Campaign still active");
require(totalRaised < goal, "Goal reached, no refund");
uint256 amount = contributions[msg.sender];
require(amount > 0, "No contribution to refund");
contributions[msg.sender] = 0; // Prevent reentrancy
payable(msg.sender).transfer(amount);
}
}
Penjelasan Fungsi:
contribute(): Menerima transfer ETH langsung menggunakan modifierpayable.claimRefund(): Memberikan garansi kepada investor jika target dana tidak tercapai; saldo di-nol-kan sebelum transfer guna mencegah Reentrancy.
04
4. Dutch Auction (Lelang Harga Menurun Otomatis)
Konsep: Dynamic Price Calculation, Refund Kembalian, Transfer ETH
Solidity ^0.8.20
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract DutchAuction {
address payable public seller;
uint256 public startingPrice;
uint256 public discountRate;
uint256 public startAt;
uint256 public expiresAt;
bool public stopped;
constructor(uint256 _startingPrice, uint256 _discountRate, uint256 _durationSeconds) {
seller = payable(msg.sender);
startingPrice = _startingPrice;
discountRate = _discountRate;
startAt = block.timestamp;
expiresAt = block.timestamp + _durationSeconds;
require(_startingPrice >= _discountRate * _durationSeconds, "Starting price too low");
}
function getPrice() public view returns (uint256) {
uint256 timeElapsed = block.timestamp - startAt;
uint256 discount = discountRate * timeElapsed;
return startingPrice - discount;
}
function buy() external payable {
require(!stopped, "Auction ended");
require(block.timestamp < expiresAt, "Auction expired");
uint256 price = getPrice();
require(msg.value >= price, "ETH sent lower than current price");
stopped = true;
seller.transfer(price);
uint256 refund = msg.value - price;
if (refund > 0) {
payable(msg.sender).transfer(refund);
}
}
}
05
5. Multi-Signature Wallet (Dompet Multi Pemilik)
Konsep: M-of-N Approval Threshold, Execution Gateways, Security Multi-Admin
Solidity ^0.8.20
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract MultiSigWallet {
address[] public owners;
mapping(address => bool) public isOwner;
uint256 public requiredApprovals;
struct Transaction {
address destination;
uint256 value;
bool executed;
uint256 approvalCount;
}
Transaction[] public transactions;
mapping(uint256 => mapping(address => bool)) public approvals;
constructor(address[] memory _owners, uint256 _required) {
require(_owners.length > 0 && _required <= _owners.length && _required > 0, "Invalid params");
for (uint256 i = 0; i < _owners.length; i++) {
isOwner[_owners[i]] = true;
}
owners = _owners;
requiredApprovals = _required;
}
function submitTransaction(address _to, uint256 _value) external {
require(isOwner[msg.sender], "Not owner");
transactions.push(Transaction({destination: _to, value: _value, executed: false, approvalCount: 0}));
}
function approveTransaction(uint256 _txId) external {
require(isOwner[msg.sender], "Not owner");
require(_txId < transactions.length, "Tx doesn't exist");
require(!transactions[_txId].executed, "Tx already executed");
require(!approvals[_txId][msg.sender], "Tx already approved by you");
approvals[_txId][msg.sender] = true;
transactions[_txId].approvalCount++;
if (transactions[_txId].approvalCount >= requiredApprovals) {
executeTransaction(_txId);
}
}
function executeTransaction(uint256 _txId) internal {
Transaction storage txn = transactions[_txId];
txn.executed = true;
(bool success, ) = txn.destination.call{value: txn.value}("");
require(success, "Tx execution failed");
}
receive() external payable {}
}
06
6. Standard ERC-20 Token (Fungible Token Protocol)
Konsep: Allowance, approve, transferFrom, OpenZeppelin Style Interface
Solidity ^0.8.20
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract ERC20Token {
string public name = "Binus Coin";
string public symbol = "BNC";
uint8 public decimals = 18;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
constructor(uint256 _initialSupply) {
totalSupply = _initialSupply * 10 ** decimals;
balanceOf[msg.sender] = totalSupply;
emit Transfer(address(0), msg.sender, totalSupply);
}
function transfer(address _to, uint256 _value) external returns (bool) {
require(balanceOf[msg.sender] >= _value, "Balance low");
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}
function approve(address _spender, uint256 _value) external returns (bool) {
allowance[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) external returns (bool) {
require(balanceOf[_from] >= _value, "Balance low");
require(allowance[_from][msg.sender] >= _value, "Allowance exceeded");
allowance[_from][msg.sender] -= _value;
balanceOf[_from] -= _value;
balanceOf[_to] += _value;
emit Transfer(_from, _to, _value);
return true;
}
}
07
7. NFT ERC-721 (Non-Fungible Token Minting)
Konsep: Unique Token ID, Token URI, Ownership Mapping
Solidity ^0.8.20
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SimpleNFT {
string public name = "CyberBadge";
string public symbol = "CBDG";
uint256 public nextTokenId;
address public minter;
mapping(uint256 => address) public ownerOf;
mapping(address => uint256) public balanceOf;
mapping(uint256 => string) private _tokenURIs;
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
constructor() { minter = msg.sender; }
function mint(address _to, string calldata _uri) external returns (uint256) {
require(msg.sender == minter, "Only minter");
uint256 tokenId = nextTokenId++;
ownerOf[tokenId] = _to;
balanceOf[_to]++;
_tokenURIs[tokenId] = _uri;
emit Transfer(address(0), _to, tokenId);
return tokenId;
}
function tokenURI(uint256 _tokenId) external view returns (string memory) {
require(ownerOf[_tokenId] != address(0), "Nonexistent token");
return _tokenURIs[_tokenId];
}
}
08
8. Staking Rewards Contract (DeFi Yield Farm)
Konsep: Reward Per Block/Second, Timestamp Calculation, Lockup
Solidity ^0.8.20
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SimpleStaking {
uint256 public constant REWARD_RATE = 100; // 100 wei per second per staked ether
mapping(address => uint256) public stakedBalance;
mapping(address => uint256) public lastClaimTime;
function stake() external payable {
require(msg.value > 0, "Cannot stake 0");
updateReward(msg.sender);
stakedBalance[msg.sender] += msg.value;
}
function withdraw(uint256 _amount) external {
require(stakedBalance[msg.sender] >= _amount, "Insufficient stake");
updateReward(msg.sender);
stakedBalance[msg.sender] -= _amount;
payable(msg.sender).transfer(_amount);
}
function updateReward(address _account) internal {
if (stakedBalance[_account] > 0) {
uint256 timePassed = block.timestamp - lastClaimTime[_account];
uint256 pending = (stakedBalance[_account] * timePassed * REWARD_RATE) / 1 ether;
// Transfer reward or mint token
}
lastClaimTime[_account] = block.timestamp;
}
}
09
9. Governance DAO (Proposal & Voting Quorum)
Konsep: Decentralized Autonomous Organization, Timelock, On-chain Governance
Solidity ^0.8.20
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SimpleDAO {
struct Proposal {
string description;
uint256 votesFor;
uint256 votesAgainst;
uint256 deadline;
bool executed;
}
Proposal[] public proposals;
mapping(uint256 => mapping(address => bool)) public voted;
mapping(address => uint256) public shares;
function createProposal(string calldata _desc) external returns (uint256) {
require(shares[msg.sender] > 0, "Must hold shares");
proposals.push(Proposal({
description: _desc,
votesFor: 0,
votesAgainst: 0,
deadline: block.timestamp + 3 days,
executed: false
}));
return proposals.length - 1;
}
function vote(uint256 _proposalId, bool _support) external {
Proposal storage p = proposals[_proposalId];
require(block.timestamp < p.deadline, "Voting closed");
require(!voted[_proposalId][msg.sender], "Already voted");
voted[_proposalId][msg.sender] = true;
if (_support) p.votesFor += shares[msg.sender];
else p.votesAgainst += shares[msg.sender];
}
}
10
10. Escrow Service (Rekening Bersama Pihak Ketiga Arbiter)
Konsep: Buyer, Seller, Arbiter State Machine (AWAITING_PAYMENT, COMPLETE, REFUNDED)
Solidity ^0.8.20
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract EscrowService {
address public buyer;
address payable public seller;
address public arbiter;
uint256 public amount;
enum State { AwaitingPayment, AwaitingDelivery, Complete, Refunded }
State public currentState;
modifier onlyBuyer() { require(msg.sender == buyer, "Only buyer"); _; }
modifier onlyBuyerOrArbiter() { require(msg.sender == buyer || msg.sender == arbiter, "Unauthorized"); _; }
constructor(address payable _seller, address _arbiter) {
buyer = msg.sender;
seller = _seller;
arbiter = _arbiter;
currentState = State.AwaitingPayment;
}
function deposit() external payable onlyBuyer {
require(currentState == State.AwaitingPayment, "Invalid state");
amount = msg.value;
currentState = State.AwaitingDelivery;
}
function releaseToSeller() external onlyBuyerOrArbiter {
require(currentState == State.AwaitingDelivery, "Invalid state");
currentState = State.Complete;
seller.transfer(amount);
}
function refundToBuyer() external {
require(msg.sender == arbiter, "Only arbiter can refund");
require(currentState == State.AwaitingDelivery, "Invalid state");
currentState = State.Refunded;
payable(buyer).transfer(amount);
}
}