@pendle/core-v2 6.9.0 → 6.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/artifacts/contracts/interfaces/IPPTLooper.sol/IPPTLooper.dbg.json +1 -1
- package/build/artifacts/contracts/interfaces/IPPTLooper.sol/IPPTLooper.json +244 -249
- package/build/artifacts/contracts/interfaces/IPPTLooper.sol/IPPTLooperEIP712Types.dbg.json +1 -1
- package/build/artifacts/contracts/pt-looping/PTLooperBase.sol/PTLooperBase.dbg.json +1 -1
- package/build/artifacts/contracts/pt-looping/PTLooperBase.sol/PTLooperBase.json +244 -249
- package/build/artifacts/contracts/pt-looping/PTLooperEIP712TypesEncodeDataLib.sol/PTLooperEIP712TypesEncodeDataLib.dbg.json +1 -1
- package/build/artifacts/contracts/pt-looping/PTLooperEIP712TypesEncodeDataLib.sol/PTLooperEIP712TypesEncodeDataLib.json +2 -2
- package/build/artifacts/contracts/pt-looping/PendlePTLooper.sol/PendlePTLooper.dbg.json +1 -1
- package/build/artifacts/contracts/pt-looping/PendlePTLooper.sol/PendlePTLooper.json +246 -251
- package/build/artifacts/contracts/pt-looping/money-market/BaseMoneyMarket.sol/BaseMoneyMarket.dbg.json +1 -1
- package/build/artifacts/contracts/pt-looping/money-market/BaseMoneyMarketView.sol/BaseMoneyMarketView.dbg.json +1 -1
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave/AaveMoneyMarket.sol/AaveMoneyMarket.dbg.json +1 -1
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave/AaveMoneyMarket.sol/AaveMoneyMarket.json +2 -2
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave/AaveMoneyMarketView.sol/AaveMoneyMarketView.dbg.json +1 -1
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave/AaveMoneyMarketView.sol/AaveMoneyMarketView.json +2 -2
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave-v4/AaveV4MMPayloadLib.sol/AaveV4MMPayloadLib.dbg.json +4 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave-v4/AaveV4MMPayloadLib.sol/AaveV4MMPayloadLib.json +10 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave-v4/AaveV4MoneyMarket.sol/AaveV4MoneyMarket.dbg.json +4 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave-v4/AaveV4MoneyMarket.sol/AaveV4MoneyMarket.json +615 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave-v4/AaveV4MoneyMarketView.sol/AaveV4MoneyMarketView.dbg.json +4 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave-v4/AaveV4MoneyMarketView.sol/AaveV4MoneyMarketView.json +280 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave-v4/dependencies/IAaveOracle.sol/IAaveOracle.dbg.json +4 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave-v4/dependencies/IAaveOracle.sol/IAaveOracle.json +30 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave-v4/dependencies/IHubBase.sol/IHubBase.dbg.json +4 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave-v4/dependencies/IHubBase.sol/IHubBase.json +322 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave-v4/dependencies/ISpoke.sol/ISpoke.dbg.json +4 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/aave-v4/dependencies/ISpoke.sol/ISpoke.json +398 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/EulerMMPayloadLib.sol/EulerMMPayloadLib.dbg.json +4 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/EulerMMPayloadLib.sol/EulerMMPayloadLib.json +10 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/EulerMoneyMarket.sol/EulerMoneyMarket.dbg.json +4 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/EulerMoneyMarket.sol/EulerMoneyMarket.json +651 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/EulerMoneyMarketView.sol/EulerMoneyMarketView.dbg.json +4 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/EulerMoneyMarketView.sol/EulerMoneyMarketView.json +298 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/dependencies/IEthereumVaultConnector.sol/IEVC.dbg.json +4 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/dependencies/IEthereumVaultConnector.sol/IEVC.json +959 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/dependencies/IEulerFactory.sol/IEulerFactory.dbg.json +4 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/dependencies/IEulerFactory.sol/IEulerFactory.json +30 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/dependencies/IEulerPriceOracle.sol/IEulerPriceOracle.dbg.json +4 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/dependencies/IEulerPriceOracle.sol/IEulerPriceOracle.json +53 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/dependencies/IEulerVault.sol/AmountCapLib.dbg.json +4 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/dependencies/IEulerVault.sol/AmountCapLib.json +10 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/dependencies/IEulerVault.sol/IEulerVault.dbg.json +4 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/euler/dependencies/IEulerVault.sol/IEulerVault.json +933 -0
- package/build/artifacts/contracts/pt-looping/money-market/implementations/morpho/MorphoBlueMoneyMarket.sol/MorphoBlueMoneyMarket.dbg.json +1 -1
- package/build/artifacts/contracts/pt-looping/money-market/implementations/morpho/MorphoBlueMoneyMarket.sol/MorphoBlueMoneyMarket.json +2 -2
- package/build/artifacts/contracts/pt-looping/money-market/implementations/morpho/MorphoBlueMoneyMarketView.sol/MorphoBlueMoneyMarketView.dbg.json +1 -1
- package/build/artifacts/contracts/pt-looping/money-market/implementations/morpho/MorphoBlueMoneyMarketView.sol/MorphoBlueMoneyMarketView.json +2 -2
- package/build/artifacts/contracts/router/swap-aggregator/OKXScaleHelper.sol/IOKXDexRouter.dbg.json +1 -1
- package/build/artifacts/contracts/router/swap-aggregator/OKXScaleHelper.sol/OKXScaleHelper.dbg.json +1 -1
- package/build/artifacts/contracts/router/swap-aggregator/PendleSwap.sol/IKyberScalingHelper.dbg.json +1 -1
- package/build/artifacts/contracts/router/swap-aggregator/PendleSwap.sol/IOdosRouterV2.dbg.json +1 -1
- package/build/artifacts/contracts/router/swap-aggregator/PendleSwap.sol/PendleSwap.dbg.json +1 -1
- package/build/artifacts/contracts/router/swap-aggregator/PendleSwap.sol/PendleSwap.json +2 -2
- package/contracts/interfaces/IPPTLooper.sol +2 -4
- package/contracts/pt-looping/PTLooperEIP712TypesEncodeDataLib.sol +6 -6
- package/contracts/pt-looping/PendlePTLooper.sol +4 -2
- package/contracts/pt-looping/money-market/implementations/aave-v4/AaveV4MMPayloadLib.sol +20 -0
- package/contracts/pt-looping/money-market/implementations/aave-v4/AaveV4MoneyMarket.sol +85 -0
- package/contracts/pt-looping/money-market/implementations/aave-v4/AaveV4MoneyMarketView.sol +180 -0
- package/contracts/pt-looping/money-market/implementations/aave-v4/dependencies/IAaveOracle.sol +12 -0
- package/contracts/pt-looping/money-market/implementations/aave-v4/dependencies/IHubBase.sol +72 -0
- package/contracts/pt-looping/money-market/implementations/aave-v4/dependencies/ISpoke.sol +88 -0
- package/contracts/pt-looping/money-market/implementations/euler/EulerMMPayloadLib.sol +25 -0
- package/contracts/pt-looping/money-market/implementations/euler/EulerMoneyMarket.sol +104 -0
- package/contracts/pt-looping/money-market/implementations/euler/EulerMoneyMarketView.sol +105 -0
- package/contracts/pt-looping/money-market/implementations/euler/dependencies/IEthereumVaultConnector.sol +432 -0
- package/contracts/pt-looping/money-market/implementations/euler/dependencies/IEulerFactory.sol +10 -0
- package/contracts/pt-looping/money-market/implementations/euler/dependencies/IEulerPriceOracle.sol +26 -0
- package/contracts/pt-looping/money-market/implementations/euler/dependencies/IEulerVault.sol +149 -0
- package/contracts/router/swap-aggregator/OKXScaleHelper.sol +3 -0
- package/deployments/143-core.json +4 -1
- package/deployments/42161-core.json +4 -1
- package/deployments/4663-core.json +48 -0
- package/deployments/4663-offchain-helper.json +12 -0
- package/package.json +1 -1
- package/typechain-types/AaveV4MoneyMarket.ts +573 -0
- package/typechain-types/AaveV4MoneyMarketView.ts +362 -0
- package/typechain-types/EulerMoneyMarket.ts +599 -0
- package/typechain-types/EulerMoneyMarketView.ts +375 -0
- package/typechain-types/IAaveOracle.ts +17 -149
- package/typechain-types/IEVC.ts +1647 -0
- package/typechain-types/IEulerFactory.ts +94 -0
- package/typechain-types/IEulerPriceOracle.ts +123 -0
- package/typechain-types/IEulerVault.ts +1298 -0
- package/typechain-types/IHubBase.ts +452 -0
- package/typechain-types/IPPTLooper.ts +68 -68
- package/typechain-types/ISpoke.ts +577 -0
- package/typechain-types/PTLooperBase.ts +68 -68
- package/typechain-types/PendlePTLooper.ts +68 -68
- package/typechain-types/factories/AaveMoneyMarketView__factory.ts +1 -1
- package/typechain-types/factories/AaveMoneyMarket__factory.ts +1 -1
- package/typechain-types/factories/AaveV4MoneyMarketView__factory.ts +349 -0
- package/typechain-types/factories/AaveV4MoneyMarket__factory.ts +673 -0
- package/typechain-types/factories/EulerMoneyMarketView__factory.ts +371 -0
- package/typechain-types/factories/EulerMoneyMarket__factory.ts +720 -0
- package/typechain-types/factories/IAaveOracle__factory.ts +3 -93
- package/typechain-types/factories/IEVC__factory.ts +968 -0
- package/typechain-types/factories/IEulerFactory__factory.ts +42 -0
- package/typechain-types/factories/IEulerPriceOracle__factory.ts +68 -0
- package/typechain-types/factories/IEulerVault__factory.ts +945 -0
- package/typechain-types/factories/IHubBase__factory.ts +334 -0
- package/typechain-types/factories/IPPTLooper__factory.ts +243 -248
- package/typechain-types/factories/ISpoke__factory.ts +407 -0
- package/typechain-types/factories/MorphoBlueMoneyMarketView__factory.ts +1 -1
- package/typechain-types/factories/MorphoBlueMoneyMarket__factory.ts +1 -1
- package/typechain-types/factories/PTLooperBase__factory.ts +243 -248
- package/typechain-types/factories/PendlePTLooper__factory.ts +244 -249
- package/typechain-types/factories/PendleSwap__factory.ts +1 -1
- package/typechain-types/hardhat.d.ts +99 -0
- package/typechain-types/index.ts +22 -2
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
//SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {IERC20Metadata as IERC20} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
5
|
+
|
|
6
|
+
import {ISpoke} from "./dependencies/ISpoke.sol";
|
|
7
|
+
import {IHubBase} from "./dependencies/IHubBase.sol";
|
|
8
|
+
import {AaveV4MMPayloadLib} from "./AaveV4MMPayloadLib.sol";
|
|
9
|
+
|
|
10
|
+
import {BaseMoneyMarketView} from "../../BaseMoneyMarketView.sol";
|
|
11
|
+
import {IPLoopPositionBox, BoxData} from "../../../../interfaces/IPLoopPositionBox.sol";
|
|
12
|
+
import {PMath} from "../../../../core/libraries/math/PMath.sol";
|
|
13
|
+
|
|
14
|
+
contract AaveV4MoneyMarketView is BaseMoneyMarketView {
|
|
15
|
+
using PMath for uint256;
|
|
16
|
+
|
|
17
|
+
constructor(string memory _moneyMarketName, address _ptLooper) BaseMoneyMarketView(_moneyMarketName, _ptLooper) {}
|
|
18
|
+
|
|
19
|
+
/// @notice Aave V4 has no separate borrow-LTV / liquidation-threshold pair — `DynamicReserveConfig`
|
|
20
|
+
/// carries a single `collateralFactor` that serves both roles, so both return values are equal.
|
|
21
|
+
/// Callers MUST NOT treat `ltv` as a safe borrowing target the way they can on the V3 adapter:
|
|
22
|
+
/// borrowing to `ltv` here means borrowing to exactly `HF == 1`, i.e. to the liquidation line.
|
|
23
|
+
/// The entire safety margin must be supplied by the caller's own `postBorrowMaxLTV` haircut.
|
|
24
|
+
function _thresholds(IPLoopPositionBox box, BoxData memory bd)
|
|
25
|
+
internal
|
|
26
|
+
view
|
|
27
|
+
virtual
|
|
28
|
+
override
|
|
29
|
+
returns (uint256 ltv, uint256 liquidationThreshold)
|
|
30
|
+
{
|
|
31
|
+
(ISpoke spoke, uint256 collateralReserveId,) = AaveV4MMPayloadLib.unwrap(bd.mmPayload);
|
|
32
|
+
uint32 dynamicConfigKey = spoke.getUserPosition(collateralReserveId, box.SPOKE_MONEY_MARKET()).dynamicConfigKey;
|
|
33
|
+
ISpoke.DynamicReserveConfig memory dc = spoke.getDynamicReserveConfig(collateralReserveId, dynamicConfigKey);
|
|
34
|
+
|
|
35
|
+
ltv = liquidationThreshold = _bpsToWad(dc.collateralFactor);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/// @notice Known limitations:
|
|
39
|
+
/// The returned `borrowing` accounts for Hub cash and the per-spoke `drawCap`, but not for
|
|
40
|
+
/// `Hub._applyPremiumDelta`'s aggregate constraint
|
|
41
|
+
/// `spoke.premiumShares <= spoke.drawnShares.percentMulUp(riskPremiumThreshold)`.
|
|
42
|
+
/// That threshold is a spoke-wide quantity driven by all borrowers on the spoke, so it cannot
|
|
43
|
+
/// be meaningfully attributed to a single box and any computed value would be immediately
|
|
44
|
+
/// stale. A borrow within the returned figure may still revert if the spoke is at its
|
|
45
|
+
/// premium threshold and this box's collateral carries a high `collateralRisk`.
|
|
46
|
+
function _liquidity(IPLoopPositionBox, BoxData memory bd)
|
|
47
|
+
internal
|
|
48
|
+
view
|
|
49
|
+
virtual
|
|
50
|
+
override
|
|
51
|
+
returns (uint256 borrowing, uint256 lending)
|
|
52
|
+
{
|
|
53
|
+
(ISpoke spoke, uint256 collateralReserveId, uint256 debtReserveId) = AaveV4MMPayloadLib.unwrap(bd.mmPayload);
|
|
54
|
+
|
|
55
|
+
// lending
|
|
56
|
+
{
|
|
57
|
+
ISpoke.Reserve memory collReserve = spoke.getReserve(collateralReserveId);
|
|
58
|
+
IHubBase.SpokeData memory sd = collReserve.hub.getSpoke(collReserve.assetId, address(spoke));
|
|
59
|
+
if (sd.addCap == type(uint40).max) {
|
|
60
|
+
lending = IERC20(collReserve.underlying).totalSupply(); // Infinite supply cap
|
|
61
|
+
} else {
|
|
62
|
+
uint256 capAssets = uint256(sd.addCap) * 10 ** collReserve.decimals;
|
|
63
|
+
uint256 currentAssets = collReserve.hub.previewRemoveByShares(collReserve.assetId, sd.addedShares);
|
|
64
|
+
lending = capAssets.subMax0(currentAssets);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// borrowing
|
|
69
|
+
{
|
|
70
|
+
ISpoke.Reserve memory reserve = spoke.getReserve(debtReserveId);
|
|
71
|
+
IHubBase hub = reserve.hub;
|
|
72
|
+
IHubBase.SpokeData memory sd = hub.getSpoke(reserve.assetId, address(spoke));
|
|
73
|
+
uint256 hubLiquidity = hub.getAsset(reserve.assetId).liquidity;
|
|
74
|
+
if (sd.drawCap == type(uint40).max) {
|
|
75
|
+
borrowing = hubLiquidity;
|
|
76
|
+
} else {
|
|
77
|
+
uint256 capAssets = uint256(sd.drawCap) * 10 ** reserve.decimals;
|
|
78
|
+
|
|
79
|
+
// * `_calculateAggregatedOwedRay`: https://github.com/aave/aave-v4/blob/2524fe4018a42750300e114f2a8c4355df62a878/src/hub/libraries/AssetLogic.sol#L229
|
|
80
|
+
// owedAssets = (drawnShares * drawnIndex) + premiumRay + deficitRay
|
|
81
|
+
// Besides `deficitRay`, the rest is `hub.getSpokeTotalOwed`
|
|
82
|
+
uint256 owedAssets = hub.getSpokeTotalOwed(reserve.assetId, address(spoke)) + _trimRay(sd.deficitRay);
|
|
83
|
+
uint256 capHeadroom = capAssets.subMax0(owedAssets);
|
|
84
|
+
borrowing = PMath.min(hubLiquidity, capHeadroom);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// @dev `borrowing` (`_borrowApy`) is position-specific -- includes this box's own risk
|
|
90
|
+
/// premium (`getUserLastRiskPremium`), a per-position surcharge, not a market-wide figure:
|
|
91
|
+
/// https://github.com/aave/aave-v4/blob/2524fe4018a42750300e114f2a8c4355df62a878/src/spoke/interfaces/ISpoke.sol#L750
|
|
92
|
+
/// `lending` (`_lendingApy`) is market-wide -- no single position's premium applies, so it
|
|
93
|
+
/// folds in premium's *aggregate* contribution via the market's drawn+premium shares instead.
|
|
94
|
+
/// See `_lendingApy`.
|
|
95
|
+
function _rates(IPLoopPositionBox box, BoxData memory bd)
|
|
96
|
+
internal
|
|
97
|
+
view
|
|
98
|
+
virtual
|
|
99
|
+
override
|
|
100
|
+
returns (uint256 borrowing, uint256 lending)
|
|
101
|
+
{
|
|
102
|
+
(ISpoke spoke, uint256 collateralReserveId, uint256 debtReserveId) = AaveV4MMPayloadLib.unwrap(bd.mmPayload);
|
|
103
|
+
|
|
104
|
+
uint256 boxRiskPremiumBps = spoke.getUserLastRiskPremium(box.SPOKE_MONEY_MARKET());
|
|
105
|
+
borrowing = _borrowApyWithPremium(spoke.getReserve(debtReserveId), boxRiskPremiumBps);
|
|
106
|
+
lending = _lendingApy(spoke.getReserve(collateralReserveId));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function _borrowApyWithPremium(ISpoke.Reserve memory reserve, uint256 riskPremiumBps)
|
|
110
|
+
internal
|
|
111
|
+
view
|
|
112
|
+
returns (uint256)
|
|
113
|
+
{
|
|
114
|
+
return _aaveV4Apy(_drawnRateWad(reserve).tweakUp(_bpsToWad(riskPremiumBps)));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/// @return lendingApy = _apy(drawnRate * usageRatio * (1 - liquidityFee), perSeconds: 365 days)
|
|
118
|
+
/// - usageRatio (or utilization): `interestBearing / totalValue`
|
|
119
|
+
function _lendingApy(ISpoke.Reserve memory reserve) internal view returns (uint256) {
|
|
120
|
+
// * `totalValue` - the real supplier claim base
|
|
121
|
+
// totalAddedAssets = liquidity + swept + (drawn+premium+deficit) - realizedFees - unrealizedFees
|
|
122
|
+
// which is exactly `Hub.getAddedAssets(assetId)` (returns `_assets[assetId].totalAddedAssets()`
|
|
123
|
+
// directly -- not a shares<->assets conversion, so unlike `previewRemoveByShares` there's no
|
|
124
|
+
// `SharesMath` virtual-offset rounding)
|
|
125
|
+
// - https://github.com/aave/aave-v4/blob/2524fe4018a42750300e114f2a8c4355df62a878/src/hub/libraries/AssetLogic.sol#L79-L96
|
|
126
|
+
|
|
127
|
+
IHubBase.Asset memory collAsset = reserve.hub.getAsset(reserve.assetId);
|
|
128
|
+
uint256 totalValue = reserve.hub.getAddedAssets(reserve.assetId);
|
|
129
|
+
|
|
130
|
+
if (totalValue == 0) return 0;
|
|
131
|
+
|
|
132
|
+
// `interestBearing` must be the principal that `drawnRate` compounds
|
|
133
|
+
// against going forward, with no offset subtracted
|
|
134
|
+
// interestBearing = (drawnShares + premiumShares) * drawnIndex,
|
|
135
|
+
uint256 interestBearingShares = uint256(collAsset.drawnShares) + collAsset.premiumShares;
|
|
136
|
+
uint256 interestBearing = reserve.hub.previewRestoreByShares(reserve.assetId, interestBearingShares);
|
|
137
|
+
|
|
138
|
+
uint256 drawnRate = _drawnRateWad(reserve);
|
|
139
|
+
uint256 usageRatio = interestBearing.divDown(totalValue);
|
|
140
|
+
uint256 supplyApr = drawnRate.mulDown(usageRatio).tweakDown(_bpsToWad(collAsset.liquidityFee));
|
|
141
|
+
return _aaveV4Apy(supplyApr);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function _drawnRateWad(ISpoke.Reserve memory reserve) internal view returns (uint256) {
|
|
145
|
+
return _rayToWad(reserve.hub.getAssetDrawnRate(reserve.assetId));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/// === Internal helpers ===
|
|
149
|
+
|
|
150
|
+
/// @dev BaseMoneyMarketView._apy is still used here, by convention, to compound
|
|
151
|
+
/// daily, uniformly across all money markets so their APYs stay mutually
|
|
152
|
+
/// comparable.
|
|
153
|
+
///
|
|
154
|
+
/// For Aave V4 in particular, `AssetLogic.accrue` applies linear
|
|
155
|
+
/// interest per interval and multiplies the index on every Hub interaction,
|
|
156
|
+
/// so the true compounding frequency equals the asset's interaction
|
|
157
|
+
/// frequency: this figure understates for busy assets and overstates for
|
|
158
|
+
/// quiet ones, and is not expected to match Aave's own frontend exactly.
|
|
159
|
+
///
|
|
160
|
+
/// The divergence is ~0.01pp at ordinary rates and only becomes material at
|
|
161
|
+
/// very high ones.
|
|
162
|
+
function _aaveV4Apy(uint256 rate) internal pure returns (uint256) {
|
|
163
|
+
return _apy({rate: rate, perSeconds: 365 days});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// BPS (1e4) -> WAD (1e18)
|
|
167
|
+
function _bpsToWad(uint256 bps) internal pure returns (uint256) {
|
|
168
|
+
return bps * 1e14;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// RAY (1e27) -> WAD (1e18)
|
|
172
|
+
function _rayToWad(uint256 ray) internal pure returns (uint256) {
|
|
173
|
+
return ray / 1e9;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// RAY (1e27) -> no decimals (1e0)
|
|
177
|
+
function _trimRay(uint256 ray) internal pure returns (uint256) {
|
|
178
|
+
return ray / 1e27;
|
|
179
|
+
}
|
|
180
|
+
}
|
package/contracts/pt-looping/money-market/implementations/aave-v4/dependencies/IAaveOracle.sol
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
/// https://github.com/aave/aave-v4/blob/main/src/spoke/interfaces/IAaveOracle.sol
|
|
5
|
+
/// (and IPriceOracle.sol, which IAaveOracle extends)
|
|
6
|
+
/// Modifications: remove all functions except `getReservePrice`.
|
|
7
|
+
|
|
8
|
+
interface IAaveOracle {
|
|
9
|
+
/// @notice Price of one whole unit of the reserve's underlying asset, scaled by `decimals()`.
|
|
10
|
+
/// @dev Reverts if price <= 0.
|
|
11
|
+
function getReservePrice(uint256 reserveId) external view returns (uint256);
|
|
12
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
/// https://github.com/aave/aave-v4/blob/main/src/hub/interfaces/IHub.sol
|
|
5
|
+
/// (and IHubBase.sol, which IHub extends)
|
|
6
|
+
/// Modifications:
|
|
7
|
+
/// - Trimmed to the read-only surface needed for rate/liquidity display and share<->asset
|
|
8
|
+
/// conversion. No mutating Hub functions (add/remove/draw/restore/sweep/reclaim/etc) are called
|
|
9
|
+
/// directly by this money market -- all fund movement goes through ISpoke.
|
|
10
|
+
interface IHubBase {
|
|
11
|
+
struct SpokeData {
|
|
12
|
+
uint120 drawnShares;
|
|
13
|
+
uint120 premiumShares;
|
|
14
|
+
int200 premiumOffsetRay;
|
|
15
|
+
uint120 addedShares;
|
|
16
|
+
uint40 addCap;
|
|
17
|
+
uint40 drawCap;
|
|
18
|
+
uint24 riskPremiumThreshold;
|
|
19
|
+
bool active;
|
|
20
|
+
bool halted;
|
|
21
|
+
uint200 deficitRay;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
struct Asset {
|
|
25
|
+
uint120 liquidity;
|
|
26
|
+
uint120 realizedFees;
|
|
27
|
+
uint8 decimals;
|
|
28
|
+
uint120 addedShares;
|
|
29
|
+
uint120 swept;
|
|
30
|
+
int200 premiumOffsetRay;
|
|
31
|
+
uint120 drawnShares;
|
|
32
|
+
uint120 premiumShares;
|
|
33
|
+
uint16 liquidityFee;
|
|
34
|
+
uint120 drawnIndex;
|
|
35
|
+
uint96 drawnRate;
|
|
36
|
+
uint40 lastUpdateTimestamp;
|
|
37
|
+
address underlying;
|
|
38
|
+
address irStrategy;
|
|
39
|
+
address reinvestmentController;
|
|
40
|
+
address feeReceiver;
|
|
41
|
+
uint200 deficitRay;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// @notice Current annualized borrow rate for `assetId`, RAY-scaled (1e27) -- same convention
|
|
45
|
+
/// as Aave V3's `currentVariableBorrowRate`. There is no supply-rate equivalent; V4 exposes
|
|
46
|
+
/// only this one rate primitive.
|
|
47
|
+
function getAssetDrawnRate(uint256 assetId) external view returns (uint256);
|
|
48
|
+
|
|
49
|
+
function getAsset(uint256 assetId) external view returns (Asset memory);
|
|
50
|
+
|
|
51
|
+
/// @notice Per-(asset, spoke) caps and current usage. `addCap`/`drawCap` are expressed in
|
|
52
|
+
/// whole assets (not scaled by decimals); `type(uint40).max` means uncapped.
|
|
53
|
+
function getSpoke(uint256 assetId, address spoke) external view returns (SpokeData memory);
|
|
54
|
+
|
|
55
|
+
function previewRemoveByShares(uint256 assetId, uint256 shares) external view returns (uint256);
|
|
56
|
+
function previewRestoreByShares(uint256 assetId, uint256 shares) external view returns (uint256);
|
|
57
|
+
|
|
58
|
+
/// @notice The asset-level total value backing all `addedShares` (liquidity + swept + owed -
|
|
59
|
+
/// fees), already aggregated Hub-side.
|
|
60
|
+
/// https://github.com/aave/aave-v4/blob/2524fe4018a42750300e114f2a8c4355df62a878/src/hub/Hub.sol#L513-L515
|
|
61
|
+
/// https://github.com/aave/aave-v4/blob/2524fe4018a42750300e114f2a8c4355df62a878/src/hub/libraries/AssetLogic.sol#L79-L96
|
|
62
|
+
function getAddedAssets(uint256 assetId) external view returns (uint256);
|
|
63
|
+
|
|
64
|
+
/// @notice Total amount of `assetId` owed to the Hub by `spoke` (drawn + premium debt,
|
|
65
|
+
/// computed internally -- handles the signed premium-offset subtraction itself, so callers
|
|
66
|
+
/// never need to touch `SpokeData.premiumOffsetRay`'s sign).
|
|
67
|
+
function getSpokeTotalOwed(uint256 assetId, address spoke) external view returns (uint256);
|
|
68
|
+
|
|
69
|
+
/// @notice Total amount of `assetId` owed to the Hub across all spokes (drawn + premium debt,
|
|
70
|
+
/// Hub-wide) -- the asset-level equivalent of `getSpokeTotalOwed`.
|
|
71
|
+
function getAssetTotalOwed(uint256 assetId) external view returns (uint256);
|
|
72
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {IHubBase} from "./IHubBase.sol";
|
|
5
|
+
|
|
6
|
+
/// https://github.com/aave/aave-v4/blob/main/src/spoke/interfaces/ISpoke.sol
|
|
7
|
+
/// Modifications:
|
|
8
|
+
/// - Trimmed to the structs and functions actually used by EulerMoneyMarket/View's Aave V4
|
|
9
|
+
/// equivalent (initialize/lend/borrow/repay/withdraw/collateralBalance/debtBalance/oracle rate,
|
|
10
|
+
/// plus thresholds/liquidity view queries). Governance, liquidation, and position-manager
|
|
11
|
+
/// surfaces are intentionally omitted -- not called by this money market.
|
|
12
|
+
interface ISpoke {
|
|
13
|
+
struct Reserve {
|
|
14
|
+
address underlying;
|
|
15
|
+
IHubBase hub;
|
|
16
|
+
uint16 assetId;
|
|
17
|
+
uint8 decimals;
|
|
18
|
+
uint24 collateralRisk;
|
|
19
|
+
uint8 flags;
|
|
20
|
+
uint32 dynamicConfigKey;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
struct DynamicReserveConfig {
|
|
24
|
+
uint16 collateralFactor;
|
|
25
|
+
uint32 maxLiquidationBonus;
|
|
26
|
+
uint16 liquidationFee;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
struct UserPosition {
|
|
30
|
+
uint120 drawnShares;
|
|
31
|
+
uint120 premiumShares;
|
|
32
|
+
int200 premiumOffsetRay;
|
|
33
|
+
uint120 suppliedShares;
|
|
34
|
+
uint32 dynamicConfigKey;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// @notice Address of this Spoke's oracle. Public immutable on the real contract (auto-getter).
|
|
38
|
+
function ORACLE() external view returns (address);
|
|
39
|
+
|
|
40
|
+
/// @notice Supplies `amount` of the reserve's underlying, crediting `onBehalfOf`.
|
|
41
|
+
/// @dev Caller must be `onBehalfOf` or an approved position manager for it -- `msg.sender ==
|
|
42
|
+
/// onBehalfOf` always satisfies this with zero registration, which is the only case we use.
|
|
43
|
+
/// @return shares, assets (assets is `amount` echoed back, not a rounded conversion).
|
|
44
|
+
function supply(uint256 reserveId, uint256 amount, address onBehalfOf) external returns (uint256, uint256);
|
|
45
|
+
|
|
46
|
+
/// @return shares, assets actually withdrawn.
|
|
47
|
+
function withdraw(uint256 reserveId, uint256 amount, address onBehalfOf) external returns (uint256, uint256);
|
|
48
|
+
|
|
49
|
+
/// @return shares, assets actually borrowed (echoed back, not rounded).
|
|
50
|
+
function borrow(uint256 reserveId, uint256 amount, address onBehalfOf) external returns (uint256, uint256);
|
|
51
|
+
|
|
52
|
+
/// @return shares, total debt value restored. This is always exactly `min(amount, totalDebt)`:
|
|
53
|
+
/// `UserPositionUtils.calculateRestoreAmount` splits `amount` into drawn and premium components
|
|
54
|
+
/// whose sum reconstitutes `amount` exactly (or the full debt, when `amount` covers it), and
|
|
55
|
+
/// `Spoke.repay` pulls precisely that sum from `msg.sender`. It can never exceed the input, so
|
|
56
|
+
/// callers may rely on `repaid <= debtBefore` without a saturating subtraction.
|
|
57
|
+
function repay(uint256 reserveId, uint256 amount, address onBehalfOf) external returns (uint256, uint256);
|
|
58
|
+
|
|
59
|
+
/// @notice Enables/disables `reserveId` as collateral for `onBehalfOf`.
|
|
60
|
+
function setUsingAsCollateral(uint256 reserveId, bool usingAsCollateral, address onBehalfOf) external;
|
|
61
|
+
|
|
62
|
+
/// @notice Reverts if the reserve associated with `reserveId` is not listed.
|
|
63
|
+
function getReserve(uint256 reserveId) external view returns (Reserve memory);
|
|
64
|
+
|
|
65
|
+
/// @notice Reverts if `reserveId` is not listed; does not revert if `dynamicConfigKey` is unset.
|
|
66
|
+
function getDynamicReserveConfig(uint256 reserveId, uint32 dynamicConfigKey)
|
|
67
|
+
external
|
|
68
|
+
view
|
|
69
|
+
returns (DynamicReserveConfig memory);
|
|
70
|
+
|
|
71
|
+
/// @notice Assets supplied by `user` for `reserveId`, already resolved from shares to assets.
|
|
72
|
+
function getUserSuppliedAssets(uint256 reserveId, address user) external view returns (uint256);
|
|
73
|
+
|
|
74
|
+
/// @notice Total debt (drawn + risk-premium components already summed) owed by `user` for `reserveId`.
|
|
75
|
+
function getUserTotalDebt(uint256 reserveId, address user) external view returns (uint256);
|
|
76
|
+
|
|
77
|
+
/// @notice Per-user, per-reserve position data. `dynamicConfigKey` here is the config version
|
|
78
|
+
/// actually pinned to this position -- it can lag `Reserve.dynamicConfigKey` (the reserve's
|
|
79
|
+
/// current version) until the position's next `borrow`/`withdraw`/`setUsingAsCollateral(true)`
|
|
80
|
+
/// refreshes it. Use this, not `getReserve(...).dynamicConfigKey`, to read the config that's
|
|
81
|
+
/// actually enforced for a given position (verified against `Spoke._processUserAccountData`).
|
|
82
|
+
function getUserPosition(uint256 reserveId, address user) external view returns (UserPosition memory);
|
|
83
|
+
|
|
84
|
+
/// @notice The risk premium from `user`'s last position update, in BPS -- a per-position
|
|
85
|
+
/// surcharge on top of the reserve's base drawn rate, based on the quality of the collateral
|
|
86
|
+
/// backing their debt. Not reflected in `getAssetDrawnRate`, which is Hub-wide/market-level.
|
|
87
|
+
function getUserLastRiskPremium(address user) external view returns (uint256);
|
|
88
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {IEulerFactory} from "./dependencies/IEulerFactory.sol";
|
|
5
|
+
import {IEulerVault} from "./dependencies/IEulerVault.sol";
|
|
6
|
+
|
|
7
|
+
/// Library for decoding Euler MM payloads.
|
|
8
|
+
/// - The first 128 bits are the base (PT) vault index.
|
|
9
|
+
/// - The last 128 bits are the quote (debt) vault index.
|
|
10
|
+
library EulerMMPayloadLib {
|
|
11
|
+
function unwrap(bytes32 payload) internal pure returns (uint128 baseIndex, uint128 quoteIndex) {
|
|
12
|
+
baseIndex = uint128(uint256(payload) >> 128);
|
|
13
|
+
quoteIndex = uint128(uint256(payload));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function baseVault(IEulerFactory factory, bytes32 payload) internal view returns (IEulerVault) {
|
|
17
|
+
(uint128 baseIndex,) = unwrap(payload);
|
|
18
|
+
return IEulerVault(factory.proxyList(baseIndex));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function quoteVault(IEulerFactory factory, bytes32 payload) internal view returns (IEulerVault) {
|
|
22
|
+
(, uint128 quoteIndex) = unwrap(payload);
|
|
23
|
+
return IEulerVault(factory.proxyList(quoteIndex));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
//SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {IERC20Metadata as IERC20} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
5
|
+
|
|
6
|
+
import {IEVC} from "./dependencies/IEthereumVaultConnector.sol";
|
|
7
|
+
import {IEulerVault} from "./dependencies/IEulerVault.sol";
|
|
8
|
+
import {IEulerPriceOracle} from "./dependencies/IEulerPriceOracle.sol";
|
|
9
|
+
import {IEulerFactory} from "./dependencies/IEulerFactory.sol";
|
|
10
|
+
|
|
11
|
+
import {MarketCtx} from "../../../../interfaces/IMoneyMarket.sol";
|
|
12
|
+
import {BaseMoneyMarket} from "../../BaseMoneyMarket.sol";
|
|
13
|
+
import {EulerMMPayloadLib} from "./EulerMMPayloadLib.sol";
|
|
14
|
+
import {PMath} from "../../../../core/libraries/math/PMath.sol";
|
|
15
|
+
|
|
16
|
+
contract EulerMoneyMarket is BaseMoneyMarket {
|
|
17
|
+
using PMath for uint256;
|
|
18
|
+
using EulerMMPayloadLib for IEulerFactory;
|
|
19
|
+
|
|
20
|
+
IEVC public immutable EVC;
|
|
21
|
+
IEulerFactory public immutable FACTORY;
|
|
22
|
+
|
|
23
|
+
constructor(address ptLooper, address _evc, address _factory) BaseMoneyMarket(ptLooper) {
|
|
24
|
+
EVC = IEVC(_evc);
|
|
25
|
+
FACTORY = IEulerFactory(_factory);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function _initialize(MarketCtx memory ctx) internal virtual override {
|
|
29
|
+
IEulerVault baseVault = FACTORY.baseVault(ctx.mmPayload);
|
|
30
|
+
IEulerVault quoteVault = FACTORY.quoteVault(ctx.mmPayload);
|
|
31
|
+
require(baseVault.asset() == ctx.collateral, "EulerMoneyMarket: base vault asset mismatch");
|
|
32
|
+
require(quoteVault.asset() == ctx.debtAsset, "EulerMoneyMarket: quote vault asset mismatch");
|
|
33
|
+
|
|
34
|
+
_safeApproveInf(ctx.collateral, address(baseVault));
|
|
35
|
+
_safeApproveInf(ctx.debtAsset, address(quoteVault));
|
|
36
|
+
|
|
37
|
+
EVC.enableCollateral(address(this), address(baseVault));
|
|
38
|
+
EVC.enableController(address(this), address(quoteVault));
|
|
39
|
+
|
|
40
|
+
// Opt in to reward-stream balance tracking on the collateral (PT) vault.
|
|
41
|
+
// No-op if the vault has no active reward streams.
|
|
42
|
+
// Cheap to always call.
|
|
43
|
+
baseVault.enableBalanceForwarder();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// @dev Function is view, it already reflects accrued interest.
|
|
47
|
+
function collateralBalance(MarketCtx memory ctx) public view override returns (uint256 balance) {
|
|
48
|
+
IEulerVault baseVault = FACTORY.baseVault(ctx.mmPayload);
|
|
49
|
+
return baseVault.convertToAssets(baseVault.balanceOf(address(this)));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// @dev Function is view, it already reflects accrued interest.
|
|
53
|
+
function debtBalance(MarketCtx memory ctx) public view override returns (uint256 balance) {
|
|
54
|
+
return FACTORY.quoteVault(ctx.mmPayload).debtOf(address(this));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function _lend(MarketCtx memory ctx, uint256 amount) internal virtual override returns (uint256 actualAmount) {
|
|
58
|
+
_transferIn(ctx.collateral, address(ctx.box), amount);
|
|
59
|
+
IEulerVault baseVault = FACTORY.baseVault(ctx.mmPayload);
|
|
60
|
+
uint256 shares = baseVault.deposit(amount, address(this));
|
|
61
|
+
actualAmount = baseVault.convertToAssets(shares);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function _borrow(MarketCtx memory ctx, uint256 amount) internal virtual override returns (uint256 actualAmount) {
|
|
65
|
+
actualAmount = FACTORY.quoteVault(ctx.mmPayload).borrow(amount, address(ctx.box));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function _repay(MarketCtx memory ctx, uint256 amount, uint256 balanceBefore)
|
|
69
|
+
internal
|
|
70
|
+
virtual
|
|
71
|
+
override
|
|
72
|
+
returns (uint256 actualAmount)
|
|
73
|
+
{
|
|
74
|
+
actualAmount = PMath.min(amount, balanceBefore);
|
|
75
|
+
if (actualAmount > 0) {
|
|
76
|
+
_transferFrom(IERC20(ctx.debtAsset), address(ctx.box), address(this), actualAmount);
|
|
77
|
+
actualAmount = FACTORY.quoteVault(ctx.mmPayload).repay(actualAmount, address(this));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function _withdraw(MarketCtx memory ctx, uint256 amount) internal virtual override returns (uint256 actualAmount) {
|
|
82
|
+
IEulerVault baseVault = FACTORY.baseVault(ctx.mmPayload);
|
|
83
|
+
baseVault.withdraw(amount, address(ctx.box), address(this));
|
|
84
|
+
actualAmount = amount;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/// @notice Sourced both PT and debt price from the debt (quote) vault's oracle and unitOfAccount,
|
|
88
|
+
/// because quote vault is the CONTROLLER vault, and Euler's price oracle is scoped per controller vault,
|
|
89
|
+
/// not global per asset.
|
|
90
|
+
/// Therefore the quote vault is the canonical price source.
|
|
91
|
+
function oraclePtDebtRate(MarketCtx memory ctx) public view override returns (uint256) {
|
|
92
|
+
IEulerVault controllerVault = FACTORY.quoteVault(ctx.mmPayload);
|
|
93
|
+
IEulerPriceOracle oracle = controllerVault.oracle();
|
|
94
|
+
address unit = controllerVault.unitOfAccount();
|
|
95
|
+
|
|
96
|
+
uint256 collatUnit = 10 ** IERC20(ctx.collateral).decimals();
|
|
97
|
+
uint256 debtUnit = 10 ** IERC20(ctx.debtAsset).decimals();
|
|
98
|
+
|
|
99
|
+
uint256 ptPrice = oracle.getQuote(collatUnit, ctx.collateral, unit);
|
|
100
|
+
uint256 debtPrice = oracle.getQuote(debtUnit, ctx.debtAsset, unit);
|
|
101
|
+
|
|
102
|
+
return (ptPrice * debtUnit).divDown(debtPrice * collatUnit);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
//SPDX-License-Identifier: BUSL-1.1
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {IERC20Metadata as IERC20} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
5
|
+
|
|
6
|
+
import {IEulerVault, AmountCap, NO_CAP} from "./dependencies/IEulerVault.sol";
|
|
7
|
+
import {IEulerFactory} from "./dependencies/IEulerFactory.sol";
|
|
8
|
+
import {EulerMMPayloadLib} from "./EulerMMPayloadLib.sol";
|
|
9
|
+
|
|
10
|
+
import {BaseMoneyMarketView} from "../../BaseMoneyMarketView.sol";
|
|
11
|
+
import {IPLoopPositionBox, BoxData} from "../../../../interfaces/IPLoopPositionBox.sol";
|
|
12
|
+
import {Reward} from "../../../../interfaces/IMoneyMarketView.sol";
|
|
13
|
+
import {PMath} from "../../../../core/libraries/math/PMath.sol";
|
|
14
|
+
|
|
15
|
+
contract EulerMoneyMarketView is BaseMoneyMarketView {
|
|
16
|
+
using EulerMMPayloadLib for IEulerFactory;
|
|
17
|
+
using PMath for uint256;
|
|
18
|
+
|
|
19
|
+
IEulerFactory public immutable FACTORY;
|
|
20
|
+
|
|
21
|
+
constructor(string memory _moneyMarketName, address _ptLooper, address _factory)
|
|
22
|
+
BaseMoneyMarketView(_moneyMarketName, _ptLooper)
|
|
23
|
+
{
|
|
24
|
+
FACTORY = IEulerFactory(_factory);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// @notice Known limitations:
|
|
28
|
+
/// `LTVFull` also returns (initialLiquidationLTV, targetTimestamp,
|
|
29
|
+
/// rampDuration) for governance LTV ramps, which are discarded here --
|
|
30
|
+
/// during an active ramp this reports the fully-converged value, which is
|
|
31
|
+
/// *lower* than the actually-in-effect value (conservative/fail-safe
|
|
32
|
+
/// direction, but can cause premature LTV-check reactions upstream).
|
|
33
|
+
/// Documented as a follow-up, not fixed now.
|
|
34
|
+
function _thresholds(IPLoopPositionBox, BoxData memory bd)
|
|
35
|
+
internal
|
|
36
|
+
view
|
|
37
|
+
virtual
|
|
38
|
+
override
|
|
39
|
+
returns (uint256 ltv, uint256 liquidationThreshold)
|
|
40
|
+
{
|
|
41
|
+
IEulerVault baseVault = FACTORY.baseVault(bd.mmPayload);
|
|
42
|
+
IEulerVault quoteVault = FACTORY.quoteVault(bd.mmPayload);
|
|
43
|
+
|
|
44
|
+
(uint256 borrowLTV, uint256 liquidationLTV,,,) = quoteVault.LTVFull(baseVault);
|
|
45
|
+
|
|
46
|
+
// Euler LTV is bps (1e4). Convert to WAD (1e18) by multiplying by 1e14.
|
|
47
|
+
ltv = borrowLTV * 1e14;
|
|
48
|
+
liquidationThreshold = liquidationLTV * 1e14;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function _liquidity(IPLoopPositionBox, BoxData memory bd)
|
|
52
|
+
internal
|
|
53
|
+
view
|
|
54
|
+
virtual
|
|
55
|
+
override
|
|
56
|
+
returns (uint256 borrowing, uint256 lending)
|
|
57
|
+
{
|
|
58
|
+
IEulerVault baseVault = FACTORY.baseVault(bd.mmPayload);
|
|
59
|
+
IEulerVault quoteVault = FACTORY.quoteVault(bd.mmPayload);
|
|
60
|
+
|
|
61
|
+
lending = PMath.min(baseVault.maxDeposit(address(0)), IERC20(bd.spokePt).totalSupply());
|
|
62
|
+
|
|
63
|
+
(, AmountCap borrowCapAmt) = quoteVault.caps();
|
|
64
|
+
uint256 borrowCap = borrowCapAmt.resolve();
|
|
65
|
+
uint256 quoteVaultCash = quoteVault.cash();
|
|
66
|
+
|
|
67
|
+
if (borrowCap == NO_CAP) {
|
|
68
|
+
borrowing = quoteVaultCash;
|
|
69
|
+
} else {
|
|
70
|
+
uint256 borrowRemain = borrowCap.subMax0(quoteVault.totalBorrows());
|
|
71
|
+
borrowing = PMath.min(quoteVaultCash, borrowRemain);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function _rates(IPLoopPositionBox, BoxData memory bd)
|
|
76
|
+
internal
|
|
77
|
+
view
|
|
78
|
+
virtual
|
|
79
|
+
override
|
|
80
|
+
returns (uint256 borrowing, uint256 lending)
|
|
81
|
+
{
|
|
82
|
+
borrowing = _borrowingApy(FACTORY.quoteVault(bd.mmPayload));
|
|
83
|
+
lending = _supplyApy(FACTORY.baseVault(bd.mmPayload));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function _borrowingApy(IEulerVault vault) internal view returns (uint256) {
|
|
87
|
+
// interestRate() is RAY (1e27). Convert to WAD (1e18) by dividing by 1e9.
|
|
88
|
+
return _apy({rate: vault.interestRate() / 1e9, perSeconds: 1});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
uint256 private constant EULER_CONFIG_SCALE = 1e4;
|
|
92
|
+
|
|
93
|
+
// See Euler's `UtilLens._computeAPYs`: https://github.com/euler-xyz/evk-periphery/blob/6e41cbec944920b30dfcccbcbfb563b4c4d3d932/src/Lens/Utils.sol#L106
|
|
94
|
+
function _supplyApy(IEulerVault vault) internal view returns (uint256) {
|
|
95
|
+
uint256 borrows = vault.totalBorrows();
|
|
96
|
+
uint256 totalAssets = vault.cash() + borrows;
|
|
97
|
+
if (totalAssets == 0) return 0;
|
|
98
|
+
|
|
99
|
+
uint256 borrowingApy = _borrowingApy(vault);
|
|
100
|
+
uint256 interestFee = vault.interestFee();
|
|
101
|
+
|
|
102
|
+
// verbatim formula: https://github.com/euler-xyz/evk-periphery/blob/6e41cbec944920b30dfcccbcbfb563b4c4d3d932/src/Lens/Utils.sol#L120
|
|
103
|
+
return borrowingApy * borrows * (EULER_CONFIG_SCALE - interestFee) / totalAssets / EULER_CONFIG_SCALE;
|
|
104
|
+
}
|
|
105
|
+
}
|