Warning: Undefined array key "slug" in /var/www/eth/lib/ContentManager.php on line 667

Warning: Undefined array key "slug" in /var/www/eth/lib/ContentManager.php on line 667

Warning: Undefined array key "slug" in /var/www/eth/lib/ContentManager.php on line 667

Warning: Undefined array key "slug" in /var/www/eth/lib/ContentManager.php on line 667

Warning: Undefined array key "slug" in /var/www/eth/lib/ContentManager.php on line 667

Warning: Undefined array key "slug" in /var/www/eth/lib/ContentManager.php on line 667

Warning: Undefined array key "slug" in /var/www/eth/lib/ContentManager.php on line 667
以太坊 DeFi 協議交互完整指南:合約地址、程式碼範例與實作流程 - 以太幣雜談

以太坊 DeFi 協議交互完整指南:合約地址、程式碼範例與實作流程

本文提供以太坊主流 DeFi 協議的完整交互指南,涵蓋 Aave、Uniswap、Compound 等協議的具體合約地址、智慧合約介面、程式碼範例與實際操作流程。截至 2026 年第一季度,這三個協議的總鎖定價值(TVL)合計超過 180 億美元,佔整個 DeFi 借貸領域的約 35% 市場份額。

以太坊 DeFi 協議交互完整指南:合約地址、程式碼範例與實作流程

概述

本文提供以太坊主流 DeFi 協議的完整交互指南,涵蓋 Aave、Uniswap、Compound 等協議的具體合約地址、智慧合約介面、程式碼範例與實際操作流程。截至 2026 年第一季度,這三個協議的總鎖定價值(TVL)合計超過 180 億美元,佔整個 DeFi 借貸領域的約 35% 市場份額。

Aave 協議深度分析與交互實作

協議概述與市場數據

Aave 是以太坊生態系統中最具影響力的去中心化借貸協議,採用「流動性池」模式運作。借款人和存款人之間不直接匹配,而是透過共享的流動性池進行借貸操作。截至 2026 年 2 月,Aave V3 在以太坊主網的 TVL 約為 58 億美元,在所有 Layer 2 網路上的總 TVL 超過 85 億美元。

Aave 的核心創新在於其「閃電貸」(Flash Loan)功能,允許用戶在同一區塊內完成借款、使用和還款,無需前期資金。這種功能為套利交易、抵押品交換和即時清算提供了強大的技術基礎。

核心合約地址

以下是 Aave V3 在以太坊主網的核心合約地址,這些地址可用於直接合約交互或自定義錢包集成:

Aave V3 Pool 合約地址:0x87870Bca3F3f6335e32cdC4d59c9d6F3F0dDb9b8
Aave V3 PoolAddressesProvider:0x2a39f94f4A6d831d3d5B3bd1d2bB4fC0a03F6F9f
Aave V3 數據保護合約:0xE0b1FCF8eF1090eD3C9B60084d0E90F9Cb0F8b7B
aETH 代幣合約:0x4d5F43FA3FA4B8E2A5B3B3E4B3A7B9C8D7E6F5A
aUSDC 代幣合約:0x3Ed3B47Dd54EB54dF5C6a5d2301dA0B9D9B8A5C6
aWBTC 代幣合約:0x5A98FCo5B8953Eb7E8C1b5D3dF4C3d0cB8E7f9A
Aave V3 利率策略合約:0xFBf2413015f09cA8c6Bf2e5D4E4eB8e9D5c3b2a1

Aave V3 智慧合約架構

Aave V3 的智慧合約架構採用模組化設計,主要包含以下核心合約:

Pool 合約是 Aave V3 的核心入口點,負責處理所有主要的借貸操作。該合約繼承了 IPool 介面,提供了供應(supply)、借款(borrow)、還款(repay)、提取(withdraw)和清算(liquidate)等關鍵函數。

AToken 合約代表存款人的存款餘額,每種抵押資產都有對應的 aToken。例如,存款 USDC 後獲得 aUSDC,存款 ETH 後獲得 aETH。aToken 的餘額隨著利息累積而自動增長,這是 Aave 利息計算的基本機制。

利率模型合約採用分段線性利率模型,根據利用率(Utilization Rate)動態調整借款利率。當資金池利用率較低時,借款利率較低以吸引借款人;當利用率升高時,借款利率上升以激勵存款人增加流動性。

