Skip to content

🔗 去中心化借贷协议:流程、收益与技术实现

1 ⛓️ 去中心化借贷协议概述

与传统金融不同,DeFi 借贷是点对合约的,无需中介。用户直接与智能合约交互,存入资产以赚取利息,或提供超额抵押品以借出资产。其核心优势在于:

  • 无许可性:任何人、任何地方均可参与。
  • 透明性:所有交易和利率在链上公开可查。
  • 可组合性:可作为其他 DeFi 应用(如杠杆交易、收益农场)的基础乐高积木。

2 🔄 核心业务流程

一个完整的借贷流程涉及多方参与和复杂的链上交互,其核心业务逻辑与资金流转如下图所示:

借贷

2.1 存款流程

  1. 用户授权:用户首先授权借贷合约(如 LendingPool)操作其代币(如 USDC)。这是一笔独立的交易。
  2. 执行存款:用户调用合约的 deposit() 方法。合约将代币从用户钱包转移至合约本身(资金池),并铸造计息凭证代币(Interest Bearing Token) 给用户,如cUSDCaUSDC
  3. 凭证代币:这些凭证代币是用户的本金和应计利息的所有权证明。它们本身是 ERC-20 代币,可以转移、交易,甚至用于其他 DeFi 协议(可组合性)。
  4. 开始计息:从此刻起,用户的存款开始根据资金池的当前利率累积利息。其凭证代币的余额会随时间增加(或单位凭证代表的底层资产增加)。

2.2 借款流程

  1. 超额抵押:借款人必须首先存入某种抵押资产(如 ETH)。借款额度由抵押物的价值和该资产的贷款价值比(LTV) 决定。例如,存入价值$100 的 ETH(LTV=75%),最多可借出$75 的其他资产。
  2. 执行借款:借款人调用 borrow() 方法,指定借款资产和数量。合约会检查借款人的健康因子是否安全。
  3. 健康因子(Health Factor):这是一个数值,代表抵押资产的价值与借款资产价值的比率。如果该值低于 1,借款人的头寸将被清算健康因子 = (抵押物价值 * 清算阈值) / 借款价值
  4. 支付利息:借款利息通常是浮动的,根据资产的需求实时变化。利息从借款人的未偿还债务中累积。

2.3 清算流程

当抵押物价值下跌或借款价值上涨导致健康因子低于 1时,任何清算人都可以调用 liquidate() 函数。

  • 清算人以折扣价购买抵押资产(如以$95 的价格购买价值$100 的抵押 ETH),差价作为利润。
  • 借款人的部分或全部债务被偿还,协议通常会对被清算的借款人收取一笔额外的清算罚金,一部分给协议,一部分给清算人。

3 💰 收益与佣金分配机制

3.1 用户的收益(存款人)

存款人的收益来源相对直接,主要来自于借款人支付的利息。

  • 存款利息:收益来自于借款人支付的利息。利率由资金利用率(U) 动态决定:U = 总借款 / 总存款
    • 利用率越低,存款利率越低(资金供给过剩)。
    • 利用率越高,存款利率越高(资金需求旺盛)。
  • 计算方式:你的收益体现在你持有的计息凭证代币的增值上。你可以通过比较 balanceOfUnderlying(yourAddress) 在你存入时和现在的差值来计算具体收益。

3.2 平台的收益

协议的收益(佣金)并非直接收取,而是通过精巧的利率模型从存贷利差中产生。

  • 利差(Spread):这是平台最核心的收入来源。协议设定的借款利率 > 存款利率,其中的差价即为协议收入。例如,借款利率为 5%,存款利率为 4%,则利差为 1%。
  • 清算罚金分成:当发生清算时,借款人需要支付一笔额外的罚金(例如,抵押物价值的 10%)。这笔罚金通常大部分给清算人(如 7.5%)作为激励,剩余部分则归协议国库(如 2.5%)。
  • 其他费用:部分协议可能会对提现或特定交易收取少量固定费用。

3.3 佣金分配案例

假设协议在某段时间内:

  • 总存款:1000 USDC
  • 总借款:600 USDC
  • 资金利用率 (U):60%
  • 存款利率:3% (根据 U 计算得出)
  • 借款利率:4% (根据 U 计算得出)
  • 利差:1%

平台收益计算: 平台佣金 = 总借款 _ 利差 = 600 USDC _ 1% = 6 USDC

这 6 USDC 会累积到协议的国库地址(Treasury)。协议治理可以决定如何使用这些资金(如分配给代币质押者、用于开发、回购销毁代币等)。

4 ⚙️ 技术实现:前后端与智能合约

4.1 智能合约开发

智能合约是协议的核心,其设计至关重要。通常采用经过审计的、模块化的架构。

4.1.1 核心合约结构

solidity
// 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/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

// 计息凭证代币合约
contract cToken is ERC20, Ownable, ReentrancyGuard {
    using SafeERC20 for IERC20;

    IERC20 public immutable underlyingAsset;
    uint256 public exchangeRate; // 1 cToken = X underlying tokens

    function mint(address to, uint256 amount) external onlyOwner {
        _mint(to, amount);
        // 更新汇率逻辑...
    }

    function burn(address from, uint256 amount) external onlyOwner {
        _burn(from, amount);
        // 更新汇率逻辑...
    }
}

