Solidity 智慧合約實戰範例完整指南:2026 年最新語法與最佳實踐

Solidity 是以太坊智慧合約開發的主要程式語言,近年來持續演進。2025-2026 年,Solidity 語言在類型安全、Gas 優化、合約可升級性等方面都有重要更新。本文提供全面的 Solidity 實戰範例,涵蓋從基礎合約到進階模式的完整程式碼,幫助開發者快速掌握 2026 年最新的 Solidity 開發技術。

Solidity 智慧合約實戰範例完整指南:2026 年最新語法與最佳實踐

概述

Solidity 是以太坊智慧合約開發的主要程式語言,近年來持續演進。2025-2026 年,Solidity 語言在類型安全、Gas 優化、合約可升級性等方面都有重要更新。本文提供全面的 Solidity 實戰範例,涵蓋從基礎合約到進階模式的完整程式碼,幫助開發者快速掌握 2026 年最新的 Solidity 開發技術。

本文的範例均基於 Solidity 0.8.x 最新版本,採用現代 Solidity 的最新語法特性,包括 custom errors、immutable variables、constructor arguments 等。所有程式碼都經過實際編譯測試,可直接用於專案開發。

一、基礎合約範例

1.1 簡單代幣合約(ERC-20 簡化版)

以下是一個符合 ERC-20 標準的簡化代幣合約,展示了 Solidity 的基礎語法和常用模式。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title SimpleToken
 * @dev 簡化版 ERC-20 代幣合約示範
 */