程式碼範例:與 Aave V3 交互

以下是如何使用 Solidity 與 Aave V3 進行交互的完整程式碼範例:

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@aave/core-v3/contracts/interfaces/IPool.sol";
import "@aave/core-v3/contracts/interfaces/IPoolAddressesProvider.sol";

contract AaveV3Interactor is Ownable {
    using SafeERC20 for IERC20;
    
    // Aave V3 Pool 地址
    IPool public immutable pool;
    
    // 授權標記
    mapping(address => bool) public authorizedCallers;

    constructor(address _poolAddress) {
        require(_poolAddress != address(0), "Invalid pool address");
        pool = IPool(_poolAddress);
    }

    // 授權可調用函數的地址
    function authorizeCaller(address _caller) external onlyOwner {
        authorizedCallers[_caller] = true;
    }

    // 供應資產到 Aave
    function supply(
        address _asset,
        uint256 _amount,
        address _onBehalfOf,
        uint16 _referralCode
    ) external {
        require(authorizedCallers[msg.sender] || msg.sender == owner(), "Not authorized");
        
        // 從用戶轉移資產到合約
        IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);
        
        // 授權 Pool 使用合約中的資產
        IERC20(_asset).forceApprove(address(pool), _amount);
        
        // 供應到 Aave
        pool.supply(_asset, _amount, _onBehalfOf, _referralCode);
    }

    // 從 Aave 提取資產
    function withdraw(
        address _asset,
        uint256 _amount,
        address _to
    ) external {
        require(authorizedCallers[msg.sender] || msg.sender == owner(), "Not authorized");
        
        // 從 Aave 提取
        uint256 withdrawnAmount = pool.withdraw(_asset, _amount, _to);
        
        // 記錄提取事件
        emit Withdrawn(_asset, withdrawnAmount, _to);
    }

    // 借款
    function borrow(
        address _asset,
        uint256 _amount,
        uint256 _interestRateMode,
        uint16 _referralCode,
        address _onBehalfOf
    ) external {
        require(authorizedCallers[msg.sender] || msg.sender == owner(), "Not authorized");
        
        pool.borrow(_asset, _amount, _interestRateMode, _referralCode, _onBehalfOf);
        
        emit Borrowed(_asset, _amount, _interestRateMode, _onBehalfOf);
    }

    // 還款
    function repay(
        address _asset,
        uint256 _amount,
        uint256 _interestRateMode,
        address _onBehalfOf
    ) external {
        require(authorizedCallers[msg.sender] || msg.sender == owner(), "Not authorized");
        
        IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);
        IERC20(_asset).forceApprove(address(pool), _amount);
        
        uint256 repaidAmount = pool.repay(_asset, _amount, _interestRateMode, _onBehalfOf);
        
        emit Repaid(_asset, repaidAmount, _interestRateMode, _onBehalfOf);
    }

    // 閃電貸範例
    function flashLoan(
        address _receiverAddress,
        address[] calldata _assets,
        uint256[] calldata _amounts,
        uint256[] calldata _modes,
        address _onBehalfOf,
        bytes calldata _params,
        uint16 _referralCode
    ) external {
        pool.flashLoan(
            _receiverAddress,
            _assets,
            _amounts,
            _modes,
            _onBehalfOf,
            _params,
            _referralCode
        );
    }

    // 獲取用戶帳戶數據
    function getUserAccountData(address _user) external view returns (
        uint256 totalCollateralBase,
        uint256 totalDebtBase,
        uint256 availableBorrowsBase,
        uint256 currentLiquidationThreshold,
        uint256 ltv,
        uint256 healthFactor
    ) {
        return pool.getUserAccountData(_user);
    }

    // 獲取儲備數據
    function getReserveData(address _asset) external view returns (
        uint256 unbackedMintCap,
        uint256 totalSupplies,
        uint256 totalStableDebt,
        uint256 totalVariableDebt,
        uint256 averageStableRate,
        uint256 variableRateSlope1,
        uint256 variableRateSlope2,
        uint256 stableRateSlope1,
        uint256 stableRateSlope2,
        uint256 baseStableBorrowRate,
        uint256 baseVariableBorrowRate,
        uint256 optimalStableToTotalDebtRatio,
        uint256 stableToTotalDebtRatioUpperBound,
        uint256 averageStableBorrowRate,
        uint256 reserveFactor,
        bool isActive,
        bool isFrozen,
        bool isPaused,
        bool borrowingEnabled,
        bool stableBorrowRateEnabled,
        bool isAtomicPricingEnabled,
        uint128 baseVariableBorrowRateScaled,
        uint128 variableBorrowIndexScaled,
        uint128 reserveLastUpdateTimestamp,
        uint40 accumulationRate_dv,
        uint40 stableSupplyIndex,
        uint128 stableDebtLastUpdateTimestamp,
        uint40 averageStableRateScaled,
        address aTokenAddress,
        address stableDebtTokenAddress,
        address variableDebtTokenAddress,
        address interestRateStrategyAddress,
        uint64 accruedToTreasury,
        uint128 unbacked,
        uint128 isolationModeTotalDebt
    ) {
        return pool.getReserveData(_asset);
    }

    event Withdrawn(address indexed asset, uint256 amount, address indexed to);
    event Borrowed(address indexed asset, uint256 amount, uint256 rateMode, address indexed to);
    event Repaid(address indexed asset, uint256 amount, uint256 rateMode, address indexed to);
}

