@pendle/core-v2 0.5.1-beta-4 → 0.6.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 (126) hide show
  1. package/contracts/SuperComposableYield/ISuperComposableYield.sol +2 -0
  2. package/contracts/SuperComposableYield/implementations/RewardManager.sol +35 -20
  3. package/contracts/SuperComposableYield/implementations/SCYBase.sol +39 -19
  4. package/contracts/SuperComposableYield/implementations/SCYBaseWithRewards.sol +26 -14
  5. package/contracts/core/PendleBaseToken.sol +2 -2
  6. package/contracts/core/PendleMarket.sol +145 -61
  7. package/contracts/core/PendleMarketFactory.sol +30 -19
  8. package/contracts/core/{PendleOwnershipToken.sol → PendlePrincipalToken.sol} +14 -2
  9. package/contracts/core/PendleRouter.sol +23 -9
  10. package/contracts/core/PendleSCYImpl/AaveV3/PendleAaveV3SCY.sol +21 -20
  11. package/contracts/core/PendleSCYImpl/PendleBenQiErc20SCY.sol +22 -16
  12. package/contracts/core/PendleSCYImpl/PendleBtrflySCY.sol +13 -13
  13. package/contracts/core/PendleSCYImpl/PendleStETHSCY.sol +107 -0
  14. package/contracts/core/PendleSCYImpl/PendleYearnVaultSCY.sol +14 -15
  15. package/contracts/core/PendleYieldContractFactory.sol +29 -14
  16. package/contracts/core/PendleYieldToken.sol +130 -76
  17. package/contracts/core/RouterStatic.sol +16 -157
  18. package/contracts/core/actions/ActionCallback.sol +36 -18
  19. package/contracts/core/actions/ActionCore.sol +41 -88
  20. package/contracts/core/actions/ActionRedeem.sol +36 -0
  21. package/contracts/core/actions/ActionYT.sol +41 -59
  22. package/contracts/core/actions/base/{ActionSCYAndOTBase.sol → ActionSCYAndPTBase.sol} +59 -45
  23. package/contracts/core/actions/base/{ActionSCYAndYOBase.sol → ActionSCYAndPYBase.sol} +57 -19
  24. package/contracts/core/actions/base/ActionSCYAndYTBase.sol +31 -21
  25. package/contracts/interfaces/IPActionCore.sol +21 -28
  26. package/contracts/interfaces/IPActionRedeem.sol +17 -0
  27. package/contracts/interfaces/IPActionYT.sol +5 -12
  28. package/contracts/interfaces/IPAllAction.sol +2 -2
  29. package/contracts/interfaces/IPMarket.sol +33 -10
  30. package/contracts/interfaces/IPMarketAddRemoveCallback.sol +2 -2
  31. package/contracts/interfaces/IPMarketFactory.sol +3 -1
  32. package/contracts/interfaces/IPMarketSwapCallback.sol +1 -1
  33. package/contracts/interfaces/{IPOwnershipToken.sol → IPPrincipalToken.sol} +1 -1
  34. package/contracts/interfaces/IPRouterStatic.sol +7 -82
  35. package/contracts/interfaces/IPYieldContractFactory.sol +10 -2
  36. package/contracts/interfaces/IPYieldToken.sol +14 -5
  37. package/contracts/interfaces/IWstETH.sol +10 -0
  38. package/contracts/libraries/JoeLibrary.sol +1 -1
  39. package/contracts/libraries/math/LogExpMath.sol +1 -1
  40. package/contracts/libraries/math/MarketApproxLib.sol +128 -88
  41. package/contracts/libraries/math/MarketMathAux.sol +13 -13
  42. package/contracts/libraries/math/MarketMathCore.sol +62 -93
  43. package/contracts/libraries/math/Math.sol +14 -2
  44. package/contracts/periphery/PendleGovernanceManager.sol +5 -3
  45. package/package.json +3 -2
  46. package/typechain-types/ActionCallback.ts +5 -5
  47. package/typechain-types/ActionCore.ts +312 -234
  48. package/typechain-types/ActionRedeem.ts +125 -0
  49. package/typechain-types/ActionSCYAndPYBase.ts +223 -0
  50. package/typechain-types/ActionSCYAndYOBase.ts +129 -3
  51. package/typechain-types/ActionSCYAndYTBase.ts +149 -3
  52. package/typechain-types/ActionYT.ts +192 -94
  53. package/typechain-types/IPActionCore.ts +183 -231
  54. package/typechain-types/IPActionRedeem.ts +125 -0
  55. package/typechain-types/IPActionYT.ts +43 -91
  56. package/typechain-types/IPAllAction.ts +258 -321
  57. package/typechain-types/IPMarket.ts +150 -50
  58. package/typechain-types/IPMarketAddRemoveCallback.ts +10 -10
  59. package/typechain-types/IPMarketFactory.ts +28 -6
  60. package/typechain-types/IPMarketSwapCallback.ts +5 -5
  61. package/typechain-types/{IPOwnershipToken.ts → IPPrincipalToken.ts} +5 -5
  62. package/typechain-types/IPRouterStatic.ts +50 -530
  63. package/typechain-types/IPYieldContractFactory.ts +85 -19
  64. package/typechain-types/IPYieldToken.ts +138 -37
  65. package/typechain-types/ISuperComposableYield.ts +13 -0
  66. package/typechain-types/IWstETH.ts +149 -0
  67. package/typechain-types/PendleAaveV3SCY.ts +110 -0
  68. package/typechain-types/PendleBaseToken.ts +1 -1
  69. package/typechain-types/PendleBenQiErc20SCY.ts +55 -12
  70. package/typechain-types/PendleBtrflyScy.ts +27 -23
  71. package/typechain-types/PendleGovernanceManager.ts +5 -5
  72. package/typechain-types/PendleMarket.ts +197 -100
  73. package/typechain-types/PendleMarketFactory.ts +96 -34
  74. package/typechain-types/{PendleOwnershipToken.ts → PendlePrincipalToken.ts} +15 -6
  75. package/typechain-types/PendleRouter.ts +19 -0
  76. package/typechain-types/PendleRouterRedeemUpg.ts +125 -0
  77. package/typechain-types/PendleStEthSCY.ts +872 -0
  78. package/typechain-types/PendleYearnVaultScy.ts +27 -23
  79. package/typechain-types/PendleYieldContractFactory.ts +94 -28
  80. package/typechain-types/PendleYieldToken.ts +142 -57
  81. package/typechain-types/RewardManager.ts +21 -0
  82. package/typechain-types/RouterStatic.ts +50 -530
  83. package/typechain-types/SCYBase.ts +25 -0
  84. package/typechain-types/SCYBaseWithRewards.ts +64 -0
  85. package/typechain-types/factories/ActionCallback__factory.ts +2 -2
  86. package/typechain-types/factories/ActionCore__factory.ts +263 -79
  87. package/typechain-types/factories/ActionRedeem__factory.ts +105 -0
  88. package/typechain-types/factories/ActionSCYAndPYBase__factory.ts +200 -0
  89. package/typechain-types/factories/ActionSCYAndYOBase__factory.ts +148 -0
  90. package/typechain-types/factories/ActionSCYAndYTBase__factory.ts +173 -0
  91. package/typechain-types/factories/ActionYT__factory.ts +265 -56
  92. package/typechain-types/factories/IPActionCore__factory.ts +114 -78
  93. package/typechain-types/factories/IPActionRedeem__factory.ts +70 -0
  94. package/typechain-types/factories/IPActionYT__factory.ts +90 -54
  95. package/typechain-types/factories/IPAllAction__factory.ts +254 -138
  96. package/typechain-types/factories/IPMarketAddRemoveCallback__factory.ts +2 -2
  97. package/typechain-types/factories/IPMarketFactory__factory.ts +26 -1
  98. package/typechain-types/factories/IPMarketSwapCallback__factory.ts +1 -1
  99. package/typechain-types/factories/IPMarket__factory.ts +125 -13
  100. package/typechain-types/factories/{IPOwnershipToken__factory.ts → IPPrincipalToken__factory.ts} +8 -8
  101. package/typechain-types/factories/IPRouterStatic__factory.ts +12 -628
  102. package/typechain-types/factories/IPYieldContractFactory__factory.ts +72 -2
  103. package/typechain-types/factories/IPYieldToken__factory.ts +93 -7
  104. package/typechain-types/factories/ISuperComposableYield__factory.ts +13 -0
  105. package/typechain-types/factories/IWstETH__factory.ts +74 -0
  106. package/typechain-types/factories/PendleAaveV3SCY__factory.ts +97 -3
  107. package/typechain-types/factories/PendleBaseToken__factory.ts +1 -1
  108. package/typechain-types/factories/PendleBenQiErc20SCY__factory.ts +62 -7
  109. package/typechain-types/factories/PendleBtrflyScy__factory.ts +42 -19
  110. package/typechain-types/factories/PendleGovernanceManager__factory.ts +2 -2
  111. package/typechain-types/factories/PendleMarketFactory__factory.ts +66 -15
  112. package/typechain-types/factories/PendleMarket__factory.ts +170 -53
  113. package/typechain-types/factories/PendlePrincipalToken__factory.ts +540 -0
  114. package/typechain-types/factories/PendleRouterRedeemUpg__factory.ts +112 -0
  115. package/typechain-types/factories/PendleRouter__factory.ts +23 -1
  116. package/typechain-types/factories/PendleStEthSCY__factory.ts +716 -0
  117. package/typechain-types/factories/PendleYearnVaultScy__factory.ts +41 -18
  118. package/typechain-types/factories/PendleYieldContractFactory__factory.ts +75 -5
  119. package/typechain-types/factories/PendleYieldToken__factory.ts +102 -33
  120. package/typechain-types/factories/RewardManager__factory.ts +13 -0
  121. package/typechain-types/factories/RouterStatic__factory.ts +13 -629
  122. package/typechain-types/factories/SCYBaseWithRewards__factory.ts +60 -2
  123. package/typechain-types/factories/SCYBase__factory.ts +28 -2
  124. package/typechain-types/hardhat.d.ts +52 -16
  125. package/typechain-types/index.ts +14 -6
  126. package/typechain-types/factories/PendleOwnershipToken__factory.ts +0 -527