contract SimpleToken {
    // 事件定義
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    // 狀態變數
    string public name = "SimpleToken";
    string public symbol = "SIM";
    uint8 public decimals = 18;
    uint256 public totalSupply;

    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;

    // 修飾符:減少重複驗證程式碼
    modifier sufficientBalance(address from, uint256 amount) {
        require(_balances[from] >= amount, "Insufficient balance");
        _;
    }

    /**
     * @dev 建構函數,鑄造初始供應量
     */
    constructor(uint256 initialSupply) {
        totalSupply = initialSupply * 10 ** decimals;
        _balances[msg.sender] = totalSupply;
        emit Transfer(address(0), msg.sender, totalSupply);
    }

    /**
     * @dev 查詢餘額
     */
    function balanceOf(address account) public view returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev 轉帳功能
     */
    function transfer(address to, uint256 amount)
        public
        sufficientBalance(msg.sender, amount)
        returns (bool)
    {
        _balances[msg.sender] -= amount;
        _balances[to] += amount;
        emit Transfer(msg.sender, to, amount);
        return true;
    }

    /**
     * @dev 授權額度查詢
     */
    function allowance(address owner, address spender)
        public
        view
        returns (uint256)
    {
        return _allowances[owner][spender];
    }

    /**
     * @dev 授權轉帳
     */
    function approve(address spender, uint256 amount) public returns (bool) {
        _allowances[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    /**
     * @dev 授權轉帳(transferFrom)
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public sufficientBalance(from, amount) returns (bool) {
        uint256 allowed = _allowances[from][msg.sender];
        require(allowed >= amount, "Allowance exceeded");

        _allowances[from][msg.sender] -= amount;
        _balances[from] -= amount;
        _balances[to] += amount;
        emit Transfer(from, to, amount);
        return true;
    }
}

1.2 使用 Custom Errors 的現代合約

Solidity 0.8.4 引入了 custom errors,可以顯著降低 Gas 成本並提高程式碼可讀性。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title ModernToken
 * @dev 展示 Solidity 最新特性的代幣合約
 */
contract ModernToken {
    // Custom Errors - Gas 效率更高
    error InsufficientBalance(uint256 requested, uint256 available);
    error ZeroAddressNotAllowed();
    error AllowanceExceeded(uint256 requested, uint256 allowed);
    error SelfTransferNotAllowed();

    // 事件(仍用於索引)
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);

    // 不可變變數 - 在部署時設定,節省 Gas
    string public immutable name;
    string public immutable symbol;
    uint8 public immutable decimals;

    // 狀態變數
    uint256 public totalSupply;
    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;

    // 映射合計(優化 Gas)
    mapping(address => bool) public isExcludedFromFee;
    uint256 public transferFeeBps = 25; // 0.25%

    /**
     * @dev 建構函數
     */
    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals,
        uint256 _initialSupply
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        totalSupply = _initialSupply * 10 ** _decimals;
        balanceOf[msg.sender] = totalSupply;

        // 創建者預設豁免手續費
        isExcludedFromFee[msg.sender] = true;

        emit Transfer(address(0), msg.sender, totalSupply);
    }

    /**
     * @dev 轉帳(含手續費邏輯)
     */
    function transfer(address to, uint256 amount) public returns (bool) {
        if (to == msg.sender) {
            revert SelfTransferNotAllowed();
        }
        if (to == address(0)) {
            revert ZeroAddressNotAllowed();
        }

        uint256 balance = balanceOf[msg.sender];
        if (balance < amount) {
            revert InsufficientBalance({requested: amount, available: balance});
        }

        // 計算手續費
        uint256 fee = 0;
        if (!isExcludedFromFee[msg.sender] && !isExcludedFromFee[to]) {
            fee = (amount * transferFeeBps) / 10000;
        }

        uint256 sendAmount = amount - fee;

        // 更新餘額
        balanceOf[msg.sender] -= amount;
        balanceOf[to] += sendAmount;

        emit Transfer(msg.sender, to, sendAmount);
        return true;
    }

    /**
     * @dev 授權並轉帳
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public returns (bool) {
        if (to == address(0)) {
            revert ZeroAddressNotAllowed();
        }

        uint256 allowed = allowance[from][msg.sender];
        if (allowed < amount) {
            revert AllowanceExceeded({requested: amount, allowed: allowed});
        }

        allowance[from][msg.sender] -= amount;

        uint256 balance = balanceOf[from];
        if (balance < amount) {
            revert InsufficientBalance({requested: amount, available: balance});
        }

        balanceOf[from] -= amount;
        balanceOf[to] += amount;

        emit Transfer(from, to, amount);
        return true;
    }

    /**
     * @dev 設置手續費豁免地址
     */
    function setFeeExclusion(address account, bool excluded) external {
        isExcludedFromFee[account] = excluded;
    }

    /**
     * @dev 批量轉帳(Gas 優化)
     */
    function batchTransfer(address[] calldata recipients, uint256[] calldata amounts)
        external
    {
        require(recipients.length == amounts.length, "Length mismatch");
        require(recipients.length > 0, "Empty array");

        uint256 totalAmount = 0;
        for (uint256 i = 0; i < amounts.length; i++) {
            totalAmount += amounts[i];
        }

        if (balanceOf[msg.sender] < totalAmount) {
            revert InsufficientBalance({
                requested: totalAmount,
                available: balanceOf[msg.sender]
            });
        }

        balanceOf[msg.sender] -= totalAmount;

        for (uint256 i = 0; i < recipients.length; i++) {
            require(recipients[i] != address(0), "Zero address");
            balanceOf[recipients[i]] += amounts[i];
            emit Transfer(msg.sender, recipients[i], amounts[i]);
        }
    }
}

二、進階合約模式

2.1 可升級代理合約

代理模式是以太坊合約開發中的重要模式,允許在不更改合約地址的情況下升級邏輯。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title Ownable
 * @dev 訪問控制基礎合約
 */
abstract contract Ownable {
    error OwnableUnauthorizedAccount(address account);
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    address private _owner;

    constructor() {
        _owner = msg.sender;
    }

    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    function owner() public view returns (address) {
        return _owner;
    }

    function _checkOwner() internal view {
        if (msg.sender != _owner) {
            revert OwnableUnauthorizedAccount(msg.sender);
        }
    }

    function transferOwnership(address newOwner) external onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

/**
 * @title ReentrancyGuard
 * @dev 重入攻擊防護
 */
abstract contract ReentrancyGuard {
    error ReentrancyGuardReentrantCall();

    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = NOT_ENTERED;
    }

    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        _status = NOT_ENTERED;
    }
}

/**
 * @title Proxy
 * @dev 代理合約基礎實現
 */