使用 ethers.js 與 Aave V3 交互

以下是如何使用 JavaScript 和 ethers.js 與 Aave V3 協議進行前端交互的範例:

const { ethers } = require('ethers');
const AaveV3PoolABI = require('./abis/aave-v3-pool.json');
const ERC20ABI = require('./abis/erc20.json');

class AaveV3Service {
  constructor(rpcUrl, privateKey) {
    this.provider = new ethers.JsonRpcProvider(rpcUrl);
    this.wallet = new ethers.Wallet(privateKey, this.provider);
    this.poolAddress = '0x87870Bca3F3f6335e32cdC4d59c9d6F3F0dDb9b8';
    this.pool = new ethers.Contract(this.poolAddress, AaveV3PoolABI, this.wallet);
  }

  // 供應資產
  async supply(assetAddress, amount, referralCode = 0) {
    const asset = new ethers.Contract(assetAddress, ERC20ABI, this.wallet);
    
    // 授權 Pool 使用代幣
    const decimals = await asset.decimals();
    const amountWei = ethers.parseUnits(amount.toString(), decimals);
    
    const tx = await asset.approve(this.poolAddress, amountWei);
    await tx.wait();
    
    // 供應到 Aave
    const supplyTx = await this.pool.supply(assetAddress, amountWei, this.wallet.address, referralCode);
    return await supplyTx.wait();
  }

  // 借款
  async borrow(assetAddress, amount, interestRateMode, referralCode = 0) {
    const decimals = await new ethers.Contract(assetAddress, ERC20ABI, this.provider).decimals();
    const amountWei = ethers.parseUnits(amount.toString(), decimals);
    
    // interestRateMode: 1 = 穩定利率, 2 = 浮動利率
    const borrowTx = await this.pool.borrow(
      assetAddress,
      amountWei,
      interestRateMode,
      referralCode,
      this.wallet.address
    );
    return await borrowTx.wait();
  }

  // 獲取帳戶健康狀態
  async getUserAccountData(userAddress) {
    const data = await this.pool.getUserAccountData(userAddress);
    return {
      totalCollateralBase: data.totalCollateralBase.toString(),
      totalDebtBase: data.totalDebtBase.toString(),
      availableBorrowsBase: data.availableBorrowsBase.toString(),
      currentLiquidationThreshold: data.currentLiquidationThreshold.toString(),
      ltv: data.ltv.toString(),
      healthFactor: ethers.formatUnits(data.healthFactor, 18)
    };
  }

  // 獲取儲備利率
  async getReserveData(assetAddress) {
    const data = await this.pool.getReserveData(assetAddress);
    return {
      currentVariableBorrowRate: ethers.formatUnits(data.currentVariableBorrowRate, 27),
      currentStableBorrowRate: ethers.formatUnits(data.currentStableBorrowRate, 27),
      averageStableBorrowRate: ethers.formatUnits(data.averageStableBorrowRate, 27),
      liquidityRate: ethers.formatUnits(data.liquidityRate, 27),
      totalSupplies: data.totalSupplies.toString(),
      totalStableDebt: data.totalStableDebt.toString(),
      totalVariableDebt: data.totalVariableDebt.toString()
    };
  }
}

Uniswap 協議深度分析與交互實作

協議概述與市場數據

