以太坊熱門 DeFi 協議深度程式碼分析與攻擊案例完整指南:MakerDAO、Uniswap、Aave

本文深入剖析 MakerDAO、Uniswap、Aave 這三個以太坊生態最具代表性的 DeFi 協議的核心合約程式碼,包含完整的 Vault 合約、清算模組、AMM 配對合約、借貸池等關鍵程式碼解析。結合實際攻擊案例,包括 2019 年 MakerDAO Oracle 操縱攻擊、2023 年 Curve Vyper 漏洞攻擊、以及多起閃電貸攻擊事件,從技術層面還原攻擊機制並提供防禦方案。透過深入理解這些頂級協議的設計模式和曾經的安全漏洞,開發者可以學習如何構建更安全的 DeFi 系統。

以太坊熱門 DeFi 協議深度程式碼分析與攻擊案例完整指南:MakerDAO、Uniswap、Aave

概述

去中心化金融(DeFi)是以太坊生態系統中最成功且最重要的應用場景之一。在數百個 DeFi 協議中,MakerDAO、Uniswap 和 Aave 是最具代表性的三個項目:MakerDAO 開創了去中心化穩定币的先河,Uniswap 發明了自動做市商(AMM)模式,Aave 則重新定義了去中心化借貸。這三個協議不僅在技術架構上各有特色,更在歷史上經歷了無數次的安全考驗。

理解這三個協議的合約程式碼,不僅能幫助開發者學習頂級 DeFi 項目的設計模式,更能從它們曾經遭受的攻擊中汲取寶貴的安全教訓。本文將深入剖析這三個協議的核心合約程式碼,並結合實際攻擊案例,提供完整的技術分析。

第一部分:MakerDAO 深入分析

1.1 協議架構概述

MakerDAO 是以太坊生態系統中最具影響力的去中心化借貸協議,其核心成就是創造了去中心化穩定币 DAI。MakerDAO 的設計哲學是「超額抵押」:用戶需要存入價值高於借款價值的加密資產作為抵押品,當抵押品價值下跌時觸發清算機制。

MakerDAO 核心合約架構:

├── Vault ( CDP )
│   ├── 抵押品存款
│   ├── DAI 鑄造
│   └── 清算觸發
│
├── Core ( MakerDAO Core )
│   ├── 系統參數管理
│   ├── 抵押品類型管理
│   └── 擔保權管理
│
├── Stability Fee Module
│   ├── 借款利率計算
│   └── DAI 鑄造費收取
│
├── Liquidation Module
│   ├── 清算觸發條件
│   ├── 拍賣機制
│   └── 罰金計算
│
├── Oracle Module
│   ├── 價格feed管理
│   └── 價格異常檢測
│
└── Governance Module
    ├── 投票機制
    └── 參數修改

1.2 核心合約程式碼分析

1.2.1 Vault 合約

// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.19;

/**
 * @title MakerDAO Vault 合約簡化實現
 * @dev 展示 MakerDAO CDP 機制的核心邏輯
 */
contract MakerVault {
    
    // 合約狀態變數
    address public owner;                    // Vault 所有者
    address public collateralToken;           // 抵押品代幣地址
    uint256 public collateralAmount;         // 存入的抵押品數量
    uint256 public debtDai;                  // 鑄造的 DAI 數量
    uint256 public accumulatedRate;          // 累積利率
    uint256 public createdAt;                // Vault 創建時間
    
    // 系統參數
    uint256 public constant CRITICAL_CR = 1.1e18;  // 臨界抵押率 110%
    uint256 public constant LIQUIDATION_PENALTY = 0.13e18; // 清算罰金 13%
    
    // 事件記錄
    event Open(address indexed usr, address indexed collateralType);
    event Give(address indexed usr, uint256 tab);
    event Gem(address indexed usr, uint256 tab);
    event Dai(address indexed usr, uint256 tab);
    event Lock(address indexed usr, uint256 tab);
    event Free(address indexed usr, uint256 tab);
    event Draw(address indexed usr, uint256 tab);
    event Wipe(address indexed usr, uint256 tab);
    event Liquate(address indexed usr, uint256 tab);
    
    // 構造函數
    constructor(address _collateralToken) {
        owner = msg.sender;
        collateralToken = _collateralToken;
        accumulatedRate = RAY;  // RAY = 10^27,用於精確計算
        createdAt = block.timestamp;
    }
    
    // 存入抵押品
    function lock(uint256 _amount) external onlyOwner {
        require(_amount > 0, "MakerVault: amount must be positive");
        
        // 從用戶轉移抵押品到合約
        IERC20(collateralToken).transferFrom(msg.sender, address(this), _amount);
        collateralAmount += _amount;
        
        emit Lock(msg.sender, _amount);
    }
    
    // 提取抵押品
    function free(uint256 _amount) external onlyOwner {
        require(_amount > 0, "MakerVault: amount must be positive");
        require(collateralAmount >= _amount, "MakerVault: insufficient collateral");
        
        // 檢查提取後是否保持健康狀態
        uint256 newCollateral = collateralAmount - _amount;
        uint256 newDebt = debtDai * accumulatedRate / RAY;
        
        require(_calcRatio(newCollateral, newDebt) >= CRITICAL_CR,
                "MakerVault: insufficient collateral ratio");
        
        collateralAmount -= _amount;
        IERC20(collateralToken).transfer(msg.sender, _amount);
        
        emit Free(msg.sender, _amount);
    }
    
    // 鑄造 DAI(借款)
    function draw(uint256 _amount) external onlyOwner {
        require(_amount > 0, "MakerVault: amount must be positive");
        
        // 新增債務
        uint256 newDebt = debtDai + _amount;
        uint256 currentCollateral = collateralAmount;
        
        // 檢查抵押率
        require(_calcRatio(currentCollateral, newDebt * accumulatedRate / RAY) >= CRITICAL_CR,
                "MakerVault: insufficient collateral ratio");
        
        debtDai = newDebt;
        
        // 鑄造 DAI 並轉給用戶
        daiMint(msg.sender, _amount);
        
        emit Draw(msg.sender, _amount);
    }
    
    // 償還 DAI
    function wipe(uint256 _amount) external onlyOwner {
        require(_amount > 0, "MakerVault: amount must be positive");
        require(debtDai >= _amount, "MakerVault: insufficient debt");
        
        // 燒毀 DAI
        daiBurn(msg.sender, _amount);
        
        // 更新債務(需要考虑累积利率)
        debtDai -= _amount * RAY / accumulatedRate;
        
        emit Wipe(msg.sender, _amount);
    }
    
    // 清算函數
    function liquidate(address _liquidator) external {
        require(_canLiquidate(), "MakerVault: cannot liquidate");
        
        // 計算清算金額
        uint256 debtToCover = debtDai * LIQUIDATION_PENALTY / RAY;
        
        // 轉移抵押品給清算人
        uint256 collateralToLiquidate = _calcLiquidationAmount(debtToCover);
        
        // 更新狀態
        collateralAmount -= collateralToLiquidate;
        debtDai = 0;
        
        // 轉移資產
        IERC20(collateralToken).transfer(_liquidator, collateralToLiquidate);
        
        emit Liquate(owner, debtToCover);
    }
    
    // 內部輔助函數
    function _calcRatio(uint256 _collateral, uint256 _debt) internal pure returns (uint256) {
        if (_debt == 0) return type(uint256).max;
        return _collateral * RAY / _debt;
    }
    
    function _canLiquidate() internal view returns (bool) {
        if (debtDai == 0) return false;
        uint256 currentRatio = _calcRatio(collateralAmount, debtDai * accumulatedRate / RAY);
        return currentRatio < CRITICAL_CR;
    }
    
    function _calcLiquidationAmount(uint256 _debt) internal pure returns (uint256) {
        // 簡化的清算數量計算
        return _debt * RAY / LIQUIDATION_PENALTY;
    }
    
    // DAI 鑄造和銷毀(需要與 DAI 合約交互)
    function daiMint(address _to, uint256 _amount) internal {
        DAI(daiAddress).mint(_to, _amount);
    }
    
    function daiBurn(address _from, uint256 _amount) internal {
        DAI(daiAddress).burn(_from, _amount);
    }
    
    // 修飾符
    modifier onlyOwner() {
        require(msg.sender == owner, "MakerVault: not owner");
        _;
    }
}