// 核心资金池合约
contract LendingPool is ReentrancyGuard, Ownable {
    using SafeERC20 for IERC20;

    struct Market {
        uint256 totalBorrows;
        uint256 totalSupplies;
        uint256 borrowRate;
        uint256 supplyRate;
        bool isActive;
        cToken cTokenAddress;
    }

    mapping(address => Market) public markets;
    mapping(address => mapping(address => uint256)) public userBorrows; // user -> asset -> amount
    mapping(address => mapping(address => uint256)) public userCollateral; // user -> asset -> amount

    address public oracle; // 价格预言机地址

    event Deposited(address indexed user, address indexed asset, uint256 amount);
    event Borrowed(address indexed user, address indexed asset, uint256 amount);

    constructor(address _oracle) {
        oracle = _oracle;
    }

    function deposit(address _asset, uint256 _amount) external nonReentrant {
        Market storage market = markets[_asset];
        require(market.isActive, "Market not active");

        IERC20(_asset).safeTransferFrom(msg.sender, address(this), _amount);
        // 根据当前汇率计算应铸造的cToken数量
        uint256 cTokensToMint = (_amount * 1e18) / market.exchangeRate;
        market.cTokenAddress.mint(msg.sender, cTokensToMint);

        market.totalSupplies += _amount;
        updateInterestRates(_asset);

        emit Deposited(msg.sender, _asset, _amount);
    }

    function borrow(address _asset, uint256 _amount) external nonReentrant {
        Market storage market = markets[_asset];
        require(market.isActive, "Market not active");
        // 检查健康因子是否安全
        require(_calculateHealthFactor(msg.sender) > 1e18, "Health factor too low");

        // 从资金池中转出资产给借款人
        IERC20(_asset).safeTransfer(msg.sender, _amount);
        userBorrows[msg.sender][_asset] += _amount;
        market.totalBorrows += _amount;
        updateInterestRates(_asset);

        emit Borrowed(msg.sender, _asset, _amount);
    }

    function _calculateHealthFactor(address _user) internal view returns (uint256) {
        // 1. 通过预言机获取用户所有抵押物的总价值(USD)
        // 2. 通过预言机获取用户所有借款的总价值(USD)
        // 3. 应用清算阈值
        // 4. 返回: (总抵押价值 * 清算阈值) / 总借款价值
        // 简化实现
        return 2e18; // 返回一个假设的安全值
    }

    function updateInterestRates(address _asset) internal {
        Market storage market = markets[_asset];
        uint256 utilizationRate = (market.totalBorrows * 1e18) / market.totalSupplies;

        // 一个非常简化的利率模型示例
        if (utilizationRate > 0.8e18) {
            market.borrowRate = 0.05e18; // 5%
            market.supplyRate = 0.04e18; // 4% -> 利差1%
        } else {
            market.borrowRate = 0.03e18; // 3%
            market.supplyRate = 0.025e18; // 2.5% -> 利差0.5%
        }
    }

    // 管理员函数:添加新市场
    function addMarket(address _asset, address _cToken) external onlyOwner {
        markets[_asset] = Market({
            totalBorrows: 0,
            totalSupplies: 0,
            borrowRate: 0,
            supplyRate: 0,
            isActive: true,
            cTokenAddress: cToken(_cToken)
        });
    }
}

4.1.2 安全考量

  • 价格预言机绝对不要使用单一来源或可操纵的价格。必须使用去中心化的预言机网络,如 Chainlink,并具有防闪电贷攻击的保护(例如,使用最近一次的价格而非实时价格)。
  • 重入攻击:使用 ReentrancyGuard 修饰所有外部函数,尤其是在转移代币之前更新状态。
  • 数学计算:使用 Solidity 0.8.x 的内置安全数学,或 OpenZeppelin 的 SafeMath 库(对于旧版本)。
  • 利率模型:确保利率计算不会导致溢出或被恶意操纵。
  • 清算激励:清算系统必须设计得当,确保有足够的激励让清算人及时清算,同时避免过度惩罚借款人。

4.2 后端开发

后端主要负责索引链上数据并提供易于查询的 API。

  • 技术栈:Node.js + Express/NestJS, Python + FastAPI, 或 Go。数据库常用 PostgreSQL 或 MongoDB。
  • 核心任务
    • 索引事件:使用ethers.js/web3.py监听并索引合约中的DepositedBorrowedLiquidated等事件,将其存入数据库。
    • 计算聚合数据:计算并缓存全局数据,如总锁仓量(TVL)、各市场利率、平台总收入等。这些数据在链上计算可能非常昂贵。
    • 提供 API
      • GET /markets: 返回所有市场详情(存款量、借款量、利率等)。
      • GET /users/:address: 返回用户头寸详情(存款、借款、健康因子)。
    • 监控健康因子:可以运行后台任务监控所有用户的风险指标,并为接近清算的用户提供预警(可选)。

4.3 前端开发

前端是用户交互的窗口,需要清晰展示复杂信息并确保交易安全。

  • 技术栈:React(主流)或 Vue.js,搭配 Ethers.js 或 Viem(新一代 Web3 库)。
  • 核心功能
    • 钱包集成:无缝连接 MetaMask、WalletConnect 等,获取用户地址和余额。
    • 数据展示:从后端 API 和链上合约获取数据,展示市场列表、用户头寸、资产价格、健康因子等。
    • 合约交互:构建交易以调用depositborrowrepay等方法。清晰显示 Gas 费预估。
    • 交易状态反馈:提供交易状态(确认中、成功/失败)和区块链浏览器链接。
    • 风险管理:在用户进行高风险操作(如借款导致健康因子过低)时给出强烈警告。