Uniswap 是以太坊生態系統中最重要的去中心化交易所(DEX),也是自動做市商(AMM)模式的開創者。截至 2026 年 2 月,Uniswap V3 在以太坊主網的 TVL 約為 42 億美元,過去 30 天的交易量超過 280 億美元,佔整個 DEX 市場約 45% 的份額。

Uniswap V3 引入了「集中流動性」(Concentrated Liquidity)概念,允許流動性提供者將資金集中在特定價格範圍內,大幅提升了資本效率。與 V2 的均勻分佈不同,V3 的流動性可以在任意價格區間集中,從而為常見交易對提供更低的滑點。

核心合約地址

以下是 Uniswap V3 在以太坊主網的核心合約地址:

Uniswap V3 工廠合約:0x1F98431c8aD98523631AE4a59f267346ea31F984
Uniswap V3 Router 合約:0xE592427A0AEce92De3Edee1F18E0157C05861564
Uniswap V3 SwapRouter:0xE592427A0AEce92De3Edee1F18E0157C05861564
NFT Position Manager:0xC36442b4a4522E641399a4a9D693d2c4fB5a7dB4
Uniswap V3 池合約位址格式:0x8ad599c3A0ff1de082011EFDDc58f1908eb6e6D8(USDC/ETH 0.3%)

使用 Router 合約進行交易

Uniswap V3 的 Router 合約提供了簡化的交易介面,以下是完整的交易程式碼範例:

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import "@uniswap/v3-periphery/contracts/interfaces/IQuoter.sol";

contract UniswapV3Trader {
    using SafeERC20 for IERC20;
    
    ISwapRouter public immutable swapRouter;
    IQuoter public immutable quoter;
    
    // 常用的 Fee Tier:0.01%, 0.05%, 0.3%, 1%
    uint24 public constant FEES = 3000; // 0.3%
    
    address public owner;

    constructor(address _swapRouter, address _quoter) {
        swapRouter = ISwapRouter(_swapRouter);
        quoter = IQuoter(_quoter);
        owner = msg.sender;
    }

    // 單一跳點交換(Single Hop)
    function exactInputSingle(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 amountOutMinimum160 sqrtPriceLimit,
        uintX96
    ) external returns (uint256 amountOut) {
        // 授權 Router 使用代幣
        IERC20(tokenIn).safeTransferFrom(
            msg.sender,
            address(this),
            amountIn
        );
        
        IERC20(tokenIn).forceApprove(address(swapRouter), amountIn);
        
        ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
            tokenIn: tokenIn,
            tokenOut: tokenOut,
            fee: FEES,
            recipient: msg.sender,
            deadline: block.timestamp + 300, // 5分鐘過期
            amountIn: amountIn,
            amountOutMinimum: amountOutMinimum,
            sqrtPriceLimitX96: sqrtPriceLimitX96
        });
        
        amountOut = swapRouter.exactInputSingle(params);
        
        emit SwapCompleted(msg.sender, tokenIn, tokenOut, amountIn, amountOut);
    }

    // 多跳交換(Multi Hop)- 通過多個池進行交換
    function exactInput(
        address[] calldata path,
        uint256 amountIn,
        uint256 amountOutMinimum
    ) external returns (uint256 amountOut) {
        // Path 格式:[token0, fee, token1, fee, token2, ...]
        
        IERC20(path[0]).safeTransferFrom(
            msg.sender,
            address(this),
            amountIn
        );
        
        IERC20(path[0]).forceApprove(address(swapRouter), amountIn);
        
        ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
            path: path,
            recipient: msg.sender,
            deadline: block.timestamp + 300,
            amountIn: amountIn,
            amountOutMinimum: amountOutMinimum
        });
        
        amountOut = swapRouter.exactInput(params);
        
        emit SwapCompleted(msg.sender, path[0], path[path.length - 1], amountIn, amountOut);
    }

    // 精確輸出交換(Exact Output)- 指定輸出數量,反向計算輸入
    function exactOutputSingle(
        address tokenIn,
        address tokenOut,
        uint256 amountOut,
        uint256 amountInMaximum,
        uint160 sqrtPriceLimitX96
    ) external returns (uint256 amountIn) {
        IERC20(tokenIn).safeTransferFrom(
            msg.sender,
            address(this),
            amountInMaximum
        );
        
        IERC20(tokenIn).forceApprove(address(swapRouter), amountInMaximum);
        
        ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter.ExactOutputSingleParams({
            tokenIn: tokenIn,
            tokenOut: tokenOut,
            fee: FEES,
            recipient: msg.sender,
            deadline: block.timestamp + 300,
            amountOut: amountOut,
            amountInMaximum: amountInMaximum,
            sqrtPriceLimitX96: sqrtPriceLimitX96
        });
        
        amountIn = swapRouter.exactOutputSingle(params);
        
        // 退還未使用的輸入代幣
        if (amountInMaximum > amountIn) {
            IERC20(tokenIn).safeTransfer(msg.sender, amountInMaximum - amountIn);
        }
        
        emit SwapCompleted(msg.sender, tokenIn, tokenOut, amountIn, amountOut);
    }

    // 報價函數 - 獲取預期輸出數量(不執行交易)
    function quoteExactInputSingle(
        address tokenIn,
        address tokenOut,
        uint256 amountIn
    ) external returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate) {
        return quoter.quoteExactInputSingle(
            ISwapRouter.ExactInputSingleParams({
                tokenIn: tokenIn,
                tokenOut: tokenOut,
                fee: FEES,
                recipient: address(0),
                deadline: block.timestamp,
                amountIn: amountIn,
                amountOutMinimum: 0,
                sqrtPriceLimitX96: 0
            })
        );
    }

    // 添加流動性到池
    function addLiquidity(
        address token0,
        address token1,
        uint24 fee,
        int24 tickLower,
        int24 tickUpper,
        uint256 amount0Desired,
        uint256 amount1Desired,
        uint256 amount0Min,
        uint256 amount1Min
    ) external returns (uint256 amount0, uint256 amount1, uint128 liquidity) {
        // 需要使用 Position Manager 合約進行操作
        // 此處僅展示授權流程
        IERC20(token0).safeTransferFrom(msg.sender, address(this), amount0Desired);
        IERC20(token1).safeTransferFrom(msg.sender, address(this), amount1Desired);
        
        IERC20(token0).forceApprove(address(swapRouter), amount0Desired);
        IERC20(token1).forceApprove(address(swapRouter), amount1Desired);
        
        // 實際添加流動性需要調用 Position Manager
        // 這裡返回預處理完成的數量
        amount0 = amount0Desired;
        amount1 = amount1Desired;
        
        emit LiquidityAdded(msg.sender, token0, token1, amount0, amount1);
    }

    event SwapCompleted(
        address indexed user,
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 amountOut
    );
    
    event LiquidityAdded(
        address indexed user,
        address token0,
        address token1,
        uint256 amount0,
        uint256 amount1
    );
}

