compoundV3 完全新的优化版 的独立资金借贷池。主要分析其新特性,并对比 v2 v3 的区别
全新的模型,丰富扩展性,更少清算风险,更低清算罚款,省gas
compound v2 模型 —— 混合资金池子
各种资产都有自己的 ctoken
compound v3 模型 —— 主资金+抵押品池
只有base 资产有 ctoken v3
v2 根据 市场利用率 计算(borrows / (cash + borrows - reserves)),supply 计算获得利息 和 borrow 计算归还利息(根据区块),可以通过exchangeRate一起算出,所有supply assets 都可以盈利
v3 资金利用率(totalBorrow/ totalSupply)有转折值 kink,利用率低于 kink 利率直线上升(根据时间),支持单独配置提供和借出kink
v2 supply 提供supply asset 对应的ctoken 作为可挖矿存储凭证(每个资产1个ctoken),v3提供 baseasset 的 ctoken v3(所有资产仅提供一种 ctoken,目前为 cUSDC v3 提案升级了一次),
v2 可激励 comp 平台币(按区块 supply和borrow),v3 最细开启激励平台币,只对borrow 进行激励
v2 清算叫 liquidiation,原本的清算机器人是检测所有的质押,然后指定一种 ctoken,针对ctoken 进行清算,(有大量的开源清算机器人,拼算力和速度 )
v3 清算叫 absorb,全新设计了一套清算引擎(撸代码中),分池子检测market,针对 全部资产(基础 资产 + 抵押品)进行清算,ctoken v3 只有 cusdc v3
v3 拆分了清算步骤,清算的抵押品 需要先将抵押品的归属权变更为 合约持有,记录gas消耗情况 和 清算数量+次数,然后可选的合约上 按照既定折扣购买抵押品(目前不限制购买人,任何人都能买可出售的抵押品)。
v2 的治理 是通过dao 对整个 混合大资金池进行参数修改,风险较高
v3 治理 通过dao 对单个comet 资金池进行修改,且每个参数都提到了单独的合约 configer 中,操作起来更清晰,一旦出错 只是单个资金池出错
依次为:资产,预言机,代币精度,借贷因子,抵押品清算因子(计算是否能清算),清算折扣(被1减得到discount),可supply上限
---- comp
0xc00e94Cb662C3520282E6f5717214004A7f26888,0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5,18,650000000000000000,700000000000000000,930000000000000000,200000000000000000000000,
---- wbtc
0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599,0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c,8,700000000000000000,770000000000000000,950000000000000000,210000000000,
--- weth
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2,0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419,18,825000000000000000,895000000000000000,950000000000000000,27000000000000000000000,
--- uni
0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984,0x553303d460EE0afB37EdFf9bE42922D8FF63220e,18,750000000000000000,810000000000000000,930000000000000000,1250000000000000000000000,
--- link
0x514910771AF9Ca656af840dff83E8264EcF986CA,0x2c1d072e956AFFC0D435Cb7AC38EF18d24d9127c,18,790000000000000000,850000000000000000,930000000000000000,1250000000000000000000000
v2 获取chainlink 价格后,需要跟 uniswap v2 的池子做一次价格验证,偏差在允许范围内才是有效价格
v3 直接读取 chainlink 价格获取,不再依赖 twap,主要是预防 the merge 之后 防止价格操控 (关于twap 的不足说明可见 euler 的 提案),base 和 collateral 都有对usd的chainlink 预言机
v2 完整跑起来很费劲(最简化部署可见其他大佬的文档)
v3 有各种 官方周边仓库,(快速部署,各类操作,清算)不管干啥 快速上手
usdc 池子 kink supply 和 borrow 都为 0.8,资金利用率过高会导致利息剧增(按时间)
borrow 和 supply 可以配置成不同的,已经解耦,现在配置的完全一样是 80%
都有单独方法获取: Utilization = TotalBorrows / TotalSupply
官方文档有误差实际去掉了 reserveRate相关内容,具体公式见下面
资金利用率 kink <= 0.8时 借款利率
BorrowRate = InterestRateBase + InterestRateSlopeLow * Utilization
kink > 0.8 借款利率
BorrowRate = InterestRateBase + InterestRateSlopeLow Kink + InterestRateSlopeHigh (Utilization - Kink)
官方文档有错误实际这个没用,用的还是借款利率相同算法
资金利用率 kink <= 0.8时 存款利率 SupplyRate = (InterestRateBase + InterestRateSlopeLow Utilization) Utilization (1 - ReserveRate) kink > 0.8 存款利率 SupplyRate = (InterestRateBase + InterestRateSlopeLow Kink + InterestRateSlopeHigh (Utilization - Kink)) Utilization (1 - ReserveRate)*
"rates": { "supplyKink": 0.8, "supplySlopeLow": 0.0325, "supplySlopeHigh": 0.4, "supplyBase": 0, "borrowKink": 0.8, "borrowSlopeLow": 0.035, "borrowSlopeHigh": 0.25, "borrowBase": 0.015 },
备注: 其实代码跟公式对不上,没有 reserverate,而且两者 公式一模一样(借款利率)
查询可以直接查 isLiquidatable(account)
具体执行需要有2步操作:执行清算 和 购买抵押品, 第一步将抵押品交给协议,会记录liquidator 的记录,官方说后续给补偿; 对清算人来说关键抢的是第二步,按照reserves 的限额值 抢购打折抵押品(真正获利)
官方给出了一个使用uniswap V3 的 flason swap 的清算合约和检测,清算的例子
执行具体步骤:
部分核心代码: 获取抵押品报价
/**
* @notice Gets the quote for a collateral asset in exchange for an amount of base asset
* @param asset The collateral asset to get the quote for
* @param baseAmount The amount of the base asset to get the quote for
* @return The quote in terms of the collateral asset
*/
function quoteCollateral(address asset, uint baseAmount) override public view returns (uint) {
AssetInfo memory assetInfo = getAssetInfoByAddress(asset);
uint256 assetPrice = getPrice(assetInfo.priceFeed);
// Store front discount is derived from the collateral asset's liquidationFactor and storeFrontPriceFactor
// discount = storeFrontPriceFactor * (1e18 - liquidationFactor)
uint256 discountFactor = mulFactor(storeFrontPriceFactor, FACTOR_SCALE - assetInfo.liquidationFactor);
uint256 assetPriceDiscounted = mulFactor(assetPrice, FACTOR_SCALE - discountFactor);
uint256 basePrice = getPrice(baseTokenPriceFeed);
// 主要是这里 可以看到 base 和collateral 都是通过本方法获取,清算的时候一起给回了reserve,但只有 collateral 可以购买
// # of collateral assets
// = (TotalValueOfBaseAmount / DiscountedPriceOfCollateralAsset) * assetScale
// = ((basePrice * baseAmount / baseScale) / assetPriceDiscounted) * assetScale
return basePrice * baseAmount * assetInfo.scale / assetPriceDiscounted / baseScale;
}
清算时合约内部逻辑(将抵押品交给合约)
/**
* @notice Absorb a list of underwater accounts onto the protocol balance sheet 清算入口合约(只会把抵押品给到合约)
* @param absorber The recipient of the incentive paid to the caller of absorb
* @param accounts The list of underwater accounts to absorb
*/
function absorb(address absorber, address[] calldata accounts) override external {
if (isAbsorbPaused()) revert Paused();
uint startGas = gasleft();
// 这里只是把抵押品给回了当前合约
accrueInternal();
for (uint i = 0; i < accounts.length; ) {
absorbInternal(absorber, accounts[i]);
unchecked { i++; }
}
uint gasUsed = startGas - gasleft();
// 这里记录了清算的消耗情况
// Note: liquidator points are an imperfect tool for governance,
// to be used while evaluating strategies for incentivizing absorption.
// Using gas price instead of base fee would more accurately reflect spend,
// but is also subject to abuse if refunds were to be given automatically.
LiquidatorPoints memory points = liquidatorPoints[absorber];
points.numAbsorbs++;
points.numAbsorbed += safe64(accounts.length);
points.approxSpend += safe128(gasUsed * block.basefee);
liquidatorPoints[absorber] = points;
}
购买抵押品
/**
* @notice Buy collateral from the protocol using base tokens, increasing protocol reserves
A minimum collateral amount should be specified to indicate the maximum slippage acceptable for the buyer.
* @param asset The asset to buy
* @param minAmount The minimum amount of collateral tokens that should be received by the buyer
* @param baseAmount The amount of base tokens used to buy the collateral
* @param recipient The recipient address
*/
function buyCollateral(address asset, uint minAmount, uint baseAmount, address recipient) override external {
if (isBuyPaused()) revert Paused();
// targetreserve 之上,不允许购买抵押品
int reserves = getReserves();
if (reserves >= 0 && uint(reserves) >= targetReserves) revert NotForSale();
// Note: Re-entrancy can skip the reserves check above on a second buyCollateral call.
doTransferIn(baseToken, msg.sender, baseAmount);
uint collateralAmount = quoteCollateral(asset, baseAmount);
if (collateralAmount < minAmount) revert TooMuchSlippage();
// Note: Pre-transfer hook can re-enter buyCollateral with a stale collateral ERC20 balance.
// This is a problem if quoteCollateral derives its discount from the collateral ERC20 balance.
withdrawCollateral(address(this), recipient, asset, safe128(collateralAmount));
emit BuyCollateral(msg.sender, asset, baseAmount, collateralAmount);
}
借助 user nonce 模型,可以允许 其他账户 管理自己账户的 抵押品资产。 意义不明
简化治理,治理系统更改,治理通过单个合约(配置器)进行(之前是统一的管理地址)
与v2 相同,社区投票,投票通过后执行 timelock,将提案 queue 上去,等待指定周期后 exec 即可。具体执行见v2就行。
完全依赖 chainklink 价格,每30分钟更新一次价格,通过configor 读取assetinfo 获取到地址(包含 basetoken 和 collateral 各类资产的对 usd价格),
不适用 twap 的价格模式应该跟 the merge 有关。具体见 euler 的提案
核心合约-- 快速部署
3个代理 1个helper(bulker)
"comet": "0xc3d688B66703497DAA19211EEdff47f25384cdc3", "configurator": "0x316f9708bB98af7dA9c68C1C3b5e79039cD336E3", "rewards": "0x1B0e765F6224C21223AeA2af16c1C46E38885a40", "bulker": "0x74a81F84268744a40FEBc48f8b812a1f188D80C3"
代码小工具 SPEC 快速同步线上各种参数 spider 将合约同步到其他网络
compound v2 检测与清算机器人-- How to Build a Compound Liquidation Bot – bwd (baowebdev.com) compound v3 透明公正高利用率-- Compound III is Live. Transparent, fair, autonomous interest… | by Robert Leshner | Compound | Aug, 2022 | Medium 官方文档-- Compound III Documentation v3 的简单清算-- comet/contracts/liquidator at main · compound-finance/comet (github.com) compound v3 的改动-- 今日上线的 Compound III 都有哪些改动? | DeFi之道 (defidaonews.com)
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!