DeFi 協議程式碼實作完整指南:從智能合約到前端交互
本文提供從智能合約層到前端交互的完整程式碼範例,涵蓋 ERC-20 代幣合約、借貸協議、AMM 交易所、質押協議等主要 DeFi 應用場景,使用 Solidity 和 JavaScript/TypeScript 提供可直接運行的程式碼範例。
DeFi 協議程式碼實作完整指南:從智能合約到底層交互
DeFi 這玩意兒,說起來容易,真正要搞懂卻得折騰好幾層。你知道 Uniswap 的 swapExactTokensForTokens 背後到底呼叫了什麼合約嗎?Aave 的借貸利率是怎麼算出來的?Compound 的 cToken 又是什麼原理?這篇文章就是要把這些問題從頭到尾拆解清楚,帶你從智能合約層一路摸到前端交互。
我踩過太多坑了——一開始以為會用 ethers.js 就懂 DeFi,結果被現實狠狠打臉。後來硬著頭去啃合約原始碼,才發現介面文件和實際實作差了十萬八千里。所以這篇文章不只教你「怎麼用」,更要讓你搞懂「為什麼這樣設計」。
數據截止到 2026 年 3 月,所有合約位址和 API 以太坊主網為準。
以太坊智能合約交互基礎
為什麼要直接交互合約
大部分人接觸 DeFi 是從 MetaMask 錢包 + 網頁介面開始的。點點滑鼠就能swap代幣、質押借貸,看似很簡單。但這種方式有幾個問題:
- 介面被做了很多「包裝」,你看不清底層操作
- 無法批次執行多個操作
- 無法自動化,沒法做量化交易
- 介面背後藏了什麼你根本不知道
直接用程式碼交互合約,就像從「用美圖秀秀」變成「用 Photoshop」——自由度高了,但門檻也高了。
ABI:合約的「說明書」
要與智能合約交互,首先得有 ABI(Application Binary Interface)。ABI 就像是合約的 API 文件,告訴你這個合約有什麼函數、參數是什麼類型、返回值是什麼。
// Uniswap V2 Router 的 ABI 片段
const uniswapV2RouterABI = [
{
"inputs": [
{ "internalType": "uint256", "name": "amountIn", "type": "uint256" },
{ "internalType": "uint256", "name": "amountOutMin", "type": "uint256" },
{ "internalType": "address[]", "name": "path", "type": "address[]" },
{ "internalType": "address", "name": "to", "type": "address" },
{ "internalType": "uint256", "name": "deadline", "type": "uint256" }
],
"name": "swapExactTokensForTokens",
"outputs": [
{ "internalType": "uint256[]", "name": "amounts", "type": "uint256[]" }
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "factory",
"outputs": [
{ "internalType": "address", "name": "", "type": "address" }
],
"stateMutability": "view",
"type": "function"
}
];
ABI 可以從 Etherscan 上直接複製。點進合約頁面,點「Contract」標籤頁,然後點「ABI」就能看到了。
與合約交互的基本流程
// 使用 ethers.js 與合約交互
const { ethers } = require('ethers');
async function interactWithContract() {
// 1. 連接到區塊鏈
const provider = new ethers.providers.JsonRpcProvider(
'https://mainnet.infura.io/v3/YOUR_PROJECT_ID'
);
// 2. 建立合約實例
// 參數:合約位址、ABI、provider(唯讀)或 signer(可寫入)
const uniswapRouter = new ethers.Contract(
'0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', // Uniswap V2 Router
uniswapV2RouterABI,
provider
);
// 3. 唯讀呼叫(不發交易)
const factory = await uniswapRouter.factory();
console.log('Uniswap Factory:', factory);
// 4. 發送交易(需要簽名)
const signer = new ethers.Wallet('YOUR_PRIVATE_KEY', provider);
const signerContract = uniswapRouter.connect(signer);
// 這裡是交易呼叫,需要 gas
const tx = await signerContract.swapExactTokensForTokens(
amountIn,
amountOutMin,
path,
toAddress,
deadline
);
// 等待交易確認
const receipt = await tx.wait();
console.log('Transaction hash:', receipt.transactionHash);
}
Uniswap:AMM 交換協議
Uniswap V2 交換原理
Uniswap 是以太坊上最經典的 AMM(自動做市商)協議。它的核心公式很簡單:
$$x \times y = k$$
其中 $x$ 和 $y$ 是池子裡兩種代幣的數量,$k$ 是常數。交易時,池子會保證這個等式成立。
// Uniswap V2 Pair 合約核心邏輯
// 這個合約管理一個流動性池的代幣交換
contract UniswapV2Pair {
// 兩個代幣的位址
address public token0;
address public token1;
// 儲備量(用於定價)
uint112 private reserve0;
uint112 private reserve1;
// 交換函數
function swap(
uint256 amount0Out,
uint256 amount1Out,
address to,
bytes calldata data
) external {
// 1. 獲取當前儲備量
(uint112 _reserve0, uint112 _reserve1,) = getReserves();
// 2. 驗證輸出金額
require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
// 3. 計算新的儲備量
uint256 balance0 = IERC20(token0).balanceOf(address(this));
uint256 balance1 = IERC20(token1).balanceOf(address(this));
uint256 amount0In = balance0 > _reserve0 - amount0Out
? balance0 - (_reserve0 - amount0Out)
: 0;
uint256 amount1In = balance1 > _reserve1 - amount1Out
? balance1 - (_reserve1 - amount1Out)
: 0;
require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
// 4. 執行交換並更新儲備量
_update(balance0, balance1, _reserve0, _reserve1);
// 5. 轉帳給接收者
if (amount0Out > 0) _safeTransfer(token0, to, amount0Out);
if (amount1Out > 0) _safeTransfer(token1, to, amount1Out);
}
}
計算交換價格與滑點
在區塊鏈上執行交換前,你得先計算出你能拿到多少代幣、滑點是多少:
# Python 實現 Uniswap V2 交換計算
class UniswapV2SwapCalculator:
"""
計算 Uniswap V2 的交換價格和滑點
"""
@staticmethod
def get_amount_out(amount_in, reserve_in, reserve_out, fee=997, fee_denom=1000):
"""
計算輸出金額
公式:amountOut = (amountIn * fee * reserveOut) / (reserveIn * feeDenom + amountIn * fee)
fee = 997/1000 表示 0.3% 的交易費用
"""
if amount_in <= 0:
raise ValueError("amount_in must be greater than 0")
if reserve_in <= 0 or reserve_out <= 0:
raise ValueError("Reserves must be greater than 0")
# 考慮交易費用
amount_in_with_fee = amount_in * fee
numerator = amount_in_with_fee * reserve_out
denominator = reserve_in * fee_denom + amount_in_with_fee
amount_out = numerator // denominator # 整數除法,避免浮點誤差
return amount_out
@staticmethod
def get_amount_in(amount_out, reserve_in, reserve_out, fee=997, fee_denom=1000):
"""
計算達到目標輸出需要的輸入金額
"""
if amount_out <= 0:
raise ValueError("amount_out must be greater than 0")
if reserve_in <= 0 or reserve_out <= 0:
raise ValueError("Reserves must be greater than 0")
numerator = reserve_in * amount_out * fee_denom
denominator = (reserve_out - amount_out) * fee
amount_in = (numerator // denominator) + 1
return amount_in
@staticmethod
def calculate_price_impact(amount_in, reserve_in, reserve_out, fee=997, fee_denom=1000):
"""
計算價格影響(Price Impact)
公式:
- 市場價格 = reserveOut / reserveIn
- 實際價格 = amountOut / amountIn
- 滑點 = (市場價格 - 實際價格) / 市場價格
"""
market_price = reserve_out / reserve_in
amount_out = UniswapV2SwapCalculator.get_amount_out(
amount_in, reserve_in, reserve_out, fee, fee_denom
)
actual_price = amount_out / amount_in
price_impact = (market_price - actual_price) / market_price
return price_impact # 例如:0.005 表示 0.5% 的滑點
@staticmethod
def simulate_swap(path, amounts, reserves):
"""
模擬多跳路徑的交換
path: 代幣位址陣列,例如 [WETH, USDC, DAI]
amounts: amounts[0] 是輸入金額
reserves: 每個池子的儲備量
例如:要用 WETH 換 DAI,可能路徑是 WETH -> USDC -> DAI
"""
amount = amounts[0]
for i in range(len(path)):
if i == 0:
# 第一跳:amount 是輸入
continue
elif i == 1:
# 第二跳:amount 是第一跳的輸出
amount = UniswapV2SwapCalculator.get_amount_out(
amount,
reserves[i-1][0], # 第一個池的 token0 儲備
reserves[i-1][1], # 第一個池的 token1 儲備
)
else:
amount = UniswapV2SwapCalculator.get_amount_out(
amount,
reserves[i-1][0],
reserves[i-1][1],
)
return amount # 最終輸出金額
# 使用範例
calculator = UniswapV2SwapCalculator()
# 池子參數(需要從區塊鏈讀取)
reserve_in = 5000 * 10**18 # 5000 ETH (假設是 WETH/DAI 池)
reserve_out = 15_000_000 * 10**18 # 1500萬 DAI
amount_in = 10 * 10**18 # 10 ETH
# 計算輸出
amount_out = calculator.get_amount_out(amount_in, reserve_in, reserve_out)
print(f"預期輸出: {amount_out / 10**18} DAI")
# 計算滑點
price_impact = calculator.calculate_price_impact(
amount_in, reserve_in, reserve_out
)
print(f"價格影響: {price_impact * 100:.3f}%")
完整的 Swap 交易程式碼
// 完整的 Uniswap V2 Swap 交易流程
const { ethers } = require('ethers');
const { Token, CurrencyAmount, Trade, Route, Pair } = require('@uniswap/sdk');
// 合約 ABI
const UNISWAP_V2_ROUTER_ABI = [
'function getAmountsOut(uint amountIn, address[] memory path) public view returns (uint[] memory amounts)',
'function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) returns (uint[] memory amounts)',
'function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) payable returns (uint[] memory amounts)',
'function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) returns (uint[] memory amounts)',
'function WETH() external pure returns (address)'
];
// 合約位址
const UNISWAP_V2_ROUTER = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D';
const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
const DAI = '0x6B175474E89094C44Da98b954EedeAC495271d0F';
class UniswapV2Swapper {
constructor(privateKey, rpcUrl) {
this.provider = new ethers.providers.JsonRpcProvider(rpcUrl);
this.wallet = new ethers.Wallet(privateKey, this.provider);
this.router = new ethers.Contract(
UNISWAP_V2_ROUTER,
UNISWAP_V2_ROUTER_ABI,
this.wallet
);
}
async getAmountsOut(amountIn, path) {
return await this.router.getAmountsOut(amountIn, path);
}
async swapExactTokensForTokens({
amountIn,
amountOutMin,
path,
deadline = Math.floor(Date.now() / 1000) + 60 * 20 // 20 分鐘後
}) {
// 檢查代幣授權
const tokenIn = new ethers.Contract(path[0], [
'function allowance(address owner, address spender) view returns (uint256)',
'function approve(address spender, uint256 amount) returns (bool)'
], this.wallet);
const signerBalance = await tokenIn.balanceOf(this.wallet.address);
console.log(`錢包餘額: ${ethers.utils.formatUnits(signerBalance, 18)}`);
if (signerBalance.lt(amountIn)) {
throw new Error('餘額不足');
}
const currentAllowance = await tokenIn.allowance(
this.wallet.address,
UNISWAP_V2_ROUTER
);
if (currentAllowance.lt(amountIn)) {
console.log('需要授權...');
const approveTx = await tokenIn.approve(
UNISWAP_V2_ROUTER,
ethers.constants.MaxUint256 // 授權最大值
);
await approveTx.wait();
console.log('授權成功');
}
// 執行交換
console.log('開始交換...');
const tx = await this.router.swapExactTokensForTokens(
amountIn,
amountOutMin,
path,
this.wallet.address,
deadline
);
const receipt = await tx.wait();
console.log(`交換成功!交易雜湊: ${receipt.transactionHash}`);
return receipt;
}
// 估算最佳交換路徑
async findBestPath(amountIn, tokenIn, tokenOut) {
// 常見的直接路徑和橋接路徑
const commonPaths = [
[tokenIn, tokenOut], // 直接路徑
[tokenIn, WETH, tokenOut], // 透過 WETH
];
let bestPath = null;
let bestAmountOut = 0;
for (const path of commonPaths) {
try {
const amounts = await this.getAmountsOut(amountIn, path);
const amountOut = amounts[amounts.length - 1];
if (amountOut.gt(bestAmountOut)) {
bestAmountOut = amountOut;
bestPath = path;
}
} catch (e) {
// 這個路徑可能不存在
continue;
}
}
return { bestPath, bestAmountOut };
}
}
// 使用範例
async function main() {
const swapper = new UniswapV2Swapper(
process.env.PRIVATE_KEY,
'https://eth-mainnet.alchemyapi.io/v2/YOUR_API_KEY'
);
// 交換 1 ETH 等值的 USDC 到 DAI
const wethAmount = ethers.utils.parseUnits('1', 18);
// 找最佳路徑
const { bestPath, bestAmountOut } = await swapper.findBestPath(
wethAmount,
WETH,
DAI
);
console.log(`最佳路徑: ${bestPath.join(' -> ')}`);
console.log(`預期輸出: ${ethers.utils.formatUnits(bestAmountOut, 18)} DAI`);
// 執行交換(設定 0.5% 滑點)
const amountOutMin = bestAmountOut.mul(995).div(1000);
const receipt = await swapper.swapExactTokensForTokens({
amountIn: wethAmount,
amountOutMin: amountOutMin,
path: bestPath
});
}
main().catch(console.error);
Aave V3:去中心化借貸協議
Aave 借貸核心概念
Aave 是以太坊生態中最成功的借貸協議。它的運作方式是:存款人把代幣存入池子獲取利息,借款人用自己的資產作為抵押品借走其他代幣。
核心參數:
- 健康因子(Health Factor):你的抵押品價值 / 借款價值。低於 1 就會被清算
- 存款利率:根據利用率動態計算
- 借款利率:同樣根據利用率計算
// Aave V3 Pool 合約核心介面
// 這是與 Aave V3 交互的主要合約
interface IPool {
// 存款
function supply(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external;
// 借款
function borrow(
address asset,
uint256 amount,
uint256 interestRateMode,
uint16 referralCode,
address onBehalfOf
) external;
// 還款
function repay(
address asset,
uint256 amount,
uint256 rateMode,
address onBehalfOf
) external;
// 提款
function withdraw(
address asset,
uint256 amount,
address to
) external;
// 獲取使用者帳戶數據
function getUserAccountData(address user)
external
view
returns (
uint256 totalCollateralBase,
uint256 totalDebtBase,
uint256 availableBorrowsBase,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
);
}
// Aave V3 Data Types
struct ReserveData {
// 基本參數
ReserveConfigurationMap configuration;
uint128 liquidityIndex;
uint128 currentLiquidityRate; // 存款 APY (in ray)
uint128 variableBorrowIndex; // 浮動借款利率指數
uint128 variableBorrowRate; // 浮動借款 APY (in ray)
uint128 stableBorrowRate; // 固定借款 APY (in ray)
uint40 lastUpdateTimestamp;
// 合約位址
address aTokenAddress;
address stableDebtTokenAddress;
address variableDebtTokenAddress;
address interestRateStrategyAddress;
// 其他參數
uint128 reservesTupleIdentification;
uint128 unbackedMintCap;
uint128 debtCeiling;
uint128 data;
}
計算健康因子
健康因子是 Aave 借貸系統的核心風控指標:
$$HF = \frac{\sum (Collaterali \times LTVi)}{\sum (Debt_i)}$$
# Python 實現健康因子計算
class AaveHealthFactorCalculator:
"""
計算 Aave 健康因子
公式:HF = Σ(抵押品價值 × LTV) / Σ(借款價值)
健康因子 > 1.0:安全
健康因子 < 1.0:可被清算
健康因子 = 1.0:清算門檻
"""
# Aave V3 主要資產的 LTV(Loan-to-Value)配置
LTV_CONFIG = {
'ETH': 0.80, # 80% LTV
'WBTC': 0.70, # 70% LTV
'USDC': 0.80, # 80% LTV(作為抵押品)
'DAI': 0.75, # 75% LTV
}
@staticmethod
def calculate_health_factor(collaterals, debts, prices):
"""
計算健康因子
Args:
collaterals: Dict[str, float] - 抵押品數量 {代幣: 數量}
debts: Dict[str, float] - 借款數量 {代幣: 數量}
prices: Dict[str, float] - 代幣價格 {代幣: USD價格}
Returns:
float - 健康因子
"""
weighted_collateral_value = 0.0
total_debt_value = 0.0
# 計算加權抵押品價值
for token, amount in collaterals.items():
if token in prices and token in AaveHealthFactorCalculator.LTV_CONFIG:
collateral_value = amount * prices[token]
ltv = AaveHealthFactorCalculator.LTV_CONFIG[token]
weighted_collateral_value += collateral_value * ltv
# 計算總借款價值
for token, amount in debts.items():
if token in prices:
total_debt_value += amount * prices[token]
if total_debt_value == 0:
return float('inf') # 無借款,健康因子無限大
health_factor = weighted_collateral_value / total_debt_value
return health_factor
@staticmethod
def calculate_liquidation_threshold(collaterals, debts, prices):
"""
計算清算門檻
清算發生在 health_factor < 1.0
"""
health_factor = AaveHealthFactorCalculator.calculate_health_factor(
collaterals, debts, prices
)
return health_factor < 1.0
@staticmethod
def calculate_max_borrow(collateral_amount, collateral_price, ltv, debt_price=1.0):
"""
計算最大可借款金額
根據抵押品數量和 LTV 計算最大借款量
"""
max_borrow = (collateral_amount * collateral_price * ltv) / debt_price
return max_borrow
@staticmethod
def simulate_price_drop(collaterals, debts, prices, drop_percentage):
"""
模擬抵押品價格下跌後的健康因子
這個函數用來評估市場下跌時的風險
"""
# 複製價格(避免修改原始數據)
new_prices = prices.copy()
# 應用價格下跌
for token in collaterals:
if token in new_prices:
new_prices[token] *= (1 - drop_percentage / 100)
return AaveHealthFactorCalculator.calculate_health_factor(
collaterals, debts, new_prices
)
# 使用範例
calculator = AaveHealthFactorCalculator()
# 假設用戶的持倉
collaterals = {
'ETH': 10.0, # 10 ETH
'WBTC': 0.5, # 0.5 WBTC
}
debts = {
'USDC': 15000.0, # 借了 15000 USDC
}
prices = {
'ETH': 3000.0, # ETH = $3000
'WBTC': 60000.0, # WBTC = $60000
'USDC': 1.0, # USDC = $1
}
# 計算健康因子
health_factor = calculator.calculate_health_factor(collaterals, debts, prices)
print(f"當前健康因子: {health_factor:.3f}")
# 模擬 ETH 價格下跌 20%
new_hf = calculator.simulate_price_drop(collaterals, debts, prices, 20)
print(f"ETH 下跌 20% 後健康因子: {new_hf:.3f}")
# 模擬 ETH 價格下跌 30%
new_hf = calculator.simulate_price_drop(collaterals, debts, prices, 30)
print(f"ETH 下跌 30% 後健康因子: {new_hf:.3f}")
# 計算清算門檻
is_liquidation = calculator.calculate_liquidation_threshold(collaterals, debts, prices)
print(f"是否處於清算邊緣: {is_liquidation}")
與 Aave V3 交互
// 與 Aave V3 完整交互流程
const { ethers } = require('ethers');
// Aave V3 Pool 合約 ABI(精簡版)
const AAVE_V3_POOL_ABI = [
// 供應(存款)
'function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode)',
// 借款
'function borrow(address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf)',
// 還款
'function repay(address asset, uint256 amount, uint256 rateMode, address onBehalfOf) returns (uint256)',
// 提款
'function withdraw(address asset, uint256 amount, address to) returns (uint256)',
// 獲取帳戶數據
'function getUserAccountData(address user) view returns (uint256, uint256, uint256, uint256, uint256, uint256)',
// 獲取儲備數據
'function getReserveData(address asset) view returns ((...), address aToken)',
];
// 合約位址(以太坊主網)
const AAVE_V3_POOL = '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2';
const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2';
const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
const aUSDC = '00a9B...'; // aUSDC 合約位址
class AaveV3Lender {
constructor(privateKey, rpcUrl) {
this.provider = new ethers.providers.JsonRpcProvider(rpcUrl);
this.wallet = new ethers.Wallet(privateKey, this.provider);
this.pool = new ethers.Contract(AAVE_V3_POOL, AAVE_V3_POOL_ABI, this.wallet);
}
// 供應(存款)ETH 到 Aave
async supplyETH(amount) {
// ETH 需要用 payable 方式傳入
const tx = await this.pool.supply(WETH, amount, this.wallet.address, 0, {
value: amount // 直接把 ETH 轉入
});
const receipt = await tx.wait();
console.log(`存款成功: ${ethers.utils.formatUnits(amount, 18)} ETH`);
return receipt;
}
// 借款 USDC
async borrowUSDC(amount, interestRateMode = 2) {
// interestRateMode: 1 = 固定利率, 2 = 浮動利率
const tx = await this.pool.borrow(
USDC, // 借款幣種
amount, // 借款數量
interestRateMode, // 利率模式
0, // referral code
this.wallet.address // 借款去向
);
const receipt = await tx.wait();
console.log(`借款成功: ${ethers.utils.formatUnits(amount, 6)} USDC`);
return receipt;
}
// 還款
async repayUSDC(amount, rateMode = 2) {
// rateMode: 1 = 固定利率債務, 2 = 浮動利率債務
// 需要先授權 USDC
const usdc = new ethers.Contract(USDC, [
'function approve(address spender, uint256 amount) returns (bool)'
], this.wallet);
await usdc.approve(AAVE_V3_POOL, amount);
const tx = await this.pool.repay(USDC, amount, rateMode, this.wallet.address);
const receipt = await tx.wait();
console.log(`還款成功`);
return receipt;
}
// 查詢帳戶數據
async getAccountData() {
const data = await this.pool.getUserAccountData(this.wallet.address);
return {
totalCollateralBase: data[0], // 總抵押品(以 USD 計算)
totalDebtBase: data[1], // 總債務
availableBorrowsBase: data[2], // 可借款額度
currentLiquidationThreshold: data[3], // 當前清算門檻
ltv: data[4], // LTV
healthFactor: data[5] // 健康因子
};
}
// 計算清算風險
async assessLiquidationRisk() {
const accountData = await this.getAccountData();
const healthFactor = parseFloat(ethers.utils.formatUnits(accountData.healthFactor, 18));
let riskLevel;
if (healthFactor > 2.0) {
riskLevel = 'LOW';
} else if (healthFactor > 1.5) {
riskLevel = 'MEDIUM';
} else if (healthFactor > 1.0) {
riskLevel = 'HIGH';
} else {
riskLevel = 'CRITICAL - LIQUIDATION IMMINENT';
}
console.log(`
===== 清算風險評估 =====
健康因子: ${healthFactor.toFixed(3)}
風險等級: ${riskLevel}
總抵押品: $${parseFloat(ethers.utils.formatUnits(accountData.totalCollateralBase, 8)).toLocaleString()}
總債務: $${parseFloat(ethers.utils.formatUnits(accountData.totalDebtBase, 8)).toLocaleString()}
可借款額度: $${parseFloat(ethers.utils.formatUnits(accountData.availableBorrowsBase, 8)).toLocaleString()}
`);
return { healthFactor, riskLevel, ...accountData };
}
}
// 使用範例
async function main() {
const aave = new AaveV3Lender(
process.env.PRIVATE_KEY,
'https://eth-mainnet.alchemyapi.io/v2/YOUR_API_KEY'
);
// 評估清算風險
await aave.assessLiquidationRisk();
// 借款
const borrowAmount = ethers.utils.parseUnits('1000', 6); // 1000 USDC
await aave.borrowUSDC(borrowAmount);
// 再次評估
await aave.assessLiquidationRisk();
}
main().catch(console.error);
Compound III:cToken 機制詳解
cToken 的運作原理
Compound 採用了一種很巧妙的設計:存款得到 cToken,cToken 餘額代表你在池子中的份額。
公式是:
$$cTokenAmount = \frac{suppliedAmount}{cTokenExchangeRate}$$
Exchange Rate 會隨著存款利息累積而不斷增加。
// Compound III cToken 合約核心邏輯
contract cToken {
// 累計利率指數(每個區塊都在增加)
uint256 public accrualBlockNumber;
uint256 public borrowIndex; // 借款利率累計指數
// 總借款量
uint256 public totalBorrows;
// 用戶的借款記錄
mapping(address => BorrowSnapshot) internal accountBorrows;
struct BorrowSnapshot {
uint256 principal;
uint256 interestIndex;
}
// 存款利率計算
function interestRateModel() public view returns (address) {
return address(interestRateModel);
}
// 獲取當前的存款利率
function supplyRatePerBlock() public view returns (uint256) {
uint256 utilization = utilizationRate();
return interestRateModel.getSupplyRate(utilization, 0, 0);
}
// 獲取借款利率
function borrowRatePerBlock() public view returns (uint256) {
uint256 utilization = utilizationRate();
return interestRateModel.getBorrowRate(utilization, 0, 0);
}
// 利用率計算
function utilizationRate() public view returns (uint256) {
uint256 cash = getCash();
uint256 borrows = totalBorrows;
uint256 reserves = totalReserves;
if (borrows == 0) return 0;
return borrows * 1e18 / (cash + borrows - reserves);
}
// 存款(mint cToken)
function mint(uint mintAmount) external returns (uint) {
// 1. 計算將獲得的 cToken 數量
uint exchangeRate = exchangeRateCurrent();
uint mintTokens;
// 2. 計算 cToken 數量 = mintAmount / exchangeRate
mintTokens = div_(mintAmount, exchangeRate);
// 3. 轉入 underlying token
require(EIP20(token).transferFrom(msg.sender, address(this), mintAmount));
// 4. 更新內部記錄
totalSupply = totalSupply + mintTokens;
accountTokens[msg.sender] = accountTokens[msg.sender] + mintTokens;
emit Mint(msg.sender, mintAmount, mintTokens);
return 0;
}
// 贖回(redeem cToken)
function redeem(uint redeemTokens) external returns (uint) {
// 計算可贖回的 underlying token 數量
uint exchangeRate = exchangeRateCurrent();
uint redeemAmount = mul_(redeemTokens, exchangeRate);
// 驗證流動性
require(accrueInterest() == 0);
require(getCash(msg.sender) >= redeemAmount);
// 更新記錄
totalSupply = totalSupply - redeemTokens;
accountTokens[msg.sender] = accountTokens[msg.sender] - redeemTokens;
// 轉出代幣
require(EIP20(token).transfer(msg.sender, redeemAmount));
return 0;
}
}
借款利率模型
Compound 使用分段線性利率模型:
Utilization Rate (U) Supply Rate Borrow Rate
0% ──────────────── 0% 0%
(免費借錢!)
80% ──────────────── 8% 10%
(線性增加)
100% ──────────────── 20% 40%
(陡峭增加)
# Compound 利率模型實現
class CompoundInterestRateModel:
"""
Compound 的分段線性利率模型
Borrow Rate =
0%, if U < U_optimal
kink_rate + (U - U_optimal) * (max_borrow_rate - kink_rate) / (1 - U_optimal), otherwise
"""
def __init__(self, kink=0.8, base_rate=0.0, multiplier=0.1, jump_multiplier=2.5):
"""
Args:
kink: 轉折點利用率,默認 80%
base_rate: 基礎借款利率,默認 0%
multiplier: 轉折點前的斜率,默認 10%
jump_multiplier: 超過轉折點後的倍數,默認 2.5x
"""
self.kink = kink
self.base_rate = base_rate
self.multiplier = multiplier
self.jump_multiplier = jump_multiplier
def get_borrow_rate(self, utilization):
"""
計算借款利率
Args:
utilization: 利用率 (0-1)
Returns:
float: 年化借款利率
"""
if utilization <= self.kink:
# 低利用率區間:線性增加
return self.base_rate + utilization * self.multiplier
else:
# 高利用率區間:陡峭增加
normal_rate = self.base_rate + self.kink * self.multiplier
excess_util = utilization - self.kink
jump_rate = excess_util * self.multiplier * (self.jump_multiplier - 1) / (1 - self.kink)
return normal_rate + jump_rate
def get_supply_rate(self, utilization):
"""
計算存款利率
存款利率 = 借款利率 × 利用率 × (1 - 儲備係數)
"""
if utilization == 0:
return 0.0
borrow_rate = self.get_borrow_rate(utilization)
reserve_factor = 0.15 # Compound 默認 15% 進入儲備金
# 存款利率 = 借款利率 × 利用率 × (1 - 儲備係數)
supply_rate = borrow_rate * utilization * (1 - reserve_factor)
return supply_rate
def calculate_apr(self, utilization, periods_per_year=2628000):
"""
計算 APY(年化收益率)
Args:
utilization: 利用率
periods_per_year: 每年區塊數(以太坊約 12 秒/區塊)
Returns:
float: 年化收益率
"""
rate_per_block = self.get_supply_rate(utilization)
apy = (1 + rate_per_block / periods_per_year) ** periods_per_year - 1
return apy
# 使用範例
model = CompoundInterestRateModel()
print("利用率 | 借款 APR | 存款 APR | 存款 APY")
print("-" * 50)
for u in [0.0, 0.2, 0.4, 0.6, 0.8, 0.9, 0.95]:
borrow_apr = model.get_borrow_rate(u)
supply_apr = model.get_supply_rate(u)
supply_apy = model.calculate_apr(u)
print(f"{u*100:6.1f}% | {borrow_apr*100:7.2f}% | {supply_apr*100:7.2f}% | {supply_apy*100:7.2f}%")
結語:從代碼到理解
折騰 DeFi 合約交互這麼久,最大的感悟是:不要只會用前端,要搞懂底層邏輯。
每次你點擊「Swap」按鈕,背後其實是:
- 你的代幣被 approve 轉移到合約
- 合約執行了 swapExactTokensForTokens
- 根據 AMM 公式計算輸出金額
- 扣除 0.3% 的交易費用
- 把代幣轉回你的錢包
理解這些之後,你就能做很多前端做不到的事:
- 批次操作省 Gas
- 自動化套利策略
- 量化風控模型
DeFi 世界的門檻是高,但不代表不能學。只要願意折騰,遲早能搞懂。
本網站內容僅供教育與資訊目的,不構成任何技術建議或投資建議。在進行任何 DeFi 操作前,請自行研究並諮詢專業人士意見。所有智能合約操作均涉及風險,請謹慎評估。
數據截止:2026 年 3 月
技術參考來源:
- Uniswap V2 合約原始碼:https://github.com/Uniswap/v2-core
- Aave V3 官方文檔:https://docs.aave.com
- Compound 合約原始碼:https://github.com/compound-finance/compound-protocol
- Etherscan:以太坊區塊瀏覽器,https://etherscan.io
COMMIT: Add DeFi protocol code examples guide with extensive Solidity and Python code
相關文章
- DeFi 自動做市商(AMM)數學推導完整指南:從常數乘積到穩定幣模型的深度解析 — 自動做市商(AMM)是 DeFi 生態系統中最具創新性的基礎設施之一。本文從數學視角出發,系統性地推導各類 AMM 模型的定價公式、交易滑點計算、流動性提供者收益模型、以及無常損失的數學證明。我們涵蓋從最基礎的常數乘積公式到 StableSwap 演算法、加權池、以及集中流動性模型的完整推到過程,所有推導都附帶具體數值示例和程式碼範例。
- 新興DeFi協議安全評估框架:從基礎審查到進階量化分析 — 系統性構建DeFi協議安全評估框架,涵蓋智能合約審計、經濟模型、治理機制、流動性風險等維度。提供可直接使用的Python風險評估代碼、借貸與DEX協議的專門評估方法、以及2024-2025年安全事件數據分析。
- AAVE V3 健康因子數學推導完整指南:從基礎公式到量化風險管理的深度解析 — 本文深入剖析 Aave V3 健康因子的完整數學推導。從基本的 HF 定義出發,推導單一抵押品和多抵押品場景下的計算公式,分析利率累積和抵押品價值波動對 HF 的動態影響。提供完整的隨機微分方程建模、蒙特卡羅模擬、以及清算 penalty 的量化分析。包含完整的 Solidity 和 TypeScript 程式碼範例,幫助開發者和量化風險管理人員建立對借貸協議風險模型的嚴格理解。
- DeFi 進階合約模式完整指南:從設計模式到 production-ready 程式碼實踐 — 本文深入探討以太坊 DeFi 協議開發中的進階合約模式,這些模式是構建生產級去中心化金融應用的核心技術基礎。相較於基礎的代幣轉帳和簡單借貸,進階 DeFi 協議需要處理複雜的定價邏輯、流動性管理、風險控制和多層次的激勵機制。本文從資深工程師視角出發,提供可直接應用於生產環境的程式碼範例,涵蓋 AMM 深度實現、質押衍生品、借貸協議進階風控、協議治理等關鍵領域。
- DeFi 攻擊事件漏洞程式碼重現技術深度指南:2024-2026 年完整實作教學 — 本文收錄 2024 年至 2026 年第一季度以太坊生態系統中最具代表性的 DeFi 攻擊事件,提供完整的漏洞程式碼重現、數學推導與量化損失分析。本文的獨特價值在於:透過可運行的 Solidity 程式碼重現漏洞機制,並提供詳盡的數學推導來解釋攻擊成功的原理。涵蓋重入攻擊、Curve Vyper JIT Bug、閃電貸操縱、跨鏈橋漏洞等主流攻擊類型。
延伸閱讀與來源
- Aave V3 文檔 頭部借貸協議技術規格
- Uniswap V4 文檔 DEX 協議規格與鉤子機制
- DeFi Llama DeFi TVL 聚合數據
- Dune Analytics DeFi 協議數據分析儀表板
這篇文章對您有幫助嗎?
請告訴我們如何改進:
評論
發表評論
注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。
目前尚無評論,成為第一個發表評論的人吧!