Skip to content

代币借贷系统

1. 系统概述

代币借贷系统是一个基于 Solidity 实现的去中心化借贷平台,支持多种代币的存借和利息计算。系统实现了灵活的利率模型和完善的风险控制机制。

1.1 主要特点

  • 多币种支持:支持多种代币的存借
  • 灵活利率:动态利率模型
  • 超额抵押:安全的抵押机制
  • 清算机制:自动化清算流程
  • 风险控制:完善的安全措施

2. 合约实现

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

/**
 * @title TokenLending
 * @dev 代币借贷合约
 */
contract TokenLending is Ownable, ReentrancyGuard {
    using SafeMath for uint256;

    // 市场信息
    struct Market {
        bool isListed;             // 是否已上市
        uint256 totalSupply;       // 总供应量
        uint256 totalBorrows;      // 总借款量
        uint256 supplyRate;        // 存款利率
        uint256 borrowRate;        // 借款利率
        uint256 lastUpdateTime;    // 最后更新时间
        uint256 reserveFactor;     // 储备金率
        uint256 collateralFactor;  // 抵押率
    }

    // 用户存款信息
    struct SupplyInfo {
        uint256 balance;           // 存款余额
        uint256 interestIndex;     // 利息指数
    }

    // 用户借款信息
    struct BorrowInfo {
        uint256 balance;           // 借款余额
        uint256 interestIndex;     // 利息指数
        uint256 lastUpdateTime;    // 最后更新时间
    }

    // 状态变量
    mapping(address => Market) public markets;                    // 市场信息
    mapping(address => mapping(address => SupplyInfo)) public supplyInfo;    // 用户存款信息
    mapping(address => mapping(address => BorrowInfo)) public borrowInfo;    // 用户借款信息
    mapping(address => bool) public isMarketListed;              // 市场列表
    address[] public marketList;                                 // 市场列表数组

    // 常量
    uint256 public constant PRECISION = 1e18;                    // 精度
    uint256 public constant LIQUIDATION_DISCOUNT = 95e16;        // 清算折扣(95%)
    uint256 public constant MIN_COLLATERAL_RATIO = 125e16;       // 最小抵押率(125%)

    // 事件
    event MarketListed(address token);
    event Supply(address indexed token, address indexed user, uint256 amount);
    event Withdraw(address indexed token, address indexed user, uint256 amount);
    event Borrow(address indexed token, address indexed user, uint256 amount);
    event Repay(address indexed token, address indexed user, uint256 amount);
    event Liquidate(
        address indexed liquidator,
        address indexed borrower,
        address indexed repayToken,
        address collateralToken,
        uint256 repayAmount,
        uint256 collateralAmount
    );

    /**
     * @dev 构造函数
     */
    constructor() {}

    /**
     * @dev 添加市场
     */
    function listMarket(
        address token,
        uint256 _collateralFactor,
        uint256 _reserveFactor
    ) external onlyOwner {
        require(!isMarketListed[token], "Market already listed");
        require(_collateralFactor <= PRECISION, "Invalid collateral factor");
        require(_reserveFactor <= PRECISION, "Invalid reserve factor");

        markets[token] = Market({
            isListed: true,
            totalSupply: 0,
            totalBorrows: 0,
            supplyRate: 0,
            borrowRate: 0,
            lastUpdateTime: block.timestamp,
            reserveFactor: _reserveFactor,
            collateralFactor: _collateralFactor
        });

        isMarketListed[token] = true;
        marketList.push(token);
        emit MarketListed(token);
    }

    /**
     * @dev 存款
     */
    function supply(address token, uint256 amount) external nonReentrant {
        require(isMarketListed[token], "Market not listed");
        require(amount > 0, "Amount must be greater than 0");

        Market storage market = markets[token];
        SupplyInfo storage info = supplyInfo[token][msg.sender];

        // 更新市场
        updateMarket(token);

        // 转入代币
        IERC20(token).transferFrom(msg.sender, address(this), amount);

        // 更新存款信息
        info.balance = info.balance.add(amount);
        market.totalSupply = market.totalSupply.add(amount);

        emit Supply(token, msg.sender, amount);
    }

    /**
     * @dev 提款
     */
    function withdraw(address token, uint256 amount) external nonReentrant {
        require(isMarketListed[token], "Market not listed");
        
        Market storage market = markets[token];
        SupplyInfo storage info = supplyInfo[token][msg.sender];
        require(info.balance >= amount, "Insufficient balance");

        // 检查提款后的健康度
        require(getAccountHealth(msg.sender) >= MIN_COLLATERAL_RATIO, "Unhealthy position");

        // 更新市场
        updateMarket(token);

        // 更新存款信息
        info.balance = info.balance.sub(amount);
        market.totalSupply = market.totalSupply.sub(amount);

        // 转出代币
        IERC20(token).transfer(msg.sender, amount);

        emit Withdraw(token, msg.sender, amount);
    }

    /**
     * @dev 借款
     */
    function borrow(address token, uint256 amount) external nonReentrant {
        require(isMarketListed[token], "Market not listed");
        require(amount > 0, "Amount must be greater than 0");

        Market storage market = markets[token];
        BorrowInfo storage info = borrowInfo[token][msg.sender];

        // 更新市场
        updateMarket(token);

        // 检查借款后的健康度
        uint256 newBorrowBalance = info.balance.add(amount);
        require(
            getAccountHealthWithBorrow(msg.sender, token, newBorrowBalance) >= MIN_COLLATERAL_RATIO,
            "Insufficient collateral"
        );

        // 更新借款信息
        info.balance = newBorrowBalance;
        info.lastUpdateTime = block.timestamp;
        market.totalBorrows = market.totalBorrows.add(amount);

        // 转出代币
        IERC20(token).transfer(msg.sender, amount);

        emit Borrow(token, msg.sender, amount);
    }

    /**
     * @dev 还款
     */
    function repay(address token, uint256 amount) external nonReentrant {
        require(isMarketListed[token], "Market not listed");
        
        Market storage market = markets[token];
        BorrowInfo storage info = borrowInfo[token][msg.sender];
        require(info.balance > 0, "No borrow balance");

        // 更新市场
        updateMarket(token);

        // 计算实际还款金额
        uint256 repayAmount = amount;
        if (repayAmount > info.balance) {
            repayAmount = info.balance;
        }

        // 转入代币
        IERC20(token).transferFrom(msg.sender, address(this), repayAmount);

        // 更新借款信息
        info.balance = info.balance.sub(repayAmount);
        info.lastUpdateTime = block.timestamp;
        market.totalBorrows = market.totalBorrows.sub(repayAmount);

        emit Repay(token, msg.sender, repayAmount);
    }

    /**
     * @dev 清算
     */
    function liquidate(
        address borrower,
        address repayToken,
        address collateralToken,
        uint256 repayAmount
    ) external nonReentrant {
        require(isMarketListed[repayToken], "Repay token not listed");
        require(isMarketListed[collateralToken], "Collateral token not listed");
        require(borrower != msg.sender, "Cannot liquidate self");
        require(repayAmount > 0, "Amount must be greater than 0");

        // 检查账户健康度
        require(getAccountHealth(borrower) < MIN_COLLATERAL_RATIO, "Account is healthy");

        // 更新市场
        updateMarket(repayToken);
        updateMarket(collateralToken);

        // 获取借款信息
        BorrowInfo storage borrowInfo = borrowInfo[repayToken][borrower];
        require(borrowInfo.balance >= repayAmount, "Repay amount too high");

        // 计算可获得的抵押品数量
        uint256 collateralAmount = repayAmount
            .mul(PRECISION)
            .div(LIQUIDATION_DISCOUNT)
            .mul(getPrice(repayToken))
            .div(getPrice(collateralToken));

        // 转入还款代币
        IERC20(repayToken).transferFrom(msg.sender, address(this), repayAmount);

        // 更新借款信息
        borrowInfo.balance = borrowInfo.balance.sub(repayAmount);
        borrowInfo.lastUpdateTime = block.timestamp;
        markets[repayToken].totalBorrows = markets[repayToken].totalBorrows.sub(repayAmount);

        // 更新抵押信息
        SupplyInfo storage supplyInfo = supplyInfo[collateralToken][borrower];
        supplyInfo.balance = supplyInfo.balance.sub(collateralAmount);
        markets[collateralToken].totalSupply = markets[collateralToken].totalSupply.sub(collateralAmount);

        // 转出抵押品
        IERC20(collateralToken).transfer(msg.sender, collateralAmount);

        emit Liquidate(
            msg.sender,
            borrower,
            repayToken,
            collateralToken,
            repayAmount,
            collateralAmount
        );
    }

    /**
     * @dev 更新市场
     */
    function updateMarket(address token) internal {
        Market storage market = markets[token];
        
        // 计算时间间隔
        uint256 timeElapsed = block.timestamp.sub(market.lastUpdateTime);
        if (timeElapsed == 0) return;

        // 更新利率
        market.supplyRate = getSupplyRate(token);
        market.borrowRate = getBorrowRate(token);
        market.lastUpdateTime = block.timestamp;
    }

    /**
     * @dev 获取存款利率
     */
    function getSupplyRate(address token) public view returns (uint256) {
        Market storage market = markets[token];
        uint256 utilizationRate = getUtilizationRate(token);
        return utilizationRate.mul(market.borrowRate).mul(PRECISION.sub(market.reserveFactor)).div(PRECISION).div(PRECISION);
    }

    /**
     * @dev 获取借款利率
     */
    function getBorrowRate(address token) public view returns (uint256) {
        uint256 utilizationRate = getUtilizationRate(token);
        return utilizationRate.mul(10e16); // 基础利率 + 利用率 * 斜率
    }

    /**
     * @dev 获取资金利用率
     */
    function getUtilizationRate(address token) public view returns (uint256) {
        Market storage market = markets[token];
        if (market.totalSupply == 0) return 0;
        return market.totalBorrows.mul(PRECISION).div(market.totalSupply);
    }

    /**
     * @dev 获取账户健康度
     */
    function getAccountHealth(address account) public view returns (uint256) {
        uint256 totalCollateralValue = 0;
        uint256 totalBorrowValue = 0;

        // 计算总抵押价值
        for (uint256 i = 0; i < marketList.length; i++) {
            address token = marketList[i];
            uint256 supplyBalance = supplyInfo[token][account].balance;
            if (supplyBalance > 0) {
                totalCollateralValue = totalCollateralValue.add(
                    supplyBalance.mul(getPrice(token)).mul(markets[token].collateralFactor).div(PRECISION)
                );
            }
        }

        // 计算总借款价值
        for (uint256 i = 0; i < marketList.length; i++) {
            address token = marketList[i];
            uint256 borrowBalance = borrowInfo[token][account].balance;
            if (borrowBalance > 0) {
                totalBorrowValue = totalBorrowValue.add(
                    borrowBalance.mul(getPrice(token))
                );
            }
        }

        if (totalBorrowValue == 0) return type(uint256).max;
        return totalCollateralValue.mul(PRECISION).div(totalBorrowValue);
    }

    /**
     * @dev 获取账户在新增借款后的健康度
     */
    function getAccountHealthWithBorrow(
        address account,
        address borrowToken,
        uint256 borrowBalance
    ) internal view returns (uint256) {
        uint256 totalCollateralValue = 0;
        uint256 totalBorrowValue = 0;

        // 计算总抵押价值
        for (uint256 i = 0; i < marketList.length; i++) {
            address token = marketList[i];
            uint256 supplyBalance = supplyInfo[token][account].balance;
            if (supplyBalance > 0) {
                totalCollateralValue = totalCollateralValue.add(
                    supplyBalance.mul(getPrice(token)).mul(markets[token].collateralFactor).div(PRECISION)
                );
            }
        }

        // 计算总借款价值
        for (uint256 i = 0; i < marketList.length; i++) {
            address token = marketList[i];
            uint256 balance = token == borrowToken ? borrowBalance : borrowInfo[token][account].balance;
            if (balance > 0) {
                totalBorrowValue = totalBorrowValue.add(
                    balance.mul(getPrice(token))
                );
            }
        }

        if (totalBorrowValue == 0) return type(uint256).max;
        return totalCollateralValue.mul(PRECISION).div(totalBorrowValue);
    }

    /**
     * @dev 获取代币价格(从预言机获取)
     */
    function getPrice(address token) internal view returns (uint256) {
        // 这里应该对接预言机
        return PRECISION;
    }

    /**
     * @dev 获取市场信息
     */
    function getMarketInfo(address token) external view returns (
        bool isListed,
        uint256 totalSupply,
        uint256 totalBorrows,
        uint256 supplyRate,
        uint256 borrowRate,
        uint256 lastUpdateTime,
        uint256 reserveFactor,
        uint256 collateralFactor
    ) {
        Market storage market = markets[token];
        return (
            market.isListed,
            market.totalSupply,
            market.totalBorrows,
            market.supplyRate,
            market.borrowRate,
            market.lastUpdateTime,
            market.reserveFactor,
            market.collateralFactor
        );
    }

    /**
     * @dev 获取用户存款信息
     */
    function getUserSupplyInfo(address token, address user) external view returns (
        uint256 balance,
        uint256 interestIndex
    ) {
        SupplyInfo storage info = supplyInfo[token][user];
        return (info.balance, info.interestIndex);
    }

    /**
     * @dev 获取用户借款信息
     */
    function getUserBorrowInfo(address token, address user) external view returns (
        uint256 balance,
        uint256 interestIndex,
        uint256 lastUpdateTime
    ) {
        BorrowInfo storage info = borrowInfo[token][user];
        return (info.balance, info.interestIndex, info.lastUpdateTime);
    }
}

