🏦 第18课:借贷协议

DeFi 阶段四 ✅ 验证通过

🎯 学习目标:理解DeFi借贷协议的核心机制(超额抵押、利率模型、清算),掌握Compound/Aave的运作原理,实现简易借贷合约,理解闪电贷(Flash Loan)概念。

📖 一、传统借贷 vs DeFi借贷

特性传统银行借贷DeFi借贷
信用评估KYC、征信、收入证明无需信用,超额抵押
审批时间天/周即时(链上验证)
抵押率50%-80%(房产等)通常60%-75%(加密资产)
清算方式法律追讨智能合约自动清算
利息计算固定/浮动算法动态调整
准入门槛高(需要银行账户)低(只需钱包)

📖 二、核心机制

2.1 超额抵押

DeFi借贷 = 超额抵押 存入抵押品 借出资产 ┌──────────┐ ┌──────────┐ │ 150 ETH │ │ 100 ETH │ │ = $300K │ ───► │ = $200K │ │ │ │ │ │ 抵押率 │ │ 借贷率 │ │ 150% │ │ 100% │ └──────────┘ └──────────┘ 抵押率 = 抵押品价值 / 借款价值 安全线 = 150%(意味着借款≤抵押品的66.7%) 清算线 = 120%(低于此值触发清算) 为什么需要超额抵押? • 加密资产价格波动大 • 没有信用评估,只能靠抵押品保障 • 清算需要时间,抵押品可能进一步贬值

2.2 利率模型

// Compound风格:分段线性利率模型
// 利用率 u = 总借款 / 总存款

// 低利用率区间(0% - 最优利用率):
借款利率 = 基础利率 + 利用率 × 斜率1

// 高利用率区间(最优利用率 - 100%):
借款利率 = 基础利率 + 斜率1 × 最优利用率 
         + (利用率 - 最优利用率) × 斜率2

// 示例参数(USDC市场):
基础利率 = 2%         // 无人借款时的最低利率
最优利用率 = 80%      // 资金利用效率的目标值
斜率1 = 10%           // 低利用率时利率缓慢上升
斜率2 = 100%          // 高利用率时利率急剧上升(激励存款)

        利率
         │              ╱  ← 高斜率(激励存款/抑制借款)
    25%  │            ╱
         │          ╱
    12%  │        ╱ ← 最优利率点
         │      ╱
     2%  │    ╱ ← 低斜率(温和调整)
         │  ╱
     0%  └──────────────── 利用率
         0%      80%    100%

📖 三、实现简易借贷合约

// contracts/SimpleLending.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

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

/**
 * @title SimpleLending
 * @dev 简化版借贷协议——单一资产池
 */
