@pendle/core-v2 0.5.1-beta-5 → 0.6.2
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 +2 -0
- package/contracts/SuperComposableYield/implementations/RewardManager.sol +35 -20
- package/contracts/SuperComposableYield/implementations/SCYBase.sol +39 -19
- package/contracts/SuperComposableYield/implementations/SCYBaseWithRewards.sol +26 -14
- package/contracts/core/PendleBaseToken.sol +2 -2
- package/contracts/core/PendleMarket.sol +145 -61
- package/contracts/core/PendleMarketFactory.sol +30 -19
- package/contracts/core/{PendleOwnershipToken.sol → PendlePrincipalToken.sol} +14 -2
- package/contracts/core/PendleRouter.sol +23 -9
- package/contracts/core/PendleSCYImpl/AaveV3/PendleAaveV3SCY.sol +21 -20
- package/contracts/core/PendleSCYImpl/PendleBenQiErc20SCY.sol +22 -16
- package/contracts/core/PendleSCYImpl/PendleBtrflySCY.sol +13 -13
- package/contracts/core/PendleSCYImpl/PendleStETHSCY.sol +107 -0
- package/contracts/core/PendleSCYImpl/PendleYearnVaultSCY.sol +14 -15
- package/contracts/core/PendleYieldContractFactory.sol +29 -14
- package/contracts/core/PendleYieldToken.sol +130 -76
- package/contracts/core/RouterStatic.sol +51 -51
- package/contracts/core/actions/ActionCallback.sol +36 -18
- package/contracts/core/actions/ActionCore.sol +41 -88
- package/contracts/core/actions/ActionRedeem.sol +36 -0
- package/contracts/core/actions/ActionYT.sol +41 -59
- package/contracts/core/actions/base/{ActionSCYAndOTBase.sol → ActionSCYAndPTBase.sol} +59 -45
- package/contracts/core/actions/base/{ActionSCYAndYOBase.sol → ActionSCYAndPYBase.sol} +57 -19
- package/contracts/core/actions/base/ActionSCYAndYTBase.sol +31 -21
- package/contracts/interfaces/IPActionCore.sol +21 -28
- package/contracts/interfaces/IPActionRedeem.sol +17 -0
- package/contracts/interfaces/IPActionYT.sol +5 -12
- package/contracts/interfaces/IPAllAction.sol +2 -2
- package/contracts/interfaces/IPMarket.sol +33 -10
- package/contracts/interfaces/IPMarketAddRemoveCallback.sol +2 -2
- package/contracts/interfaces/IPMarketFactory.sol +3 -1
- package/contracts/interfaces/IPMarketSwapCallback.sol +1 -1
- package/contracts/interfaces/{IPOwnershipToken.sol → IPPrincipalToken.sol} +1 -1
- package/contracts/interfaces/IPRouterStatic.sol +19 -19
- package/contracts/interfaces/IPYieldContractFactory.sol +10 -2
- package/contracts/interfaces/IPYieldToken.sol +14 -5
- package/contracts/interfaces/IWstETH.sol +10 -0
- package/contracts/libraries/JoeLibrary.sol +1 -1
- package/contracts/libraries/math/LogExpMath.sol +1 -1
- package/contracts/libraries/math/MarketApproxLib.sol +128 -88
- package/contracts/libraries/math/MarketMathAux.sol +13 -13
- package/contracts/libraries/math/MarketMathCore.sol +62 -93
- package/contracts/libraries/math/Math.sol +14 -2
- package/contracts/periphery/PendleGovernanceManager.sol +5 -3
- package/package.json +3 -2
- package/typechain-types/IPRouterStatic.ts +480 -0
- package/typechain-types/RouterStatic.ts +484 -4
- package/typechain-types/factories/IPRouterStatic__factory.ts +616 -0
- package/typechain-types/factories/PendleRouter__factory.ts +1 -1
- package/typechain-types/factories/RouterStatic__factory.ts +619 -3
- package/typechain-types/ActionSCYAndYOBase.ts +0 -223
- package/typechain-types/PendleRouterRedeemUpg.ts +0 -125
- package/typechain-types/factories/ActionSCYAndYOBase__factory.ts +0 -200
- package/typechain-types/factories/PendleRouterRedeemUpg__factory.ts +0 -112
|
@@ -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/
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
56
|
+
PT = _PT;
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
/**
|
|
58
|
-
* @notice this function splits scy into
|
|
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
|
|
63
|
+
function mintPY(address receiverPT, address receiverYT)
|
|
64
|
+
external
|
|
65
|
+
nonReentrant
|
|
66
|
+
returns (uint256 amountPYOut)
|
|
67
|
+
{
|
|
62
68
|
uint256 amountToTokenize = _receiveSCY();
|
|
63
69
|
|
|
64
|
-
|
|
70
|
+
amountPYOut = _calcAmountToMint(amountToTokenize);
|
|
65
71
|
|
|
66
|
-
_mint(receiverYT,
|
|
72
|
+
_mint(receiverYT, amountPYOut);
|
|
67
73
|
|
|
68
|
-
|
|
74
|
+
IPPrincipalToken(PT).mintByYT(receiverPT, amountPYOut);
|
|
69
75
|
}
|
|
70
76
|
|
|
71
|
-
/// this function converts
|
|
72
|
-
function
|
|
73
|
-
// minimum of
|
|
74
|
-
uint256
|
|
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
|
-
|
|
77
|
-
_burn(address(this),
|
|
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
|
-
|
|
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
|
-
|
|
101
|
+
external
|
|
102
|
+
nonReentrant
|
|
89
103
|
returns (uint256 interestOut, uint256[] memory rewardsOut)
|
|
90
104
|
{
|
|
91
105
|
// redeemDueRewards before redeemDueInterest
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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)
|
|
105
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
|
115
|
-
|
|
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
|
|
153
|
+
function _updateUserInterest(address user) internal {
|
|
121
154
|
uint256 prevIndex = interestData[user].lastScyIndex;
|
|
122
155
|
|
|
123
|
-
uint256
|
|
156
|
+
(, uint256 currentIndexBeforeExpiry) = getScyIndex();
|
|
124
157
|
|
|
125
|
-
if (prevIndex ==
|
|
158
|
+
if (prevIndex == currentIndexBeforeExpiry) return;
|
|
126
159
|
if (prevIndex == 0) {
|
|
127
|
-
interestData[user].lastScyIndex =
|
|
160
|
+
interestData[user].lastScyIndex = currentIndexBeforeExpiry;
|
|
128
161
|
return;
|
|
129
162
|
}
|
|
130
163
|
|
|
131
164
|
uint256 principal = balanceOf(user);
|
|
132
165
|
|
|
133
|
-
uint256 interestFromYT = (principal * (
|
|
134
|
-
prevIndex *
|
|
166
|
+
uint256 interestFromYT = (principal * (currentIndexBeforeExpiry - prevIndex)).divDown(
|
|
167
|
+
prevIndex * currentIndexBeforeExpiry
|
|
135
168
|
);
|
|
136
169
|
|
|
137
170
|
interestData[user].dueInterest += interestFromYT;
|
|
138
|
-
interestData[user].lastScyIndex =
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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 (
|
|
168
|
-
globalReward[token].index += totalIncomeReward.divDown(
|
|
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
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
232
|
-
|
|
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
|
-
|
|
292
|
+
(, uint256 lastIndexBeforeExpiry) = getScyIndex();
|
|
293
|
+
return SCYUtils.scyToAsset(lastIndexBeforeExpiry, amount);
|
|
240
294
|
}
|
|
241
295
|
|
|
242
|
-
function
|
|
243
|
-
|
|
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
|
-
|
|
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
|
-
|
|
275
|
-
_updateUserRewardSkipGlobal(
|
|
332
|
+
_updateUserInterest(from);
|
|
333
|
+
_updateUserRewardSkipGlobal(from);
|
|
276
334
|
}
|
|
277
335
|
if (to != address(0) && to != address(this)) {
|
|
278
|
-
|
|
279
|
-
_updateUserRewardSkipGlobal(
|
|
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
|
-
*/
|
|
@@ -29,20 +29,20 @@ contract RouterStatic is IPRouterStatic {
|
|
|
29
29
|
function addLiquidityStatic(
|
|
30
30
|
address market,
|
|
31
31
|
uint256 scyDesired,
|
|
32
|
-
uint256
|
|
32
|
+
uint256 ptDesired
|
|
33
33
|
)
|
|
34
34
|
external
|
|
35
35
|
returns (
|
|
36
36
|
uint256 netLpOut,
|
|
37
37
|
uint256 scyUsed,
|
|
38
|
-
uint256
|
|
38
|
+
uint256 ptUsed
|
|
39
39
|
)
|
|
40
40
|
{
|
|
41
41
|
MarketState memory state = IPMarket(market).readState(false);
|
|
42
|
-
(, netLpOut, scyUsed,
|
|
42
|
+
(, netLpOut, scyUsed, ptUsed) = state.addLiquidity(
|
|
43
43
|
scyIndex(market),
|
|
44
44
|
scyDesired,
|
|
45
|
-
|
|
45
|
+
ptDesired,
|
|
46
46
|
false
|
|
47
47
|
);
|
|
48
48
|
}
|
|
@@ -50,33 +50,33 @@ contract RouterStatic is IPRouterStatic {
|
|
|
50
50
|
function removeLiquidityStatic(address market, uint256 lpToRemove)
|
|
51
51
|
external
|
|
52
52
|
view
|
|
53
|
-
returns (uint256
|
|
53
|
+
returns (uint256 netScpyut, uint256 netPtOut)
|
|
54
54
|
{
|
|
55
55
|
MarketState memory state = IPMarket(market).readState(false);
|
|
56
|
-
(
|
|
56
|
+
(netScpyut, netPtOut) = state.removeLiquidity(lpToRemove, false);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function
|
|
59
|
+
function swapPtForScyStatic(address market, uint256 exactPtIn)
|
|
60
60
|
external
|
|
61
|
-
returns (uint256
|
|
61
|
+
returns (uint256 netScpyut, uint256 netScyFee)
|
|
62
62
|
{
|
|
63
63
|
MarketState memory state = IPMarket(market).readState(false);
|
|
64
|
-
(
|
|
64
|
+
(netScpyut, netScyFee) = state.swapExactPtForScy(
|
|
65
65
|
scyIndex(market),
|
|
66
|
-
|
|
66
|
+
exactPtIn,
|
|
67
67
|
block.timestamp,
|
|
68
68
|
false
|
|
69
69
|
);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
function
|
|
72
|
+
function swapScyForPtStatic(address market, uint256 exactPtOut)
|
|
73
73
|
external
|
|
74
74
|
returns (uint256 netScyIn, uint256 netScyFee)
|
|
75
75
|
{
|
|
76
76
|
MarketState memory state = IPMarket(market).readState(false);
|
|
77
|
-
(netScyIn, netScyFee) = state.
|
|
77
|
+
(netScyIn, netScyFee) = state.swapScyForExactPt(
|
|
78
78
|
scyIndex(market),
|
|
79
|
-
|
|
79
|
+
exactPtOut,
|
|
80
80
|
block.timestamp,
|
|
81
81
|
false
|
|
82
82
|
);
|
|
@@ -86,10 +86,10 @@ contract RouterStatic is IPRouterStatic {
|
|
|
86
86
|
return SCYIndexLib.newIndex(IPMarket(market).SCY());
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
function
|
|
89
|
+
function getPtImpliedYield(address market) public view returns (int256) {
|
|
90
90
|
MarketState memory state = IPMarket(market).readState(false);
|
|
91
91
|
|
|
92
|
-
int256 lnImpliedRate = (state.
|
|
92
|
+
int256 lnImpliedRate = (state.lastLnImpliedRate).Int();
|
|
93
93
|
return lnImpliedRate.exp();
|
|
94
94
|
}
|
|
95
95
|
|
|
@@ -97,27 +97,27 @@ contract RouterStatic is IPRouterStatic {
|
|
|
97
97
|
external
|
|
98
98
|
view
|
|
99
99
|
returns (
|
|
100
|
-
bool
|
|
100
|
+
bool isPT,
|
|
101
101
|
bool isYT,
|
|
102
102
|
bool isMarket
|
|
103
103
|
)
|
|
104
104
|
{
|
|
105
|
-
if (yieldContractFactory.
|
|
105
|
+
if (yieldContractFactory.isPT(token)) isPT = true;
|
|
106
106
|
else if (yieldContractFactory.isYT(token)) isYT = true;
|
|
107
107
|
else if (marketFactory.isValidMarket(token)) isMarket = true;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
function
|
|
110
|
+
function getUserPYInfo(address py, address user)
|
|
111
111
|
public
|
|
112
112
|
view
|
|
113
|
-
returns (
|
|
113
|
+
returns (UserPYInfo memory userPYInfo)
|
|
114
114
|
{
|
|
115
|
-
(
|
|
116
|
-
IPYieldToken YT = IPYieldToken(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
(,
|
|
115
|
+
(userPYInfo.yt, userPYInfo.pt) = getPY(py);
|
|
116
|
+
IPYieldToken YT = IPYieldToken(userPYInfo.yt);
|
|
117
|
+
userPYInfo.ytBalance = YT.balanceOf(user);
|
|
118
|
+
userPYInfo.ptBalance = IPPrincipalToken(userPYInfo.pt).balanceOf(user);
|
|
119
|
+
userPYInfo.unclaimedInterest.token = YT.SCY();
|
|
120
|
+
(, userPYInfo.unclaimedInterest.amount) = YT.getInterestData(user);
|
|
121
121
|
address[] memory rewardTokens = YT.getRewardTokens();
|
|
122
122
|
TokenAmount[] memory unclaimedRewards = new TokenAmount[](rewardTokens.length);
|
|
123
123
|
uint256 length = 0;
|
|
@@ -130,13 +130,13 @@ contract RouterStatic is IPRouterStatic {
|
|
|
130
130
|
++length;
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
-
|
|
133
|
+
userPYInfo.unclaimedRewards = new TokenAmount[](length);
|
|
134
134
|
for (uint256 i = 0; i < length; ++i) {
|
|
135
|
-
|
|
135
|
+
userPYInfo.unclaimedRewards[i] = unclaimedRewards[i];
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
function
|
|
139
|
+
function getPYInfo(address py)
|
|
140
140
|
external
|
|
141
141
|
returns (
|
|
142
142
|
uint256 exchangeRate,
|
|
@@ -144,9 +144,9 @@ contract RouterStatic is IPRouterStatic {
|
|
|
144
144
|
RewardIndex[] memory rewardIndexes
|
|
145
145
|
)
|
|
146
146
|
{
|
|
147
|
-
(address yt, ) =
|
|
147
|
+
(address yt, ) = getPY(py);
|
|
148
148
|
IPYieldToken YT = IPYieldToken(yt);
|
|
149
|
-
exchangeRate = YT.
|
|
149
|
+
(, exchangeRate) = YT.getScyIndex();
|
|
150
150
|
totalSupply = YT.totalSupply();
|
|
151
151
|
address[] memory rewardTokens = YT.getRewardTokens();
|
|
152
152
|
rewardIndexes = new RewardIndex[](rewardTokens.length);
|
|
@@ -157,13 +157,13 @@ contract RouterStatic is IPRouterStatic {
|
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
function
|
|
161
|
-
if (yieldContractFactory.isYT(
|
|
162
|
-
yt =
|
|
163
|
-
|
|
160
|
+
function getPY(address py) public view returns (address pt, address yt) {
|
|
161
|
+
if (yieldContractFactory.isYT(py)) {
|
|
162
|
+
yt = py;
|
|
163
|
+
pt = IPYieldToken(py).PT();
|
|
164
164
|
} else {
|
|
165
|
-
yt =
|
|
166
|
-
|
|
165
|
+
yt = IPPrincipalToken(py).YT();
|
|
166
|
+
pt = py;
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
|
|
@@ -171,7 +171,7 @@ contract RouterStatic is IPRouterStatic {
|
|
|
171
171
|
external
|
|
172
172
|
view
|
|
173
173
|
returns (
|
|
174
|
-
address
|
|
174
|
+
address pt,
|
|
175
175
|
address scy,
|
|
176
176
|
MarketState memory state,
|
|
177
177
|
int256 impliedYield,
|
|
@@ -179,10 +179,10 @@ contract RouterStatic is IPRouterStatic {
|
|
|
179
179
|
)
|
|
180
180
|
{
|
|
181
181
|
IPMarket _market = IPMarket(market);
|
|
182
|
-
|
|
182
|
+
pt = _market.PT();
|
|
183
183
|
scy = _market.SCY();
|
|
184
184
|
state = _market.readState(true);
|
|
185
|
-
impliedYield =
|
|
185
|
+
impliedYield = getPtImpliedYield(market);
|
|
186
186
|
exchangeRate = 0; // TODO: get the actual exchange rate
|
|
187
187
|
}
|
|
188
188
|
|
|
@@ -194,21 +194,21 @@ contract RouterStatic is IPRouterStatic {
|
|
|
194
194
|
IPMarket _market = IPMarket(market);
|
|
195
195
|
userMarketInfo.market = market;
|
|
196
196
|
userMarketInfo.lpBalance = _market.balanceOf(user);
|
|
197
|
-
// TODO: Is there a way to convert LP to
|
|
198
|
-
userMarketInfo.
|
|
197
|
+
// TODO: Is there a way to convert LP to PT and SCY?
|
|
198
|
+
userMarketInfo.ptBalance = TokenAmount(_market.PT(), 0);
|
|
199
199
|
userMarketInfo.scyBalance = TokenAmount(_market.SCY(), 0);
|
|
200
200
|
// TODO: Get this from SCY once it is in the interface
|
|
201
201
|
userMarketInfo.assetBalance = TokenAmount(address(0), 0);
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
function
|
|
204
|
+
function getUserPYPositionsByPYs(address user, address[] calldata pys)
|
|
205
205
|
external
|
|
206
206
|
view
|
|
207
|
-
returns (
|
|
207
|
+
returns (UserPYInfo[] memory userPYPositions)
|
|
208
208
|
{
|
|
209
|
-
|
|
210
|
-
for (uint256 i = 0; i <
|
|
211
|
-
|
|
209
|
+
userPYPositions = new UserPYInfo[](pys.length);
|
|
210
|
+
for (uint256 i = 0; i < pys.length; ++i) {
|
|
211
|
+
userPYPositions[i] = getUserPYInfo(pys[i], user);
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
|
|
@@ -223,11 +223,11 @@ contract RouterStatic is IPRouterStatic {
|
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
-
function
|
|
227
|
-
hasPosition = (
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
226
|
+
function hasPYPosition(UserPYInfo memory userPYInfo) public pure returns (bool hasPosition) {
|
|
227
|
+
hasPosition = (userPYInfo.ytBalance > 0 ||
|
|
228
|
+
userPYInfo.ptBalance > 0 ||
|
|
229
|
+
userPYInfo.unclaimedInterest.amount > 0 ||
|
|
230
|
+
userPYInfo.unclaimedRewards.length > 0);
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
function getUserSCYInfo(address scy, address user)
|