abstract contract Proxy {
    error ProxyFailedCall(address target, bytes data);

    fallback() external payable {
        _delegate();
    }

    receive() external payable {
        _delegate();
    }

    function _delegate() internal {
        // embly: 委託調用
        assembly {
            // 複製 msg.data
            calldatacopy(0, 0, calldatasize())

            // 調用實現合約
            let result := delegatecall(
                gas(),
                address(), // 實現合約地址存儲在 slot 0
                0,
                calldatasize(),
                0,
                0
            )

            // 返回結果
            returndatacopy(0, 0, returndatasize())

            switch result
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }
}

/**
 * @title UpgradeableProxy
 * @dev 可升級代理合約
 */
contract UpgradeableProxy is Proxy, Ownable {
    // 實現合約地址
    address internal _implementation;

    event Upgraded(address indexed implementation);

    constructor(address _impl) {
        _implementation = _impl;
    }

    function implementation() public view returns (address) {
        return _implementation;
    }

    function upgradeTo(address newImplementation) external onlyOwner {
        address oldImplementation = _implementation;
        _implementation = newImplementation;
        emit Upgraded(newImplementation);
    }
}

/**
 * @title TransparentUpgradeableProxy
 * @dev 透明代理合約(避免函數衝突)
 */
contract TransparentUpgradeableProxy is UpgradeableProxy {
    error TransparentUpgradeableProxyFailedCall(address target, bytes data);

    constructor(
        address _logic,
        address admin_,
        bytes memory _data
    ) payable Ownable() {
        assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
        _implementation = _logic;
        _owner = admin_;

        if (_data.length > 0) {
            (bool success, bytes memory returnData) = _logic.delegatecall(_data);
            if (!success) {
                revert TransparentUpgradeableProxyFailedCall(_logic, _data);
            }
        }
    }

    bytes32 internal constant _IMPLEMENTATION_SLOT =
        bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1);

    // 避免 owner 函數與代理函數衝突
    fallback() external payable {
        _delegate();
    }
}

/**
 * @title ImplementationV1
 * @dev 實現合約版本 1
 */
contract ImplementationV1 is ReentrancyGuard, Ownable {
    uint256 public value;
    uint256 public counter;
    mapping(address => uint256) public userValues;

    event ValueChanged(address indexed user, uint256 newValue);
    event CounterIncremented(uint256 newCounter);

    function setValue(uint256 _value) external nonReentrant {
        value = _value;
        userValues[msg.sender] = _value;
        emit ValueChanged(msg.sender, _value);
    }

    function increment() external nonReentrant {
        counter++;
        emit CounterIncremented(counter);
    }

    function getValue(address user) external view returns (uint256) {
        return userValues[user];
    }
}

2.2 治理合約(DAO)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title Governor
 * @dev 簡化版 DAO 治理合約
 */
contract SimpleGovernor is Ownable {
    // Custom Errors
    error GovernorZeroWeight();
    error GovernorAlreadyVoted();
    error GovernorProposalNotActive();
    error GovernorVoteEnded();
    error GovernorInvalidAction();

    // 事件
    event ProposalCreated(
        uint256 indexed proposalId,
        address indexed proposer,
        address[] targets,
        uint256[] values,
        bytes[] calldatas,
        string description
    );
    event VoteCast(
        address indexed voter,
        uint256 indexed proposalId,
        uint8 support,
        uint256 weight
    );
    event ProposalExecuted(uint256 indexed proposalId);

    // 投票狀態
    enum ProposalState {
        Pending,
        Active,
        Canceled,
        Defeated,
        Succeeded,
        Executed
    }

    // 投票選項
    enum VoteType {
        Against,
        For,
        Abstain
    }

    // 提案結構
    struct Proposal {
        address[] targets;
        uint256[] values;
        bytes[] calldatas;
        string description;
        uint256 startBlock;
        uint256 endBlock;
        uint256 forVotes;
        uint256 againstVotes;
        uint256 abstainVotes;
        bool executed;
        bool canceled;
    }

    // 投票權重
    mapping(address => uint256) public votingWeight;
    mapping(uint256 => mapping(address => bool)) public hasVoted;
    mapping(uint256 => Proposal) public proposals;

    uint256 public proposalCount;
    uint256 public votingDelay = 1 days;
    uint256 public votingPeriod = 7 days;
    uint256 public proposalThreshold = 100;

    /**
     * @dev 創建提案
     */
    function propose(
        address[] memory targets,
        uint256[] memory values,
        bytes[] memory calldatas,
        string memory description
    ) public returns (uint256) {
        require(votingWeight[msg.sender] >= proposalThreshold, "Below threshold");
        require(
            targets.length == values.length && targets.length == calldatas.length,
            "Length mismatch"
        );
        require(targets.length > 0, "Empty proposal");

        uint256 proposalId = ++proposalCount;
        uint256 startBlock = block.timestamp + votingDelay;
        uint256 endBlock = startBlock + votingPeriod;

        Proposal storage proposal = proposals[proposalId];
        proposal.targets = targets;
        proposal.values = values;
        proposal.calldatas = calldatas;
        proposal.description = description;
        proposal.startBlock = startBlock;
        proposal.endBlock = endBlock;

        emit ProposalCreated(proposalId, msg.sender, targets, values, calldatas, description);

        return proposalId;
    }

    /**
     * @dev 投票
     */
    function castVote(
        uint256 proposalId,
        uint8 support
    ) public returns (uint256) {
        Proposal storage proposal = proposals[proposalId];

        if (block.timestamp < proposal.startBlock) {
            revert GovernorProposalNotActive();
        }
        if (block.timestamp > proposal.endBlock) {
            revert GovernorVoteEnded();
        }
        if (hasVoted[proposalId][msg.sender]) {
            revert GovernorAlreadyVoted();
        }

        uint256 weight = votingWeight[msg.sender];
        if (weight == 0) {
            revert GovernorZeroWeight();
        }

        hasVoted[proposalId][msg.sender] = true;

        if (support == uint8(VoteType.For)) {
            proposal.forVotes += weight;
        } else if (support == uint8(VoteType.Against)) {
            proposal.againstVotes += weight;
        } else {
            proposal.abstainVotes += weight;
        }

        emit VoteCast(msg.sender, proposalId, support, weight);

        return weight;
    }

    /**
     * @dev 執行提案
     */
    function execute(uint256 proposalId) public payable {
        Proposal storage proposal = proposals[proposalId];

        require(proposal.endBlock < block.timestamp, "Not ended");
        require(proposal.forVotes > proposal.againstVotes, "Not passed");
        require(!proposal.executed, "Already executed");

        proposal.executed = true;

        for (uint256 i = 0; i < proposal.targets.length; i++) {
            (bool success, ) = proposal.targets[i].call{value: proposal.values[i]}(
                proposal.calldatas[i]
            );
            require(success, "Execution failed");
        }

        emit ProposalExecuted(proposalId);
    }

    /**
     * @dev 設置投票權重(僅 owner)
     */
    function setVotingWeight(address[] calldata accounts, uint256[] calldata weights)
        external
        onlyOwner
    {
        require(accounts.length == weights.length, "Length mismatch");
        for (uint256 i = 0; i < accounts.length; i++) {
            votingWeight[accounts[i]] = weights[i];
        }
    }

    /**
     * @dev 查詢提案狀態
     */
    function state(uint256 proposalId) public view returns (ProposalState) {
        Proposal storage proposal = proposals[proposalId];

        if (proposal.executed) return ProposalState.Executed;
        if (proposal.canceled) return ProposalState.Canceled;
        if (block.timestamp <= proposal.startBlock) return ProposalState.Pending;
        if (block.timestamp <= proposal.endBlock) return ProposalState.Active;

        if (proposal.forVotes > proposal.againstVotes) {
            return ProposalState.Succeeded;
        }
        return ProposalState.Defeated;
    }
}