contract SimpleLending {
    using SafeERC20 for IERC20;

    IERC20 public immutable asset;    // 借贷资产(如USDC)
    uint256 public constant COLLATERAL_RATIO = 150;   // 150%抵押率
    uint256 public constant LIQUIDATION_RATIO = 120;  // 120%清算线
    uint256 public constant LIQUIDATION_BONUS = 5;    // 5%清算奖金
    uint256 public constant PRECISION = 100;

    // 利率参数
    uint256 public constant BASE_RATE = 2e16;       // 2%/年
    uint256 public constant SLOPE_1 = 1e17;        // 10%/年
    uint256 public constant OPTIMAL_UTIL = 8e17;   // 80%最优利用率
    uint256 public constant SLOPE_2 = 1e18;        // 100%/年

    // 用户账户
    struct Account {
        uint256 collateral;    // 抵押品(ETH)
        uint256 borrowed;      // 已借金额
        uint256 lastUpdate;    // 利息最后更新时间
    }
    mapping(address => Account) public accounts;

    // 市场状态
    uint256 public totalDeposits;
    uint256 public totalBorrows;
    uint256 public lastIndex;
    uint256 public lastUpdateTimestamp;

    event Deposited(address indexed user, uint256 amount);
    event Borrowed(address indexed user, uint256 amount);
    event Repaid(address indexed user, uint256 amount);
    event Liquidated(address indexed borrower, address liquidator, uint256 debtRepaid, uint256 collateralSeized);

    constructor(address _asset) {
        asset = IERC20(_asset);
        lastUpdateTimestamp = block.timestamp;
        lastIndex = 1e18; // 初始利率指数=1
    }

    // ═══════ 存款 ═══════
    function deposit(uint256 amount) external {
        require(amount > 0, "Zero amount");
        _updateIndex();

        asset.safeTransferFrom(msg.sender, address(this), amount);
        accounts[msg.sender].collateral += amount;
        totalDeposits += amount;

        emit Deposited(msg.sender, amount);
    }

    // ═══════ 借款 ═══════
    function borrow(uint256 amount) external {
        require(amount > 0, "Zero amount");
        _updateIndex();
        _accrueInterest(msg.sender);

        Account storage acc = accounts[msg.sender];
        acc.borrowed += amount;
        
        // 检查抵押率
        require(
            acc.collateral * PRECISION / acc.borrowed >= COLLATERAL_RATIO,
            "Insufficient collateral"
        );
        // 检查流动性
        require(
            totalDeposits - totalBorrows >= amount,
            "Insufficient liquidity"
        );

        totalBorrows += amount;
        asset.safeTransfer(msg.sender, amount);

        emit Borrowed(msg.sender, amount);
    }

    // ═══════ 还款 ═══════
    function repay(uint256 amount) external {
        _updateIndex();
        _accrueInterest(msg.sender);

        Account storage acc = accounts[msg.sender];
        require(amount <= acc.borrowed, "Repay exceeds debt");

        asset.safeTransferFrom(msg.sender, address(this), amount);
        acc.borrowed -= amount;
        totalBorrows -= amount;

        emit Repaid(msg.sender, amount);
    }

    // ═══════ 清算 ═══════
    function liquidate(address borrower, uint256 repayAmount) external {
        _updateIndex();
        _accrueInterest(borrower);

        Account storage acc = accounts[borrower];
        require(
            acc.collateral * PRECISION / acc.borrowed < LIQUIDATION_RATIO,
            "Not liquidatable"
        );

        // 最多清算50%的债务
        uint256 maxRepay = acc.borrowed / 2;
        if (repayAmount > maxRepay) {
            repayAmount = maxRepay;
        }

        // 计算可获得的抵押品(含清算奖金)
        uint256 collateralSeized = repayAmount * (PRECISION + LIQUIDATION_BONUS) / PRECISION;

        // 执行清算
        asset.safeTransferFrom(msg.sender, address(this), repayAmount);
        acc.borrowed -= repayAmount;
        acc.collateral -= collateralSeized;
        totalBorrows -= repayAmount;

        // 将抵押品转给清算者
        asset.safeTransfer(msg.sender, collateralSeized);

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

    // ═══════ 利率计算 ═══════
    function _updateIndex() internal {
        if (totalDeposits == 0) return;
        
        uint256 timeDelta = block.timestamp - lastUpdateTimestamp;
        if (timeDelta == 0) return;

        uint256 utilizationRate = (totalBorrows * 1e18) / totalDeposits;
        uint256 borrowRate;

        if (utilizationRate <= OPTIMAL_UTIL) {
            borrowRate = BASE_RATE + (utilizationRate * SLOPE_1 / OPTIMAL_UTIL);
        } else {
            uint256 excessUtil = utilizationRate - OPTIMAL_UTIL;
            uint256 excessFraction = excessUtil * 1e18 / (1e18 - OPTIMAL_UTIL);
            borrowRate = BASE_RATE + SLOPE_1 + (excessFraction * SLOPE_2 / 1e18);
        }

        // 更新利率指数
        uint256 interestFactor = borrowRate * timeDelta / 365 days;
        lastIndex += lastIndex * interestFactor / 1e18;
        lastUpdateTimestamp = block.timestamp;
    }

    function _accrueInterest(address user) internal {
        Account storage acc = accounts[user];
        if (acc.borrowed == 0 || acc.lastUpdate == 0) {
            acc.lastUpdate = block.timestamp;
            return;
        }
        // 简化利息计算
        acc.lastUpdate = block.timestamp;
    }

    // ═══════ 查询函数 ═══════
    function getHealthFactor(address user) external view returns (uint256) {
        Account storage acc = accounts[user];
        if (acc.borrowed == 0) return type(uint256).max;
        return (acc.collateral * PRECISION * 1e18) / (acc.borrowed * COLLATERAL_RATIO);
    }

    function getBorrowRate() external view returns (uint256) {
        if (totalDeposits == 0) return BASE_RATE;
        uint256 u = (totalBorrows * 1e18) / totalDeposits;
        if (u <= OPTIMAL_UTIL) {
            return BASE_RATE + u * SLOPE_1 / OPTIMAL_UTIL;
        }
        return BASE_RATE + SLOPE_1 + (u - OPTIMAL_UTIL) * SLOPE_2 / (1e18 - OPTIMAL_UTIL);
    }
}

📖 四、闪电贷(Flash Loan)

闪电贷:无抵押借款(在一个交易内完成) ┌────────────────────────────────────┐ │ 单个交易 │ │ │ │ 1. 借出1000 ETH ──────────┐ │ │ │ │ │ 2. 执行套利/清算操作 ◄────┘ │ │ (DEX价差/清算获利) │ │ │ │ │ 3. 归还1000 ETH + 手续费 ─┘ │ │ (0.09% = 0.9 ETH) │ │ │ │ ✅ 归还成功 → 交易执行 │ │ ❌ 归还失败 → 整个交易回滚 │ │ (就像从未发生过) │ └────────────────────────────────────┘ 关键:无需抵押! 原因:原子性——如果还不上,交易回滚,钱从未借出
// 闪电贷回调接口
interface IFlashBorrower {
    function onFlashLoan(
        address initiator,
        address token,
        uint256 amount,
        uint256 fee,
        bytes memory params
    ) external returns (bytes32);
}

// 闪电贷实现(简化)
contract FlashLender {
    uint256 public constant FEE = 9e14; // 0.09%
    bytes32 public constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan");

    function flashLoan(
        IFlashBorrower borrower,
        address token,
        uint256 amount,
        bytes memory params
    ) external {
        uint256 fee = amount * FEE / 1e18;

        // 1. 借出资金
        IERC20(token).transfer(address(borrower), amount);

        // 2. 调用借款者的回调函数
        bytes32 result = borrower.onFlashLoan(
            msg.sender, token, amount, fee, params
        );
        require(result == CALLBACK_SUCCESS, "Callback failed");

        // 3. 收回资金 + 手续费
        IERC20(token).transferFrom(address(borrower), address(this), amount + fee);
    }
}

🧪 练习

1. DeFi借贷为什么需要超额抵押? 2. 闪电贷为什么不需要抵押? 3. 清算者的动机是什么?

📖 九、利率模型对比

// 三种主流利率模型对比
// 1. Jump Rate Model (Compound)
//    利用率<Kink: 线性增长
//    利用率>Kink: 陡增(鼓励存款补充流动性)

// 2. Kinked Model (Aave)
//    在Kink点利率曲线斜率突变
//    最佳利用率=Kink点(资金效率和安全平衡)

// 3. Dynamic Rate Model
//    利率根据市场条件动态调整
//    目标利用率自动调节Kink位置

contract RateModelComparison {
    function jumpRate(uint256 u) public pure returns (uint256) {
        uint256 kink = 80e16;
        if (u <= kink) return 2e16 + u * 10e16 / 1e18;
        return 10e16 + (u - kink) * 90e16 / 1e18;
    }
    // 利用率80%以下: 2%~10%利率
    // 利用率80%以上: 10%~100%利率(陡增!)
    // 目的: 鼓励存款,防止流动性耗尽
}
// ✅ 验证通过
🏦

🏆 成就解锁:DeFi银行家

你已掌握DeFi借贷协议!从超额抵押到利率模型,从清算机制到闪电贷,你理解了去中心化金融的信贷体系。

关键收获:

✅ 超额抵押与清算机制
✅ 分段线性利率模型
✅ 借贷合约完整实现
✅ 闪电贷原理与套利应用
✅ 健康因子与风险监控

📋 课程目录