1.2.2 清算模組詳細邏輯

/**
 * @title MakerDAO 清算拍賣合約
 * @dev 展示 Dutch Auction 機制
 */
contract LiquidationEngine {
    
    // 拍賣參數
    uint256 public constant START_PRICE_MULTIPLIER = 3e18;  // 起始價格倍數
    uint256 public constant PRICE_DECAY_FACTOR = 0.95e18;   // 價格衰減因子
    uint256 public constant PRICE_STEP_DURATION = 6 seconds; // 價格調整間隔
    
    // 拍賣狀態
    struct Auction {
        address vault;           // 被清算的 Vault
        address collateralToken; // 抵押品類型
        uint256 collateralAmount; // 拍賣數量
        uint256 debtToCover;     // 覆蓋的債務
        uint256 startTime;       // 拍賣開始時間
        uint256 startPrice;      // 起始價格
        bool ended;              // 拍賣是否結束
    }
    
    mapping(uint256 => Auction) public auctions;
    uint256 public auctionCount;
    
    // 事件
    event Kick(uint256 id, address vault, uint256 collateral, uint256 debt);
    event Take(uint256 id, uint256 amount, uint256 price);
    event End(uint256 id);
    
    // 啟動清算拍賣
    function kick(
        address _vault,
        address _collateralToken,
        uint256 _collateralAmount,
        uint256 _debtToCover
    ) external returns (uint256) {
        uint256 id = ++auctionCount;
        
        // 計算起始價格(使用溢價)
        uint256 currentPrice = getPrice(_collateralToken);
        uint256 startPrice = currentPrice * START_PRICE_MULTIPLIER / RAY;
        
        auctions[id] = Auction({
            vault: _vault,
            collateralToken: _collateralToken,
            collateralAmount: _collateralAmount,
            debtToCover: _debtToCover,
            startTime: block.timestamp,
            startPrice: startPrice,
            ended: false
        });
        
        emit Kick(id, _vault, _collateralAmount, _debtToCover);
        return id;
    }
    
    // 參與拍賣(購買抵押品)
    function take(uint256 _id, uint256 _amount) external returns (uint256) {
        Auction storage auction = auctions[_id];
        require(!auction.ended, "Auction ended");
        
        // 計算當前價格
        uint256 currentPrice = _getCurrentPrice(auction);
        
        // 計算支付金額
        uint256 payAmount = _amount * currentPrice / RAY;
        
        // 確保不超過拍賣總量
        require(_amount <= auction.collateralAmount, "Exceeds auction amount");
        
        // 轉移 DAI 並發放抵押品
        IERC20(dai).transferFrom(msg.sender, address(this), payAmount);
        IERC20(auction.collateralToken).transfer(msg.sender, _amount);
        
        // 更新拍賣狀態
        auction.collateralAmount -= _amount;
        
        // 如果全部賣出,結束拍賣
        if (auction.collateralAmount == 0) {
            auction.ended = true;
            emit End(_id);
        }
        
        emit Take(_id, _amount, currentPrice);
        
        return payAmount;
    }
    
    // 計算當前拍賣價格(Dutch Auction 機制)
    function _getCurrentPrice(Auction storage _auction) internal view returns (uint256) {
        uint256 timePassed = block.timestamp - _auction.startTime;
        uint256 steps = timePassed / PRICE_STEP_DURATION;
        
        // 價格指數衰減:price = startPrice * (decayFactor ^ steps)
        uint256 price = _auction.startPrice;
        
        for (uint256 i = 0; i < steps; i++) {
            price = price * PRICE_DECAY_FACTOR / RAY;
        }
        
        // 確保價格不會低於起始價格的 50%
        uint256 floorPrice = _auction.startPrice / 2;
        return price < floorPrice ? floorPrice : price;
    }
    
    // 獲取抵押品價格
    function getPrice(address _token) public view returns (uint256) {
        // 調用價格預言機
        return oracle.getPrice(_token);
    }
}

1.3 MakerDAO 歷史攻擊案例

1.3.1 2019 年 Oracle 操縱攻擊

事件概述:2019 年 5 月,攻擊者利用 MakerDAO 價格預言機的漏洞,操縱 DAI 的抵押品價格,實現了約 500 萬美元的獲利。

攻擊技術分析

/**
 * @title Oracle 操縱攻擊示例
 * @dev 展示攻擊者如何利用預言機漏洞
 */