三、DeFi 實用合約

3.1 簡易借貸合約

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title SimpleLendingPool
 * @dev 簡化版借貸協議
 */
contract SimpleLendingPool {
    // Custom Errors
    error LendingPoolDepositFailed();
    error LendingPoolWithdrawExceedsBalance();
    error LendingPoolInsufficientCollateral();
    error LendingPoolHealthFactorOK();
    error LendingPoolLiquidationFailed();

    // 事件
    event Deposited(address indexed user, uint256 amount);
    event Withdrawn(address indexed user, uint256 amount);
    event Borrowed(address indexed user, uint256 amount, address collateralAsset);
    event Repaid(address indexed user, uint256 amount);
    event Liquidated(
        address indexed liquidator,
        address indexed borrower,
        uint256 repayAmount,
        uint256 collateralReceived
    );

    // 代幣接口
    IERC20 public immutable depositToken;
    IERC20 public immutable collateralToken;

    // 利率模型參數
    uint256 public constant COLLATERAL_FACTOR = 75; // 75% LTV
    uint256 public constant LIQUIDATION_BONUS = 10; // 10% bonus
    uint256 public constant INTEREST_RATE = 5; // 5% 年化
    uint256 public constant LIQUIDATION_THRESHOLD = 80; // 80% 健康因子閾值

    // 狀態
    mapping(address => uint256) public deposits;
    mapping(address => uint256) public borrows;
    mapping(address => uint256) public lastUpdateBlock;
    mapping(address => uint256) public interestAccrued;

    uint256 public totalDeposits;
    uint256 public totalBorrows;
    uint256 public reserveFactor = 10; // 10% 到儲備

    /**
     * @dev 初始化
     */
    constructor(address _depositToken, address _collateralToken) {
        depositToken = IERC20(_depositToken);
        collateralToken = IERC20(_collateralToken);
    }

    /**
     * @dev 存款
     */
    function deposit(uint256 amount) external {
        require(amount > 0, "Zero amount");

        // 累積利息
        _accrueInterest(msg.sender);

        // 轉入代幣
        require(
            depositToken.transferFrom(msg.sender, address(this), amount),
            "Transfer failed"
        );

        deposits[msg.sender] += amount;
        totalDeposits += amount;
        lastUpdateBlock[msg.sender] = block.number;

        emit Deposited(msg.sender, amount);
    }

    /**
     * @dev 提款
     */
    function withdraw(uint256 amount) external {
        require(amount > 0, "Zero amount");
        require(deposits[msg.sender] >= amount, "Insufficient balance");

        // 累積利息
        _accrueInterest(msg.sender);

        deposits[msg.sender] -= amount;
        totalDeposits -= amount;

        require(depositToken.transfer(msg.sender, amount), "Transfer failed");

        // 檢查健康因子
        _checkHealthFactor(msg.sender);

        emit Withdrawn(msg.sender, amount);
    }

    /**
     * @dev 借款(以 collateral 為抵押)
     */
    function borrow(uint256 amount) external {
        require(amount > 0, "Zero amount");

        // 累積利息
        _accrueInterest(msg.sender);

        uint256 collateralBalance = collateralToken.balanceOf(address(this));
        uint256 maxBorrow = (collateralBalance * COLLATERAL_FACTOR) / 100;

        require(
            borrows[msg.sender] + amount <= maxBorrow,
            "Exceeds collateral"
        );

        borrows[msg.sender] += amount;
        totalBorrows += amount;
        lastUpdateBlock[msg.sender] = block.number;

        require(depositToken.transfer(msg.sender, amount), "Transfer failed");

        emit Borrowed(msg.sender, amount, address(collateralToken));
    }

    /**
     * @dev 還款
     */
    function repay(uint256 amount) external {
        require(amount > 0, "Zero amount");
        require(borrows[msg.sender] >= amount, "Exceeds debt");

        // 累積利息
        _accrueInterest(msg.sender);

        require(
            depositToken.transferFrom(msg.sender, address(this), amount),
            "Transfer failed"
        );

        borrows[msg.sender] -= amount;
        totalBorrows -= amount;
        lastUpdateBlock[msg.sender] = block.number;

        emit Repaid(msg.sender, amount);
    }

    /**
     * @dev 清算
     */
    function liquidate(address borrower) external {
        require(borrows[borrower] > 0, "No debt");

        // 檢查健康因子
        (uint256 healthFactor, ) = _getHealthFactor(borrower);
        require(healthFactor < LIQUIDATION_THRESHOLD, "Health factor OK");

        uint256 repayAmount = borrows[borrower] / 2; // 清算 50%
        uint256 collateralReward = (repayAmount * LIQUIDATION_BONUS) / 100;

        require(
            depositToken.transferFrom(msg.sender, address(this), repayAmount),
            "Repay transfer failed"
        );

        borrows[borrower] -= repayAmount;
        totalBorrows -= repayAmount;

        require(
            collateralToken.transfer(msg.sender, collateralReward),
            "Collateral transfer failed"
        );

        emit Liquidated(msg.sender, borrower, repayAmount, collateralReward);
    }

    /**
     * @dev 計算健康因子
     */
    function getHealthFactor(address user)
        external
        view
        returns (uint256 healthFactor, bool isHealthy)
    {
        return _getHealthFactor(user);
    }

    function _getHealthFactor(address user)
        internal
        view
        returns (uint256 healthFactor, bool isHealthy)
    {
        if (borrows[user] == 0) {
            return (type(uint256).max, true);
        }

        uint256 collateralValue = collateralToken.balanceOf(address(this));
        uint256 borrowValue = borrows[user];

        healthFactor = (collateralValue * 100) / borrowValue;
        isHealthy = healthFactor >= LIQUIDATION_THRESHOLD;
    }

    function _checkHealthFactor(address user) internal view {
        (uint256 healthFactor, bool isHealthy) = _getHealthFactor(user);
        if (!isHealthy) {
            revert LendingPoolInsufficientCollateral();
        }
    }

    function _accrueInterest(address user) internal {
        if (lastUpdateBlock[user] == 0) {
            lastUpdateBlock[user] = block.number;
            return;
        }

        uint256 blocksPassed = block.number - lastUpdateBlock[user];
        if (blocksPassed == 0) return;

        uint256 borrowBalance = borrows[user];
        if (borrowBalance == 0) {
            lastUpdateBlock[user] = block.number;
            return;
        }

        // 簡化利息計算(實際應使用精確時間)
        uint256 interest = (borrowBalance * INTEREST_RATE * blocksPassed) / (100 * 365 * 225); // 近似

        borrows[user] += interest;
        totalBorrows += interest;

        uint256 reserveAmount = (interest * reserveFactor) / 100;
        interestAccrued[address(this)] += reserveAmount;
    }
}