@@ -4,22 +4,23 @@ pragma solidity 0.8.9;
4
4
  import "./PendleBaseToken.sol";
5
5
  import "../SuperComposableYield/ISuperComposableYield.sol";
6
6
  import "../interfaces/IPYieldToken.sol";
7
- import "../interfaces/IPOwnershipToken.sol";
7
+ import "../interfaces/IPPrincipalToken.sol";
8
8
  import "../libraries/math/Math.sol";
9
9
  import "../interfaces/IPYieldContractFactory.sol";
10
- import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
11
10
  import "../SuperComposableYield/SCYUtils.sol";
12
11
  import "../SuperComposableYield/implementations/RewardManager.sol";
12
+ import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
13
+ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
13
14
 
14
15
  /*
15
16
  With YT yielding more SCYs overtime, which is allowed to be redeemed by users, the reward distribution should
16
17
  be based on the amount of SCYs that their YT currently represent, plus with their dueInterest.
17
18
 
18
- It has been proven and tested that impliedScyBalance will not change over time, unless users redeem their interest or redeemYO.
19
+ It has been proven and tested that impliedScyBalance will not change over time, unless users redeem their interest or redeemPY.
19
20
 
20
21
  Due to this, it is required to update users' accruedReward STRICTLY BEFORE redeeming their interest.
21
22
  */
