Compound协议(2)

文章目录

  • [3. Comptroller - Compound的⻛控中枢](#3. Comptroller - Compound的⻛控中枢)
    • [3.1 Comptroller核⼼职责](#3.1 Comptroller核⼼职责)
    • [3.2 账户流动性计算](#3.2 账户流动性计算)
  • [4. 利率模型详解](#4. 利率模型详解)
    • [4.1 JumpRateModel - 跳跃利率模型](#4.1 JumpRateModel - 跳跃利率模型)
      • [4.1.1 利率模型原理](#4.1.1 利率模型原理)
      • [4.1.2 利率曲线可视化](#4.1.2 利率曲线可视化)
      • [4.1.3 利率模型合约实现](#4.1.3 利率模型合约实现)
    • [4.2 利率实时更新机制](#4.2 利率实时更新机制)
  • [5. Compound V3 (Comet) - 新⼀代架构](#5. Compound V3 (Comet) - 新⼀代架构)
    • [5.1 为什么需要V3?](#5.1 为什么需要V3?)
    • [5.2 Comet核⼼设计](#5.2 Comet核⼼设计)
      • [5.2.1 V2 vs V3 架构对⽐](#5.2.1 V2 vs V3 架构对⽐)
      • [5.2.2 Comet核⼼合约](#5.2.2 Comet核⼼合约)
    • [5.3 Comet的优势](#5.3 Comet的优势)

3. Comptroller - Compound的⻛控中枢

3.1 Comptroller核⼼职责

Comptroller是Compound协议的"⼤脑",负责所有⻛险控制和市场管理功能。它的核⼼职责包括:

  1. 市场准⼊控制:决定哪些资产可以作为抵押品
  2. 借款额度计算:实时计算⽤户的借款能⼒
  3. 清算判定:判断账户是否可以被清算
  4. ⻛险参数管理:设置和更新各种⻛险参数
  5. COMP奖励分发:管理流动性挖矿奖励

3.2 账户流动性计算

流动性计算是Comptroller最核⼼的功能,决定了⽤户能否借款、是否会被清算。

c 复制代码
// Comptroller.sol - 账户流动性计算
contract Comptroller {
	/**
	* @notice 计算账户流动性
	* @param account 账户地址
	* @return (error, liquidity, shortfall)
	*/
	function getAccountLiquidity(address account) 
	public
	view
	returns (uint, uint, uint) 
	 {
		// 获取账户在所有市场的资产和负债
		 (uint err, uint liquidity, uint shortfall) =
		getHypotheticalAccountLiquidityInternal(
			account,
			CToken(address(0)),
			0,
			0
		 );
		return (err, liquidity, shortfall);
	 }
	/**
	* @notice 内部流动性计算函数
	* @dev 核⼼算法:遍历所有市场,累加抵押品和债务
	*/
	function getHypotheticalAccountLiquidityInternal(
		address account,
		CToken cTokenModify,
		uint redeemTokens,
		uint borrowAmount
	 ) internal view returns (uint, uint, uint) {
		// 累加变量
		uint sumCollateral = 0; // 总抵押品价值(加权)
		uint sumBorrowPlusEffects = 0; // 总债务价值
		// 遍历⽤户参与的所有市场
		CToken[] memory assets = accountAssets[account];
		for (uint i = 0; i < assets.length; i++) {
			CToken asset = assets[i];
			// 获取⽤户在该市场的cToken余额
			uint cTokenBalance = asset.balanceOf(account);
			// 获取汇率和价格
			uint exchangeRate = asset.exchangeRateStored();
			uint oraclePrice = oracle.getUnderlyingPrice(asset);
			// 获取市场配置
			 (, uint collateralFactorMantissa) = markets[address(asset)];
			// 计算该资产的抵押品价值
			// tokenValue = cTokenBalance × exchangeRate × price
			uint tokensToDenom = (cTokenBalance * exchangeRate) / 1e18;
			uint collateralValue = (tokensToDenom * oraclePrice) / 1e18;
			// 如果该资产启⽤了抵押,累加到总抵押品
			if (collateralFactorMantissa > 0) {
				sumCollateral += (collateralValue * collateralFactorMantissa) / 1e18;
			 }
			// 计算该资产的借款价值
			uint borrowBalance = asset.borrowBalanceStored(account);
			uint borrowValue = (borrowBalance * oraclePrice) / 1e18;
			sumBorrowPlusEffects += borrowValue;
			// 如果是假设性操作,调整计算
			if (asset == cTokenModify) {
				// 假设赎回
				if (redeemTokens > 0) {
					uint redeemValue = (redeemTokens * exchangeRate * oraclePrice) / (1e18 * 1e18);
					sumCollateral -= (redeemValue * collateralFactorMantissa) / 1e18;
				 }
			   // 假设借款
				if (borrowAmount > 0) {
					uint borrowValue = (borrowAmount * oraclePrice) / 1e18;
					sumBorrowPlusEffects += borrowValue;
				 }
			 }
		 }
		// 计算流动性和资⾦缺⼝
		if (sumCollateral > sumBorrowPlusEffects) {
			return (0, sumCollateral - sumBorrowPlusEffects, 0);
		 } else {
			return (0, 0, sumBorrowPlusEffects - sumCollateral);
		 }
	 }
	/**
	* @notice 检查是否允许借款
	*/
	function borrowAllowed(
		address cToken,
		address borrower,
		uint borrowAmount
	 ) external returns (uint) {
		// 1. 检查市场是否上线
		if (!markets[cToken].isListed) {
			return uint(Error.MARKET_NOT_LISTED);
		 }
		// 2. 检查借款上限
		if (!markets[cToken].accountMembership[borrower]) {
			// 如果⽤户⾸次借款,⾃动加⼊市场
			markets[cToken].accountMembership[borrower] = true;
			accountAssets[borrower].push(CToken(cToken));
		 }
		// 3. 计算假设性的账户流动性
		 (uint err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(
			borrower,
			CToken(cToken),
			0,
			borrowAmount
		 );
		if (err != 0) {
			return err;
		 }
		// 如果会产⽣资⾦缺⼝,拒绝借款
		if (shortfall > 0) {
			return uint(Error.INSUFFICIENT_LIQUIDITY);
		 }
		return uint(Error.NO_ERROR);
	 }
	/**
	* @notice 检查是否允许赎回
	*/
	function redeemAllowed(
		address cToken,
		address redeemer,
		uint redeemTokens
	 ) external returns (uint) {
	// 检查赎回后是否会产⽣资⾦缺⼝
	 (uint err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(
		redeemer,
		CToken(cToken),
		redeemTokens,
		0
	 );
	 if (err != 0) {
		return err;
	 }
	 if (shortfall > 0) {
		return uint(Error.INSUFFICIENT_LIQUIDITY);
	 }
	 return uint(Error.NO_ERROR);
	 }
	}

3.3 ⻛险参数配置

主要⻛险参数:

不同资产的⻛险参数对⽐:

4. 利率模型详解

4.1 JumpRateModel - 跳跃利率模型

Compound采⽤JumpRateModel(跳跃利率模型),这是⼀种分段线性的利率曲线,在达到最优利⽤率前线性增⻓,超过后陡峭上升。

4.1.1 利率模型原理

核⼼思想:

通过利率调节市场供需,保护资⾦池流动性:

  • 利⽤率低时:低利率吸引借款
  • 利⽤率适中时:线性增⻓
  • 利⽤率过⾼时:利率跳跃,抑制借款

利率计算公式:

利⽤率 U = Borrows / (Cash + Borrows - Reserves)

借款利率:

  • 当 U ≤ Kink: BorrowRate = baseRate + multiplier × U
  • 当 U > Kink: BorrowRate = baseRate + multiplier × Kink + jumpMultiplier × (U - Kink)
    存款利率:

SupplyRate = BorrowRate × U × (1 - ReserveFactor)

参数说明:

  • baseRate :基础利率(通常为0或很⼩的值)
  • multiplier :正常斜率系数
  • jumpMultiplier :跳跃斜率系数(远⼤于multiplier)
  • Kink :拐点,通常设置为80%

4.1.2 利率曲线可视化

USDC利率曲线示例:

参数配置:

  • baseRatePerYear: 0%
  • multiplierPerYear: 5%
  • jumpMultiplierPerYear: 109%
  • kink: 80%
    利率计算:

U = 50%(正常区间)

borrowAPY = 0% + 5% × (50%/80%) = 3.125%

supplyAPY = 3.125% × 50% × 90% = 1.41%
U = 80%(拐点)

borrowAPY = 0% + 5% × 1 = 5%

supplyAPY = 5% × 80% × 90% = 3.6%
U = 90%(⾼利⽤率)

borrowAPY = 0% + 5% + 109% × (10%/20%) = 59.5%

supplyAPY = 59.5% × 90% × 90% = 48.2%
U = 95%(接近枯竭)

borrowAPY = 0% + 5% + 109% × (15%/20%) = 86.75%

supplyAPY = 86.75% × 95% × 90% = 74.1%

4.1.3 利率模型合约实现

c 复制代码
// JumpRateModel.sol
pragma solidity ^0.8.10;
/**
* @title JumpRateModel
* @notice 实现分段线性利率模型
*/
contract JumpRateModel {
	// ⼀年的区块数(假设12秒⼀个区块)
	uint public constant blocksPerYear = 2628000;
	// 基础利率(每区块)
	uint public baseRatePerBlock;
	// 正常斜率乘数(每区块)
	uint public multiplierPerBlock;
	// 跳跃斜率乘数(每区块)
	uint public jumpMultiplierPerBlock;
	// 拐点(利⽤率阈值)
	uint public kink;
	/**
	* @notice 构造函数
	* @param baseRatePerYear 年化基础利率
	* @param multiplierPerYear 年化正常斜率
	* @param jumpMultiplierPerYear 年化跳跃斜率
	* @param kink_ 拐点
	*/
	constructor(
		uint baseRatePerYear,
		uint multiplierPerYear,
		uint jumpMultiplierPerYear,
		uint kink_
	 ) {
		baseRatePerBlock = baseRatePerYear / blocksPerYear;
		multiplierPerBlock = multiplierPerYear / blocksPerYear;
		jumpMultiplierPerBlock = jumpMultiplierPerYear / blocksPerYear;
		kink = kink_;
	 }
	/**
	* @notice 计算利⽤率
	* @param cash 可⽤现⾦
	* @param borrows 总借款
	* @param reserves 储备⾦
	* @return 利⽤率(scaled by 1e18)
	*/
	function utilizationRate(
		uint cash,
		uint borrows,
		uint reserves
	 ) public pure returns (uint) {
		// 如果没有借款,利⽤率为0
		if (borrows == 0) {
			return 0;
		 }
		// U = borrows / (cash + borrows - reserves)
		return (borrows * 1e18) / (cash + borrows - reserves);
	 }
	/**
	* @notice 计算每区块借款利率
	* @param cash 可⽤现⾦
	* @param borrows 总借款
	* @param reserves 储备⾦
	* @return 每区块借款利率
	*/
	function getBorrowRate(
		uint cash,
		uint borrows,
		uint reserves
	 ) public view returns (uint) {
		uint util = utilizationRate(cash, borrows, reserves);
		// 如果利⽤率 <= kink,使⽤正常斜率
		if (util <= kink) {
			// rate = base + multiplier × util
			return ((util * multiplierPerBlock) / 1e18) + baseRatePerBlock;
		 } else {
			// 如果利⽤率 > kink,使⽤跳跃斜率
			// normalRate = base + multiplier × kink
			uint normalRate = ((kink * multiplierPerBlock) / 1e18) + baseRatePerBlock;
			// excessUtil = util - kink
			uint excessUtil = util - kink;
			// jumpRate = normalRate + jumpMultiplier × excessUtil
			return ((excessUtil * jumpMultiplierPerBlock) / 1e18) + normalRate;
		 }
	 }
	/**
	* @notice 计算每区块存款利率
	* @param cash 可⽤现⾦
	* @param borrows 总借款
	* @param reserves 储备⾦
	* @param reserveFactorMantissa 储备因⼦
	* @return 每区块存款利率
	*/
	function getSupplyRate(
		uint cash,
		uint borrows,
		uint reserves,
		uint reserveFactorMantissa
	 ) public view returns (uint) {
		uint oneMinusReserveFactor = 1e18 - reserveFactorMantissa;
		uint borrowRate = getBorrowRate(cash, borrows, reserves);
		uint util = utilizationRate(cash, borrows, reserves);
		// supplyRate = borrowRate × util × (1 - reserveFactor)
		uint rateToPool = (borrowRate * oneMinusReserveFactor) / 1e18;
		return (util * rateToPool) / 1e18;
	 }
}

4.2 利率实时更新机制

区块级利息累积:

Compound的利息在每个区块累积,通过 accrueInterest 函数更新:

c 复制代码
/**
* @notice 累积利息
* @dev 在任何状态变更操作前都必须调⽤
*/
function accrueInterest() public returns (uint) {
	// 1. 获取当前区块号
	uint currentBlockNumber = block.number;
	uint accrualBlockNumberPrior = accrualBlockNumber;
	// 如果在同⼀区块内,⽆需重复计算
	if (accrualBlockNumberPrior == currentBlockNumber) {
		return uint(Error.NO_ERROR);
	 }
	// 2. 读取存储的状态
	uint cashPrior = getCash();
	uint borrowsPrior = totalBorrows;
	uint reservesPrior = totalReserves;
	uint borrowIndexPrior = borrowIndex;
	// 3. 计算当前借款利率
	uint borrowRateMantissa = interestRateModel.getBorrowRate(
		cashPrior,
		borrowsPrior,
		reservesPrior
	 );
	require(borrowRateMantissa <= borrowRateMaxMantissa, "Borrow rate too high");
	// 4. 计算区块差
	uint blockDelta = currentBlockNumber - accrualBlockNumberPrior;
	// 5. 计算利息
	// interestAccumulated = borrowRate × borrows × blockDelta
	uint simpleInterestFactor = borrowRateMantissa * blockDelta;
	uint interestAccumulated = (simpleInterestFactor * borrowsPrior) / 1e18;
	// 6. 更新总借款(加上新增利息)
	uint totalBorrowsNew = interestAccumulated + borrowsPrior;
	// 7. 更新储备⾦(协议收⼊)
	uint totalReservesNew = (interestAccumulated * reserveFactorMantissa) / 1e18 +
	reservesPrior;
	// 8. 更新借款索引
	uint borrowIndexNew = (simpleInterestFactor * borrowIndexPrior) / 1e18 +
	borrowIndexPrior;
	// 9. 写⼊状态
	accrualBlockNumber = currentBlockNumber;
	borrowIndex = borrowIndexNew;
	totalBorrows = totalBorrowsNew;
	totalReserves = totalReservesNew;
	emit AccrueInterest(
		cashPrior,
		interestAccumulated,
		borrowIndexNew,
		totalBorrowsNew
	 );
	return uint(Error.NO_ERROR);
}

区块利息 vs 时间利息:

5. Compound V3 (Comet) - 新⼀代架构

5.1 为什么需要V3?

Compound V2虽然成功,但也暴露了⼀些问题:

  1. 资本效率低:每个资产独⽴的资⾦池导致流动性分散
  2. Gas成本⾼:复杂的跨市场计算消耗⼤量Gas
  3. ⻛险管理复杂:多抵押品组合增加了⻛险评估难度
  4. ⽤户体验差:借多种资产需要多次操作

5.2 Comet核⼼设计

Compound V3(代号Comet)采⽤了全新的架构设计:

核⼼改变:

  1. 单⼀借款资产:每个Comet市场只⽀持借⼀种资产(如USDC)
  2. 多种抵押品:但可以接受多种资产作为抵押
  3. 原⽣收益:借款资产⾃动赚取收益(如USDC存⼊Compound Treasury)
  4. 简化逻辑:⼤幅降低合约复杂度和Gas成本

5.2.1 V2 vs V3 架构对⽐

V2 vs V3 功能对⽐:

5.2.2 Comet核⼼合约

c 复制代码
// Comet.sol - Compound V3核⼼合约
pragma solidity 0.8.15;
contract Comet {
	// 基础资产(唯⼀可借资产)
	address public immutable baseToken;
	// 抵押品配置
	struct AssetConfig {
		address asset;
		uint8 decimals;
		uint64 borrowCollateralFactor; // 借款抵押因⼦
		uint64 liquidateCollateralFactor; // 清算抵押因⼦
		uint128 supplyCap; // 供应上限
	 }
	 AssetConfig[] public assetConfigs;
	// ⽤户基础资产余额(可正可负)
	mapping(address => int256) public baseBalances;
	// ⽤户抵押品余额
	mapping(address => mapping(address => uint256)) public collateralBalances;
	// 利率模型
	uint64 public baseIndexScale = 1e15;
	/**
	* @notice 供应基础资产(类似V2的mint)
	* @param amount 供应数量
	*/
	function supply(address asset, uint amount) external {
		if (asset == baseToken) {
			supplyBase(msg.sender, amount);
		 } else {
			supplyCollateral(msg.sender, asset, amount);
		 }
	 }
	/**
	* @notice 供应基础资产
	*/
	function supplyBase(address from, uint amount) internal {
		// 转⼊资产
		doTransferIn(baseToken, from, amount);
		// 增加⽤户余额(可能从负变正)
		baseBalances[from] += int256(amount);
		emit Supply(from, baseToken, amount);
	 }
	/**
	* @notice 供应抵押品
	*/
	function supplyCollateral(address from, address asset, uint amount) internal {
		// 检查是否是⽀持的抵押品
		AssetConfig memory config = getAssetConfig(asset);
		require(config.asset != address(0), "Unsupported asset");
		// 转⼊抵押品
		doTransferIn(asset, from, amount);
		// 增加抵押品余额
		collateralBalances[from][asset] += amount;
		emit SupplyCollateral(from, asset, amount);
	 }
	/**
	* @notice 取出资产
	* @param asset 资产地址
	* @param amount 取出数量
	*/
	function withdraw(address asset, uint amount) external {
		if (asset == baseToken) {
			withdrawBase(msg.sender, amount);
		 } else {
			withdrawCollateral(msg.sender, asset, amount);
		 }
	 }
	/**
	* @notice 取出基础资产
	*/
	function withdrawBase(address to, uint amount) internal {
		// 减少⽤户余额(可能从正变负,即借款)
		baseBalances[to] -= int256(amount);
		// 检查是否有⾜够的借款能⼒
		if (baseBalances[to] < 0) {
			require(isBorrowCollateralized(to), "Undercollateralized");
		 }
		// 转出资产
		doTransferOut(baseToken, to, amount);
		emit Withdraw(to, baseToken, amount);
	 }
	/**
	* @notice 检查借款是否有⾜够抵押
	*/
	function isBorrowCollateralized(address account) public view returns (bool) {
		int256 baseBalance = baseBalances[account];
		// 如果余额为正,说明是供应者,不需要抵押
		if (baseBalance >= 0) {
			return true;
		 }
		// 计算借款价值
		uint256 borrowValue = presentValue(uint256(-baseBalance));
		// 计算抵押品总价值
		uint256 collateralValue = 0;
		for (uint i = 0; i < assetConfigs.length; i++) {
			AssetConfig memory config = assetConfigs[i];
			uint256 collateralBalance = collateralBalances[account][config.asset];
			if (collateralBalance > 0) {
				// 获取抵押品价格
				uint256 price = getPrice(config.asset);
				// 计算加权价值
				uint256 value = (collateralBalance * price) / (10 ** config.decimals);
				collateralValue += (value * config.borrowCollateralFactor) / 1e18;
			 }
	 	}
		// 抵押品价值必须⼤于借款价值
		return collateralValue >= borrowValue;
	 }
	/**
	* @notice 清算函数
	* @param borrower 借款⼈
	* @param asset 抵押品资产
	* @param baseAmount 偿还的基础资产数量
	*/
	function absorb(address borrower, address[] calldata assets) external {
		// 检查是否可以清算
		require(!isBorrowCollateralized(borrower), "Not liquidatable");
		// 计算借款⼈的债务
		uint256 debt = uint256(-baseBalances[borrower]);
		// 吸收所有抵押品
		for (uint i = 0; i < assets.length; i++) {
			address asset = assets[i];
			uint256 collateralBalance = collateralBalances[borrower][asset];
			if (collateralBalance > 0) {
				// 将抵押品转移到协议储备
				collateralBalances[borrower][asset] = 0;
				collateralBalances[address(this)][asset] += collateralBalance;
				emit AbsorbCollateral(borrower, asset, collateralBalance);
			 }
		 }
		// 清除借款⼈的债务
		baseBalances[borrower] = 0;
		emit Absorb(borrower, debt);
	 }
}

5.3 Comet的优势

  1. 更⾼的资本效率
    V3中,所有抵押品共享同⼀个借款池,提⾼了资本利⽤率:

V2模式:

  • ⽤户A: 在ETH市场存⼊,只能赚取ETH市场的利息

  • ⽤户B: 在USDC市场存⼊,只能赚取USDC市场的利息

  • 流动性分散
    V3模式:

  • 所有USDC需求者共享同⼀个USDC池

  • ⽆论抵押ETH、WBTC还是LINK,都从同⼀池⼦借USDC

  • 流动性集中,效率更⾼

  1. 降低Gas成本

操作对⽐(以太坊主⽹):

存款:

  • V2: ~120,000 Gas

  • V3: ~75,000 Gas

  • 节省: 37.5%
    借款:

  • V2: ~280,000 Gas

  • V3: ~160,000 Gas

  • 节省: 42.9%
    还款:

  • V2: ~180,000 Gas

  • V3: ~110,000 Gas

  • 节省: 38.9%

  1. 原⽣收益功能
    V3将基础资产(如USDC)投资到其他收益协议,为⽤户创造额外收益:

USDC Comet市场:

  • 闲置USDC⾃动存⼊Compound Treasury
  • 赚取国债收益率(~4-5% APY)
  • ⽤户获得:借款利息 + 国债收益
  • 提⾼整体APY约1-2个百分点
相关推荐
DICOM医学影像5 小时前
1. Remix编写、编译、部署、测试Solidity ERC20合约 - 基础篇
区块链·智能合约·solidity·以太坊·remix·web3.0·erc20
DICOM医学影像11 小时前
3. Metamask导入代币,转账ETH,转账代币
区块链·智能合约·solidity·以太坊·metamask·web3.0·erc20
DICOM医学影像14 小时前
5. Hardhat编写、编译、部署、测试Solidity ERC20合约 - 进阶篇 - web3.js调用合约方法
区块链·solidity·以太坊·web3.js·hardhat·erc20
小明的小名叫小明15 小时前
Compound协议(1)
区块链·defi
virtual_k1smet1 天前
梧桐·鸿鹄-中移链assistant-level
笔记·区块链
MicroTech20251 天前
微算法科技(NASDAQ :MLGO)利用量子计算增强区块链多任务处理
科技·区块链·量子计算
hopsky2 天前
加密货币与金融大变局
区块链
MicroTech20252 天前
区块链赋能,联邦协同:微算法科技(NASDAQ: MLGO)打造物联网安全分布式检测新架构
科技·算法·区块链
阿菜ACai2 天前
AAVE V4 新特性简介:更细致地管理流动性与用户仓位
智能合约·defi