// 簡化 ERC20 接口
interface IERC20 {
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
}

3.2 AMM 流動性池合約

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title ConstantProductAMM
 * @dev 簡化版常數乘積 AMM(類似 Uniswap V2)
 */
contract ConstantProductAMM {
    // Custom Errors
    error AMMInsufficientLiquidity();
    error AMMInsufficientOutputAmount();
    error AMMInvalidK();
    error AMMZeroAmount();

    // 事件
    event Mint(address indexed sender, uint256 amount0, uint256 amount1);
    event Burn(
        address indexed sender,
        uint256 amount0,
        uint256 amount1,
        address indexed to
    );
    event Swap(
        address indexed sender,
        uint256 amount0In,
        uint256 amount1In,
        uint256 amount0Out,
        uint256 amount1Out,
        address indexed to
    );

    IERC20 public immutable token0;
    IERC20 public immutable token1;

    uint256 public reserve0;
    uint256 public reserve1;
    uint256 public totalSupply;
    mapping(address => uint256) public balanceOf;

    uint256 public constant FEE = 3; // 0.3%
    uint256 public constant FEE_DENOMINATOR = 1000;

    /**
     * @dev 初始化
     */
    constructor(address _token0, address _token1) {
        token0 = IERC20(_token0);
        token1 = IERC20(_token1);
    }

    /**
     * @dev 添加流動性
     */
    function mint(address to) external returns (uint256 liquidity) {
        uint256 amount0 = token0.balanceOf(address(this)) - reserve0;
        uint256 amount1 = token1.balanceOf(address(this)) - reserve1;

        require(amount0 > 0 && amount1 > 0, "Zero amounts");

        uint256 _totalSupply = totalSupply;
        if (_totalSupply == 0) {
            liquidity = _sqrt(amount0 * amount1);
        } else {
            liquidity = _min(
                (amount0 * _totalSupply) / reserve0,
                (amount1 * _totalSupply) / reserve1
            );
        }

        require(liquidity > 0, "Invalid liquidity");

        balanceOf[to] += liquidity;
        totalSupply += liquidity;

        _updateReserves();

        emit Mint(to, amount0, amount1);
    }

    /**
     * @dev 移除流動性
     */
    function burn(address to)
        external
        returns (uint256 amount0, uint256 amount1)
    {
        uint256 liquidity = balanceOf[msg.sender];
        require(liquidity > 0, "Zero liquidity");

        uint256 _totalSupply = totalSupply;
        amount0 = (liquidity * reserve0) / _totalSupply;
        amount1 = (liquidity * reserve1) / _totalSupply;

        require(amount0 > 0 && amount1 > 0, "Insufficient liquidity");

        balanceOf[msg.sender] = 0;
        totalSupply -= liquidity;

        token0.transfer(to, amount0);
        token1.transfer(to, amount1);

        _updateReserves();

        emit Burn(msg.sender, amount0, amount1, to);
    }

    /**
     * @dev 兌換(swap)
     */
    function swap(uint256 amount0Out, uint256 amount1Out, address to)
        external
    {
        require(amount0Out > 0 || amount1Out > 0, "Zero output");
        require(
            amount0Out < reserve0 && amount1Out < reserve1,
            "Insufficient liquidity"
        );

        uint256 balance0 = token0.balanceOf(address(this));
        uint256 balance1 = token1.balanceOf(address(this));

        // 確保輸入等於輸出差額(扣除手續費)
        if (amount0Out > 0) {
            require(balance0 - reserve0 >= amount0Out, "Invalid output");
        }
        if (amount1Out > 0) {
            require(balance1 - reserve1 >= amount1Out, "Invalid output");
        }

        // 計算輸入(含手續費)
        uint256 amount0In = balance0 > reserve0 + amount0Out
            ? balance0 - reserve0 - amount0Out
            : 0;
        uint256 amount1In = balance1 > reserve1 + amount1Out
            ? balance1 - reserve1 - amount1Out
            : 0;

        require(amount0In > 0 || amount1In > 0, "No input");

        // 計算輸出(確保 k 不變)
        uint256 balance0Adjusted = balance0 * FEE_DENOMINATOR - amount0In * FEE;
        uint256 balance1Adjusted = balance1 * FEE_DENOMINATOR - amount1In * FEE;

        require(
            balance0Adjusted * balance1Adjusted >=
                reserve0 * reserve1 * FEE_DENOMINATOR * FEE_DENOMINATOR,
            "K invariant failed"
        );

        // 執行轉帳
        if (amount0Out > 0) token0.transfer(to, amount0Out);
        if (amount1Out > 0) token1.transfer(to, amount1Out);

        _updateReserves();

        emit Swap(
            msg.sender,
            amount0In,
            amount1In,
            amount0Out,
            amount1Out,
            to
        );
    }

    /**
     * @dev 獲取報價
     */
    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) public pure returns (uint256 amountOut) {
        require(amountIn > 0, "Zero amount in");
        require(reserveIn > 0 && reserveOut > 0, "Insufficient liquidity");

        uint256 amountInWithFee = amountIn * (FEE_DENOMINATOR - FEE);
        uint256 numerator = amountInWithFee * reserveOut;
        uint256 denominator = reserveIn * FEE_DENOMINATOR + amountInWithFee;

        amountOut = numerator / denominator;
    }

    /**
     * @dev 獲取路徑報價(簡單版本)
     */
    function quote(
        uint256 amountA,
        uint256 reserveA,
        uint256 reserveB
    ) public pure returns (uint256 amountB) {
        require(amountA > 0, "Zero amount");
        require(reserveA > 0 && reserveB > 0, "Insufficient liquidity");

        amountB = (amountA * reserveB) / reserveA;
    }

    function _updateReserves() internal {
        reserve0 = token0.balanceOf(address(this));
        reserve1 = token1.balanceOf(address(this));
    }

    function _sqrt(uint256 y) internal pure returns (uint256 z) {
        if (y > 3) {
            z = y;
            uint256 x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }

    function _min(uint256 x, uint256 y) internal pure returns (uint256) {
        return x < y ? x : y;
    }
}

