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 melalui this.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 sebagai owner dan 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 event Transfer untuk 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(): Fungsi view murni 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 modifier payable.
  • 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);
    }
}