22
- contract PendleYieldToken is PendleBaseToken, RewardManager, IPYieldToken {
23
+ contract PendleYieldToken is PendleBaseToken, RewardManager, IPYieldToken, ReentrancyGuard {
23
24
  using Math for uint256;
24
25
  using SafeERC20 for IERC20;
25
26
 
@@ -29,113 +30,145 @@ contract PendleYieldToken is PendleBaseToken, RewardManager, IPYieldToken {
29
30
  }
30
31
 
31
32
  address public immutable SCY;
32
- address public immutable OT;
33
+ address public immutable PT;
33
34
 
34
35
  /// params to do interests & rewards accounting
35
36
  uint256 public lastScyBalance;
36
37
  uint256 public lastScyIndexBeforeExpiry;
37
- uint256[] public paramL;
38
38
 
39
39
  /// params to do fee accounting
40
40
  mapping(address => uint256) public totalRewardsPostExpiry;
41
+ uint256 public totalInterestPostExpiry;
41
42
  uint256 public totalProtocolFee;
42
43
 
43
44
  mapping(address => InterestData) internal interestData;
44
45
 
45
46
  constructor(
46
47
  address _SCY,
47
- address _OT,
48
+ address _PT,
48
49
  string memory _name,
49
50
  string memory _symbol,
50
51
  uint8 __decimals,
51
52
  uint256 _expiry
52
53
  ) PendleBaseToken(_name, _symbol, __decimals, _expiry) {
54
+ require(_SCY != address(0) && _PT != address(0), "zero address");
53
55
  SCY = _SCY;
54
- OT = _OT;
56
+ PT = _PT;
55
57
  }
56
58
 
57
59
  /**
58
- * @notice this function splits scy into OT + YT of equal qty
60
+ * @notice this function splits scy into PT + YT of equal qty
59
61
  * @dev the scy to tokenize has to be pre-transferred to this contract prior to the function call
60
62
  */
61
- function mintYO(address receiverOT, address receiverYT) public returns (uint256 amountYOOut) {
63
+ function mintPY(address receiverPT, address receiverYT)
64
+ external
65
+ nonReentrant
66
+ returns (uint256 amountPYOut)
67
+ {
62
68
  uint256 amountToTokenize = _receiveSCY();
63
69
 
64
- amountYOOut = _calcAmountToMint(amountToTokenize);
70
+ amountPYOut = _calcAmountToMint(amountToTokenize);
65
71
 
66
- _mint(receiverYT, amountYOOut);
72
+ _mint(receiverYT, amountPYOut);
67
73
 
68
- IPOwnershipToken(OT).mintByYT(receiverOT, amountYOOut);
74
+ IPPrincipalToken(PT).mintByYT(receiverPT, amountPYOut);
69
75
  }
70
76
 
71
- /// this function converts YO tokens into scy, but interests & rewards are not included
72
- function redeemYO(address receiver) public returns (uint256 amountScyOut) {
73
- // minimum of OT & YT balance
74
- uint256 amountYOToRedeem = IERC20(OT).balanceOf(address(this));
77
+ /// @dev this function converts PY tokens into scy, but interests & rewards are not redeemed at the same time
78
+ function redeemPY(address receiver) external nonReentrant returns (uint256 amountScyOut) {
79
+ // minimum of PT & YT balance
80
+ uint256 amountPYToRedeem = IERC20(PT).balanceOf(address(this));
75
81
  if (!isExpired()) {
76
- amountYOToRedeem = Math.min(amountYOToRedeem, balanceOf(address(this)));
77
- _burn(address(this), amountYOToRedeem);
82
+ amountPYToRedeem = Math.min(amountPYToRedeem, balanceOf(address(this)));
83
+ _burn(address(this), amountPYToRedeem);
78
84
  }
79
- IPOwnershipToken(OT).burnByYT(address(this), amountYOToRedeem);
80
85
 
81
- amountScyOut = _calcAmountRedeemable(amountYOToRedeem);
86
+ IPPrincipalToken(PT).burnByYT(address(this), amountPYToRedeem);
87
+
88
+ uint256 amountScyToTreasury;
89
+ (amountScyOut, amountScyToTreasury) = _calcAmountToRedeem(amountPYToRedeem);
90
+
91
+ totalInterestPostExpiry += amountScyToTreasury;
82
92
 
83
93
  IERC20(SCY).safeTransfer(receiver, amountScyOut);
84
94
  _afterTransferOutSCY();
85
95
  }
86
96
 
97
+ /**
98
+ * @dev as mentioned in doc, updateDueReward should be placed strictly before every redeemDueInterest
99
+ */
87
100
  function redeemDueInterestAndRewards(address user)
88
- public
101
+ external
102
+ nonReentrant
89
103
  returns (uint256 interestOut, uint256[] memory rewardsOut)
90
104
  {
91
105
  // redeemDueRewards before redeemDueInterest
92
- updateUserReward(user);
93
- updateUserInterest(user);
106
+ _updateUserReward(user);
107
+ _updateUserInterest(user);
94
108
  rewardsOut = _doTransferOutRewardsForUser(user, user);
95
109
  interestOut = _doTransferOutDueInterest(user);
110
+
111
+ emit RedeemReward(user, rewardsOut);
112
+ emit RedeemInterest(user, interestOut);
96
113
  }
97
114
 
98
- function redeemDueInterest(address user) public returns (uint256 interestOut) {
99
- updateUserReward(user); /// strictly required, see above for explanation
100
- updateUserInterest(user);
115
+ /**
116
+ * @dev as mentioned in doc, updateDueReward should be placed strictly before every redeemDueInterest
117
+ */
118
+ function redeemDueInterest(address user) external nonReentrant returns (uint256 interestOut) {
119
+ _updateUserReward(user); /// strictly required, see above for explanation
120
+
121
+ _updateUserInterest(user);
101
122
  interestOut = _doTransferOutDueInterest(user);
123
+ emit RedeemInterest(user, interestOut);
102
124
  }
103
125
 
104
- function redeemDueRewards(address user) public returns (uint256[] memory rewardsOut) {
105
- updateUserReward(user);
126
+ function redeemDueRewards(address user)
127
+ external
128
+ nonReentrant
129
+ returns (uint256[] memory rewardsOut)
130
+ {
131
+ _updateUserReward(user);
132
+
106
133
  rewardsOut = _doTransferOutRewardsForUser(user, user);
134
+ emit RedeemReward(user, rewardsOut);
107
135
  }
108
136
 
109
- function updateGlobalReward() external {
110
- address[] memory rewardTokens = getRewardTokens();
111
- _updateGlobalReward(rewardTokens, IERC20(SCY).balanceOf(address(this)));
137
+ /**
138
+ * @dev updateGlobalReward does not need reentrancy modifier since the rewards
139
+ * will be transfered directly from SCY to YT, without triggering any callbacks
140
+ */
141
+ function updateGlobalReward() external nonReentrant {
142
+ _updateGlobalReward();
143
+ }
144
+
145
+ function updateUserReward(address user) external nonReentrant {
146
+ _updateUserReward(user);
112
147
  }
113
148
 
114
- function updateUserReward(address user) public {
115
- uint256 impliedScyBalance = getImpliedScyBalance(user);
116
- uint256 totalScy = IERC20(SCY).balanceOf(address(this));
117
- _updateUserReward(user, impliedScyBalance, totalScy);
149
+ function updateUserInterest(address user) external nonReentrant {
150
+ _updateUserInterest(user);
118
151
  }
119
152
 
120
- function updateUserInterest(address user) public {
153
+ function _updateUserInterest(address user) internal {
121
154
  uint256 prevIndex = interestData[user].lastScyIndex;
122
155
 
123
- uint256 currentIndex = getScyIndexBeforeExpiry();
156
+ (, uint256 currentIndexBeforeExpiry) = getScyIndex();
124
157
 
125
- if (prevIndex == currentIndex) return;
158
+ if (prevIndex == currentIndexBeforeExpiry) return;
126
159
  if (prevIndex == 0) {
127
- interestData[user].lastScyIndex = currentIndex;
160
+ interestData[user].lastScyIndex = currentIndexBeforeExpiry;
128
161
  return;
129
162
  }
130
163
 
131
164
  uint256 principal = balanceOf(user);
132
165
 
133
- uint256 interestFromYT = (principal * (currentIndex - prevIndex)).divDown(
134
- prevIndex * currentIndex
166
+ uint256 interestFromYT = (principal * (currentIndexBeforeExpiry - prevIndex)).divDown(
167
+ prevIndex * currentIndexBeforeExpiry
135
168
  );
136
169
 
137
170
  interestData[user].dueInterest += interestFromYT;
138
- interestData[user].lastScyIndex = currentIndex;
171
+ interestData[user].lastScyIndex = currentIndexBeforeExpiry;
139
172
  }
140
173
 
141
174
  function getInterestData(address user)
@@ -146,13 +179,15 @@ contract PendleYieldToken is PendleBaseToken, RewardManager, IPYieldToken {
146
179
  return (interestData[user].lastScyIndex, interestData[user].dueInterest);
147
180
  }
148
181
 
149
- function _updateGlobalReward(address[] memory rewardTokens, uint256 totalSupply)
150
- internal
151
- virtual
152
- override
153
- {
182
+ /// @dev override the default updateGlobalReward to avoid distributing the rewards after
183
+ /// YT has expired. Instead, these funds will go to the treasury
184
+ function _updateGlobalReward() internal virtual override {
185
+ if (!_shouldUpdateGlobalReward()) return;
154
186
  _redeemExternalReward();
155
187
 
188
+ uint256 totalShares = _rewardSharesTotal();
189
+
190
+ address[] memory rewardTokens = getRewardTokens();
156
191
  _initGlobalReward(rewardTokens);
157
192
 
158
193
  bool _isExpired = isExpired();
@@ -164,8 +199,8 @@ contract PendleYieldToken is PendleBaseToken, RewardManager, IPYieldToken {
164
199
 
165
200
  if (_isExpired) {
166
201
  totalRewardsPostExpiry[token] += totalIncomeReward;
167
- } else if (totalSupply != 0) {
168
- globalReward[token].index += totalIncomeReward.divDown(totalSupply);
202
+ } else if (totalShares != 0) {
203
+ globalReward[token].index += totalIncomeReward.divDown(totalShares);
169
204
  }
170
205
 
171
206
  globalReward[token].lastBalance = currentRewardBalance;
@@ -186,7 +221,17 @@ contract PendleYieldToken is PendleBaseToken, RewardManager, IPYieldToken {
186
221
  }
187
222
 
188
223
  function _redeemExternalReward() internal virtual override {
189
- ISuperComposableYield(SCY).redeemReward(address(this));
224
+ ISuperComposableYield(SCY).redeemReward(address(this)); // ignore return
225
+ }
226
+
227
+ /// @dev to be overriden if there is rewards
228
+ function _rewardSharesTotal() internal virtual override returns (uint256) {
229
+ return IERC20(SCY).balanceOf(address(this));
230
+ }
231
+
232
+ /// @dev to be overriden if there is rewards
233
+ function _rewardSharesUser(address user) internal virtual override returns (uint256) {
234
+ return getImpliedScyBalance(user);
190
235
  }
191
236
 
192
237
  function getRewardTokens()
@@ -199,48 +244,63 @@ contract PendleYieldToken is PendleBaseToken, RewardManager, IPYieldToken {
199
244
  return ISuperComposableYield(SCY).getRewardTokens();
200
245
  }
201
246
 
202
- function getScyIndexBeforeExpiry() public returns (uint256 res) {
203
- if (isExpired()) return res = lastScyIndexBeforeExpiry;
204
- res = ISuperComposableYield(SCY).scyIndexCurrent();
205
- lastScyIndexBeforeExpiry = res;
247
+ function getScyIndex() public returns (uint256 currentIndex, uint256 lastIndexBeforeExpiry) {
248
+ currentIndex = ISuperComposableYield(SCY).scyIndexCurrent();
249
+ if (isExpired()) {
250
+ lastIndexBeforeExpiry = lastScyIndexBeforeExpiry;
251
+ } else {
252
+ lastScyIndexBeforeExpiry = lastIndexBeforeExpiry = currentIndex;
253
+ }
206
254
  }
207
255
 
208
256
  /**
209
- * In case the pool is expired and there is left some SCY not yet redeemed from the contract, the rewards should
257
+ * @dev In case the pool is expired and there is left some SCY not yet redeemed from the contract, the rewards should
210
258
  * be claimed before withdrawing to treasury.
211
259
  *
212
- * And since the reward distribution (which based on users' dueInterest) stopped at the scyIndexBeforeExpiry, it is not
213
- * necessary to updateDueInterest along with reward here.
260
+ * @dev And since the reward distribution (which based on users' dueInterest) stopped at the scyIndexBeforeExpiry, it is not
261
+ * necessary to updateUserInterest along with reward here.
214
262
  */
215
263
  function withdrawFeeToTreasury() public {
216
264
  address[] memory rewardTokens = getRewardTokens();
217
265
  if (isExpired()) {
218
- _updateGlobalReward(rewardTokens, IERC20(SCY).balanceOf(address(this))); // as refered to the doc above
266
+ _updateGlobalReward(); // as refered to the doc above
219
267
  }
220
268
 
221
269
  uint256 length = rewardTokens.length;
222
270
  address treasury = IPYieldContractFactory(factory).treasury();
271
+ uint256[] memory amountRewardsOut = new uint256[](length);
223
272
 
224
273
  for (uint256 i = 0; i < length; i++) {
225
274
  address token = rewardTokens[i];
226
275
  uint256 outAmount = totalRewardsPostExpiry[token];
227
276
  if (outAmount > 0) IERC20(rewardTokens[i]).safeTransfer(treasury, outAmount);
228
277
  totalRewardsPostExpiry[token] = 0;
278
+ amountRewardsOut[i] = outAmount;
229
279
  }
230
280
 
231
- if (totalProtocolFee > 0) {
232
- IERC20(SCY).safeTransfer(treasury, totalProtocolFee);
281
+ uint256 totalScyFee = totalProtocolFee + totalInterestPostExpiry;
282
+ totalProtocolFee = totalInterestPostExpiry = 0;
283
+
284
+ if (totalScyFee > 0) {
285
+ IERC20(SCY).safeTransfer(treasury, totalScyFee);
233
286
  _afterTransferOutSCY();
234
- totalProtocolFee = 0;
235
287
  }
288
+ emit WithdrawFeeToTreasury(amountRewardsOut, totalScyFee);
236
289
  }
237
290
 
238
291
  function _calcAmountToMint(uint256 amount) internal returns (uint256) {
239
- return SCYUtils.scyToAsset(getScyIndexBeforeExpiry(), amount);
292
+ (, uint256 lastIndexBeforeExpiry) = getScyIndex();
293
+ return SCYUtils.scyToAsset(lastIndexBeforeExpiry, amount);
240
294
  }
241
295
 
242
- function _calcAmountRedeemable(uint256 amount) internal returns (uint256) {
243
- return SCYUtils.assetToScy(getScyIndexBeforeExpiry(), amount);
296
+ function _calcAmountToRedeem(uint256 amount)
297
+ internal
298
+ returns (uint256 amountToUser, uint256 amountToTreasury)
299
+ {
300
+ (uint256 currentIndex, uint256 lastIndexBeforeExpiry) = getScyIndex();
301
+ uint256 totalRedeemable = SCYUtils.assetToScy(lastIndexBeforeExpiry, amount);
302
+ amountToUser = SCYUtils.assetToScy(currentIndex, amount);
303
+ amountToTreasury = totalRedeemable - amountToUser;
244
304
  }
245
305
 
246
306
  function _receiveSCY() internal returns (uint256 amount) {
@@ -256,7 +316,6 @@ contract PendleYieldToken is PendleBaseToken, RewardManager, IPYieldToken {
256
316
 
257
317
  function getImpliedScyBalance(address user) public view returns (uint256) {
258
318
  uint256 scyIndex = interestData[user].lastScyIndex;
259
- if (scyIndex == 0) return 0;
260
319
  return SCYUtils.assetToScy(scyIndex, balanceOf(user)) + interestData[user].dueInterest;
261
320
  }
262
321
 
@@ -265,22 +324,17 @@ contract PendleYieldToken is PendleBaseToken, RewardManager, IPYieldToken {
265
324
  address to,
266
325
  uint256
267
326
  ) internal override {
268
- address[] memory rewardTokens = getRewardTokens();
269
- _updateGlobalReward(rewardTokens, IERC20(SCY).balanceOf(address(this)));
327
+ _updateGlobalReward();
270
328
 
271
329
  // Before the change in YT balance, users' impliedScyBalance is kept unchanged from last time
272
330
  // Therefore, both updating due interest before or after due reward work the same.
273
331
  if (from != address(0) && from != address(this)) {
274
- updateUserInterest(from);
275
- _updateUserRewardSkipGlobal(rewardTokens, from, getImpliedScyBalance(from));
332
+ _updateUserInterest(from);
333
+ _updateUserRewardSkipGlobal(from);
276
334
  }
277
335
  if (to != address(0) && to != address(this)) {
278
- updateUserInterest(to);
279
- _updateUserRewardSkipGlobal(rewardTokens, to, getImpliedScyBalance(to));
336
+ _updateUserInterest(to);
337
+ _updateUserRewardSkipGlobal(to);
280
338
  }
281
339
  }
282
340
  }
283
- /*INVARIANTS TO CHECK
284
- - supply OT == supply YT
285
- - reentrancy check
286
- */
@@ -1,8 +1,6 @@
1
1
  // SPDX-License-Identifier: GPL-3.0-or-later
2
2
  pragma solidity 0.8.9;
3
3
 
4
- import "../SuperComposableYield/ISuperComposableYield.sol";
5
- import "../SuperComposableYield/implementations/IRewardManager.sol";
6
4
  import "../interfaces/IPRouterStatic.sol";
7
5
  import "../interfaces/IPMarket.sol";
8
6
  import "../interfaces/IPYieldContractFactory.sol";
@@ -29,20 +27,20 @@ contract RouterStatic is IPRouterStatic {
29
27
  function addLiquidityStatic(
30
28
  address market,
31
29
  uint256 scyDesired,
32
- uint256 otDesired
30
+ uint256 ptDesired
33
31
  )
34
32
  external
35
33
  returns (
36
34
  uint256 netLpOut,
37
35
  uint256 scyUsed,
38
- uint256 otUsed
36
+ uint256 ptUsed
39
37
  )
40
38
  {
41
39
  MarketState memory state = IPMarket(market).readState(false);
42
- (, netLpOut, scyUsed, otUsed) = state.addLiquidity(
40
+ (, netLpOut, scyUsed, ptUsed) = state.addLiquidity(
43
41
  scyIndex(market),
44
42
  scyDesired,
45
- otDesired,
43
+ ptDesired,
46
44
  false
47
45
  );
48
46
  }
@@ -50,33 +48,33 @@ contract RouterStatic is IPRouterStatic {
50
48
  function removeLiquidityStatic(address market, uint256 lpToRemove)
51
49
  external
52
50
  view
53
- returns (uint256 netScyOut, uint256 netOtOut)
51
+ returns (uint256 netScyOut, uint256 netPtOut)
54
52
  {
55
53
  MarketState memory state = IPMarket(market).readState(false);
56
- (netScyOut, netOtOut) = state.removeLiquidity(lpToRemove, false);
54
+ (netScyOut, netPtOut) = state.removeLiquidity(lpToRemove, false);
57
55
  }
58
56
 
59
- function swapOtForScyStatic(address market, uint256 exactOtIn)
57
+ function swapPtForScyStatic(address market, uint256 exactPtIn)
60
58
  external
61
59
  returns (uint256 netScyOut, uint256 netScyFee)
62
60
  {
63
61
  MarketState memory state = IPMarket(market).readState(false);
64
- (netScyOut, netScyFee) = state.swapExactOtForScy(
62
+ (netScyOut, netScyFee) = state.swapExactPtForScy(
65
63
  scyIndex(market),
66
- exactOtIn,
64
+ exactPtIn,
67
65
  block.timestamp,
68
66
  false
69
67
  );
70
68
  }
71
69
 
72
- function swapScyForOtStatic(address market, uint256 exactOtOut)
70
+ function swapScyForPtStatic(address market, uint256 exactPtOut)
73
71
  external
74
72
  returns (uint256 netScyIn, uint256 netScyFee)
75
73
  {
76
74
  MarketState memory state = IPMarket(market).readState(false);
77
- (netScyIn, netScyFee) = state.swapScyForExactOt(
75
+ (netScyIn, netScyFee) = state.swapScyForExactPt(
78
76
  scyIndex(market),
79
- exactOtOut,
77
+ exactPtOut,
80
78
  block.timestamp,
81
79
  false
82
80
  );
@@ -86,10 +84,10 @@ contract RouterStatic is IPRouterStatic {
86
84
  return SCYIndexLib.newIndex(IPMarket(market).SCY());
87
85
  }
88
86
 
89
- function getOtImpliedYield(address market) public view returns (int256) {
87
+ function getPtImpliedYield(address market) external view returns (int256) {
90
88
  MarketState memory state = IPMarket(market).readState(false);
91
89
 
92
- int256 lnImpliedRate = (state.lastImpliedRate).Int();
90
+ int256 lnImpliedRate = (state.lastLnImpliedRate).Int();
93
91
  return lnImpliedRate.exp();
94
92
  }
95
93
 
@@ -97,152 +95,13 @@ contract RouterStatic is IPRouterStatic {
97
95
  external
98
96
  view
99
97
  returns (
100
- bool isOT,
98
+ bool isPT,
101
99
  bool isYT,
102
100
  bool isMarket
103
101
  )
104
102
  {
105
- if (yieldContractFactory.isOT(token)) isOT = true;
103
+ if (yieldContractFactory.isPT(token)) isPT = true;
106
104
  else if (yieldContractFactory.isYT(token)) isYT = true;
107
105
  else if (marketFactory.isValidMarket(token)) isMarket = true;
108
106
  }
109
-
110
- function getUserYOInfo(address yo, address user)
111
- public
112
- view
113
- returns (UserYOInfo memory userYOInfo)
114
- {
115
- (userYOInfo.yt, userYOInfo.ot) = getYO(yo);
116
- IPYieldToken YT = IPYieldToken(userYOInfo.yt);
117
- userYOInfo.ytBalance = YT.balanceOf(user);
118
- userYOInfo.otBalance = IPOwnershipToken(userYOInfo.ot).balanceOf(user);
119
- userYOInfo.unclaimedInterest.token = YT.SCY();
120
- (, userYOInfo.unclaimedInterest.amount) = YT.getInterestData(user);
121
- address[] memory rewardTokens = YT.getRewardTokens();
122
- TokenAmount[] memory unclaimedRewards = new TokenAmount[](rewardTokens.length);
123
- uint256 length = 0;
124
- for (uint256 i = 0; i < rewardTokens.length; ++i) {
125
- address rewardToken = rewardTokens[i];
126
- (, uint256 amount) = YT.getUserReward(user, rewardToken);
127
- if (amount > 0) {
128
- unclaimedRewards[length].token = rewardToken;
129
- unclaimedRewards[length].amount = amount;
130
- ++length;
131
- }
132
- }
133
- userYOInfo.unclaimedRewards = new TokenAmount[](length);
134
- for (uint256 i = 0; i < length; ++i) {
135
- userYOInfo.unclaimedRewards[i] = unclaimedRewards[i];
136
- }
137
- }
138
-
139
- function getYOInfo(address yo)
140
- external
141
- returns (
142
- uint256 exchangeRate,
143
- uint256 totalSupply,
144
- RewardIndex[] memory rewardIndexes
145
- )
146
- {
147
- (address yt, ) = getYO(yo);
148
- IPYieldToken YT = IPYieldToken(yt);
149
- exchangeRate = YT.getScyIndexBeforeExpiry();
150
- totalSupply = YT.totalSupply();
151
- address[] memory rewardTokens = YT.getRewardTokens();
152
- rewardIndexes = new RewardIndex[](rewardTokens.length);
153
- for (uint256 i = 0; i < rewardTokens.length; ++i) {
154
- address rewardToken = rewardTokens[i];
155
- rewardIndexes[i].rewardToken = rewardToken;
156
- (, rewardIndexes[i].index) = YT.getGlobalReward(rewardToken);
157
- }
158
- }
159
-
160
- function getYO(address yo) public view returns (address ot, address yt) {
161
- if (yieldContractFactory.isYT(yo)) {
162
- yt = yo;
163
- ot = IPYieldToken(yo).OT();
164
- } else {
165
- yt = IPOwnershipToken(yo).YT();
166
- ot = yo;
167
- }
168
- }
169
-
170
- function getMarketInfo(address market)
171
- external
172
- view
173
- returns (
174
- address ot,
175
- address scy,
176
- MarketState memory state,
177
- int256 impliedYield,
178
- uint256 exchangeRate
179
- )
180
- {
181
- IPMarket _market = IPMarket(market);
182
- ot = _market.OT();
183
- scy = _market.SCY();
184
- state = _market.readState(true);
185
- impliedYield = getOtImpliedYield(market);
186
- exchangeRate = 0; // TODO: get the actual exchange rate
187
- }
188
-
189
- function getUserMarketInfo(address market, address user)
190
- public
191
- view
192
- returns (UserMarketInfo memory userMarketInfo)
193
- {
194
- IPMarket _market = IPMarket(market);
195
- userMarketInfo.market = market;
196
- userMarketInfo.lpBalance = _market.balanceOf(user);
197
- // TODO: Is there a way to convert LP to OT and SCY?
198
- userMarketInfo.otBalance = TokenAmount(_market.OT(), 0);
199
- userMarketInfo.scyBalance = TokenAmount(_market.SCY(), 0);
200
- // TODO: Get this from SCY once it is in the interface
201
- userMarketInfo.assetBalance = TokenAmount(address(0), 0);
202
- }
203
-
204
- function getUserYOPositionsByYOs(address user, address[] calldata yos)
205
- external
206
- view
207
- returns (UserYOInfo[] memory userYOPositions)
208
- {
209
- userYOPositions = new UserYOInfo[](yos.length);
210
- for (uint256 i = 0; i < yos.length; ++i) {
211
- userYOPositions[i] = getUserYOInfo(yos[i], user);
212
- }
213
- }
214
-
215
- function getUserMarketPositions(address user, address[] calldata markets)
216
- external
217
- view
218
- returns (UserMarketInfo[] memory userMarketPositions)
219
- {
220
- userMarketPositions = new UserMarketInfo[](markets.length);
221
- for (uint256 i = 0; i < markets.length; ++i) {
222
- userMarketPositions[i] = getUserMarketInfo(markets[i], user);
223
- }
224
- }
225
-
226
- function hasYOPosition(UserYOInfo memory userYOInfo) public pure returns (bool hasPosition) {
227
- hasPosition = (userYOInfo.ytBalance > 0 ||
228
- userYOInfo.otBalance > 0 ||
229
- userYOInfo.unclaimedInterest.amount > 0 ||
230
- userYOInfo.unclaimedRewards.length > 0);
231
- }
232
-
233
- function getUserSCYInfo(address scy, address user)
234
- external
235
- view
236
- returns (uint256 balance, TokenAmount[] memory rewards)
237
- {
238
- ISuperComposableYield SCY = ISuperComposableYield(scy);
239
- balance = SCY.balanceOf(scy);
240
- address[] memory rewardTokens = SCY.getRewardTokens();
241
- rewards = new TokenAmount[](rewardTokens.length);
242
- for (uint256 i = 0; i < rewardTokens.length; ++i) {
243
- address rewardToken = rewardTokens[i];
244
- rewards[i].token = rewardToken;
245
- (, rewards[i].amount) = IRewardManager(scy).getUserReward(user, rewardToken);
246
- }
247
- }
248
107
  }