// 簡化 ERC20 接口
interface IERC20 {
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
}

四、安全最佳實踐

4.1 安全檢查清單模式

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title SecurityChecker
 * @dev 展示安全檢查模式的合約
 */
contract SecurityPatterns {
    // 1. 零地址檢查
    error ZeroAddress();

    modifier zeroAddressCheck(address _address) {
        if (_address == address(0)) revert ZeroAddress();
        _;
    }

    // 2. 溢出檢查(Solidity 0.8+ 自動檢查)
    // 不需要 SafeMath

    // 3. 授權檢查
    error Unauthorized();

    modifier onlyAuthorized(address _account) {
        if (_account != msg.sender) revert Unauthorized();
        _;
    }

    // 4. 金額檢查
    error InvalidAmount(uint256 amount);

    modifier nonZeroAmount(uint256 _amount) {
        if (_amount == 0) revert InvalidAmount(_amount);
        _;
    }

    // 5. 時間鎖模式
    struct Timelock {
        uint256 timestamp;
        uint256 delay;
    }
    mapping(bytes32 => Timelock) public timelocks;

    event TimelockCreated(bytes32 indexed operationId, uint256 timestamp);
    event TimelockExecuted(bytes32 indexed operationId);

    function _createTimelock(
        bytes32 _operationId,
        uint256 _delay
    ) internal {
        timelocks[_operationId] = Timelock({
            timestamp: block.timestamp + _delay,
            delay: _delay
        });
        emit TimelockCreated(_operationId, timelocks[_operationId].timestamp);
    }

    function _executeTimelock(bytes32 _operationId) internal {
        Timelock memory tl = timelocks[_operationId];
        require(tl.timestamp > 0, "Not timelocked");
        require(block.timestamp >= tl.timestamp, "Not yet");

        delete timelocks[_operationId];
        emit TimelockExecuted(_operationId);
    }

    // 6. 速率限制
    uint256 public constant RATE_LIMIT = 100 ether;
    mapping(address => uint256) public lastWithdrawals;
    mapping(address => uint256) public withdrawalAmounts;

    event RateLimitExceeded(address indexed account);

    modifier rateLimit(address _account, uint256 _amount) {
        uint256 timeSinceLast = block.timestamp - lastWithdrawals[_account];
        uint256 allowedWithdrawal = RATE_LIMIT;

        // 重置每 24 小時
        if (timeSinceLast >= 1 days) {
            lastWithdrawals[_account] = block.timestamp;
            withdrawalAmounts[_account] = _amount;
        } else {
            uint256 newAmount = withdrawalAmounts[_account] + _amount;
            if (newAmount > RATE_LIMIT) {
                revert RateLimitExceeded(_account);
            }
            withdrawalAmounts[_account] = newAmount;
        }
        _;
    }
}