3. 功能说明

3.1 市场管理

  • 市场上线
  • 利率更新
  • 风险参数设置

3.2 存款功能

  • 存入代币
  • 提取代币
  • 利息计算

3.3 借款功能

  • 借入代币
  • 归还代币
  • 利息计算

3.4 清算功能

  • 清算触发
  • 清算奖励
  • 风��控制

4. 安全机制

4.1 风险控制

  • 最小抵押率
  • 清算折扣
  • 储备金率

4.2 访问控制

  • 权限管理
  • 重入保护
  • 参数验证

4.3 状态管理

  • 市场状态
  • 用户状态
  • 利率更新

5. 使用示例

5.1 存款

javascript
const amount = ethers.utils.parseEther("100");
await token.approve(lending.address, amount);
await lending.supply(token.address, amount);

5.2 借款

javascript
const amount = ethers.utils.parseEther("50");
await lending.borrow(token.address, amount);

5.3 还款

javascript
const amount = ethers.utils.parseEther("50");
await token.approve(lending.address, amount);
await lending.repay(token.address, amount);

6. 总结

该代币借贷系统实现了完整的借贷功能,包括:

  • 多币种支持
  • 灵活的利率模型
  • 安全的抵押机制
  • 自动化清算流程
  • 完善的风险控制