contract OracleManipulationAttack {
    
    // 攻擊步驟:
    
    // 步驟1:準備階段
    // 攻擊者首先在一個交易平台(如 Compound)上大量存入 ETH
    // 並借入大量 DAI
    
    // 步驟2:價格操縱
    // 攻擊者通過大量買入操作影響交易所價格
    // 然後調用 MakerDAO 的價格預言機
    
    /**
     * 攻擊核心邏輯:
     * 
     * 1. MakerDAO 使用的 ETH/DAI 價格 feed 響應速度較慢
     * 2. 攻擊者在短時間內大量購買 ETH,推高市場價格
     * 3. 當預言機更新價格時,記錄的是被人為操縱的高價
     * 4. 攻擊者使用低價值抵押品(如 USDC)借出更多 DAI
     * 5. 在價格恢復正常前,撤回抵押品
     */
    
    // 漏洞根因分析:
    // 1. 價格更新延遲過大
    // 2. 缺乏異常價格檢測機制
    // 3. 沒有交易量加權平均
    
    function attack() external {
        // 1. 存入抵押品
        usdc.approve(address(makerVault), type(uint256).max);
        usdc.transferFrom(attacker, address(this), attackCapital);
        usdc.approve(address(makerVault), attackCapital);
        makerVault.deposit(attackCapital);
        
        // 2. 借款(利用高估的抵押品價格)
        // 預言機此時報告的是被人為操縱的高價格
        uint256 maxBorrow = makerVault.getMaxBorrow(collateralAmount, manipulatedPrice);
        makerVault.borrow(maxBorrow);
        
        // 3. 撤回抵押品
        makerVault.withdraw(collateralAmount);
        
        // 4. 歸還借款(在價格恢復後)
        // 由於時間延遲,攻擊者可以在價格恢復前完成攻擊
    }
}

安全改進措施

/**
 * @title 改進後的 MakerDAO 預言機模組
 * @dev 加入多重保護機制
 */
contract ImprovedOracleModule {
    
    // 多重價格源
    mapping(address => AggregatorV3Interface[]) public priceFeeds;
    
    // 異常檢測參數
    uint256 public constant MAX_PRICE_DEVIATION = 0.05e18;  // 5% 最大偏差
    uint256 public constant PRICE_STALENESS = 1 hours;       // 價格過期時間
    
    // 時間加權平均
    mapping(address => uint256) public lastPrice;
    mapping(address => uint256) public priceUpdateTimestamp;
    
    /**
     * @dev 獲取保護後的價格
     */
    function getProtectedPrice(address _token) external view returns (uint256) {
        // 1. 獲取多個價格源
        uint256[] memory prices = _getMultiplePrices(_token);
        
        // 2. 驗證價格有效性
        _validatePrices(prices);
        
        // 3. 計算加權平均價格
        uint256 weightedPrice = _calculateWeightedAverage(prices);
        
        // 4. 異常檢測
        _checkPriceAnomaly(_token, weightedPrice);
        
        return weightedPrice;
    }
    
    function _validatePrices(uint256[] memory _prices) internal pure {
        require(_prices.length >= 2, "Insufficient price sources");
        
        for (uint256 i = 0; i < _prices.length; i++) {
            require(_prices[i] > 0, "Invalid price");
        }
    }
    
    function _calculateWeightedAverage(uint256[] memory _prices) 
        internal pure returns (uint256) {
        
        uint256 sum = 0;
        for (uint256 i = 0; i < _prices.length; i++) {
            sum += _prices[i];
        }
        return sum / _prices.length;
    }
    
    function _checkPriceAnomaly(address _token, uint256 _newPrice) 
        internal view {
        
        uint256 lastPriceValue = lastPrice[_token];
        if (lastPriceValue == 0) return;
        
        uint256 deviation = _newPrice > lastPriceValue 
            ? (_newPrice - lastPriceValue) * RAY / lastPriceValue
            : (lastPriceValue - _newPrice) * RAY / lastPriceValue;
        
        require(deviation <= MAX_PRICE_DEVIATION, "Price anomaly detected");
    }
}

第二部分:Uniswap 深入分析

2.1 協議架構概述

Uniswap 是自動做市商(AMM)模式的發明者和持續創新者。其核心創新是使用「常數乘積公式」實現去中心化交易,無需傳統的訂單簿匹配。

Uniswap V2 核心合約架構:

├── Factory
│   ├── createPair()
│   └── allPairs()
│
├── Pair (AMM Core)
│   ├── mint()     - 流動性添加
│   ├── burn()     - 流動性移除
│   ├── swap()     - 代幣交換
│   └── getReserves() - 獲取儲備
│
├── Router
│   ├── addLiquidity()
│   ├── removeLiquidity()
│   ├── swapExactETHForTokens()
│   └── swapExactTokensForETH()
│
└── Library
    ├── quote()
    ├── getAmountOut()
    ├── getAmountIn()
    └── pairFor()

2.2 核心合約程式碼分析

2.2.1 Pair 合約

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

/**
 * @title Uniswap V2 Pair 合約
 * @dev 展示 AMM 常數乘積公式實現
 */
