- 示例工程
- 区块链
【免费下载链接】Dapp-Learning
Dapp learning project for developers at all stages. Becoming and cultivating sovereign individuals. Nonprofit organization.
本文基于 Dapp-Learning 仓库中 defi/Euler/LeverageLendingAlgorithmOfEuler.md 及同目录源码笔记整理而成。
Euler 是一个以"杠杆借贷"为核心特色的去中心化借贷协议,它在 Compound 的抵押因子(Collateral Factor)框架之上,引入 Borrow Factor、Self-Collateralization(自我抵押)与 Uniswap V3 TWAP 定价,使得一个纯粹的借贷协议同时具备杠杆做多、杠杆做空乃至近似永续合约的能力。本文将完整拆解 Euler 的 Liquidity(流动性)计算算法——从风险调整因子、健康系数到六个递进式数值案例、19 倍最大杠杆的数学推导,再到合约中computeLiquidityRaw的源码级实现与动态 self 价值优化,并最终对比 Euler 与 Perpetual V2 在杠杆机制上的异同。读完本文,你将掌握 Euler 杠杆算法的完整数学模型,并能对照 RiskManager.md 与 BaseLogic.md 中给出的合约代码理解其工程实现。
一、背景:从 Compound 的 Account Liquidity 到 Euler 的改进
借贷协议中的 Liquidity 概念首先由 Compound 提出(Account Liquidity):用户每一种进入 Markets 的资产都需要乘以相应的 Collateral Factor 累加起来,再扣除用户每一种借贷资产的价值总和。下图为 Compound 的流动性计算示意:
而 Euler 在 Compound 基础上做了两处关键改进:
- 债务侧也做风险调整:不仅抵押资产计算价值时需要乘以
CF(Collateral Factor),债务资产也需要除以BF(Borrow Factor)做风险价值调整; - 引入 self 机制:由于其特有的 mint 机制(自我借贷),在计算时还需要考虑
Self_Collateral和Self_Liability两类特殊资产。
下图为 Euler 流动性计算的完整示意(抵押、负债、self 抵押、self 负债四部分):
Euler 中所有资产价值都需要乘以一个风险价值调整因子,上述四个部分的调整系数各不相同。为方便叙述,下文将所有经过风险系数调整后的资产价值后缀标记为_RiskAdjusted,简写为_RA。于是有:
Liquidity = Collateral_RA - Liabilities_RA + (Self_Collateral_RA - Self_Liability_RA)而Self_Collateral与Self_Liability两者的调整价值在 Euler 的算法机制下总是保持一致,即 self 部分的抵押和负债两者的风险调整价值会相互抵消(两者流动性相减为 0),所以实际上 Liquidity 的表达式最终化简为:
Liquidity = Collateral_RA - Liabilities_RA二、核心概念:HealthScore 与四类风险调整因子
2.1 HealthScore 健康系数
健康系数(HealthScore)与 Compound 类似,是总抵押价值与负债价值的比值,两者都经过风险价值调整因子调整,不同资产的抵押和借贷因子不同(CF、BF)。当抵押与借贷相等时,HealthScore为 1,处于临界点;一旦小于 1,账户即可被清算。这正是 Liquidation.md 中computeLiqOpp判断"是否可以清算"的依据:healthScore = collateralValue * 1e18 / liabilityValue,当collateralValue < liabilityValue即 HealthScore < 1 时进入清算流程。
2.2 四类资产的风险调整因子表
区别于其他借贷协议,Euler 独有的 Self-Collateralization 概念允许用户使用杠杆做空或者挖矿。抵押资产分为Collateral(前端页面中命名为 Supply)和Self_Collateral,负债资产分为Liabilities和Self_Liability,四种资产均有不同的风险价值调整因子 factor:
| Collateral | factor | RiskAdjusted | info |
|---|---|---|---|
| Collateral(DepositValue) | CF | value × CF | decided by governance |
| Self_Collateral | SCF | value × SCF | constant value 0.95 |
| Liabilities(BorrowValue) | BF | value / BF | decided by governance |
| Self_Liability | SBF | value / SBF | constant value 1 |
要点归纳:
- 常规抵押和负债(
Collateral、Liabilities)的 CF、BF 都是小于 1 的数,且前者比后者小。Euler 在 Compound 的 CF 基础上增加了 BF,使得不同资产之间的风险调整更加灵活; - mint 操作会产生等价值的抵押和债务,即价值都为 MintValue 的
Self_Collateral和Self_Liability; - self 部分的风险调整系数是固定常量:
Self_Collateral的SCF固定为 0.95,Self_Liabilities的SBF固定为 1,因此经过风险价值因子调整后的负债与抵押关系为:
Self_Collateral * SCF = Self_Liability / SBF也就是说,Self_Collateral是和 MintValue 等值的抵押,但因为需要乘以 0.95 的风险调整因子,只能抵消 0.95 倍的债务;剩余的 0.05 倍 MintValue 债务被直接计入了Liabilities——这正是 Euler 杠杆机制中"0.05 缺口"的来源,也是 19 倍最大杠杆的数学根基。
在合约层面,这两个常量定义于CONFIG_FACTOR_SCALE精度体系之下(见 RiskManager.md):
uint internal constant CONFIG_FACTOR_SCALE = 4_000_000_000; // must fit into a uint32 uint32 internal constant SELF_COLLATERAL_FACTOR = uint32(0.95 * 4_000_000_000);三、Compute Liquidity:六个递进式数值案例
Euler 的所有资产都以 WETH 计价,并从相应资产与 WETH 组成的交易对的 UniswapV3Pool 中获取 TWAP 价格,WETH 的价格恒等于 1(对应 RiskManager.md 中PRICINGTYPE__PEGGED定价类型与 WETH 参考资产机制,详见 oracle/readme.md)。
假设当下 USDC 的价格是 1/3000 WETH,并给定以下因子:
| symbol | CF | BF |
|---|---|---|
| USDC | 0.9 | 0.94 |
| WETH | 0.88 | 0.91 |
下面从易到难梳理计算流动性的六个场景。
case 1:仅抵押,无债务
用户 deposit 了 3000 USDC,DepositValue 为3000 * (1/3000) = 1 WETH,并未产生债务。
- Liquidity 非常好计算,只需考虑
Collateral_RA,即抵押的 USDC 经过风险价值调整后的价值:
Liquidity = Collateral_RA = DepositValue * CF = 1 * 0.9 = 0.9case 2:抵押 + 普通借贷
用户 deposit 了 3000 USDC(DepositValue 为 1 WETH),并 borrow 0.5 WETH(BorrowValue 是 0.5 WETH)。
- Liquidity 为
Collateral_RA - Liabilities_RA:
Liquidity = DepositValue * CF - BorrowValue / BF = 1 * 0.9 - 0.5 / 0.91 = 0.3505上述情况 Liquidity 还有盈余。显然,当用户的 Liquidity 为 0 时,健康系数达到被清算临界点 1,此时 BorrowValue 达到最大值:
BorrowValue_Max = DepositValue * CF * BF = 1 * 0.9 * 0.91 = 0.819case 3:抵押 + mint(self 借贷)
用户 deposit 了 3000 USDC(DepositValue 为 1 WETH),然后 mint 出 2 WETH,MintValue 为 2 WETH。
- 由于 mint 操作会产生等量的债务和抵押,得到价值都为 2 WETH 的抵押和负债,所以用户的债务和抵押的底层资产价值(UnderlyingValue)将各增加 MintValue(2 WETH);
Self_Collateral = MintValue = 2 WETH;- 调整后价值为
Self_Collateral_RA = MintValue * SCF = 2 * 0.95 = 1.9 WETH(SCF是常量系数 0.95); Self_Liability_RA = Self_Collateral * SCF / SBF = 2 * 0.95 / 1 = 1.9 WETH(SBF是常量系数 1);- Liquidity 为
Collateral_RA - Liabilities_RA + (Self_Collateral_RA - Self_Liability_RA),而(Self_Collateral_RA - Self_Liability_RA)由于 self 部分债务和抵押的调整价值总是相互抵消,这部分 Liquidity 恒为 0; - 需要注意的是,mint 出的债务只有 0.95 倍被
Self_Collateral_RA抵消,仍有MintValue * (1 - SCF)价值的债务需要计入Liabilities_RA:
Collateral_RA = DepositValue * CF = 1 * 0.9 Liabilities_RA = MintValue * (1 - SCF) / BF = 2 * (1 - 0.95) / 0.91 Liquidity = Collateral_RA - Liabilities_RA = 1 * 0.9 - 2 * (1 - 0.95) / 0.91 = 0.7901case 4:抵押 + mint + 普通借贷
用户在 case 3 的基础上再 borrow 0.5 WETH(用户 deposit 3000 USDC,DepositValue 是 1 WETH,mint 2 WETH 后 borrow 0.5 WETH)。
- 新增的借贷只需按照常规债务计算:
Liquidity = 0.7901 - 0.5 / BF = 0.2406case 5:多资产抵押 + 大额 mint
用户 deposit 1500 USDC + 0.5 WETH,DepositValue 总计 1 WETH,mint 12 WETH,分别生成了价值都是 12 WETH 的Self_Collateral和Self_Liability。
- 同 case 3 所述,self 部分抵押与债务相互抵消,
Liabilities_RA增加MintValue * (1 - SCF) = 12 * (1 - 0.95) = 0.6 WETH; - 由于 deposit 只存入了 0.5 WETH,WETH 的
Collateral_RA = 0.5 * 0.88 = 0.44 WETH,而Liabilities_RA = 0.6 / 0.91 = 0.6593; - WETH 部分的
Collateral最终只能承担 0.44 WETH 的债务,剩下的0.6593 - 0.44 = 0.2193 WETH债务需要由 USDC 的Collateral_RA承担,用户在 WETH 资产上的 Liquidity 已经清零; - USDC 的流动性为
Liquidity_USDC = 0.5 * 0.9 - 0.2193 = 0.2307; - 由于 WETH 流动性已经为 0,总流动性即为 USDC 的流动性
Liquidity = 0.2307 WETH。
case 6:累计利息对 self 部分的影响
在 case 3 的基础上考虑累计利息:用户在 A 时刻 deposit 3000 USDC(DepositValue 1 WETH),然后 mint 2 WETH(MintValue 2 WETH),经过一年来到 B 时刻,抵押和债务各有不同利息累计。
假设两种资产的抵押和借贷 APY 如下:
| symbol | Deposit APY | Borrow APY |
|---|---|---|
| USDC | 2% | 6% |
| WETH | 6% | 9% |
推导过程:
DepositValue_B = DepositValue_A * (1 + 2%) = 1 * 1.02 = 1.02 LiabilitiesValue_B = Liabilities_A * (1 + 6%) = 0.1 * 1.06 = 0.106 Self_Collateral_B = Self_Collateral_A * (1 + 6%) = MintValue * 1.06 Self_Liability_B = Self_Liability_A * (1 + 9%) = MintValue * 1.09当时刻 A 用户 mint 之后,self 部分的抵押和债务价值相等(相互抵消)即Self_Collateral_A = Self_Liability_A;来到时刻 B 后两者由于 APY 不同价值不再相等,通常债务的利息增长更多,超出的债务部分计入Liabilities_RA:
delta_Liabilities_RA = Self_Liability_B - Self_Collateral_B = MintValue * (1.09 - 1.06) / BF = 2 * 0.03 / 0.91 = 0.0659 Liabilities_RA_B = Liabilities_RA_A * (1 + 6%) + delta_Liabilities_RA = 0.106 + 0.0659 = 0.1719 Liquidity_B = Collateral_RA_B - Liabilities_RA_B = 1.02 * 0.9 - 0.1719 = 0.7461这个案例为下一节的"动态 self 价值算法优化"埋下伏笔:债务利率通常高于抵押利率,self 部分的价值缺口会随时间持续累积进Liabilities。
四、Max Mint Leverage:19 倍最大杠杆的数学推导
假设用户在 deposit 操作之后只进行 mint 操作、没有单独 borrow,债务可以表示为:
Liabilities_RA = MintValue * (1 - SCF)转换为 MintValue 的表达式:
MintValue = Liabilities_RA / (1 - SCF)MintValue 的最大值由Liabilities_RA决定——当Liabilities_RA最大时 Liquidity 为 0、HealthScore 变为 1,处于将被清算的临界点。此时Liabilities_MAX_RA = Collateral_RA,对应的 MintValue 即为最大值 MintValueMax:
MintValueMax = LiabilitiesMax_RA / (1 - SCF) = Collateral_RA / (1 - SCF)此时用户拥有最多数量的Self_Collateral和Self_Liability(mint 数量如果再增加,合约会报错拒绝执行):
SelfCollateral = MintValueMax = Collateral_RA / (1 - SCF) SelfLiability_RA = SelfCollateral * SCF = Collateral_RA / (1 - SCF) * SCF计算杠杆倍数,即Self_Liability与Collateral_RA的比值(因为只做 mint 操作,没有Liabilities部分):
LeverageMax = SelfLiability / Collateral_RA = (SelfLiability_RA * SBF) / Collateral_RA化简得:
Leverage_Max = SCF * SBF / (1 - SCF)代入SCF = 0.95、SBF = 1,可得 Leverage 的最大值是19,即 mint 所能使用的最大杠杆倍数。当SBF固定为 1 时,影响最大杠杆倍数的因素其实只有SCF,例如当SCF = 0.90时,最大倍数是 9。
这一推导与 defi/Euler/README.md 中的表述完全一致:每次 mint 产生的等量 EToken 和 DToken 并不能完全相互抵消,因为SELF_COLLATERAL_FACTOR = 0.95,抵押的调整后价值总是比债务少(1 - 0.95),这缺少的 0.05 倍价值被计入Liabilities,需要用用户 deposit 存入的抵押价值抵消,最大不能超过 deposit 资产的调整价值(需乘以 CF)。
五、算法优化:Dynamic Self Value 与合约源码解析
5.1 为什么要优化
在 case 6 中,我们分别记录了Self_Collateral和Self_Liability的数量来计算利息差值。但这种做法在智能合约中会带来巨大的存储开销:因为这样计算,总是会在用户mint或者burn时去修改这两个 storage 变量(昂贵的SSTORE操作),并且每一位用户的每一种资产都需要单独设置两个变量记录Self_Collateral和Self_Liability,在实际业务中显然不合理。
5.2 优化思路
Euler 的算法优化不需要为每一位用户的每一种资产单独记录两个变量,省去了频繁的SSTORE操作,核心步骤是:
- 首先,
Self_Collateral与Collateral利率相同,Self_Liability与Liabilities利率相同。之所以把抵押资产和负债资产各分为两类,根本原因是风险价值调整系数不同(SCF=0.95 vs CF;SBF=1 vs BF),对 Liquidity 的影响不同; - 如果排除同时 deposit 和 borrow 同一种资产的情况,就能大大简化区分 self 数量的步骤:
- 例如用户 deposit 3000 USDC 又 borrow 1000 USDC,若依然保留 3000 USDC 抵押和 1000 USDC 债务,将大大增加区分 self 部分的复杂性;
- 因此 Euler 的处理方式是直接从 deposit 中扣除 borrow:当用户 borrow 1000 USDC 时,实际上没有产生新的债务,而是直接减少抵押的部分,变为 2000 USDC;
- 有了上述约束,计算每种资产的 self 部分和非 self 部分时,先假设该资产的所有债务都是
Self_Liability,进而推导Self_Collateral = Self_Collateral_RA / SCF = Self_Liability / SCF; - 如果推导出的
Self_Collateral比实际该资产的抵押要大,重设为实际的抵押数量,而剩下的未抵消的Self_Liability计入Liabilities。
defi/Euler/README.md 对这一动态过程做了更直观的描述:随着债务利息增长更快,Self_Collateral也随之增长(Self_Collateral = Self_Liability / 0.95),而总抵押数量不变(不考虑 deposit/withdraw),所以常规Collateral数量随之减少;当推导出的Self_Collateral未超过实际抵押数量时,常规债务为 0;一旦超过,则以实际数量balanceInUnderlying为上限重设,超出的债务部分持续累计入Liabilities。
5.3 合约源码:computeLiquidityRaw
下面是 RiskManager.md 中给出的 Euler 合约关于流动性计算的主要代码逻辑和注释(即 LeverageLendingAlgorithmOfEuler.md 原文代码,两处一致):
// contracts/modules/RiskManager.sol function computeLiquidityRaw( address account, address[] memory underlyings ) private view returns (LiquidityStatus memory status) { ... // compute Liquidity every asset for (uint i = 0; i < underlyings.length; ++i) { ... // balance and owned are storage variables, not the latest value uint balance = assetStorage.users[account].balance; // balanceOf EToken uint owed = assetStorage.users[account].owed; // balanceOf DToken // If user has Liabilities in this asset, should compute // both Liabilities and collateral, otherwise, accumulate // collateralValue memory variable directly if (owed != 0) { ... // The latest balanceOf DToken, // often different from owed (greater) uint assetLiability = getCurrentOwed( assetStorage, assetCache, account ); // self-collateralization // If balance of EToken is not zero, // means user has collateral in this asset if (balance != 0) { // User's actually collateral balance of underlying uint balanceInUnderlying = balanceToUnderlyingAmount( assetCache, balance ); // CONFIG_FACTOR_SCALE for adjusting decimals (1 in uint32) // SELF_COLLATERAL_FACTOR is constant value (0.95) // selfAmount cache Liabilities amount (Self_Collateral_RA) // First of all, we assume that all debt are Self_Liability, // then we can get Self_Collateral // Self_Collateral * SCF = Self_Collateral_RA = Self_Liability // Self_Collateral = Self_Liability / SCF uint selfAmount = assetLiability; uint selfAmountAdjusted = assetLiability * CONFIG_FACTOR_SCALE / SELF_COLLATERAL_FACTOR; // If assetLiability > all collateral of the asset at this time, // this is obviously. unreasonable, indicating that the assumption // does not hold. So we reset Self_Collateral to balanceInUnderlying // and the remaining part of // assetLiability - balanceInUnderlying is the actual Liabilities if (selfAmountAdjusted > balanceInUnderlying) { selfAmount = balanceInUnderlying * SELF_COLLATERAL_FACTOR / CONFIG_FACTOR_SCALE; selfAmountAdjusted = balanceInUnderlying; } { // (balanceInUnderlying - selfAmountAdjusted)*BF*price is // actually Collateral_RA without Self_Collateral uint assetCollateral = (balanceInUnderlying - selfAmountAdjusted) * config.collateralFactor / CONFIG_FACTOR_SCALE; assetCollateral += selfAmount; // accumulate collateralValue status.collateralValue += assetCollateral * price / 1e18; } // Liabilities without Self_Liabilities assetLiability -= selfAmount; status.liabilityValue += selfAmount * price / 1e18; ... } // accumulate debt without self-liabilities // this part use borrowFactor is different from // self_collateral_factor (constant 0.95) assetLiability = assetLiability * price / 1e18; assetLiability = config.borrowFactor != 0 ? assetLiability * CONFIG_FACTOR_SCALE / config.borrowFactor : MAX_SANE_DEBT_AMOUNT; status.liabilityValue += assetLiability; } else if (balance != 0 && config.collateralFactor != 0) { // owed == 0 && balance != 0 // user only have Collateral in this asset ... // convert Etoken amount to Underlying_amount // collateralValue += Underlying_amount * collateralFactor uint balanceInUnderlying = balanceToUnderlyingAmount(assetCache, balance); uint assetCollateral = balanceInUnderlying * price / 1e18; assetCollateral = assetCollateral * config.collateralFactor / CONFIG_FACTOR_SCALE; status.collateralValue += assetCollateral; } } }这段代码与上文数学模型严格对应,可以逐行印证:
selfAmountAdjusted = assetLiability * CONFIG_FACTOR_SCALE / SELF_COLLATERAL_FACTOR即"先假设所有债务都是 Self_Liability,再由Self_Collateral = Self_Liability / SCF反推 self 抵押数量";if (selfAmountAdjusted > balanceInUnderlying)分支即第 4 步的"重设上限"逻辑:当反推的 self 抵押超过实际抵押时,将selfAmount重设为balanceInUnderlying * SCF,剩余部分assetLiability - selfAmount作为真正的Liabilities继续按borrowFactor调整;- 当用户仅有抵押无债务时(
owed == 0 && balance != 0),走else if分支直接把balanceInUnderlying * CF * price累加到collateralValue,对应 case 1。
配套地,EToken.md 展示了 mint 操作的合约入口——increaseBalance增加 EToken(抵押凭证)余额、increaseBorrow增加 DToken(债务凭证)余额,随后调用checkLiquidity(account)校验流动性,从而把"mint 产生等值抵押与债务"落实为链上动作:
/// @notice Mint eTokens and a corresponding amount of dTokens ("self-borrow") /// @param subAccountId 0 for primary, 1-255 for a sub-account /// @param amount In underlying units function mint(uint subAccountId, uint amount) external nonReentrant { (address underlying, AssetStorage storage assetStorage, address proxyAddr, address msgSender) = CALLER(); address account = getSubAccount(msgSender, subAccountId); updateAverageLiquidity(account); emit RequestMint(account, amount); AssetCache memory assetCache = loadAssetCache(underlying, assetStorage); amount = decodeExternalAmount(assetCache, amount); uint amountInternal = underlyingAmountToBalanceRoundUp(assetCache, amount); amount = balanceToUnderlyingAmount(assetCache, amountInternal); // Mint ETokens increaseBalance(assetStorage, assetCache, proxyAddr, account, amountInternal); // Mint DTokens increaseBorrow(assetStorage, assetCache, assetStorage.dTokenAddress, account, amount); checkLiquidity(account); logAssetStatus(assetCache); }此外,BaseLogic.md 补充了流动性相关的辅助机制:updateAverageLiquidity维护用户 24 小时时间窗口内的平均流动性(AVERAGE_LIQUIDITY_PERIOD = 24 * 60 * 60),采用时间加权算法;getAccountLiquidity通过内部模块调用RiskManager.computeLiquidity获得抵押与负债价值。这些机制为清算折扣的discountBooster计算(清算人提供的平均流动性越多,享受折扣越大)提供数据基础,参见 Liquidation.md 的computeDiscountBooster。
六、Short:用 mint 机制实现杠杆做空
6.1 做空流程
Euler 允许使用杠杆做空。假设使用 WETH 做空 UNI(ShortOn: UNI, Against: WETH):
- 首先 deposit 1 WETH;
- mint 价值 3 WETH 的 UNI,同时产生 3 WETH 价值的抵押和债务(UNI-EToken 与 UNI-DToken);
- 其中
3 * 0.95 = 2.85 WETH价值的Self_Liability被 3 WETH 价值的Self_Collateral抵消; - 产生了
3 * (1 - 0.95) = 0.15 WETH价值的Liabilities; - 调用 Euler 的 swap 接口,Euler 合约将 UNI-EToken 转换成 UNI,然后去 UNI-WETH UniV3Pool 中兑换成等值的 WETH,最后 Euler 合约转换成 WETH-EToken 返回给用户;
- 一段时间后,当 UNI 价格下降,用户的 UNI-DToken 债务价值下降,此时用户用 WETH-EToken 去兑换 UNI-EToken 将花费更少,剩下未兑换部分的 WETH-EToken 即做空利润;
- 忽略借贷利息成本和抵押资产收益,假设 UNI-DToken 的价值因价格下跌减小了 1%,用户将获利
3 WETH 债务的 1% = 0.03 WETH;相比直接做空 1 WETH 的 UNI 获利 0.01 WETH,上述方式获得了3 倍杠杆的加成。
在最大杠杆情形下(mint 19 倍,见第四节推导),对应 defi/Euler/README.md 中的数字示例:mint 19 WETH 产生19 * 0.95 = 18.05的 self 抵消与19 * 0.05 = 0.95 WETH的Liabilities,此时 Liquidity 已为 0、HealthScore 为 1,处于将被清算的临界点,再想提高杠杆率合约会禁止执行。
6.2 交互脚本(fork-mainnet 模拟)
由于 Euler 前端代码暂未开源(截至 2022 年 5 月 10 日),仓库作者编写了交互脚本与 Euler 合约交互,模拟使用 Euler mint 特性来用 WETH 做空 UNI,并使用 fork-mainnet 网络模拟交易环境(相关脚本示例见仓库同主题文档 defi/Euler/README.md 的 short scripts 小节)。
七、Euler vs. Perpetual:借贷协议如何"长出"永续合约
Euler 独有的杠杆借贷机制使借贷协议同时具备杠杆做多和杠杆做空功能。与之对比,Perpetual Protocol 是基于 Uniswap V3 实现的去中心化永续合约交易协议,其实现思路是将用户的抵押资产与交易资产分隔:抵押资产存入 Vault 模块,而永续合约市场所交易的实际上是协议内部生成的 virtual token(详见 defi/Perpetual-V2/README.md)。
Perp v1 和 v2 都有一个 vAMM 模块(Uniswap pool),交易对 token 都是 vToken(virtual token),永续合约的交易实际上就是做 vToken 的交易;得益于 vToken 只在协议内部流通,Perp 可以根据用户设置的杠杆倍数来 mint vToken 的数量。
Euler 与 Perpetual V2 永续合约功能的特性对比:
| Features | Euler | Perpetual V2 |
|---|---|---|
| Max Leverage | < 10X(根据 Factor 计算) | 10X |
| Trade Cost | Borrowing interest cost | Funding Payments & Swap fees |
| Price Oracle | UniswapV3 TWAP | Chainlink |
| limit order | 不支持 | 未来会支持 |
逐项展开:
- 做多与做空的最大杠杆倍数:Perp V2 目前最大杠杆被设定为 10 倍;Euler 要根据抵押资产和做空资产的 factor 计算,通常小于 10 倍(使用前端的 Short/Long 功能);
- 交易成本:Perp V2 的交易成本除了永续合约的 Funding Payments,还有 vToken 做 Swap 时产生的交易费;Euler 对永续合约交易者而言成本是借贷利息费用支出——当然用户抵押资产有利息收入,且 Euler 曾开启流动性挖矿,借贷资产还有额外 EUL token 奖励,计算交易成本时还需考虑这两部分收益,实际交易费用支出会比较小,甚至随 EUL 价格波动可能出现无费用的情况;
- 价格预言机(永续合约中的 Index Price):Perp V2 主要使用 Chainlink 作为喂价来源(链下数据);Euler 则完全依赖 Uniswap V3 pool TWAP(链上数据);
- 永续合约的限价单功能:Perp V2 理论上可以实现 limit order,项目方当时正在开发中;Euler 目前没有规划此功能。
关于 Perp V2 的 Funding Payment 机制细节(基于区块的 funding 结算、taker/maker 累计变量等),可进一步参考 defi/Perpetual-V2/Block-basedFundingPayments.md。
可以看出 Euler 与 Perpetual 各有优势。Euler 虽然是基于借贷协议的框架,却凭借其独有的杠杆借贷算法机制实现了永续合约功能,是一项颇具巧思的创新。
八、延伸阅读:同一仓库中的 Euler 源码笔记
本仓库defi/Euler目录下还沉淀了一套完整的 Euler 合约源码级笔记,可与本文相互印证:
- defi/Euler/README.md:Euler 原理总览,Liquidity、Self-Collateralisation、Short and Max Leverage、Dynamic Self Value、Liquidation(荷兰式拍卖清算、Stability Pools 暂未实现)等;
- defi/Euler/contracts/RiskManager.md:
getNewMarketParameters(默认市场参数与隔离层安全默认值)、getPriceInternal(PEGGED / UNISWAP3_TWAP 定价)与computeLiquidityRaw完整代码; - defi/Euler/contracts/EToken.md:mint/burn 等 EToken 操作入口;
- defi/Euler/contracts/Markets.md:市场激活
doActivateMarket流程; - defi/Euler/contracts/BaseLogic.md:模块体系(9 个模块)、子账户按位异或机制、AssetConfig/AssetCache、分段利率模型与平均流动性更新;
- defi/Euler/contracts/Liquidation.md:清算流程
liquidate/computeLiqOpp/computeDiscountBooster; - defi/Euler/contracts/Proxy.md 与 defi/Euler/ModuleProxyMode.md:模块化可升级 Proxy 架构(MainProxy + 各模块 ModuleProxy,delegatecall 执行、call 转发、日志分模块记录);
- defi/Euler/oracle/readme.md:Uniswap V3 TWAP 预言机风险评级体系;
- defi/Euler/hack/EulerAttack.md:2023 年 3 月 Euler 攻击事件的复盘笔记,其中明确指出攻击根因与本文的杠杆机制直接相关——mint 最大可放大 19 倍杠杆、清算最多可获得约 0.25 倍利润,
19 * 0.25 > 1超过本金,攻击者通过 donate 构造资不抵债的自我清算条件获利。这也从反面印证了理解本文杠杆算法(self 部分 0.05 缺口、清算临界点、隔离层约束)对于 DeFi 安全分析的重要性。
- 示例工程
- 区块链
【免费下载链接】Dapp-Learning
Dapp learning project for developers at all stages. Becoming and cultivating sovereign individuals. Nonprofit organization.
相关推荐
Euler 借贷协议深度解析:Liquidity 计算、Self-Collateralisation 与杠杆清算机制
Euler 借贷协议深度解析:Liquidity 计算、Self Collateralisation 与杠杆清算机制 Euler 是一个基于以太坊的非托管借贷协
示例工程区块链Euler EToken 源码解析:mint/burn 自借贷机制与 19 倍杠杆的实现原理
Euler EToken 源码解析:mint/burn 自借贷机制与 19 倍杠杆的实现原理 本文以 EToken.md https://link.gitcod
示例工程区块链Euler 协议 3·13 攻击深度复盘:模块化代理架构、19 倍杠杆清算机制与 Donate 自清算漏洞全解析
Euler 协议 3·13 攻击深度复盘:模块化代理架构、19 倍杠杆清算机制与 Donate 自清算漏洞全解析 2023 年 3 月 13 日,Euler 借
示例工程区块链
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考