系统通过精心设计的利率模型和风险控制机制,确保了借贷过程的安全性和可靠性。

常见问题解答(FAQ)

1. 基本概念

Q: 什么是代币借贷? A: 代币借贷是一种DeFi服务,允许用户:

  • 存入代币获得利息
  • 抵押资产进行借贷
  • 自动计算利息
  • 管理借贷风险
  • 提供流动性激励

Q: 借贷协议的主要组成部分是什么? A: 主要包括:

  • 存款池管理
  • 借贷逻辑
  • 利率模型
  • 清算机制
  • 风险控制

2. 功能相关

Q: 如何计算借贷利率? A: 利率计算考虑:

solidity
function calculateInterestRate(uint256 utilization) public pure returns (uint256) {
    if(utilization < OPTIMAL_UTILIZATION) {
        return baseRate + (utilization * rateSlope1) / UTILIZATION_PRECISION;
    } else {
        return baseRate + rateSlope1 + 
            ((utilization - OPTIMAL_UTILIZATION) * rateSlope2) / UTILIZATION_PRECISION;
    }
}

Q: 清算机制如何工作? A: 清算流程包括:

  • 健康因子监控
  • 清算阈值判断
  • 清算人激励
  • 债务结算
  • 抵押品处理

3. 安全相关

Q: 如何防止清算套利? A: 防护措施:

  • 动态清算阈值
  • 清算奖励上限
  • 最小清算规模
  • 冷却期设置
  • 价格操纵防护