contract UniswapV2Pair {
    
    // 代幣地址
    address public token0;
    address public token1;
    
    // 儲備量
    uint112 private reserve0;
    uint112 private reserve1;
    
    // 累積時間戳
    uint32 private blockTimestampLast;
    
    // 價格累積
    uint256 public price0CumulativeLast;
    uint256 public price1CumulativeLast;
    
    // 流動性代幣
    uint256 public totalSupply;
    mapping(address => uint256) public balanceOf;
    
    // 事件
    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 amount0Out,
        uint256 amount1Out,
        address indexed to,
        bytes data
    );
    event Sync(uint112 reserve0, uint112 reserve1);
    
    // 常數乘積 K
    // K = reserve0 * reserve1
    // 這個值在添加/移除流動性時必須保持或增加
    
    /**
     * @dev 交換函數 - AMM 核心
     * @param amount0Out 要輸出 的 token0 數量
     * @param amount1Out 要輸出的 token1 數量
     * @param to 接收者地址
     * @param data 附加數據
     * 
     * 常數乘積公式:
     * (x + dx) * (y - dy) = x * y
     * 其中 x, y 是交易前的儲備
     * dx, dy 是交易數量
     * 
     * 求解 dy:
     * dy = y * dx / (x + dx)
     * 
     * 考慮滑點:
     * dy = y * dx * 999 / (x + dx * 999)
     */
    function swap(
        uint256 amount0Out,
        uint256 amount1Out,
        address to,
        bytes calldata data
    ) external {
        require(amount0Out > 0 || amount1Out > 0, "INSUFFICIENT_OUTPUT_AMOUNT");
        
        // 獲取當前儲備
        (uint112 _reserve0, uint112 _reserve1) = (reserve0, reserve1);
        
        // 驗證輸出不會超過可用儲備
        require(amount0Out < _reserve0, "INSUFFICIENT_LIQUIDITY");
        require(amount1Out < _reserve1, "INSUFFICIENT_LIQUIDITY");
        
        // 計算輸入數量(用于費用計算)
        uint256 amount0In = 0;
        uint256 amount1In = 0;
        
        if (data.length > 0) {
            // 如果有輸入,更新儲備
            if (token0 == to) {
                amount0In = _reserve0 - amount0Out;
            } else {
                amount1In = _reserve1 - amount1Out;
            }
        }
        
        // 計算輸出
        // 這裡體現常數乘積公式
        // (x + dx) * (y - dy) = k
        // dy = y - k / (x + dx)
        
        uint256 balance0 = _reserve0 + amount0In - amount0Out;
        uint256 balance1 = _reserve1 + amount1In - amount1Out;
        
        // 驗證常數乘積
        // 需要考慮 0.3% 手續費
        // 實際輸入 = amountIn * 997 / 1000
        uint256 balance0Adjusted = balance0 * 1000 - amount0In * 3;
        uint256 balance1Adjusted = balance1 * 1000 - amount1In * 3;
        
        require(
            balance0Adjusted * balance1Adjusted >= uint256(_reserve0) * _reserve1 * 1000 * 1000,
            "K"
        );
        
        // 更新儲備
        _update(balance0, balance1, _reserve0, _reserve1);
        
        // 輸出代幣
        if (amount0Out > 0) IERC20(token0).transfer(to, amount0Out);
        if (amount1Out > 0) IERC20(token1).transfer(to, amount1Out);
        
        // 觸發回調(如果需要)
        if (data.length > 0) {
            // UniswapV2Call 回調
        }
        
        emit Swap(msg.sender, amount0Out, amount1Out, to, data);
    }
    
    // 添加流動性
    function mint(address to) external returns (uint256 liquidity) {
        (uint112 _reserve0, uint112 _reserve1) = (reserve0, reserve1);
        uint256 balance0 = IERC20(token0).balanceOf(address(this));
        uint256 balance1 = IERC20(token1).balanceOf(address(this));
        
        uint256 amount0 = balance0 - _reserve0;
        uint256 amount1 = balance1 - _reserve1;
        
        // 計算流動性代幣
        if (totalSupply == 0) {
            // 首次添加:liquidity = sqrt(amount0 * amount1)
            liquidity = sqrt(amount0 * amount1);
        } else {
            // 後續添加:按比例計算
            liquidity = min(
                amount0 * totalSupply / _reserve0,
                amount1 * totalSupply / _reserve1
            );
        }
        
        require(liquidity > 0, "INSUFFICIENT_LIQUIDITY_MINTED");
        
        // 鑄造流動性代幣
        _mint(to, liquidity);
        
        // 更新儲備
        _update(balance0, balance1, _reserve0, _reserve1);
        
        emit Mint(msg.sender, amount0, amount1);
    }
    
    // 移除流動性
    function burn(address to) external returns (uint256 amount0, uint256 amount1) {
        uint256 liquidity = balanceOf[address(this)];
        
        // 按比例計算輸出
        amount0 = liquidity * reserve0 / totalSupply;
        amount1 = liquidity * reserve1 / totalSupply;
        
        require(amount0 > 0 && amount1 > 0, "INSUFFICIENT_LIQUIDITY_BURNED");
        
        // 燒毀流動性代幣
        _burn(address(this), liquidity);
        
        // 轉移代幣
        IERC20(token0).transfer(to, amount0);
        IERC20(token1).transfer(to, amount1);
        
        // 更新儲備
        (uint112 _reserve0, uint112 _reserve1) = (reserve0, reserve1);
        _update(
            IERC20(token0).balanceOf(address(this)),
            IERC20(token1).balanceOf(address(this)),
            _reserve0,
            _reserve1
        );
        
        emit Burn(msg.sender, amount0, amount1, to);
    }
    
    // 更新儲備和價格累積器
    function _update(
        uint256 balance0,
        uint256 balance1,
        uint112 _reserve0,
        uint112 _reserve1
    ) private {
        require(balance0 <= type(uint112).max && balance1 <= type(uint112).max, "OVERFLOW");
        
        uint32 blockTimestamp = uint32(block.timestamp % 2**32);
        uint32 timeElapsed = blockTimestamp - blockTimestampLast;
        
        if (timeElapsed > 0 && _reserve0 > 0 && _reserve1 > 0) {
            price0CumulativeLast += uint256(_reserve1) * timeElapsed / _reserve0;
            price1CumulativeLast += uint256(_reserve0) * timeElapsed / _reserve1;
        }
        
        reserve0 = uint112(balance0);
        reserve1 = uint112(balance1);
        blockTimestampLast = blockTimestamp;
        
        emit Sync(reserve0, reserve1);
    }
    
    // 計算輸出數量
    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) external pure returns (uint256 amountOut) {
        require(amountIn > 0, "INSUFFICIENT_INPUT_AMOUNT");
        require(reserveIn > 0 && reserveOut > 0, "INSUFFICIENT_LIQUIDITY");
        
        // 扣除 0.3% 手續費
        uint256 amountInWithFee = amountIn * 997;
        
        // 常數乘積公式
        amountOut = (amountInWithFee * reserveOut) / (reserveIn * 1000 + amountInWithFee);
    }
    
    // 輔助函數
    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 z) {
        z = x < y ? x : y;
    }
    
    // 代幣鑄造和銷毀
    function _mint(address to, uint256 value) internal {
        totalSupply += value;
        balanceOf[to] += value;
    }
    
    function _burn(address from, uint256 value) internal {
        totalSupply -= value;
        balanceOf[from] -= value;
    }
}

2.2.2 Router 合約

/**
 * @title Uniswap V2 Router 合約
 * @dev 展示路由合約的交換邏輯
 */