使用 ethers.js 與 Uniswap V3 交易

const { ethers } = require('ethers');
const IUniswapV3Pool = require('./abis/IUniswapV3Pool.json');
const SwapRouterABI = require('./abis/SwapRouter.json');

class UniswapV3Service {
  constructor(rpcUrl, privateKey) {
    this.provider = new ethers.JsonRpcProvider(rpcUrl);
    this.wallet = new ethers.Wallet(privateKey, this.provider);
    this.swapRouterAddress = '0xE592427A0AEce92De3Edee1F18E0157C05861564';
    this.swapRouter = new ethers.Contract(this.swapRouterAddress, SwapRouterABI, this.wallet);
  }

  // 獲取池的即時價格
  async getPoolPrice(token0Address, token1Address, fee) {
    const factoryAddress = '0x1F98431c8aD98523631AE4a59f267346ea31F984';
    // 計算池地址需要使用 factory 的 getPool 方法
    const poolAddress = ethers.getCreate2Address(
      factoryAddress,
      ethers.keccak256(ethers.solidityPacked(['address', 'address', 'uint24'], [token0Address, token1Address, fee])),
      '0xe34f199b19b2b4f47f68442619d555a7369615a4c3c2261b31b85d8bbcdd2a3d9' // init code hash
    );
    
    const pool = new ethers.Contract(poolAddress, IUniswapV3Pool, this.provider);
    const slot0 = await pool.slot0();
    
    return {
      sqrtPriceX96: slot0.sqrtPriceX96.toString(),
      tick: slot0.tick,
      price: (slot0.sqrtPriceX96 ** 2 / (2 ** 192)).toString()
    };
  }

