@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.
Files changed (87) hide show
  1. package/contracts/SuperComposableYield/ISuperComposableYield.sol +1 -5
  2. package/contracts/SuperComposableYield/implementations/RewardManager.sol +2 -2
  3. package/contracts/SuperComposableYield/implementations/SCYBase.sol +2 -2
  4. package/contracts/SuperComposableYield/implementations/SCYBaseWithRewards.sol +4 -7
  5. package/contracts/core/PendleMarket.sol +24 -22
  6. package/contracts/core/PendleMarketFactory.sol +7 -6
  7. package/contracts/core/PendleSCYImpl/PendleBenQiErc20SCY.sol +1 -1
  8. package/contracts/core/PendleSCYImpl/PendleBtrflySCY.sol +2 -7
  9. package/contracts/core/PendleSCYImpl/PendleYearnVaultSCY.sol +2 -7
  10. package/contracts/core/PendleYieldContractFactory.sol +12 -7
  11. package/contracts/core/PendleYieldToken.sol +93 -55
  12. package/contracts/core/router/PendleRouterCoreUpg.sol +35 -21
  13. package/contracts/core/router/PendleRouterProxy.sol +12 -6
  14. package/contracts/core/router/{PendleRouterStatic.sol → PendleRouterStaticUpg.sol} +43 -18
  15. package/contracts/core/router/PendleRouterYTUpg.sol +47 -13
  16. package/contracts/core/router/base/PendleRouterOTBaseUpg.sol +22 -32
  17. package/contracts/core/router/base/PendleRouterSCYAndForgeBaseUpg.sol +1 -1
  18. package/contracts/core/router/base/PendleRouterYTBaseUpg.sol +62 -43
  19. package/contracts/interfaces/IPMarket.sol +4 -2
  20. package/contracts/interfaces/{IPPermissionsV2.sol → IPPermissionsV2Upg.sol} +1 -1
  21. package/contracts/interfaces/IPRouterCore.sol +24 -21
  22. package/contracts/interfaces/IPRouterStatic.sol +10 -1
  23. package/contracts/interfaces/IPRouterYT.sol +16 -4
  24. package/contracts/interfaces/IPYieldContractFactory.sol +4 -0
  25. package/contracts/interfaces/IPYieldToken.sol +2 -4
  26. package/contracts/libraries/SCYIndex.sol +3 -3
  27. package/contracts/libraries/math/LogExpMath.sol +1 -90
  28. package/contracts/libraries/math/MarketApproxLib.sol +336 -94
  29. package/contracts/libraries/math/MarketMathAux.sol +101 -0
  30. package/contracts/libraries/math/MarketMathCore.sol +423 -0
  31. package/contracts/libraries/math/Math.sol +144 -0
  32. package/contracts/{core/misc → periphery}/PendleJoeSwapHelperUpg.sol +2 -2
  33. package/contracts/periphery/{PermissionsV2.sol → PermissionsV2Upg.sol} +2 -2
  34. package/package.json +1 -1
  35. package/typechain-types/IPMarket.ts +22 -11
  36. package/typechain-types/IPRouterCore.ts +68 -58
  37. package/typechain-types/IPRouterStatic.ts +52 -0
  38. package/typechain-types/IPRouterYT.ts +96 -12
  39. package/typechain-types/IPYieldContractFactory.ts +32 -0
  40. package/typechain-types/IPYieldToken.ts +14 -48
  41. package/typechain-types/IPermissionsV2Upg.ts +87 -0
  42. package/typechain-types/ISuperComposableYield.ts +3 -11
  43. package/typechain-types/OwnableUpgradeable.ts +165 -0
  44. package/typechain-types/PendleAaveV3SCY.ts +3 -11
  45. package/typechain-types/PendleBenQiErc20SCY.ts +3 -11
  46. package/typechain-types/PendleBtrflyScy.ts +3 -11
  47. package/typechain-types/PendleMarket.ts +22 -11
  48. package/typechain-types/PendleRouterCoreUpg.ts +66 -48
  49. package/typechain-types/PendleRouterProxy.ts +15 -118
  50. package/typechain-types/PendleRouterStaticUpg.ts +92 -0
  51. package/typechain-types/PendleRouterYTUpg.ts +96 -12
  52. package/typechain-types/PendleYearnVaultScy.ts +3 -11
  53. package/typechain-types/PendleYieldContractFactory.ts +40 -113
  54. package/typechain-types/PendleYieldToken.ts +106 -7
  55. package/typechain-types/PermissionsV2Upg.ts +87 -0
  56. package/typechain-types/SCYBase.ts +3 -11
  57. package/typechain-types/SCYBaseWithRewards.ts +3 -11
  58. package/typechain-types/factories/IPMarket__factory.ts +8 -2
  59. package/typechain-types/factories/IPRouterCore__factory.ts +38 -23
  60. package/typechain-types/factories/IPRouterStatic__factory.ts +29 -0
  61. package/typechain-types/factories/IPRouterYT__factory.ts +65 -6
  62. package/typechain-types/factories/IPYieldContractFactory__factory.ts +38 -0
  63. package/typechain-types/factories/IPYieldToken__factory.ts +1 -20
  64. package/typechain-types/factories/IPermissionsV2Upg__factory.ts +39 -0
  65. package/typechain-types/factories/ISuperComposableYield__factory.ts +0 -5
  66. package/typechain-types/factories/OwnableUpgradeable__factory.ts +78 -0
  67. package/typechain-types/factories/PendleAaveV3SCY__factory.ts +1 -6
  68. package/typechain-types/factories/PendleBenQiErc20SCY__factory.ts +1 -6
  69. package/typechain-types/factories/PendleBtrflyScy__factory.ts +1 -6
  70. package/typechain-types/factories/PendleMarketFactory__factory.ts +1 -1
  71. package/typechain-types/factories/PendleMarket__factory.ts +9 -3
  72. package/typechain-types/factories/PendleRouterCoreUpg__factory.ts +26 -11
  73. package/typechain-types/factories/PendleRouterProxy__factory.ts +14 -67
  74. package/typechain-types/factories/PendleRouterStaticUpg__factory.ts +63 -17
  75. package/typechain-types/factories/PendleRouterYTUpg__factory.ts +66 -7
  76. package/typechain-types/factories/PendleYearnVaultScy__factory.ts +1 -6
  77. package/typechain-types/factories/PendleYieldContractFactory__factory.ts +39 -54
  78. package/typechain-types/factories/PendleYieldToken__factory.ts +53 -2
  79. package/typechain-types/factories/PermissionsV2Upg__factory.ts +39 -0
  80. package/typechain-types/factories/SCYBaseWithRewards__factory.ts +0 -5
  81. package/typechain-types/factories/SCYBase__factory.ts +0 -5
  82. package/typechain-types/hardhat.d.ts +26 -53
  83. package/typechain-types/index.ts +8 -14
  84. package/contracts/core/misc/BoringOwnable.sol +0 -62
  85. package/contracts/core/misc/BoringOwnableUpg.sol +0 -64
  86. package/contracts/libraries/math/FixedPoint.sol +0 -205
  87. 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
- }