Q: 如何保护用户资产? A: 安全机制:

  • 抵押率控制
  • 紧急暂停
  • 风险参数调整
  • 价格预言机- 多重签名

4. 优化相关

Q: 如何优化Gas成本? A: 优化策略:

  • 批量处理
  • 存储优化
  • 计算简化
  • 事件替代存储
  • 缓存中间结果

Q: 如何提高资金利用率? A: 改进方案:

  • 利率模型优化
  • 资金池效率
  • 借贷限额调整
  • 激励机制设计
  • 市场策略优化

5. 实现细节

Q: 如何实现利率更新? A: 实现方法:

solidity
function updateInterest() internal {
    uint256 timeElapsed = block.timestamp - lastUpdateTime;
    if(timeElapsed > 0) {
        uint256 utilization = calculateUtilization();
        uint256 rate = calculateInterestRate(utilization);
        accumulatedRate += rate * timeElapsed;
        lastUpdateTime = block.timestamp;
    }
}

Q: 如何处理坏账? A: 处理机制:

  • 风险准备金
  • 社区基金补偿
  • 债务拍卖
  • 损失分摊
  • 保险机制

6. 最佳实践

Q: 如何设置风险参数? A: 参数考虑:

  • 市场波动性
  • 资产流动性
  • 历史数据分析
  • 风险承受能力
  • 竞争对手参考