contract UniswapV2Router {
    
    address public immutable factory;
    address public immutable WETH;
    
    // 確保正確的工廠地址和 WETH 地址
    modifier ensure(uint256 deadline) {
        require(deadline >= block.timestamp, "EXPIRED");
        _;
    }
    
    // 添加流動性
    function addLiquidity(
        address tokenA,
        address tokenB,
        uint256 amountADesired,
        uint256 amountBDesired,
        uint256 amountAMin,
        uint256 amountBMin,
        address to,
        uint256 deadline
    ) external ensure(deadline) returns (uint256 amountA, uint256 amountB, uint256 liquidity) {
        
        // 創建配對(如不存在)
        if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
            IUniswapV2Factory(factory).createPair(tokenA, tokenB);
        }
        
        // 計算最佳數量
        (amountA, amountB) = _quote(
            amountADesired,
            amountBDesired,
            IUniswapV2Pair(pair).reserve0(),
            IUniswapV2Pair(pair).reserve1()
        );
        
        // 確保在滑點範圍內
        require(amountA >= amountAMin, "INSUFFICIENT_A_AMOUNT");
        require(amountB >= amountBMin, "INSUFFICIENT_B_AMOUNT");
        
        // 轉移代幣
        IERC20(tokenA).transferFrom(msg.sender, pair, amountA);
        IERC20(tokenB).transferFrom(msg.sender, pair, amountB);
        
        // 鑄造流動性代幣
        liquidity = pair.mint(to);
    }
    
    // 交換代幣(多跳)
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external ensure(deadline) returns (uint256[] memory amounts) {
        
        // 計算路徑上每個步驟的輸出數量
        amounts = getAmountsOut(amountIn, path);
        
        require(amounts[amounts.length - 1] >= amountOutMin, "INSUFFICIENT_OUTPUT_AMOUNT");
        
        // 轉移輸入代幣
        IERC20(path[0]).transferFrom(
            msg.sender,
            IUniswapV2Library.pairFor(factory, path[0], path[1]),
            amounts[0]
        );
        
        // 執行交換
        _swap(amounts, path, to);
    }
    
    // 執行多跳交換
    function _swap(
        uint256[] memory amounts,
        address[] memory path,
        address to
    ) internal {
        for (uint256 i; i < path.length - 1; i++) {
            (address input, address output) = (path[i], path[i + 1]);
            (address token0, ) = UniswapV2Library.sortTokens(input, output);
            
            uint256 amountOut = amounts[i + 1];
            
            (uint256 amount0Out, uint256 amount1Out) = input == token0
                ? (uint256(0), amountOut)
                : (amountOut, uint256(0));
            
            address to2 = i < path.length - 2 
                ? UniswapV2Library.pairFor(factory, output, path[i + 2])
                : to;
            
            // 調用配對合約的 swap 函數
            IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap(
                amount0Out,
                amount1Out,
                to2,
                new bytes(0)
            );
        }
    }
}

2.3 Uniswap 歷史攻擊案例

2.3.1 2023 年 Curve 漏洞攻擊(涉及 Vyper 編譯器漏洞)

事件概述:2023 年 7 月,Vyper 編譯器被發現存在漏洞,導致多個使用 Vyper 0.3.0 編譯的 Curve 池遭受攻擊,損失約 7000 萬美元。

漏洞技術分析

/**
 * @title Vyper 編譯器漏洞分析
 * @dev 展示重入保護失效問題
 */

// Vyper 0.3.0 的漏洞:
// 1. 重入保護在某些情況下失效
// 2. fallback 函數可以被多次調用

/**
 * 攻擊流程:
 * 
 * 1. 攻擊者部署惡意合約
 * 2. 調用目標池的 remove_liquidity_one_coin
 * 3. 在 callback 中再次調用 remove_liquidity_one_coin
 * 4. 由於重入保護失效,可以提取超過應得的流動性
 * 
 * 攻擊合約示例:
 */

contract CurveExploit {
    
    IStableSwap public pool;
    IERC20 public lpToken;
    IERC20 public token;
    
    uint256 public initialLpBalance;
    uint256 public stolenAmount;
    
    constructor(address _pool, address _lpToken, address _token) {
        pool = IStableSwap(_pool);
        lpToken = IERC20(_lpToken);
        token = IERC20(_token);
    }
    
    function attack(uint256 _amount) external {
        initialLpBalance = lpToken.balanceOf(address(this));
        
        // 調用移除流動性
        // 這會觸發 callback
        pool.remove_liquidity_one_coin(_amount, 0);
    }
    
    // Vyper 合約的回調
    // 由於漏洞,這個函數可以被多次調用
    function callback() external {
        // 檢查是否已經提取了超過應得的數量
        uint256 currentBalance = lpToken.balanceOf(address(this));
        
        if (currentBalance > initialLpBalance && stolenAmount == 0) {
            // 再次調用移除
            // 由於重入保護失效,這裡可以成功
            uint256 lpToRemove = lpToken.balanceOf(address(this)) - initialLpBalance;
            pool.remove_liquidity_one_coin(lpToRemove, 0);
            
            stolenAmount = token.balanceOf(address(this));
        }
    }
}

安全改進

/**
 * @title 防止重入攻擊的 Safe 合約模式
 * @dev 展示正確的重入保護實現
 */

// Solidity 實現的正確模式:

// 1. 使用 ReentrancyGuard
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract SafeUniswapPair is ReentrancyGuard {
    
    // 標記:nonReentrant 修飾符防止重入
    function swap(
        uint256 amount0Out,
        uint256 amount1Out,
        address to,
        bytes calldata data
    ) external nonReentrant {
        // 函數邏輯
    }
}

// 2. 使用 Checks-Effects-Interactions 模式
contract SafeSwap {
    
    function safeSwap(
        uint256 amountOut,
        address tokenOut,
        address to
    ) external {
        // 1. Checks - 驗證條件
        require(amountOut > 0, "Zero amount");
        require(reserves[tokenOut] >= amountOut, "Insufficient reserves");
        
        // 2. Effects - 更新內部狀態(在任何外部調用之前)
        balances[tokenOut] -= amountOut;
        
        // 3. Interactions - 外部調用(最後)
        IERC20(tokenOut).transfer(to, amountOut);
    }
}

// 3. 使用 Pull Payment 模式
contract PullPayment {
    
    mapping(address => uint256) public payments;
    
    function pay(address payable _to, uint256 _amount) external {
        // 不直接轉帳,而是記錄欠款
        payments[_to] += _amount;
    }
    
    function withdraw() external {
        uint256 payment = payments[msg.sender];
        require(payment > 0, "No payment");
        
        // 先將餘額設為 0,再轉帳(防止重入)
        payments[msg.sender] = 0;
        
        (bool success, ) = msg.sender.call{value: payment}("");
        require(success, "Transfer failed");
    }
}

第三部分:Aave 深入分析

3.1 協議架構概述

Aave 是以太坊生態系統中最具影響力的去中心化借貸協議,其獨特之處在於「利率模型」和「清算機制」的創新設計。

Aave V3 核心合約架構:

├── Pool
│   ├── supply()         - 存款
│   ├── borrow()         - 借款
│   ├── repay()          - 還款
│   ├── liquidate()      - 清算
│   └── flashLoan()      - 閃電貸
│
├── PoolConfigurator
│   ├── initReserve()    - 初始化儲備
│   ├── setReserveFactor() - 設置儲備因子
│   └── setEModeCategory() - 設置高效模式
│
├── aToken
│   ├── mint()          - 鑄造利息代幣
│   ├── burn()          - 銷毀利息代幣
│   └── transfer()      - 轉讓(帶利息)
│
├── MathUtils
│   ├── calculateLinearInterest() - 線性利率
│   ├── calculateCompoundedInterest() - 複利
│   └── wadToRay()      - 精度轉換
│
└── ValidationLogic
    ├── validateBorrow() - 借款驗證
    ├── validateLiquidation() - 清算驗證
    └── validateSupply() - 存款驗證

3.2 核心合約程式碼分析

3.2.1 Pool 合約

// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.19;

/**
 * @title Aave V3 Pool 合約核心邏輯
 * @dev 展示借貸協議的核心實現
 */
contract AaveV3Pool {
    
    // 儲備數據結構
    struct ReserveData {
        // 配置
        uint256 configuration;
        uint128 liquidityIndex;       // 累積流動性指數
        uint128 variableBorrowIndex;  // 累積變動借款指數
        uint128 currentLiquidityRate; // 當前流動性利率
        uint128 variableBorrowRate;   // 當前變動借款利率
        uint40 lastUpdateTimestamp;   // 最後更新時間
        // 帳戶
        address aTokenAddress;         // aToken 地址
        uint64 stableDebtTokenAddress;
        uint64 variableDebtTokenAddress;
        // 利率
        uint40 stableRateLastUpdated;
        uint256 reserveFactor;        // 儲備因子
    }
    
    mapping(address => ReserveData) public reserves;
    mapping(address => mapping(address => uint256)) public usersDeposits;
    mapping(address => mapping(address => uint256)) public usersBorrows;
    
    // 事件
    event Supply(
        address indexed user,
        address indexed reserve,
        uint256 amount
    );
    event Borrow(
        address indexed user,
        address indexed reserve,
        uint256 amount,
        uint256 borrowRate,
        uint256 currentRateMultiple
    );
    event LiquidationCall(
        address indexed user,
        address indexed reserve,
        address indexed collateral,
        uint256 debtToCover,
        uint256 liquidatedCollateralAmount,
        address liquidator
    );
    
    // 存款函數
    function supply(
        address asset,
        uint256 amount,
        address onBehalfOf,
        uint16 referralCode
    ) external {
        // 1. 更新儲備利率
        _updateInterestRates(asset);
        
        // 2. 計算用戶存款
        uint256 amountAToken = _supplyBalanceToAToken(asset, amount);
        
        // 3. 轉移代幣
        IERC20(asset).transferFrom(msg.sender, address(this), amount);
        
        // 4. 鑄造 aToken
        IAToken(reserves[asset].aTokenAddress).mint(
            onBehalfOf,
            amountAToken,
            reserves[asset].liquidityIndex
        );
        
        // 5. 更新用戶存款餘額
        usersDeposits[onBehalfOf][asset] += amount;
        
        emit Supply(onBehalfOf, asset, amount);
    }
    
    // 借款函數
    function borrow(
        address asset,
        uint256 amount,
        uint256 interestRateMode,
        uint16 referralCode,
        address onBehalfOf
    ) external {
        // 1. 驗證借款
        _validateBorrow(asset, amount, interestRateMode, onBehalfOf);
        
        // 2. 更新利率
        _updateInterestRates(asset);
        
        // 3. 計算借款金額
        uint256 currentRate = _getCurrentBorrowRate(asset, interestRateMode);
        
        // 4. 轉移借款
        IERC20(asset).transfer(msg.sender, amount);
        
        // 5. 鑄造債務代幣
        if (interestRateMode == 1) {
            // 穩定利率借款
        } else {
            // 變動利率借款
            IVariableDebtToken(reserves[asset].variableDebtTokenAddress).mint(
                onBehalfOf,
                amount,
                reserves[asset].variableBorrowIndex
            );
        }
        
        // 6. 更新用戶借款餘額
        usersBorrows[onBehalfOf][asset] += amount;
        
        emit Borrow(onBehalfOf, asset, amount, currentRate, 0);
    }
    
    // 清算函數
    function liquidationCall(
        address collateralAsset,
        address debtAsset,
        address user,
        uint256 debtToCover,
        bool receiveAToken
    ) external {
        // 1. 獲取用戶健康因子
        (uint256 healthFactor, ) = _getUserAccountData(user);
        
        // 2. 驗證健康因子
        require(healthFactor < HEALTH_FACTOR_LIQUIDATION_THRESHOLD, "HEALTH_FACTOR_NOT_BELOW_THRESHOLD");
        
        // 3. 獲取清算獎勵
        uint256 collateralAmount = _calculateLiquidationCollateral(
            collateralAsset,
            debtAsset,
            debtToCover
        );
        
        // 4. 執行清算
        // 轉移債務人的抵押品給清算人
        if (receiveAToken) {
            // 直接轉移 aToken
            IAToken(reserves[collateralAsset].aTokenAddress).transferOnLiquidation(
                user,
                msg.sender,
                collateralAmount
            );
        } else {
            // 提取底層資產
            // 先burn aToken
            IAToken(reserves[collateralAsset].aTokenAddress).burn(
                user,
                collateralAmount,
                msg.sender
            );
        }
        
        // 5. 處理債務
        // 清償部分或全部債務
        if (debtToCover >= usersBorrows[user][debtAsset]) {
            usersBorrows[user][debtAsset] = 0;
        } else {
            usersBorrows[user][debtAsset] -= debtToCover;
        }
        
        emit LiquidationCall(
            user,
            debtAsset,
            collateralAsset,
            debtToCover,
            collateralAmount,
            msg.sender
        );
    }
    
    // 健康因子計算
    function _getUserAccountData(address user) internal view returns (
        uint256 totalCollateralBase,
        uint256 totalDebtBase,
        uint256 availableBorrowsBase,
        uint256 currentLiquidationThreshold,
        uint256 ltv,
        uint256 healthFactor
    ) {
        // 遍歷所有資產
        // 計算總抵押品價值
        // 計算總債務價值
        // 計算健康因子
        // healthFactor = (totalCollateral * liquidationThreshold) / totalDebt
        
        if (totalDebtBase == 0) {
            healthFactor = type(uint256).max;
        } else {
            healthFactor = (totalCollateralBase * currentLiquidationThreshold) / totalDebtBase;
        }
    }
}

3.2.2 利率模型

/**
 * @title Aave 利率模型
 * @dev 展示精確的利率計算
 */