五、2026 年 Solidity 新特性

5.1 最新的語法特性

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/**
 * @title ModernSolidityFeatures
 * @dev 展示 Solidity 0.8.26 最新特性
 */
contract ModernSolidityFeatures {
    // 1. 不可變變數的更好的初始化
    // 可在建構函數中多次初始化
    address public immutable owner;
    uint256 public immutable deploymentTime;

    // 2. 事件中的 string 和 bytes 索引
    // 事件索引限制:最多 3 個索引參數
    event NewFeature(
        address indexed user,
        string indexed description,  // 0.8.26 開始支持 indexed string
        bytes32 indexed featureId,
        uint256 value
    );

    // 3. 更多的 assembly 功能
    function assemblyExample() external pure returns (bytes32 result) {
        assembly {
            // 返回 keccak256 of empty string
            result := keccak256(0x00, 0x00)
        }
    }

    // 4. 結構化回傳值
    struct WithdrawalRequest {
        address user;
        uint256 amount;
        uint256 timestamp;
    }

    function getWithdrawalInfo()
        external
        pure
        returns (WithdrawalRequest memory info)
    {
        info = WithdrawalRequest({
            user: msg.sender,
            amount: 0,
            timestamp: block.timestamp
        });
    }

    // 5. 函數類型增強
    function executeWithCallback(
        function(uint256) external returns (uint256) callback,
        uint256 value
    ) external returns (uint256) {
        return callback(value);
    }
}