Q: 如何提高协议效率? A: 优化方向:

  • 自动化管理
  • 智能定价
  • 资金池优化
  • 激励机制
  • 用户体验

7. 错误处理

Q: 常见错误及解决方案? A: 错误类型:

  • "Insufficient collateral": 检查抵押率
  • "Borrow limit exceeded": 验证借贷限额
  • "Invalid liquidation": 确认清算条件
  • "Oracle error": 使用备用价格源
  • "System paused": 等待系统恢复

Q: 如何处理紧急情况? A: 应急措施:

  • 暂停功能
  • 限制提款
  • 调整参数
  • 社区治理
  • 技术支持

8. 升级维护

Q: 如何升级借贷协议? A: 升级策略:

  • 可升级合约
  • 渐进式更新
  • 向后兼容
  • 充分测试
  • 社区投票

Q: 如何监控系统健康? A: 监控方案:

  • 健康因子追踪
  • 资金流向分析
  • 风险指标监控
  • 市场数据分析
  • 用户行为分析

9. 与其他模块集成

Q: 如何与其他DeFi协议集成? A: 集成方案:

  • 标准接口实现
  • 流动性共享
  • 风险数据共享
  • 跨协议激励
  • 统一的用户体验

Q: 如何实现跨链借贷? A: 实现策略:

  • 跨链桥接
  • 统一风控
  • 资产映射
  • 状态同步
  • 清算协调

Released under the MIT License by Vogeb.