contract AaveInterestRateModel {
    
    // 利率參數結構
    struct InterestRateModelParams {
        uint256 optimalUtilizationRate;  // 最佳利用率
        uint256 baseVariableBorrowRate;   // 基礎變動借款利率
        uint256 variableRateSlope1;      // 變動利率斜率1
        uint256 variableRateSlope2;      // 變動利率斜率2
        uint256 stableRateSlope1;        // 穩定利率斜率1
        uint256 stableRateSlope2;        // 穩定利率斜率2
    }
    
    /**
     * @dev 計算變動借款利率
     * @param params 利率模型參數
     * @param utilizationRate 利用率
     * 
     * 利率曲線:
     * 
     *           |      /
     *           |     /
     *           |    /
     *           |   /
     *   slope2  |  /
     *           | /
     * slope1    |/
     *           |___________________
     *                最佳利用率
     * 
     * 公式:
     * if utilization <= optimal:
     *   rate = base + utilization * slope1 / optimal
     * else:
     *   rate = base + slope1 + (utilization - optimal) * slope2 / (1 - optimal)
     */
    function calculateVariableBorrowRate(
        InterestRateModelParams memory params,
        uint256 utilizationRate
    ) public pure returns (uint256) {
        if (utilizationRate <= params.optimalUtilizationRate) {
            // 線性增長區間
            return params.baseVariableBorrowRate +
                (utilizationRate * params.variableRateSlope1) /
                params.optimalUtilizationRate;
        } else {
            // 加速增長區間
            uint256 rateAtOptimal = params.baseVariableBorrowRate +
                params.variableRateSlope1;
            
            uint256 excessUtilization = utilizationRate -
                params.optimalUtilizationRate;
            
            uint256 excessRate = (excessUtilization * params.variableRateSlope2) /
                (RESERVE_PERCENTAGE - params.optimalUtilizationRate);
            
            return rateAtOptimal + excessRate;
        }
    }
    
    /**
     * @dev 計算流動性利率(存款利率)
     * @param variableBorrowRate 變動借款利率
     * @param reserveFactor 儲備因子
     * 
     * 公式:
     * liquidityRate = variableBorrowRate * utilizationRate * (1 - reserveFactor)
     */
    function calculateLiquidityRate(
        uint256 variableBorrowRate,
        uint256 utilizationRate,
        uint256 reserveFactor
    ) public pure returns (uint256) {
        return
            (variableBorrowRate * utilizationRate * (RESERVE_PERCENTAGE - reserveFactor)) /
            RESERVE_PERCENTAGE;
    }
    
    // 複利計算
    /**
     * @dev 計算複利利息
     * @param rate 年化利率
     * @param lastUpdateTimestamp 上次更新時間
     * @param currentTimestamp 當前時間
     * 
     * 公式:
     * compoundRate = (1 + rate / SECONDS_PER_YEAR) ^ time
     * 
     * 近似計算(使用指數函數):
     * compoundRate ~= e^(rate * time / SECONDS_PER_YEAR)
     */
    function calculateCompoundedInterest(
        uint256 rate,
        uint256 lastUpdateTimestamp,
        uint256 currentTimestamp
    ) public pure returns (uint256) {
        uint256 timeDifference = currentTimestamp - lastUpdateTimestamp;
        
        // 確保不會發生溢位
        if (timeDifference == 0) {
            return RAY;  // RAY = 10^27
        }
        
        // 使用近似公式
        // ln(1 + x) ≈ x - x^2/2 + x^3/3 - ...
        // 但為效率使用預編譯合約或查找表
        
        // 精確實現:
        // ratePerSecond = rate / SECONDS_PER_YEAR
        // compoundFactor = (1 + ratePerSecond) ^ timeDifference
        
        // 為避免溢位,使用指數算法
        uint256 exp = rate * timeDifference / SECONDS_PER_YEAR;
        
        // 使用泰勒展開近似
        return _pow(RAY + exp, timeDifference);
    }
    
    // 指數函數實現
    function _pow(uint256 x, uint256 n) internal pure returns (uint256) {
        uint256 result = RAY;
        
        while (n > 0) {
            if (n % 2 == 1) {
                result = (result * x) / RAY;
            }
            x = (x * x) / RAY;
            n /= 2;
        }
        
        return result;
    }
}

3.3 Aave 歷史攻擊案例

3.3.1 閃電貸攻擊案例分析

事件概述:雖然 Aave 本身沒有遭受直接攻擊,但多次攻擊事件使用了 Aave 的閃電貸功能作為攻擊向量。

攻擊模式分析

/**
 * @title 典型閃電貸攻擊模式
 * @dev 展示如何利用閃電貸進行攻擊
 */

// 攻擊模式:
// 1. 從 Aave 借出大量代幣
// 2. 操縱目標協議的價格或狀態
// 3. 執行獲利操作
// 4. 歸還閃電貸

contract FlashLoanAttackExample {
    
    IAavePool public aavePool;
    address public attacker;
    
    // 攻擊步驟
    function executeAttack(
        address[] memory assets,
        uint256[] memory amounts
    ) external {
        // 1. 發動閃電貸
        // callback 函數會被調用
        aavePool.flashLoan(
            address(this),
            assets,
            amounts,
            new uint256[](assets.length),
            address(this),
            "0x00",
            0
        );
    }
    
    // Aave 閃電貸回調函數
    function executeOperation(
        address[] calldata assets,
        uint256[] calldata amounts,
        uint256[] calldata premiums,
        address initiator,
        bytes calldata params
    ) external returns (bool) {
        
        // 2. 在這裡執行攻擊邏輯
        
        // 示例:操縱價格
        _manipulatePrice(assets[0], amounts[0]);
        
        // 3. 執行獲利操作
        uint256 profit = _extractProfit();
        
        // 4. 歸還閃電貸(本金 + 手續費)
        for (uint256 i = 0; i < assets.length; i++) {
            IERC20(assets[i]).approve(
                msg.sender,
                amounts[i] + premiums[i]
            );
        }
        
        // 轉移利潤到攻擊者地址
        _transferProfit(profit);
        
        return true;
    }
    
    function _manipulatePrice(address asset, uint256 amount) internal {
        // 具體的價格操縱邏輯
    }
    
    function _extractProfit() internal returns (uint256) {
        // 具體的獲利邏輯
    }
    
    function _transferProfit(uint256 profit) internal {
        // 轉移利潤
    }
}

3.3.2 防禦機制

/**
 * @title Aave 安全增強
 * @dev 展示如何防範閃電貸攻擊
 */