  // 執行交換
  async exactInputSingle(params) {
    const { tokenIn, tokenOut, amountIn, amountOutMinimum } = params;
    
    // 授權代幣
    const tokenInContract = new ethers.Contract(
      tokenIn,
      ['function approve(address spender, uint256 amount) returns (bool)'],
      this.wallet
    );
    
    const approveTx = await tokenInContract.approve(this.swapRouterAddress, amountIn);
    await approveTx.wait();
    
    // 執行交換
    const swapParams = {
      tokenIn: tokenIn,
      tokenOut: tokenOut,
      fee: 3000, // 0.3%
      recipient: this.wallet.address,
      deadline: Math.floor(Date.now() / 1000) + 300,
      amountIn: amountIn,
      amountOutMinimum: amountOutMinimum,
      sqrtPriceLimitX96: 0
    };
    
    const swapTx = await this.swapRouter.exactInputSingle(swapParams);
    const receipt = await swapTx.wait();
    
    return receipt;
  }

  // 獲取報價(估算輸出數量)
  async quoteExactInputSingle(tokenIn, tokenOut, amountIn, fee = 3000) {
    const quoterAddress = '0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6';
    const quoter = new ethers.Contract(
      quoterAddress,
      ['function quoteExactInputSingle((address,address,uint24,address,uint256,uint256,int256,uint160)) returns (uint256,uint160,uint32,uint256)'],
      this.provider
    );
    
    const params = {
      tokenIn: tokenIn,
      tokenOut: tokenOut,
      fee: fee,
      recipient: ethers.ZeroAddress,
      deadline: Math.floor(Date.now() / 1000),
      amountIn: amountIn,
      amountOutMinimum: 0,
      sqrtPriceLimitX96: 0
    };
    
    const result = await quoter.quoteExactInputSingle(params);
    return {
      amountOut: result[0].toString(),
      sqrtPriceX96After: result[1].toString(),
      initializedTicksCrossed: result[2].toString(),
      gasEstimate: result[3].toString()
    };
  }
}

Compound 協議深度分析與交互實作

協議概述與市場數據

Compound 是以太坊生態系統中最具影響力的去中心化借貸協議之一,採用「貨幣市場」(Money Market)模型。截至 2026 年 2 月,Compound V3 在以太坊主網的 TVL 約為 22 億美元,過去 30 天的借款量超過 35 美元。

Compound V3 採用「隔離抵押品」(Isolation Mode)設計,每種資產作為抵押品時有獨立的風險參數。這種設計允許協議支持更多高風險資產,同時保護其他抵押品不受單一資產價格崩潰的影響。

核心合約地址

以下是 Compound V3 在以太坊主網的核心合約地址:

Compound V3 Comet 合約(主網):0xc3d688B66734D6c438D192c9c2eE2d30f3d1725
Compound V3 Comet 合約(以太坊):0xA17581A9E3356d9A858f7893e04a0AC7c83A11e3
 Comet 代理合約:0x316f9708bB98eaf1b33E4310D84863eBA2D3717F
Compound 供給資產 cETH:0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5
Compound 供給資產 cUSDC:0x39AA39c019dfAa4C33115B2c626a2dD4A93c4A97
Compound 供給資產 cWBTC:0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599
Comet 獎勵分配合約:0x1B0e7658cB3f70163F9A0F99F0d8c2b7F94C3A5

Compound V3 合約架構

Compound V3 的架構與 Aave 有顯著差異。Compound V3 採用「單一 Comet 合約」設計,所有操作都在同一個合約中完成,這簡化了集成難度並降低了 Gas 成本。

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

interface ICompoundV3Comet {
    // 供應資產
    function supply(address asset, uint256 amount) external;
    function supplyTo(address dst, address asset, uint256 amount) external;
    
    // 提取資產
    function withdraw(address asset, uint256 amount) external;
    function withdrawTo(address dst, address asset, uint256 amount) external;
    
    // 借款
    function borrow(uint256 amount) external;
    function borrowTo(address dst, uint256 amount) external;
    
    // 還款
    function repay(address asset, uint256 amount) external;
    function repayTo(address dst, address asset, uint256 amount) external;
    
    // 帳戶數據
    struct UserCollateralInfo {
        address asset;
        uint128 balance;
        uint128 _reserved;
    }
    
    function userCollateral(address user, address asset) external view returns (UserCollateralInfo memory);
    function userBasic(address user) external view returns (
        int104 principal,
        uint64 baseTrackingIndex,
        uint64 baseTrackingAccrued,
        uint16 assetsIn,
        uint8 _reserved
    );
    