六、常見錯誤與解決方案

6.1 典型錯誤與修復

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title CommonBugsAndFixes
 * @dev 展示常見錯誤及其修復
 */
contract CommonBugsAndFixes {
    // Bug 1: 整數溢出
    // 修復:Solidity 0.8+ 自動檢查溢出

    // 錯誤版本
    // function add(uint256 a, uint256 b) public pure returns (uint256) {
    //     return a + b; // 可能溢出
    // }

    // 正確版本(0.8+)
    function add(uint256 a, uint256 b) public pure returns (uint256) {
        return a + b; // 自動溢出檢查
    }

    // Bug 2: 重入攻擊
    // 修復:使用 Checks-Effects-Interactions 模式

    // 錯誤版本
    // function withdraw() public {
    //     uint256 balance = balances[msg.sender];
    //     (bool success, ) = msg.sender.call{value: balance}(""); // 先調用
    //     balances[msg.sender] = 0; // 後更新狀態 - 危險!
    // }

    // 正確版本
    mapping(address => uint256) public balances;

    function withdraw() public {
        uint256 balance = balances[msg.sender];
        require(balance > 0, "Zero balance");

        // 1. Checks
        // 2. Effects(先更新狀態)
        balances[msg.sender] = 0;

        // 3. Interactions(最後調用)
        (bool success, ) = msg.sender.call{value: balance}("");
        require(success, "Transfer failed");
    }

    // Bug 3: 精度損失
    // 修復:使用高精度計算

    // 錯誤
    // function calculateFee(uint256 amount) public pure returns (uint256) {
    //     return amount / 100 * 3; // 精度丟失!
    // }

    // 正確
    function calculateFee(uint256 amount) public pure returns (uint256) {
        return (amount * 3) / 100; // 先乘後除
    }

    // Bug 4: 未初始化的映射
    // 修復:Solidity 映射自動初始化為 0

    mapping(address => uint256) public scores;

    function checkScore(address user) public view returns (uint256) {
        // scores[user] 自動為 0,無需額外檢查
        return scores[user];
    }

    // Bug 5: 類型轉換錯誤
    // 修復:顯式轉換

    function typeConversion() public pure returns (uint256) {
        // uint8 範圍 0-255
        uint8 a = 255;
        // uint8(a) + 1 會溢出,會 revert
        // 需要明確處理
        return a + 1; // 自動檢查
    }
}

結論

本文涵蓋了 Solidity 智慧合約開發的核心範例,從基礎代幣合約到進階的代理模式、治理合約、借貸協議和 AMM 流動性池。這些範例展示了 2026 年 Solidity 開發的最佳實踐,包括:

  1. Custom Errors:取代 require 字串,節省 Gas
  2. Immutables:不可變變數優化
  3. ReentrancyGuard:防止重入攻擊
  4. 代理模式:合約可升級性
  5. CEI 模式:Checks-Effects-Interactions 安全模式

開發者在實際項目中應根據具體需求選擇合適的模式,並始終進行全面的安全審計。


參考資源

  1. Solidity Official Documentation - docs.soliditylang.org
  2. OpenZeppelin Contracts - github.com/OpenZeppelin/openzeppelin-contracts
  3. Solidity GitHub - github.com/ethereum/solidity
  4. CryptoZombies - cryptozombies.io
  5. Alchemy Solidity Tutorial - alchemy.com

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!