// 1. 延遲價格更新
contract DelayedPriceOracle {
    
    uint256 public priceUpdateDelay = 15 minutes;
    mapping(address => uint256) public lastUpdateTime;
    mapping(address => uint256) public lastPrice;
    mapping(address => uint256) public pendingPrice;
    
    function setPrice(address asset, uint256 newPrice) external {
        // 設置待生效價格
        pendingPrice[asset] = newPrice;
        
        // 延遲生效
        if (lastUpdateTime[asset] == 0) {
            lastPrice[asset] = newPrice;
            lastUpdateTime[asset] = block.timestamp;
        }
    }
    
    function getPrice(address asset) external view returns (uint256) {
        // 返回最近的確認價格
        if (block.timestamp - lastUpdateTime[asset] >= priceUpdateDelay) {
            return pendingPrice[asset];
        }
        return lastPrice[asset];
    }
    
    function confirmPriceUpdate(address asset) external {
        require(
            block.timestamp - lastUpdateTime[asset] >= priceUpdateDelay,
            "Delay not passed"
        );
        
        lastPrice[asset] = pendingPrice[asset];
        lastUpdateTime[asset] = block.timestamp;
    }
}

// 2. 借款限額檢查
contract BorrowLimitController {
    
    uint256 public maxBorrowAmount;
    mapping(address => uint256) public userBorrowAmount;
    
    modifier checkBorrowLimit(address user, uint256 amount) {
        require(
            userBorrowAmount[user] + amount <= maxBorrowAmount,
            "Exceeds borrow limit"
        );
        _;
    }
}

// 3. 異常交易檢測
contract AnomalyDetector {
    
    struct TradePattern {
        uint256 frequency;
        uint256 avgAmount;
        uint256 lastTradeTime;
    }
    
    mapping(address => TradePattern) public patterns;
    
    function detectAnomaly(
        address user,
        uint256 amount,
        address asset
    ) external view returns (bool) {
        TradePattern memory pattern = patterns[user];
        
        // 檢查是否異常
        // 1. 短時間內大量交易
        if (block.timestamp - pattern.lastTradeTime < 1 minutes) {
            return true;
        }
        
        // 2. 金額遠超平均
        if (amount > pattern.avgAmount * 10) {
            return true;
        }
        
        return false;
    }
}

第四部分:三個協議的安全教訓總結

4.1 共同安全模式

/**
 * @title DeFi 安全最佳實踐總結
 * @dev 從三個協議中提取的安全原則
 */

// 1. 權限控制
contract AccessControl {
    mapping(bytes32 => mapping(address => bool)) roles;
    
    modifier onlyRole(bytes32 role) {
        require(roles[role][msg.sender], "Access denied");
        _;
    }
    
    function grantRole(bytes32 role, address account) internal {
        roles[role][account] = true;
    }
}

// 2. 溢出保護(Solidity 0.8+ 內建)
// 使用 SafeMath 或 0.8+ 內建檢查

// 3. 重入保護
contract ReentrancyGuard {
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;
    uint256 private _status;
    
    constructor() {
        _status = _NOT_ENTERED;
    }
    
    modifier nonReentrant() {
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
        _status = _ENTERED;
        _;
        _status = _NOT_ENTERED;
    }
}

// 4. 價格操縱保護
contract PriceGuard {
    AggregatorV3Interface[] public priceFeeds;
    uint256 public maxPriceDeviation = 0.05e18;
    
    function getSecurePrice(address asset) external view returns (uint256) {
        uint256[] memory prices = new uint256[](priceFeeds.length);
        
        for (uint256 i = 0; i < priceFeeds.length; i++) {
            (, int256 price, , , ) = priceFeeds[i].latestRoundData();
            prices[i] = uint256(price);
        }
        
        // 計算中位數
        return _median(prices);
    }
    
    function _median(uint256[] memory arr) internal pure returns (uint256) {
        // 排序並返回中位數
    }
}

// 5. 緊急暫停
contract EmergencyPause {
    bool public paused;
    
    modifier whenNotPaused() {
        require(!paused, "Paused");
        _;
    }
    
    function pause() external onlyOwner {
        paused = true;
    }
    
    function unpause() external onlyOwner {
        paused = false;
    }
}

4.2 安全檢查清單

DeFi 合約安全檢查清單:

□ 1. 權限控制
   □ 所有關鍵函數都有適當的訪問控制
   □ 多簽名機制用於重要操作
   □ 時間鎖用於參數變更

□ 2. 溢出保護
   □ 使用 SafeMath 或 Solidity 0.8+
   □ 所有算術運算都有邊界檢查

□ 3. 重入保護
   □ 使用 Checks-Effects-Interactions 模式
   □ 使用 ReentrancyGuard
   □ 避免在不安全的順序進行外部調用

□ 4. 價格預言機
   □ 使用多個價格源
   □ 實施延遲生效機制
   □ 異常價格檢測

□ 5. 清算機制
   □ 激勵足夠的清算人參與
   □ 防止清算機器人Front-Running
   □ 確保清算不會導致壞帳

□ 6. 利率模型
   □ 正確計算複利
   □ 避免利率為0或無限大
   □ 壓力測試極端情況

□ 7. 緊急機制
   □ 緊急暫停功能
   □ 資金回收機制
   □ 升級路徑

□ 8. 測試覆蓋
   □ 完整單元測試
   □ 集成測試
   □ 形式化驗證(如可能)

結論

MakerDAO、Uniswap 和 Aave 是以太坊 DeFi 生態系統的三大支柱,它們的技術架構和設計模式對整個行業產生了深遠影響。通過深入分析這三個協議的合約程式碼,我們可以總結出以下關鍵教訓:

第一,DeFi 協議的安全性需要多層防護。從程式碼層面的權限控制、溢出保護、重入保護,到系統層面的價格預言機保護、緊急暫停機制,每一層都至關重要。

第二,歷史攻擊案例是最寶貴的安全教材。MakerDAO 的 Oracle 操縱攻擊提醒我們預言機安全的重要性;Curve 的重入漏洞揭示了編譯器層面的風險;閃電貸攻擊則展示了金融槓桿放大風險的能力。

第三,安全是一個持續的過程。即使是經過審計的知名協議,也需要持續監控、壓力測試和快速響應能力。社區的安全意識和協作是整個生態系統健康的關鍵。


參考資源

  1. MakerDAO GitHub - github.com/makerdao
  2. Uniswap V2 Documentation - docs.uniswap.org
  3. Aave V3 Technical Paper - aave.com
  4. OpenZeppelin Security - openzeppelin.com/security
  5. Trail of Bits Audit Reports - trailofbits.com

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

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

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