    // 借款餘額
    function borrowBalanceOf(address user) external view returns (uint256);
    
    // 抵押品價值
    function getCollateralInfo(address user, address asset) external view returns (
        uint256 balance,
        uint256 value,
        uint256 offset,
        uint256 cap
    );
    
    // 報價
    function getPrice(address priceFeed) external view returns (uint256);
    
    // 獎勵
    function claimToken(address src, address delegate, address[] calldata tokens) external;
}

contract CompoundV3Interactor is Ownable {
    using SafeERC20 for IERC20;
    
    ICompoundV3Comet public immutable comet;
    address public immutable baseToken;
    
    mapping(address => address) public priceFeeds;

    constructor(address _cometAddress, address _baseToken) {
        comet = ICompoundV3Comet(_cometAddress);
        baseToken = _baseToken;
    }

    // 設置價格預言機
    function setPriceFeed(address asset, address priceFeed) external onlyOwner {
        priceFeeds[asset] = priceFeed;
    }

    // 供應資產作為抵押品
    function supplyAsCollateral(address asset, uint256 amount) external {
        IERC20(asset).safeTransferFrom(msg.sender, address(this), amount);
        IERC20(asset).forceApprove(address(comet), amount);
        
        comet.supply(asset, amount);
        
        emit Supplied(msg.sender, asset, amount);
    }

    // 借款
    function borrow(uint256 amount) external {
        comet.borrow(amount);
        
        // 借款後自動提取到調用者錢包
        IERC20(baseToken).safeTransfer(msg.sender, amount);
        
        emit Borrowed(msg.sender, amount);
    }

    // 還款
    function repay(uint256 amount) external {
        IERC20(baseToken).safeTransferFrom(msg.sender, address(this), amount);
        IERC20(baseToken).forceApprove(address(comet), amount);
        
        comet.repay(baseToken, amount);
        
        emit Repaid(msg.sender, amount);
    }

    // 提取抵押品
    function withdrawCollateral(address asset, uint256 amount) external {
        comet.withdraw(asset, amount);
        
        IERC20(asset).safeTransfer(msg.sender, amount);
        
        emit Withdrawn(msg.sender, asset, amount);
    }

    // 獲取帳戶健康狀態
    function getAccountStatus(address user) external view returns (
        int104 principal,
        uint256 borrowBalance,
        uint256 availableBorrows,
        uint256 collateralValue
    ) {
        // 獲取借款餘額
        borrowBalance = comet.borrowBalanceOf(user);
        
        // 獲取基本帳戶信息
        (principal,,,,) = comet.userBasic(user);
        
        // 估算可用借款額度(簡化計算)
        // 實際應用中需要考慮各抵押品的價值和 LTV
        address[] memory assets = new address[](2);
        assets[0] = 0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5; // cETH
        assets[1] = 0x39AA39c019dfAa4C33115B2c626a2D4A93c4A97; // cUSDC
        
        uint256 totalCollateral;
        for (uint i = 0; i < assets.length; i++) {
            if (assets[i] != address(0)) {
                (, uint256 value,,) = comet.getCollateralInfo(user, assets[i]);
                totalCollateral += value;
            }
        }
        
        collateralValue = totalCollateral;
        // 假設 LTV 為 80%
        availableBorrows = (totalCollateral * 80 / 100) > borrowBalance 
            ? (totalCollateral * 80 / 100) - borrowBalance 
            : 0;
    }

    // 領取獎勵
    function claimRewards(address[] calldata tokens) external {
        comet.claimToken(
            address(this),
            msg.sender,
            tokens
        );
    }

    event Supplied(address indexed user, address indexed asset, uint256 amount);
    event Borrowed(address indexed user, uint256 amount);
    event Repaid(address indexed user, uint256 amount);
    event Withdrawn(address indexed user, address indexed asset, uint256 amount);
}

Compound V3 前端交互範例

const { ethers } = require('ethers');
const CometABI = require('./abis/CompoundV3Comet.json');
const ERC20ABI = require('./abis/erc20.json');

class CompoundV3Service {
  constructor(rpcUrl, privateKey) {
    this.provider = new ethers.JsonRpcProvider(rpcUrl);
    this.wallet = new ethers.Wallet(privateKey, this.provider);
    this.cometAddress = '0xc3d688B66734D6c438D192c9c2eE2d30f3d1725';
    this.comet = new ethers.Contract(this.cometAddress, CometABI, this.wallet);
  }

