@pendle/core-v2 0.5.1-beta-5 → 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.
- 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 +16 -157
- 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 +7 -82
- 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
|
@@ -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
|
-
*/
|
|
@@ -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
|
|
30
|
+
uint256 ptDesired
|
|
33
31
|
)
|
|
34
32
|
external
|
|
35
33
|
returns (
|
|
36
34
|
uint256 netLpOut,
|
|
37
35
|
uint256 scyUsed,
|
|
38
|
-
uint256
|
|
36
|
+
uint256 ptUsed
|
|
39
37
|
)
|
|
40
38
|
{
|
|
41
39
|
MarketState memory state = IPMarket(market).readState(false);
|
|
42
|
-
(, netLpOut, scyUsed,
|
|
40
|
+
(, netLpOut, scyUsed, ptUsed) = state.addLiquidity(
|
|
43
41
|
scyIndex(market),
|
|
44
42
|
scyDesired,
|
|
45
|
-
|
|
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
|
|
51
|
+
returns (uint256 netScyOut, uint256 netPtOut)
|
|
54
52
|
{
|
|
55
53
|
MarketState memory state = IPMarket(market).readState(false);
|
|
56
|
-
(netScyOut,
|
|
54
|
+
(netScyOut, netPtOut) = state.removeLiquidity(lpToRemove, false);
|
|
57
55
|
}
|
|
58
56
|
|
|
59
|
-
function
|
|
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.
|
|
62
|
+
(netScyOut, netScyFee) = state.swapExactPtForScy(
|
|
65
63
|
scyIndex(market),
|
|
66
|
-
|
|
64
|
+
exactPtIn,
|
|
67
65
|
block.timestamp,
|
|
68
66
|
false
|
|
69
67
|
);
|
|
70
68
|
}
|
|
71
69
|
|
|
72
|
-
function
|
|
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.
|
|
75
|
+
(netScyIn, netScyFee) = state.swapScyForExactPt(
|
|
78
76
|
scyIndex(market),
|
|
79
|
-
|
|
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
|
|
87
|
+
function getPtImpliedYield(address market) external view returns (int256) {
|
|
90
88
|
MarketState memory state = IPMarket(market).readState(false);
|
|
91
89
|
|
|
92
|
-
int256 lnImpliedRate = (state.
|
|
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
|
|
98
|
+
bool isPT,
|
|
101
99
|
bool isYT,
|
|
102
100
|
bool isMarket
|
|
103
101
|
)
|
|
104
102
|
{
|
|
105
|
-
if (yieldContractFactory.
|
|
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
|
}
|