@pendle/core-v2 0.2.2 → 0.3.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/contracts/SuperComposableYield/ISuperComposableYield.sol +1 -5
- package/contracts/SuperComposableYield/implementations/RewardManager.sol +2 -2
- package/contracts/SuperComposableYield/implementations/SCYBase.sol +2 -2
- package/contracts/SuperComposableYield/implementations/SCYBaseWithRewards.sol +4 -7
- package/contracts/core/PendleMarket.sol +24 -22
- package/contracts/core/PendleMarketFactory.sol +7 -6
- package/contracts/core/PendleSCYImpl/PendleBenQiErc20SCY.sol +1 -1
- package/contracts/core/PendleSCYImpl/PendleBtrflySCY.sol +2 -7
- package/contracts/core/PendleSCYImpl/PendleYearnVaultSCY.sol +2 -7
- package/contracts/core/PendleYieldContractFactory.sol +12 -7
- package/contracts/core/PendleYieldToken.sol +93 -55
- package/contracts/core/router/PendleRouterCoreUpg.sol +35 -21
- package/contracts/core/router/PendleRouterProxy.sol +12 -6
- package/contracts/core/router/{PendleRouterStatic.sol → PendleRouterStaticUpg.sol} +43 -18
- package/contracts/core/router/PendleRouterYTUpg.sol +47 -13
- package/contracts/core/router/base/PendleRouterOTBaseUpg.sol +22 -32
- package/contracts/core/router/base/PendleRouterSCYAndForgeBaseUpg.sol +1 -1
- package/contracts/core/router/base/PendleRouterYTBaseUpg.sol +62 -43
- package/contracts/interfaces/IPMarket.sol +4 -2
- package/contracts/interfaces/{IPPermissionsV2.sol → IPPermissionsV2Upg.sol} +1 -1
- package/contracts/interfaces/IPRouterCore.sol +24 -21
- package/contracts/interfaces/IPRouterStatic.sol +10 -1
- package/contracts/interfaces/IPRouterYT.sol +16 -4
- package/contracts/interfaces/IPYieldContractFactory.sol +4 -0
- package/contracts/interfaces/IPYieldToken.sol +2 -4
- package/contracts/libraries/SCYIndex.sol +3 -3
- package/contracts/libraries/math/LogExpMath.sol +1 -90
- package/contracts/libraries/math/MarketApproxLib.sol +336 -94
- package/contracts/libraries/math/MarketMathAux.sol +101 -0
- package/contracts/libraries/math/MarketMathCore.sol +423 -0
- package/contracts/libraries/math/Math.sol +144 -0
- package/contracts/{core/misc → periphery}/PendleJoeSwapHelperUpg.sol +2 -2
- package/contracts/periphery/{PermissionsV2.sol → PermissionsV2Upg.sol} +2 -2
- package/package.json +1 -1
- package/typechain-types/IPMarket.ts +22 -11
- package/typechain-types/IPRouterCore.ts +68 -58
- package/typechain-types/IPRouterStatic.ts +52 -0
- package/typechain-types/IPRouterYT.ts +96 -12
- package/typechain-types/IPYieldContractFactory.ts +32 -0
- package/typechain-types/IPYieldToken.ts +14 -48
- package/typechain-types/IPermissionsV2Upg.ts +87 -0
- package/typechain-types/ISuperComposableYield.ts +3 -11
- package/typechain-types/OwnableUpgradeable.ts +165 -0
- package/typechain-types/PendleAaveV3SCY.ts +3 -11
- package/typechain-types/PendleBenQiErc20SCY.ts +3 -11
- package/typechain-types/PendleBtrflyScy.ts +3 -11
- package/typechain-types/PendleMarket.ts +22 -11
- package/typechain-types/PendleRouterCoreUpg.ts +66 -48
- package/typechain-types/PendleRouterProxy.ts +15 -118
- package/typechain-types/PendleRouterStaticUpg.ts +92 -0
- package/typechain-types/PendleRouterYTUpg.ts +96 -12
- package/typechain-types/PendleYearnVaultScy.ts +3 -11
- package/typechain-types/PendleYieldContractFactory.ts +40 -113
- package/typechain-types/PendleYieldToken.ts +106 -7
- package/typechain-types/PermissionsV2Upg.ts +87 -0
- package/typechain-types/SCYBase.ts +3 -11
- package/typechain-types/SCYBaseWithRewards.ts +3 -11
- package/typechain-types/factories/IPMarket__factory.ts +8 -2
- package/typechain-types/factories/IPRouterCore__factory.ts +38 -23
- package/typechain-types/factories/IPRouterStatic__factory.ts +29 -0
- package/typechain-types/factories/IPRouterYT__factory.ts +65 -6
- package/typechain-types/factories/IPYieldContractFactory__factory.ts +38 -0
- package/typechain-types/factories/IPYieldToken__factory.ts +1 -20
- package/typechain-types/factories/IPermissionsV2Upg__factory.ts +39 -0
- package/typechain-types/factories/ISuperComposableYield__factory.ts +0 -5
- package/typechain-types/factories/OwnableUpgradeable__factory.ts +78 -0
- package/typechain-types/factories/PendleAaveV3SCY__factory.ts +1 -6
- package/typechain-types/factories/PendleBenQiErc20SCY__factory.ts +1 -6
- package/typechain-types/factories/PendleBtrflyScy__factory.ts +1 -6
- package/typechain-types/factories/PendleMarketFactory__factory.ts +1 -1
- package/typechain-types/factories/PendleMarket__factory.ts +9 -3
- package/typechain-types/factories/PendleRouterCoreUpg__factory.ts +26 -11
- package/typechain-types/factories/PendleRouterProxy__factory.ts +14 -67
- package/typechain-types/factories/PendleRouterStaticUpg__factory.ts +63 -17
- package/typechain-types/factories/PendleRouterYTUpg__factory.ts +66 -7
- package/typechain-types/factories/PendleYearnVaultScy__factory.ts +1 -6
- package/typechain-types/factories/PendleYieldContractFactory__factory.ts +39 -54
- package/typechain-types/factories/PendleYieldToken__factory.ts +53 -2
- package/typechain-types/factories/PermissionsV2Upg__factory.ts +39 -0
- package/typechain-types/factories/SCYBaseWithRewards__factory.ts +0 -5
- package/typechain-types/factories/SCYBase__factory.ts +0 -5
- package/typechain-types/hardhat.d.ts +26 -53
- package/typechain-types/index.ts +8 -14
- package/contracts/core/misc/BoringOwnable.sol +0 -62
- package/contracts/core/misc/BoringOwnableUpg.sol +0 -64
- package/contracts/libraries/math/FixedPoint.sol +0 -205
- package/contracts/libraries/math/MarketMathLib.sol +0 -614
|
@@ -1,614 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
-
pragma solidity 0.8.9;
|
|
3
|
-
|
|
4
|
-
import "../../interfaces/IPOwnershipToken.sol";
|
|
5
|
-
import "../../interfaces/IPYieldToken.sol";
|
|
6
|
-
import "../../SuperComposableYield/ISuperComposableYield.sol";
|
|
7
|
-
import "../../interfaces/IPMarket.sol";
|
|
8
|
-
import "./FixedPoint.sol";
|
|
9
|
-
import "./LogExpMath.sol";
|
|
10
|
-
import "../SCYIndex.sol";
|
|
11
|
-
|
|
12
|
-
// if this is changed, change deepCloneMarket as well
|
|
13
|
-
struct MarketParameters {
|
|
14
|
-
int256 totalOt;
|
|
15
|
-
int256 totalScy;
|
|
16
|
-
int256 totalLp;
|
|
17
|
-
uint256 oracleRate;
|
|
18
|
-
/// immutable variables ///
|
|
19
|
-
int256 scalarRoot;
|
|
20
|
-
uint256 feeRateRoot;
|
|
21
|
-
uint256 rateOracleTimeWindow;
|
|
22
|
-
uint256 expiry;
|
|
23
|
-
int256 reserveFeePercent; // base 100
|
|
24
|
-
/// last trade data ///
|
|
25
|
-
uint256 lastImpliedRate;
|
|
26
|
-
uint256 lastTradeTime;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
struct MarketStorage {
|
|
30
|
-
int128 totalOt;
|
|
31
|
-
int128 totalScy;
|
|
32
|
-
uint112 lastImpliedRate;
|
|
33
|
-
uint112 oracleRate;
|
|
34
|
-
uint32 lastTradeTime;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// solhint-disable ordering
|
|
38
|
-
library MarketMathLib {
|
|
39
|
-
using FixedPoint for uint256;
|
|
40
|
-
using FixedPoint for int256;
|
|
41
|
-
using LogExpMath for int256;
|
|
42
|
-
using SCYIndexLib for SCYIndex;
|
|
43
|
-
|
|
44
|
-
struct NetTo {
|
|
45
|
-
int256 toAccount;
|
|
46
|
-
int256 toMarket;
|
|
47
|
-
int256 toReserve;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
struct ExecuteTradeSlot {
|
|
51
|
-
uint256 timeToExpiry;
|
|
52
|
-
int256 rateScalar;
|
|
53
|
-
int256 totalAsset;
|
|
54
|
-
int256 rateAnchor;
|
|
55
|
-
int256 preFeeExchangeRate;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
int256 internal constant MINIMUM_LIQUIDITY = 10**3;
|
|
59
|
-
int256 internal constant PERCENTAGE_DECIMALS = 100;
|
|
60
|
-
uint256 internal constant DAY = 86400;
|
|
61
|
-
uint256 internal constant IMPLIED_RATE_TIME = 360 * DAY;
|
|
62
|
-
|
|
63
|
-
int256 internal constant MAX_MARKET_PROPORTION = (1e18 * 96) / 100;
|
|
64
|
-
|
|
65
|
-
function addLiquidity(
|
|
66
|
-
MarketParameters memory market,
|
|
67
|
-
SCYIndex index,
|
|
68
|
-
uint256 scyDesired,
|
|
69
|
-
uint256 otDesired
|
|
70
|
-
)
|
|
71
|
-
internal
|
|
72
|
-
pure
|
|
73
|
-
returns (
|
|
74
|
-
uint256 lpToReserve,
|
|
75
|
-
uint256 lpToAccount,
|
|
76
|
-
uint256 scyUsed,
|
|
77
|
-
uint256 otUsed
|
|
78
|
-
)
|
|
79
|
-
{
|
|
80
|
-
(
|
|
81
|
-
int256 _lpToReserve,
|
|
82
|
-
int256 _lpToAccount,
|
|
83
|
-
int256 _scyUsed,
|
|
84
|
-
int256 _otUsed
|
|
85
|
-
) = _addLiquidity(market, index, scyDesired.Int(), otDesired.Int());
|
|
86
|
-
|
|
87
|
-
lpToReserve = _lpToReserve.Uint();
|
|
88
|
-
lpToAccount = _lpToAccount.Uint();
|
|
89
|
-
scyUsed = _scyUsed.Uint();
|
|
90
|
-
otUsed = _otUsed.Uint();
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function removeLiquidity(MarketParameters memory market, uint256 lpToRemove)
|
|
94
|
-
internal
|
|
95
|
-
pure
|
|
96
|
-
returns (uint256 scyToAccount, uint256 otToAccount)
|
|
97
|
-
{
|
|
98
|
-
(int256 _scyToAccount, int256 _otToAccount) = _removeLiquidity(market, lpToRemove.Int());
|
|
99
|
-
|
|
100
|
-
scyToAccount = _scyToAccount.Uint();
|
|
101
|
-
otToAccount = _otToAccount.Uint();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function swapExactOtForScy(
|
|
105
|
-
MarketParameters memory market,
|
|
106
|
-
SCYIndex index,
|
|
107
|
-
uint256 exactOtToMarket,
|
|
108
|
-
uint256 blockTime
|
|
109
|
-
) internal pure returns (uint256 netScyToAccount, uint256 netScyToReserve) {
|
|
110
|
-
(int256 _netScyToAccount, int256 _netScyToReserve) = _executeTrade(
|
|
111
|
-
market,
|
|
112
|
-
index,
|
|
113
|
-
exactOtToMarket.neg(),
|
|
114
|
-
blockTime
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
netScyToAccount = _netScyToAccount.Uint();
|
|
118
|
-
netScyToReserve = _netScyToReserve.Uint();
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function swapScyForExactOt(
|
|
122
|
-
MarketParameters memory market,
|
|
123
|
-
SCYIndex index,
|
|
124
|
-
uint256 exactOtToAccount,
|
|
125
|
-
uint256 blockTime
|
|
126
|
-
) internal pure returns (uint256 netScyToMarket, uint256 netScyToReserve) {
|
|
127
|
-
(int256 _netScyToAccount, int256 _netScyToReserve) = _executeTrade(
|
|
128
|
-
market,
|
|
129
|
-
index,
|
|
130
|
-
exactOtToAccount.Int(),
|
|
131
|
-
blockTime
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
netScyToMarket = _netScyToAccount.neg().Uint();
|
|
135
|
-
netScyToReserve = _netScyToReserve.Uint();
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function setInitialImpliedRate(
|
|
139
|
-
MarketParameters memory market,
|
|
140
|
-
SCYIndex index,
|
|
141
|
-
int256 initialAnchor,
|
|
142
|
-
uint256 blockTime
|
|
143
|
-
) internal pure {
|
|
144
|
-
require(blockTime < market.expiry, "market expired");
|
|
145
|
-
int256 totalAsset = index.scyToAsset(market.totalScy);
|
|
146
|
-
uint256 timeToExpiry = market.expiry - blockTime;
|
|
147
|
-
int256 rateScalar = _getRateScalar(market, timeToExpiry);
|
|
148
|
-
market.lastImpliedRate = _getImpliedRate(
|
|
149
|
-
market.totalOt,
|
|
150
|
-
totalAsset,
|
|
151
|
-
rateScalar,
|
|
152
|
-
initialAnchor,
|
|
153
|
-
market.expiry - blockTime
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function updateNewRateOracle(MarketParameters memory market, uint256 blockTime)
|
|
158
|
-
internal
|
|
159
|
-
pure
|
|
160
|
-
returns (uint256)
|
|
161
|
-
{
|
|
162
|
-
// require(rateOracleTimeWindow > 0); // dev: update rate oracle, time window zero
|
|
163
|
-
|
|
164
|
-
// This can occur when using a view function get to a market state in the past
|
|
165
|
-
if (market.lastTradeTime > blockTime) {
|
|
166
|
-
market.oracleRate = market.lastImpliedRate;
|
|
167
|
-
return market.oracleRate;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
uint256 timeDiff = blockTime - market.lastTradeTime;
|
|
171
|
-
if (timeDiff > market.rateOracleTimeWindow) {
|
|
172
|
-
// If past the time window just return the market.lastImpliedRate
|
|
173
|
-
market.oracleRate = market.lastImpliedRate;
|
|
174
|
-
return market.oracleRate;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// (currentTs - previousTs) / timeWindow
|
|
178
|
-
uint256 lastTradeWeight = timeDiff.divDown(market.rateOracleTimeWindow);
|
|
179
|
-
|
|
180
|
-
// 1 - (currentTs - previousTs) / timeWindow
|
|
181
|
-
uint256 oracleWeight = FixedPoint.ONE - lastTradeWeight;
|
|
182
|
-
|
|
183
|
-
uint256 newOracleRate = market.lastTradeTime.mulDown(lastTradeWeight) +
|
|
184
|
-
market.oracleRate.mulDown(oracleWeight);
|
|
185
|
-
|
|
186
|
-
market.oracleRate = newOracleRate;
|
|
187
|
-
return market.oracleRate;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/*///////////////////////////////////////////////////////////////
|
|
191
|
-
END OF HIGH LEVEL FUNCTIONS
|
|
192
|
-
//////////////////////////////////////////////////////////////*/
|
|
193
|
-
|
|
194
|
-
function _addLiquidity(
|
|
195
|
-
MarketParameters memory market,
|
|
196
|
-
SCYIndex index,
|
|
197
|
-
int256 scyDesired,
|
|
198
|
-
int256 otDesired
|
|
199
|
-
)
|
|
200
|
-
private
|
|
201
|
-
pure
|
|
202
|
-
returns (
|
|
203
|
-
int256 lpToReserve,
|
|
204
|
-
int256 lpToAccount,
|
|
205
|
-
int256 scyUsed,
|
|
206
|
-
int256 otUsed
|
|
207
|
-
)
|
|
208
|
-
{
|
|
209
|
-
require(scyDesired > 0 && otDesired > 0, "ZERO_AMOUNTS");
|
|
210
|
-
|
|
211
|
-
if (market.totalLp == 0) {
|
|
212
|
-
lpToAccount = index.scyToAsset(scyDesired).subNoNeg(MINIMUM_LIQUIDITY);
|
|
213
|
-
lpToReserve = MINIMUM_LIQUIDITY;
|
|
214
|
-
scyUsed = scyDesired;
|
|
215
|
-
otUsed = otDesired;
|
|
216
|
-
} else {
|
|
217
|
-
int256 netLpByOt = (otDesired * market.totalLp) / market.totalOt;
|
|
218
|
-
int256 netLpByScy = (scyDesired * market.totalLp) / market.totalScy;
|
|
219
|
-
if (netLpByOt < netLpByScy) {
|
|
220
|
-
lpToAccount = netLpByOt;
|
|
221
|
-
otUsed = otDesired;
|
|
222
|
-
scyUsed = (market.totalScy * lpToAccount) / market.totalLp;
|
|
223
|
-
} else {
|
|
224
|
-
lpToAccount = netLpByScy;
|
|
225
|
-
scyUsed = scyDesired;
|
|
226
|
-
otUsed = (market.totalOt * lpToAccount) / market.totalLp;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
require(lpToAccount > 0, "INSUFFICIENT_LIQUIDITY_MINTED");
|
|
231
|
-
|
|
232
|
-
market.totalScy += scyUsed;
|
|
233
|
-
market.totalOt += otUsed;
|
|
234
|
-
market.totalLp += lpToAccount + lpToReserve;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function _removeLiquidity(MarketParameters memory market, int256 lpToRemove)
|
|
238
|
-
private
|
|
239
|
-
pure
|
|
240
|
-
returns (int256 scyToAccount, int256 otToAccount)
|
|
241
|
-
{
|
|
242
|
-
require(lpToRemove > 0, "invalid lp amount");
|
|
243
|
-
|
|
244
|
-
scyToAccount = (lpToRemove * market.totalScy) / market.totalLp;
|
|
245
|
-
otToAccount = (lpToRemove * market.totalOt) / market.totalLp;
|
|
246
|
-
|
|
247
|
-
market.totalLp = market.totalLp.subNoNeg(lpToRemove);
|
|
248
|
-
market.totalOt = market.totalOt.subNoNeg(otToAccount);
|
|
249
|
-
market.totalScy = market.totalScy.subNoNeg(scyToAccount);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/// @notice Calculates the asset amount the results from trading otToAccount with the market. A positive
|
|
253
|
-
/// otToAccount is equivalent of swapping OT into the market, a negative is taking OT out.
|
|
254
|
-
/// Updates the market state in memory.
|
|
255
|
-
/// @param market the current market state
|
|
256
|
-
/// @param otToAccount the OT amount that will be deposited into the user's portfolio. The net change
|
|
257
|
-
/// to the market is in the opposite direction.
|
|
258
|
-
/// @return netScyToAccount netScyToReserve
|
|
259
|
-
function _executeTrade(
|
|
260
|
-
MarketParameters memory market,
|
|
261
|
-
SCYIndex index,
|
|
262
|
-
int256 otToAccount,
|
|
263
|
-
uint256 blockTime
|
|
264
|
-
) private pure returns (int256 netScyToAccount, int256 netScyToReserve) {
|
|
265
|
-
require(blockTime < market.expiry, "market expired");
|
|
266
|
-
|
|
267
|
-
ExecuteTradeSlot memory slot;
|
|
268
|
-
slot.timeToExpiry = market.expiry - blockTime;
|
|
269
|
-
|
|
270
|
-
// We return false if there is not enough Ot to support this trade.
|
|
271
|
-
// if otToAccount > 0 and totalOt - otToAccount <= 0 then the trade will fail
|
|
272
|
-
// if otToAccount < 0 and totalOt > 0 then this will always pass
|
|
273
|
-
require(market.totalOt > otToAccount, "insufficient liquidity");
|
|
274
|
-
|
|
275
|
-
// Calculates initial rate factors for the trade
|
|
276
|
-
(slot.rateScalar, slot.totalAsset, slot.rateAnchor) = _getExchangeRateFactors(
|
|
277
|
-
market,
|
|
278
|
-
index,
|
|
279
|
-
slot.timeToExpiry
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
// Calculates the exchange rate from Asset to OT before any liquidity fees
|
|
283
|
-
// are applied
|
|
284
|
-
slot.preFeeExchangeRate = _getExchangeRate(
|
|
285
|
-
market.totalOt,
|
|
286
|
-
slot.totalAsset,
|
|
287
|
-
slot.rateScalar,
|
|
288
|
-
slot.rateAnchor,
|
|
289
|
-
otToAccount
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
NetTo memory netAsset;
|
|
293
|
-
// Given the exchange rate, returns the netAsset amounts to apply to each of the
|
|
294
|
-
// three relevant balances.
|
|
295
|
-
(
|
|
296
|
-
netAsset.toAccount,
|
|
297
|
-
netAsset.toMarket,
|
|
298
|
-
netAsset.toReserve
|
|
299
|
-
) = _getNetAssetAmountsToAddresses(
|
|
300
|
-
market.feeRateRoot,
|
|
301
|
-
slot.preFeeExchangeRate,
|
|
302
|
-
otToAccount,
|
|
303
|
-
slot.timeToExpiry,
|
|
304
|
-
market.reserveFeePercent
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
//////////////////////////////////
|
|
308
|
-
/// Update params in the market///
|
|
309
|
-
///////////////////////////////x///
|
|
310
|
-
// Set the new implied interest rate after the trade has taken effect, this
|
|
311
|
-
// will be used to calculate the next trader's interest rate.
|
|
312
|
-
market.totalOt = market.totalOt.subNoNeg(otToAccount);
|
|
313
|
-
market.lastImpliedRate = _getImpliedRate(
|
|
314
|
-
market.totalOt,
|
|
315
|
-
slot.totalAsset + netAsset.toMarket,
|
|
316
|
-
slot.rateScalar,
|
|
317
|
-
slot.rateAnchor,
|
|
318
|
-
slot.timeToExpiry
|
|
319
|
-
);
|
|
320
|
-
|
|
321
|
-
// It's technically possible that the implied rate is actually exactly zero (or
|
|
322
|
-
// more accurately the natural log rounds down to zero) but we will still fail
|
|
323
|
-
// in this case. If this does happen we may assume that markets are not initialized.
|
|
324
|
-
require(market.lastImpliedRate != 0, "zero impliedRate");
|
|
325
|
-
|
|
326
|
-
(netScyToAccount, netScyToReserve) = _setNewMarketState(
|
|
327
|
-
market,
|
|
328
|
-
index,
|
|
329
|
-
netAsset.toAccount,
|
|
330
|
-
netAsset.toMarket,
|
|
331
|
-
netAsset.toReserve,
|
|
332
|
-
blockTime
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/// @notice Returns factors for calculating exchange rates
|
|
337
|
-
/// @return rateScalar a value in rate precision that defines the slope of the line
|
|
338
|
-
/// @return totalAsset the converted SCY to Asset for calculatin the exchange rates for the trade
|
|
339
|
-
/// @return rateAnchor an offset from the x axis to maintain interest rate continuity over time
|
|
340
|
-
function _getExchangeRateFactors(
|
|
341
|
-
MarketParameters memory market,
|
|
342
|
-
SCYIndex index,
|
|
343
|
-
uint256 timeToExpiry
|
|
344
|
-
)
|
|
345
|
-
private
|
|
346
|
-
pure
|
|
347
|
-
returns (
|
|
348
|
-
int256 rateScalar,
|
|
349
|
-
int256 totalAsset,
|
|
350
|
-
int256 rateAnchor
|
|
351
|
-
)
|
|
352
|
-
{
|
|
353
|
-
rateScalar = _getRateScalar(market, timeToExpiry);
|
|
354
|
-
totalAsset = index.scyToAsset(market.totalScy);
|
|
355
|
-
|
|
356
|
-
require(market.totalOt != 0 && totalAsset != 0, "invalid market state");
|
|
357
|
-
|
|
358
|
-
// Get the rateAnchor given the market state, this will establish the baseline for where
|
|
359
|
-
// the exchange rate is set.
|
|
360
|
-
{
|
|
361
|
-
rateAnchor = _getRateAnchor(
|
|
362
|
-
market.totalOt,
|
|
363
|
-
market.lastImpliedRate,
|
|
364
|
-
totalAsset,
|
|
365
|
-
rateScalar,
|
|
366
|
-
timeToExpiry
|
|
367
|
-
);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/// @dev Returns net Asset amounts to the account, the market and the reserve. netAssetToReserve
|
|
372
|
-
/// is actually the fee portion of the trade
|
|
373
|
-
/// @return netAssetToAccount this is a positive or negative amount of Asset change to the account
|
|
374
|
-
/// @return netAssetToMarket this is a positive or negative amount of Asset change in the market
|
|
375
|
-
/// @return netAssetToReserve this is always a positive amount of Asset accrued to the reserve
|
|
376
|
-
function _getNetAssetAmountsToAddresses(
|
|
377
|
-
uint256 feeRateRoot,
|
|
378
|
-
int256 preFeeExchangeRate,
|
|
379
|
-
int256 otToAccount,
|
|
380
|
-
uint256 timeToExpiry,
|
|
381
|
-
int256 reserveFeePercent
|
|
382
|
-
)
|
|
383
|
-
private
|
|
384
|
-
pure
|
|
385
|
-
returns (
|
|
386
|
-
int256 netAssetToAccount,
|
|
387
|
-
int256 netAssetToMarket,
|
|
388
|
-
int256 netAssetToReserve
|
|
389
|
-
)
|
|
390
|
-
{
|
|
391
|
-
// Fees are specified in basis points which is an rate precision denomination. We convert this to
|
|
392
|
-
// an exchange rate denomination for the given time to expiry. (i.e. get e^(fee * t) and multiply
|
|
393
|
-
// or divide depending on the side of the trade).
|
|
394
|
-
// tradeExchangeRate = exp((tradeInterestRateNoFee +/- fee) * timeToExpiry)
|
|
395
|
-
// tradeExchangeRate = tradeExchangeRateNoFee (* or /) exp(fee * timeToExpiry)
|
|
396
|
-
// Asset = OT / exchangeRate, exchangeRate > 1
|
|
397
|
-
int256 preFeeAssetToAccount = otToAccount.divDown(preFeeExchangeRate).neg();
|
|
398
|
-
int256 fee = _getExchangeRateFromImpliedRate(feeRateRoot, timeToExpiry);
|
|
399
|
-
|
|
400
|
-
if (otToAccount > 0) {
|
|
401
|
-
// swapping SCY for OT
|
|
402
|
-
|
|
403
|
-
// Dividing reduces exchange rate, swapping SCY to OT means account should receive less OT
|
|
404
|
-
int256 postFeeExchangeRate = preFeeExchangeRate.divDown(fee);
|
|
405
|
-
// It's possible that the fee pushes exchange rates into negative territory. This is not possible
|
|
406
|
-
// when swapping OT to SCY. If this happens then the trade has failed.
|
|
407
|
-
require(postFeeExchangeRate >= FixedPoint.ONE_INT, "exchange rate below 1");
|
|
408
|
-
|
|
409
|
-
// assetToAccount = -(otToAccount / exchangeRate)
|
|
410
|
-
// postFeeExchangeRate = preFeeExchangeRate / feeExchangeRate
|
|
411
|
-
// preFeeAssetToAccount = -(otToAccount / preFeeExchangeRate)
|
|
412
|
-
// postFeeAssetToAccount = -(otToAccount / postFeeExchangeRate)
|
|
413
|
-
// netFee = preFeeAssetToAccount - postFeeAssetToAccount
|
|
414
|
-
// netFee = (otToAccount / postFeeExchangeRate) - (otToAccount / preFeeExchangeRate)
|
|
415
|
-
// netFee = ((otToAccount * feeExchangeRate) / preFeeExchangeRate) - (otToAccount / preFeeExchangeRate)
|
|
416
|
-
// netFee = (otToAccount / preFeeExchangeRate) * (feeExchangeRate - 1)
|
|
417
|
-
// netFee = -(preFeeAssetToAccount) * (feeExchangeRate - 1)
|
|
418
|
-
// netFee = preFeeAssetToAccount * (1 - feeExchangeRate)
|
|
419
|
-
// RATE_PRECISION - fee will be negative here, preFeeAssetToAccount < 0, fee > 0
|
|
420
|
-
fee = preFeeAssetToAccount.mulDown(FixedPoint.ONE_INT - fee);
|
|
421
|
-
} else {
|
|
422
|
-
// swapping OT for SCY
|
|
423
|
-
|
|
424
|
-
// assetToAccount = -(otToAccount / exchangeRate)
|
|
425
|
-
// postFeeExchangeRate = preFeeExchangeRate * feeExchangeRate
|
|
426
|
-
|
|
427
|
-
// netFee = preFeeAssetToAccount - postFeeAssetToAccount
|
|
428
|
-
// netFee = (otToAccount / postFeeExchangeRate) - (otToAccount / preFeeExchangeRate)
|
|
429
|
-
// netFee = ((otToAccount / (feeExchangeRate * preFeeExchangeRate)) - (otToAccount / preFeeExchangeRate)
|
|
430
|
-
// netFee = (otToAccount / preFeeExchangeRate) * (1 / feeExchangeRate - 1)
|
|
431
|
-
// netFee = preFeeAssetToAccount * ((1 - feeExchangeRate) / feeExchangeRate)
|
|
432
|
-
// NOTE: preFeeAssetToAccount is negative in this branch so we negate it to ensure that fee is a positive number
|
|
433
|
-
// preFee * (1 - fee) / fee will be negative, use neg() to flip to positive
|
|
434
|
-
// RATE_PRECISION - fee will be negative
|
|
435
|
-
fee = ((preFeeAssetToAccount * (FixedPoint.ONE_INT - fee)) / fee).neg();
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
netAssetToReserve = (fee * reserveFeePercent) / PERCENTAGE_DECIMALS;
|
|
439
|
-
|
|
440
|
-
// postFeeAssetToAccount = preFeeAssetToAccount - fee
|
|
441
|
-
netAssetToAccount = preFeeAssetToAccount - fee;
|
|
442
|
-
netAssetToMarket = (preFeeAssetToAccount - fee + netAssetToReserve).neg();
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/// @notice Sets the new market state
|
|
446
|
-
/// @return netScyToAccount the positive or negative change in asset scy to the account
|
|
447
|
-
/// @return netScyToReserve the positive amount of scy that accrues to the reserve
|
|
448
|
-
function _setNewMarketState(
|
|
449
|
-
MarketParameters memory market,
|
|
450
|
-
SCYIndex index,
|
|
451
|
-
int256 netAssetToAccount,
|
|
452
|
-
int256 netAssetToMarket,
|
|
453
|
-
int256 netAssetToReserve,
|
|
454
|
-
uint256 blockTime
|
|
455
|
-
) private pure returns (int256 netScyToAccount, int256 netScyToReserve) {
|
|
456
|
-
int256 netScyToMarket = index.assetToScy(netAssetToMarket);
|
|
457
|
-
// Set storage checks that total asset scy is above zero
|
|
458
|
-
market.totalScy = market.totalScy + netScyToMarket;
|
|
459
|
-
|
|
460
|
-
market.lastTradeTime = blockTime;
|
|
461
|
-
netScyToReserve = index.assetToScy(netAssetToReserve);
|
|
462
|
-
netScyToAccount = index.assetToScy(netAssetToAccount);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
/// @notice Rate anchors update as the market gets closer to expiry. Rate anchors are not comparable
|
|
466
|
-
/// across time or markets but implied rates are. The goal here is to ensure that the implied rate
|
|
467
|
-
/// before and after the rate anchor update is the same. Therefore, the market will trade at the same implied
|
|
468
|
-
/// rate that it last traded at. If these anchors do not update then it opens up the opportunity for arbitrage
|
|
469
|
-
/// which will hurt the liquidity providers.
|
|
470
|
-
///
|
|
471
|
-
/// The rate anchor will update as the market rolls down to expiry. The calculation is:
|
|
472
|
-
/// newExchangeRate = e^(lastImpliedRate * timeToExpiry / Constants.IMPLIED_RATE_TIME)
|
|
473
|
-
/// newAnchor = newExchangeRate - ln((proportion / (1 - proportion)) / rateScalar
|
|
474
|
-
///
|
|
475
|
-
/// where:
|
|
476
|
-
/// lastImpliedRate = ln(exchangeRate') * (Constants.IMPLIED_RATE_TIME / timeToExpiry')
|
|
477
|
-
/// (calculated when the last trade in the market was made)
|
|
478
|
-
/// @return rateAnchor the new rateAnchor
|
|
479
|
-
function _getRateAnchor(
|
|
480
|
-
int256 totalOt,
|
|
481
|
-
uint256 lastImpliedRate,
|
|
482
|
-
int256 totalAsset,
|
|
483
|
-
int256 rateScalar,
|
|
484
|
-
uint256 timeToExpiry
|
|
485
|
-
) private pure returns (int256 rateAnchor) {
|
|
486
|
-
// This is the exchange rate at the new time to expiry
|
|
487
|
-
int256 newExchangeRate = _getExchangeRateFromImpliedRate(lastImpliedRate, timeToExpiry);
|
|
488
|
-
|
|
489
|
-
require(newExchangeRate >= FixedPoint.ONE_INT, "exchange rate below 1");
|
|
490
|
-
|
|
491
|
-
{
|
|
492
|
-
// totalOt / (totalOt + totalAsset)
|
|
493
|
-
int256 proportion = totalOt.divDown(totalOt + totalAsset);
|
|
494
|
-
|
|
495
|
-
int256 lnProportion = _logProportion(proportion);
|
|
496
|
-
|
|
497
|
-
// newExchangeRate - ln(proportion / (1 - proportion)) / rateScalar
|
|
498
|
-
rateAnchor = newExchangeRate - lnProportion.divDown(rateScalar);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
/// @notice Calculates the current market implied rate.
|
|
503
|
-
/// @return impliedRate the implied rate
|
|
504
|
-
function _getImpliedRate(
|
|
505
|
-
int256 totalOt,
|
|
506
|
-
int256 totalAsset,
|
|
507
|
-
int256 rateScalar,
|
|
508
|
-
int256 rateAnchor,
|
|
509
|
-
uint256 timeToExpiry
|
|
510
|
-
) private pure returns (uint256 impliedRate) {
|
|
511
|
-
// This will check for exchange rates < FixedPoint.ONE_INT
|
|
512
|
-
int256 exchangeRate = _getExchangeRate(totalOt, totalAsset, rateScalar, rateAnchor, 0);
|
|
513
|
-
|
|
514
|
-
// exchangeRate >= 1 so its ln >= 0
|
|
515
|
-
uint256 lnRate = exchangeRate.ln().Uint();
|
|
516
|
-
|
|
517
|
-
impliedRate = (lnRate * IMPLIED_RATE_TIME) / timeToExpiry;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
/// @notice Converts an implied rate to an exchange rate given a time to expiry. The
|
|
521
|
-
/// formula is E = e^rt
|
|
522
|
-
function _getExchangeRateFromImpliedRate(uint256 impliedRate, uint256 timeToExpiry)
|
|
523
|
-
private
|
|
524
|
-
pure
|
|
525
|
-
returns (int256 exchangeRate)
|
|
526
|
-
{
|
|
527
|
-
uint256 rt = (impliedRate * timeToExpiry) / IMPLIED_RATE_TIME;
|
|
528
|
-
|
|
529
|
-
exchangeRate = LogExpMath.exp(rt.Int());
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/// @notice Returns the exchange rate between OT and Asset for the given market
|
|
533
|
-
/// Calculates the following exchange rate:
|
|
534
|
-
/// (1 / rateScalar) * ln(proportion / (1 - proportion)) + rateAnchor
|
|
535
|
-
/// where:
|
|
536
|
-
/// proportion = totalOt / (totalOt + totalUnderlyingAsset)
|
|
537
|
-
function _getExchangeRate(
|
|
538
|
-
int256 totalOt,
|
|
539
|
-
int256 totalAsset,
|
|
540
|
-
int256 rateScalar,
|
|
541
|
-
int256 rateAnchor,
|
|
542
|
-
int256 otToAccount
|
|
543
|
-
) private pure returns (int256 exchangeRate) {
|
|
544
|
-
int256 numerator = totalOt.subNoNeg(otToAccount);
|
|
545
|
-
|
|
546
|
-
// This is the proportion scaled by FixedPoint.ONE_INT
|
|
547
|
-
// (totalOt + otToMarket) / (totalOt + totalAsset)
|
|
548
|
-
int256 proportion = (numerator.divDown(totalOt + totalAsset));
|
|
549
|
-
|
|
550
|
-
// This limit is here to prevent the market from reaching extremely high interest rates via an
|
|
551
|
-
// excessively large proportion (high amounts of OT relative to Asset).
|
|
552
|
-
// Market proportion can only increase via swapping OT to SCY (OT is added to the market and SCY is
|
|
553
|
-
// removed). Over time, the yield from SCY will slightly decrease the proportion (the
|
|
554
|
-
// amount of Asset in the market must be monotonically increasing). Therefore it is not
|
|
555
|
-
// possible for the proportion to go over max market proportion unless borrowing occurs.
|
|
556
|
-
require(proportion <= MAX_MARKET_PROPORTION, "max proportion exceeded");
|
|
557
|
-
|
|
558
|
-
int256 lnProportion = _logProportion(proportion);
|
|
559
|
-
|
|
560
|
-
// lnProportion / rateScalar + rateAnchor
|
|
561
|
-
exchangeRate = lnProportion.divDown(rateScalar) + rateAnchor;
|
|
562
|
-
|
|
563
|
-
// Do not succeed if interest rates fall below 1
|
|
564
|
-
require(exchangeRate >= FixedPoint.ONE_INT, "exchange rate below 1");
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
function _logProportion(int256 proportion) private pure returns (int256 res) {
|
|
568
|
-
// This will result in divide by zero, short circuit
|
|
569
|
-
require(proportion != FixedPoint.ONE_INT, "proportion must not be one");
|
|
570
|
-
|
|
571
|
-
// Convert proportion to what is used inside the logit function (p / (1-p))
|
|
572
|
-
int256 logitP = proportion.divDown(FixedPoint.ONE_INT - proportion);
|
|
573
|
-
|
|
574
|
-
res = logitP.ln();
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
function _getRateScalar(MarketParameters memory market, uint256 timeToExpiry)
|
|
578
|
-
private
|
|
579
|
-
pure
|
|
580
|
-
returns (int256 rateScalar)
|
|
581
|
-
{
|
|
582
|
-
rateScalar = (market.scalarRoot * IMPLIED_RATE_TIME.Int()) / timeToExpiry.Int();
|
|
583
|
-
require(rateScalar > 0, "rateScalar underflow");
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
587
|
-
/// Utility functions ////
|
|
588
|
-
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
589
|
-
|
|
590
|
-
// function timeToExpiry(MarketParameters memory market) internal view returns (uint256) {
|
|
591
|
-
// unchecked {
|
|
592
|
-
// require(block.timestamp <= market.expiry, "market expired");
|
|
593
|
-
// return market.expiry - block.timestamp;
|
|
594
|
-
// }
|
|
595
|
-
// }
|
|
596
|
-
|
|
597
|
-
function deepCloneMarket(MarketParameters memory marketImmutable)
|
|
598
|
-
internal
|
|
599
|
-
pure
|
|
600
|
-
returns (MarketParameters memory market)
|
|
601
|
-
{
|
|
602
|
-
market.totalOt = marketImmutable.totalOt;
|
|
603
|
-
market.totalScy = marketImmutable.totalScy;
|
|
604
|
-
market.totalLp = marketImmutable.totalLp;
|
|
605
|
-
market.oracleRate = marketImmutable.oracleRate;
|
|
606
|
-
market.scalarRoot = marketImmutable.scalarRoot;
|
|
607
|
-
market.feeRateRoot = marketImmutable.feeRateRoot;
|
|
608
|
-
market.rateOracleTimeWindow = marketImmutable.rateOracleTimeWindow;
|
|
609
|
-
market.expiry = marketImmutable.expiry;
|
|
610
|
-
market.reserveFeePercent = marketImmutable.reserveFeePercent;
|
|
611
|
-
market.lastImpliedRate = marketImmutable.lastImpliedRate;
|
|
612
|
-
market.lastTradeTime = marketImmutable.lastTradeTime;
|
|
613
|
-
}
|
|
614
|
-
}
|