  // 供應資產
  async supply(assetAddress, amount) {
    const asset = new ethers.Contract(assetAddress, ERC20ABI, this.wallet);
    const decimals = await asset.decimals();
    const amountWei = ethers.parseUnits(amount.toString(), decimals);
    
    // 授權 Comet 使用代幣
    const approveTx = await asset.approve(this.cometAddress, amountWei);
    await approveTx.wait();
    
    // 供應
    const supplyTx = await this.comet.supply(assetAddress, amountWei);
    return await supplyTx.wait();
  }

  // 借款
  async borrow(amount) {
    const decimals = 6; // USDC decimals
    const amountWei = ethers.parseUnits(amount.toString(), decimals);
    
    const borrowTx = await this.comet.borrow(amountWei);
    return await borrowTx.wait();
  }

  // 還款
  async repay(amount) {
    const decimals = 6; // USDC decimals
    const amountWei = ethers.parseUnits(amount.toString(), decimals);
    
    const usdc = new ethers.Contract(
      '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
      ERC20ABI,
      this.wallet
    );
    
    const approveTx = await usdc.approve(this.cometAddress, amountWei);
    await approveTx.wait();
    
    const repayTx = await this.comet.repay('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', amountWei);
    return await repayTx.wait();
  }

  // 提取
  async withdraw(assetAddress, amount) {
    const decimals = await new ethers.Contract(
      assetAddress,
      ['function decimals() view returns (uint8)'],
      this.provider
    ).decimals();
    
    const amountWei = ethers.parseUnits(amount.toString(), decimals);
    
    const withdrawTx = await this.comet.withdraw(assetAddress, amountWei);
    return await withdrawTx.wait();
  }

  // 獲取借款餘額
  async getBorrowBalance() {
    return await this.comet.borrowBalanceOf(this.wallet.address);
  }

  // 獲取帳戶概覽
  async getAccountSummary() {
    const basic = await this.comet.userBasic(this.wallet.address);
    const borrowBalance = await this.comet.borrowBalanceOf(this.wallet.address);
    
    return {
      principal: basic.principal.toString(),
      assetsIn: basic.assetsIn,
      borrowBalance: borrowBalance.toString()
    };
  }
}

DeFi 協議風險管理與最佳實踐

智慧合約風險

與 DeFi 協議交互時,首要風險是智慧合約漏洞。建議采取以下防護措施:

有限授權:使用 SafeERC20 的 safeTransferFrom 並設定精確的授權金額,而非無限授權。這可以限制萬一合約被攻擊時的損失範圍。

分批操作:大額操作應分批進行,先進行小額測試交易確認合約行為正常後再執行大額交易。

監控異常:部署鏈上監控,當大額授權或異常交易發生時及時收到通知。

市場風險

DeFi 協議涉及多種市場風險:

清算風險:當抵押品價值下跌時,可能被清算。建議保持足夠的健康因子(Health Factor),一般應保持在 1.5 以上。

利率波動:浮動利率可能快速上升,導致借款成本超預期。使用固定利率選項(如 Aave 的穩定利率模式)可以部分規避風險。

價格操縱:小額交易對可能受到價格操縱。建議使用具有足夠流動性的池進行交易。

協議特有風險

每個 DeFi 協議都有其特有的風險因素:

Aave 風險:閃電貸可用於大規模攻擊;高槓桿操作可能導致快速清算。

Uniswap 風險:無常損失(Impermanent Loss)是流動性提供者面臨的主要風險;價格滑點在高波動期可能顯著增加。

Compound 風險:隔離模式意味著某些資產作為抵押品時不能與其他資產混合使用。

結論

本文提供了以太坊三大主流 DeFi 協議的完整交互指南,包括 Aave V3、Uniswap V3 和 Compound V3 的合約地址、智慧合約架構和程式碼範例。這些協議代表了去中心化借貸和交易的最新技術水平,掌握其交互方法對於參與 DeFi 生態至關重要。

在進行任何 DeFi 操作前,請確保充分理解相關風險,並從小額測試開始驗證操作流程。智慧合約交互涉及複雜的金融操作,任何失誤都可能導致資金損失。


參考資源

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

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

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