@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.
Files changed (54) 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 +51 -51
  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 +19 -19
  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/IPRouterStatic.ts +480 -0
  47. package/typechain-types/RouterStatic.ts +484 -4
  48. package/typechain-types/factories/IPRouterStatic__factory.ts +616 -0
  49. package/typechain-types/factories/PendleRouter__factory.ts +1 -1
  50. package/typechain-types/factories/RouterStatic__factory.ts +619 -3
  51. package/typechain-types/ActionSCYAndYOBase.ts +0 -223
  52. package/typechain-types/PendleRouterRedeemUpg.ts +0 -125
  53. package/typechain-types/factories/ActionSCYAndYOBase__factory.ts +0 -200
  54. package/typechain-types/factories/PendleRouterRedeemUpg__factory.ts +0 -112
@@ -2,7 +2,7 @@
2
2
  pragma solidity 0.8.9;
3
3
 
4
4
  import "./PendleBaseToken.sol";
5
- import "../interfaces/IPOwnershipToken.sol";
5
+ import "../interfaces/IPPrincipalToken.sol";
6
6
  import "../SuperComposableYield/ISuperComposableYield.sol";
7
7
  import "../interfaces/IPMarket.sol";
8
8
  import "../interfaces/IPMarketFactory.sol";
@@ -14,10 +14,9 @@ import "../libraries/math/Math.sol";
14
14
  import "../libraries/math/MarketMathAux.sol";
15
15
 
16
16
  import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
17
- import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
18
17
 
19
18
  // solhint-disable reason-string
20
- contract PendleMarket is PendleBaseToken, IPMarket, ReentrancyGuard {
19
+ contract PendleMarket is PendleBaseToken, IPMarket {
21
20
  using Math for uint256;
22
21
  using Math for int256;
23
22
  using Math for uint128;
@@ -26,34 +25,72 @@ contract PendleMarket is PendleBaseToken, IPMarket, ReentrancyGuard {
26
25
  using MarketMathCore for MarketState;
27
26
  using SafeERC20 for IERC20;
28
27
 
28
+ struct MarketStorage {
29
+ int128 totalPt;
30
+ int128 totalScy;
31
+ // 1 SLOT = 256 bits
32
+ uint96 lastLnImpliedRate;
33
+ uint96 oracleRate;
34
+ uint32 lastTradeTime;
35
+ uint8 _reentrancyStatus;
36
+ // 1 SLOT = 232 bits
37
+ }
38
+
39
+ uint8 private constant _NOT_ENTERED = 1;
40
+ uint8 private constant _ENTERED = 2;
41
+
29
42
  string private constant NAME = "Pendle Market";
30
43
  string private constant SYMBOL = "PENDLE-LPT";
31
44
 
32
- address public immutable OT;
45
+ address public immutable PT;
33
46
  address public immutable SCY;
34
47
  address public immutable YT;
35
48
 
36
49
  int256 public immutable scalarRoot;
37
50
  int256 public immutable initialAnchor;
38
51
 
39
- MarketStorage public marketStorage;
52
+ MarketStorage public _storage;
53
+
54
+ modifier nonReentrant() {
55
+ // On the first call to nonReentrant, _notEntered will be true
56
+ require(_storage._reentrancyStatus != _ENTERED, "ReentrancyGuard: reentrant call");
57
+
58
+ // Any calls to nonReentrant after this point will fail
59
+ _storage._reentrancyStatus = _ENTERED;
60
+
61
+ _;
62
+
63
+ // By storing the original value once again, a refund is triggered (see
64
+ // https://eips.ethereum.org/EIPS/eip-2200)
65
+ _storage._reentrancyStatus = _NOT_ENTERED;
66
+ }
40
67
 
41
68
  constructor(
42
- address _OT,
69
+ address _PT,
43
70
  int256 _scalarRoot,
44
71
  int256 _initialAnchor
45
- ) PendleBaseToken(NAME, SYMBOL, 18, IPOwnershipToken(_OT).expiry()) {
46
- OT = _OT;
47
- SCY = IPOwnershipToken(_OT).SCY();
48
- YT = IPOwnershipToken(_OT).YT();
72
+ ) PendleBaseToken(NAME, SYMBOL, 18, IPPrincipalToken(_PT).expiry()) {
73
+ PT = _PT;
74
+ SCY = IPPrincipalToken(_PT).SCY();
75
+ YT = IPPrincipalToken(_PT).YT();
49
76
  scalarRoot = _scalarRoot;
50
77
  initialAnchor = _initialAnchor;
78
+ _storage._reentrancyStatus = _NOT_ENTERED;
51
79
  }
52
80
 
81
+ /**
82
+ * @notice PendleMarket allows users to provide in PT & SCY in exchange for LPs, which
83
+ * will grant LP holders more exchange fee over time
84
+ * @dev steps working of this function:
85
+ - Releases the proportional amount of LP to receiver
86
+ - Callback to msg.sender if data.length > 0
87
+ - Ensure that the corresponding amount of SCY & PT have been transferred to this address
88
+ * @param data bytes data to be sent in the callback
89
+ */
53
90
  function addLiquidity(
54
91
  address receiver,
55
92
  uint256 scyDesired,
56
- uint256 otDesired,
93
+ uint256 ptDesired,
57
94
  bytes calldata data
58
95
  )
59
96
  external
@@ -61,23 +98,23 @@ contract PendleMarket is PendleBaseToken, IPMarket, ReentrancyGuard {
61
98
  returns (
62
99
  uint256 lpToAccount,
63
100
  uint256 scyUsed,
64
- uint256 otUsed
101
+ uint256 ptUsed
65
102
  )
66
103
  {
67
104
  MarketState memory market = readState(true);
68
105
  SCYIndex index = SCYIndexLib.newIndex(SCY);
69
106
 
70
107
  uint256 lpToReserve;
71
- (lpToReserve, lpToAccount, scyUsed, otUsed) = market.addLiquidity(
108
+ (lpToReserve, lpToAccount, scyUsed, ptUsed) = market.addLiquidity(
72
109
  index,
73
110
  scyDesired,
74
- otDesired,
111
+ ptDesired,
75
112
  true
76
113
  );
77
114
 
78
115
  // initializing the market
79
116
  if (lpToReserve != 0) {
80
- market.setInitialImpliedRate(index, initialAnchor, block.timestamp);
117
+ market.setInitialLnImpliedRate(index, initialAnchor, block.timestamp);
81
118
  _mint(address(1), lpToReserve);
82
119
  }
83
120
 
@@ -87,34 +124,45 @@ contract PendleMarket is PendleBaseToken, IPMarket, ReentrancyGuard {
87
124
  IPMarketAddRemoveCallback(msg.sender).addLiquidityCallback(
88
125
  lpToAccount,
89
126
  scyUsed,
90
- otUsed,
127
+ ptUsed,
91
128
  data
92
129
  );
93
130
  }
94
131
 
95
- require(market.totalOt.Uint() <= IERC20(OT).balanceOf(address(this)));
132
+ require(market.totalPt.Uint() <= IERC20(PT).balanceOf(address(this)));
96
133
  require(market.totalScy.Uint() <= IERC20(SCY).balanceOf(address(this)));
97
134
 
98
135
  _writeState(market);
136
+
137
+ emit AddLiquidity(receiver, lpToAccount, scyUsed, ptUsed);
99
138
  }
100
139
 
140
+ /**
141
+ * @notice LP Holders can burn their LP to receive back SCY & PT proportionally
142
+ * to their share of market
143
+ * @dev steps working of this contract
144
+ - SCY & PT will be first transferred out to receiver
145
+ - Callback to msg.sender if data.length > 0
146
+ - Ensure the corresponding amount of LP has been transferred to this contract
147
+ * @param data bytes data to be sent in the callback
148
+ */
101
149
  function removeLiquidity(
102
150
  address receiver,
103
151
  uint256 lpToRemove,
104
152
  bytes calldata data
105
- ) external nonReentrant returns (uint256 scyToAccount, uint256 otToAccount) {
153
+ ) external nonReentrant returns (uint256 scyToAccount, uint256 ptToAccount) {
106
154
  MarketState memory market = readState(true);
107
155
 
108
- (scyToAccount, otToAccount) = market.removeLiquidity(lpToRemove, true);
156
+ (scyToAccount, ptToAccount) = market.removeLiquidity(lpToRemove, true);
109
157
 
110
158
  IERC20(SCY).safeTransfer(receiver, scyToAccount);
111
- IERC20(OT).safeTransfer(receiver, otToAccount);
159
+ IERC20(PT).safeTransfer(receiver, ptToAccount);
112
160
 
113
161
  if (data.length > 0) {
114
162
  IPMarketAddRemoveCallback(msg.sender).removeLiquidityCallback(
115
163
  lpToRemove,
116
164
  scyToAccount,
117
- otToAccount,
165
+ ptToAccount,
118
166
  data
119
167
  );
120
168
  }
@@ -122,11 +170,22 @@ contract PendleMarket is PendleBaseToken, IPMarket, ReentrancyGuard {
122
170
  _burn(address(this), lpToRemove);
123
171
 
124
172
  _writeState(market);
173
+ emit RemoveLiquidity(receiver, lpToRemove, scyToAccount, ptToAccount);
125
174
  }
126
175
 
127
- function swapExactOtForScy(
176
+ /**
177
+ * @notice Pendle Market allows swaps between PT & SCY it is holding. This function
178
+ * aims to swap an exact amount of PT to SCY.
179
+ * @dev steps working of this contract
180
+ - The outcome amount of SCY will be precomputed by MarketMathLib
181
+ - Release the calculated amount of SCY to receiver
182
+ - Callback to msg.sender if data.length > 0
183
+ - Ensure exactPtIn amount of PT has been transferred to this address
184
+ * @param data bytes data to be sent in the callback
185
+ */
186
+ function swapExactPtForScy(
128
187
  address receiver,
129
- uint256 exactOtIn,
188
+ uint256 exactPtIn,
130
189
  uint256 minScyOut,
131
190
  bytes calldata data
132
191
  ) external nonReentrant returns (uint256 netScyOut, uint256 netScyToReserve) {
@@ -134,28 +193,40 @@ contract PendleMarket is PendleBaseToken, IPMarket, ReentrancyGuard {
134
193
 
135
194
  MarketState memory market = readState(true);
136
195
 
137
- (netScyOut, netScyToReserve) = market.swapExactOtForScy(
196
+ (netScyOut, netScyToReserve) = market.swapExactPtForScy(
138
197
  SCYIndexLib.newIndex(SCY),
139
- exactOtIn,
198
+ exactPtIn,
140
199
  block.timestamp,
141
200
  true
142
201
  );
143
202
  require(netScyOut >= minScyOut, "insufficient scy out");
203
+
144
204
  IERC20(SCY).safeTransfer(receiver, netScyOut);
145
205
  IERC20(SCY).safeTransfer(market.treasury, netScyToReserve);
146
206
 
147
207
  if (data.length > 0) {
148
- IPMarketSwapCallback(msg.sender).swapCallback(exactOtIn.neg(), netScyOut.Int(), data);
208
+ IPMarketSwapCallback(msg.sender).swapCallback(exactPtIn.neg(), netScyOut.Int(), data);
149
209
  }
150
210
 
151
- // have received enough OT
152
- require(market.totalOt.Uint() <= IERC20(OT).balanceOf(address(this)));
211
+ // have received enough PT
212
+ require(market.totalPt.Uint() <= IERC20(PT).balanceOf(address(this)));
153
213
  _writeState(market);
214
+
215
+ emit Swap(receiver, exactPtIn.neg(), netScyOut.Int(), netScyToReserve);
154
216
  }
155
217
 
156
- function swapScyForExactOt(
218
+ /**
219
+ * @notice Pendle Market allows swaps between PT & SCY it is holding. This function
220
+ * aims to swap an exact amount of SCY to PT.
221
+ * @dev steps working of this function
222
+ - The exact outcome amount of PT will be transferred to receiver
223
+ - Callback to msg.sender if data.length > 0
224
+ - Ensure the calculated required amount of SCY is transferred to this address
225
+ * @param data bytes data to be sent in the callback
226
+ */
227
+ function swapScyForExactPt(
157
228
  address receiver,
158
- uint256 exactOtOut,
229
+ uint256 exactPtOut,
159
230
  uint256 maxScyIn,
160
231
  bytes calldata data
161
232
  ) external nonReentrant returns (uint256 netScyIn, uint256 netScyToReserve) {
@@ -163,41 +234,64 @@ contract PendleMarket is PendleBaseToken, IPMarket, ReentrancyGuard {
163
234
 
164
235
  MarketState memory market = readState(true);
165
236
 
166
- (netScyIn, netScyToReserve) = market.swapScyForExactOt(
237
+ (netScyIn, netScyToReserve) = market.swapScyForExactPt(
167
238
  SCYIndexLib.newIndex(SCY),
168
- exactOtOut,
239
+ exactPtOut,
169
240
  block.timestamp,
170
241
  true
171
242
  );
172
243
  require(netScyIn <= maxScyIn, "scy in exceed limit");
173
- IERC20(OT).safeTransfer(receiver, exactOtOut);
244
+
245
+ IERC20(PT).safeTransfer(receiver, exactPtOut);
174
246
  IERC20(SCY).safeTransfer(market.treasury, netScyToReserve);
175
247
 
176
248
  if (data.length > 0) {
177
- IPMarketSwapCallback(msg.sender).swapCallback(exactOtOut.Int(), netScyIn.neg(), data);
249
+ IPMarketSwapCallback(msg.sender).swapCallback(exactPtOut.Int(), netScyIn.neg(), data);
178
250
  }
179
251
 
180
252
  // have received enough SCY
181
253
  require(market.totalScy.Uint() <= IERC20(SCY).balanceOf(address(this)));
182
254
  _writeState(market);
255
+
256
+ emit Swap(receiver, exactPtOut.Int(), netScyIn.neg(), netScyToReserve);
183
257
  }
184
258
 
185
259
  /// @dev this function is just a place holder. Later on the rewards will be transferred to the liquidity minining
186
260
  /// instead
187
261
  function redeemScyReward() external returns (uint256[] memory outAmounts) {
188
262
  outAmounts = ISuperComposableYield(SCY).redeemReward(address(this));
263
+ address[] memory rewardTokens = ISuperComposableYield(SCY).getRewardTokens();
264
+ address treasury = IPMarketFactory(factory).treasury();
265
+ for (uint256 i = 0; i < rewardTokens.length; i++) {
266
+ IERC20(rewardTokens[i]).safeTransfer(treasury, outAmounts[i]);
267
+ }
268
+ }
269
+
270
+ function readTokens()
271
+ external
272
+ view
273
+ returns (
274
+ ISuperComposableYield _SCY,
275
+ IPPrincipalToken _PT,
276
+ IPYieldToken _YT
277
+ )
278
+ {
279
+ _SCY = ISuperComposableYield(SCY);
280
+ _PT = IPPrincipalToken(PT);
281
+ _YT = IPYieldToken(YT);
189
282
  }
190
283
 
191
284
  function readState(bool updateRateOracle) public view returns (MarketState memory market) {
192
- MarketStorage storage store = marketStorage;
193
- market.totalOt = store.totalOt;
194
- market.totalScy = store.totalScy;
285
+ MarketStorage memory local = _storage;
286
+
287
+ market.totalPt = local.totalPt;
288
+ market.totalScy = local.totalScy;
195
289
  market.totalLp = totalSupply().Int();
196
- market.oracleRate = store.oracleRate;
290
+ market.oracleRate = local.oracleRate;
197
291
 
198
292
  (
199
293
  market.treasury,
200
- market.feeRateRoot,
294
+ market.lnFeeRateRoot,
201
295
  market.rateOracleTimeWindow,
202
296
  market.reserveFeePercent
203
297
  ) = IPMarketFactory(factory).marketConfig();
@@ -205,35 +299,25 @@ contract PendleMarket is PendleBaseToken, IPMarket, ReentrancyGuard {
205
299
  market.scalarRoot = scalarRoot;
206
300
  market.expiry = expiry;
207
301
 
208
- market.lastImpliedRate = store.lastImpliedRate;
209
- market.lastTradeTime = store.lastTradeTime;
302
+ market.lastLnImpliedRate = local.lastLnImpliedRate;
303
+ market.lastTradeTime = local.lastTradeTime;
210
304
 
211
305
  if (updateRateOracle) {
212
- market.updateNewRateOracle(block.timestamp);
306
+ market.oracleRate = market.getNewRateOracle(block.timestamp);
213
307
  }
214
308
  }
215
309
 
216
310
  function _writeState(MarketState memory market) internal {
217
- MarketStorage storage store = marketStorage;
311
+ MarketStorage memory tempStore;
218
312
 
219
- store.totalOt = market.totalOt.Int128();
220
- store.totalScy = market.totalScy.Int128();
221
- store.lastImpliedRate = market.lastImpliedRate.Uint112();
222
- store.oracleRate = market.oracleRate.Uint112();
223
- store.lastTradeTime = market.lastTradeTime.Uint32();
224
- }
313
+ tempStore.totalPt = market.totalPt.Int128();
314
+ tempStore.totalScy = market.totalScy.Int128();
315
+ tempStore.lastLnImpliedRate = market.lastLnImpliedRate.Uint96();
316
+ tempStore.oracleRate = market.oracleRate.Uint96();
317
+ tempStore.lastTradeTime = market.lastTradeTime.Uint32();
225
318
 
226
- function readTokens()
227
- external
228
- view
229
- returns (
230
- ISuperComposableYield _SCY,
231
- IPOwnershipToken _OT,
232
- IPYieldToken _YT
233
- )
234
- {
235
- _SCY = ISuperComposableYield(SCY);
236
- _OT = IPOwnershipToken(OT);
237
- _YT = IPYieldToken(YT);
319
+ _storage = tempStore;
320
+
321
+ emit UpdateImpliedRate(block.timestamp, market.lastLnImpliedRate);
238
322
  }
239
323
  }
@@ -11,16 +11,19 @@ contract PendleMarketFactory is PermissionsV2Upg, IPMarketFactory {
11
11
  using EnumerableSet for EnumerableSet.AddressSet;
12
12
 
13
13
  struct MarketConfig {
14
- address treasury; // 160 bit
15
- uint96 feeRateRoot; // 96 bit
16
- // 1 SLOT
14
+ address treasury;
15
+ uint96 lnFeeRateRoot;
16
+ // 1 SLOT = 256 bits
17
17
  uint32 rateOracleTimeWindow;
18
18
  uint8 reserveFeePercent;
19
+ // 1 SLOT = 40 bits
19
20
  }
20
21
 
21
22
  mapping(address => EnumerableSet.AddressSet) internal markets;
22
23
 
23
24
  address public immutable yieldContractFactory;
25
+ uint256 public immutable maxLnFeeRateRoot;
26
+ uint256 public constant minRateOracleTimeWindow = 300 seconds;
24
27
 
25
28
  MarketConfig public marketConfig;
26
29
 
@@ -28,38 +31,46 @@ contract PendleMarketFactory is PermissionsV2Upg, IPMarketFactory {
28
31
  address _governanceManager,
29
32
  address _yieldContractFactory,
30
33
  address _treasury,
31
- uint96 _feeRateRoot,
34
+ uint96 _lnFeeRateRoot,
32
35
  uint32 _rateOracleTimeWindow,
33
36
  uint8 _reserveFeePercent
34
37
  ) PermissionsV2Upg(_governanceManager) {
38
+ require(_yieldContractFactory != address(0), "zero address");
35
39
  yieldContractFactory = _yieldContractFactory;
40
+ maxLnFeeRateRoot = uint256(LogExpMath.ln(int256((105 * Math.IONE) / 100))); // ln(1.05)
36
41
 
37
42
  setTreasury(_treasury);
38
- setFeeRateRoot(_feeRateRoot);
43
+ setlnFeeRateRoot(_lnFeeRateRoot);
39
44
  setRateOracleTimeWindow(_rateOracleTimeWindow);
40
45
  setReserveFeePercent(_reserveFeePercent);
41
46
  }
42
47
 
48
+ /**
49
+ * @notice Create a market between PT and its corresponding SCY
50
+ * with scalar & anchor config. Anyone is allowed to create a market on their own.
51
+ */
43
52
  function createNewMarket(
44
- address OT,
53
+ address PT,
45
54
  int256 scalarRoot,
46
55
  int256 initialAnchor
47
56
  ) external returns (address market) {
48
- address SCY = IPOwnershipToken(OT).SCY();
49
- uint256 expiry = IPOwnershipToken(OT).expiry();
57
+ address SCY = IPPrincipalToken(PT).SCY();
58
+ uint256 expiry = IPPrincipalToken(PT).expiry();
50
59
 
51
60
  require(
52
- IPYieldContractFactory(yieldContractFactory).getOT(SCY, expiry) == OT,
53
- "INVALID_OT"
61
+ IPYieldContractFactory(yieldContractFactory).getPT(SCY, expiry) == PT,
62
+ "INVALID_PT"
54
63
  );
55
64
 
56
- market = address(new PendleMarket(OT, scalarRoot, initialAnchor));
57
- markets[OT].add(market);
65
+ market = address(new PendleMarket(PT, scalarRoot, initialAnchor));
66
+ require(markets[PT].add(market), "market add failed");
67
+
68
+ emit CreateNewMarket(PT, scalarRoot, initialAnchor);
58
69
  }
59
70
 
60
71
  function isValidMarket(address market) external view returns (bool) {
61
- address OT = IPMarket(market).OT();
62
- return markets[OT].contains(market);
72
+ address PT = IPMarket(market).PT();
73
+ return markets[PT].contains(market);
63
74
  }
64
75
 
65
76
  function treasury() external view returns (address) {
@@ -71,18 +82,18 @@ contract PendleMarketFactory is PermissionsV2Upg, IPMarketFactory {
71
82
  marketConfig.treasury = newTreasury;
72
83
  }
73
84
 
74
- function setFeeRateRoot(uint96 newFeeRateRoot) public onlyGovernance {
75
- // TODO: hard cap on the fee
76
- marketConfig.feeRateRoot = newFeeRateRoot;
85
+ function setlnFeeRateRoot(uint96 newlnFeeRateRoot) public onlyGovernance {
86
+ require(newlnFeeRateRoot <= maxLnFeeRateRoot, "invalid fee rate root");
87
+ marketConfig.lnFeeRateRoot = newlnFeeRateRoot;
77
88
  }
78
89
 
79
90
  function setRateOracleTimeWindow(uint32 newRateOracleTimeWindow) public onlyGovernance {
80
- // TODO: hard min for the time window
91
+ require(newRateOracleTimeWindow >= minRateOracleTimeWindow, "invalid time window");
81
92
  marketConfig.rateOracleTimeWindow = newRateOracleTimeWindow;
82
93
  }
83
94
 
84
95
  function setReserveFeePercent(uint8 newReserveFeePercent) public onlyGovernance {
85
- require(newReserveFeePercent <= 100, "invalid fee rate");
96
+ require(newReserveFeePercent <= 100, "invalid reserve fee percent");
86
97
  marketConfig.reserveFeePercent = newReserveFeePercent;
87
98
  }
88
99
  }
@@ -2,12 +2,15 @@
2
2
  pragma solidity 0.8.9;
3
3
 
4
4
  import "./PendleBaseToken.sol";
5
- import "../interfaces/IPOwnershipToken.sol";
5
+ import "../interfaces/IPPrincipalToken.sol";
6
+ import "../interfaces/IPYieldToken.sol";
6
7
 
7
- contract PendleOwnershipToken is PendleBaseToken, IPOwnershipToken {
8
+ contract PendlePrincipalToken is PendleBaseToken, IPPrincipalToken {
8
9
  address public immutable SCY;
9
10
  address public YT;
10
11
 
12
+ event YTSet(address indexed YT);
13
+
11
14
  modifier onlyYT() {
12
15
  require(msg.sender == address(YT), "ONLY_YT");
13
16
  _;
@@ -25,17 +28,26 @@ contract PendleOwnershipToken is PendleBaseToken, IPOwnershipToken {
25
28
  uint8 __decimals,
26
29
  uint256 _expiry
27
30
  ) PendleBaseToken(_name, _symbol, __decimals, _expiry) {
31
+ require(_SCY != address(0), "zero address");
28
32
  SCY = _SCY;
29
33
  }
30
34
 
31
35
  function initialize(address _YT) external onlyFactory {
36
+ require(IPYieldToken(_YT).PT() == address(this), "invalid YT");
32
37
  YT = _YT;
38
+ emit YTSet(_YT);
33
39
  }
34
40
 
41
+ /**
42
+ * @dev only callable by the YT correspond to this OT
43
+ */
35
44
  function burnByYT(address user, uint256 amount) external onlyYT {
36
45
  _burn(user, amount);
37
46
  }
38
47
 
48
+ /**
49
+ * @dev only callable by the YT correspond to this OT
50
+ */
39
51
  function mintByYT(address user, uint256 amount) external onlyYT {
40
52
  _mint(user, amount);
41
53
  }
@@ -8,6 +8,7 @@ import "../interfaces/IPActionCore.sol";
8
8
  import "../interfaces/IPActionYT.sol";
9
9
  import "../interfaces/IPRouterStatic.sol";
10
10
  import "../interfaces/IPMarketSwapCallback.sol";
11
+ import "../interfaces/IPActionRedeem.sol";
11
12
  import "../periphery/PermissionsV2Upg.sol";
12
13
 
13
14
  /// @dev this contract will be deployed behind an ERC1967 proxy
@@ -19,16 +20,26 @@ contract PendleRouter is Proxy, Initializable, UUPSUpgradeable, PermissionsV2Upg
19
20
  address public immutable ACTION_CORE;
20
21
  address public immutable ACTION_YT;
21
22
  address public immutable ACTION_CALLBACK;
23
+ address public immutable ACTION_REDEEM;
22
24
 
23
25
  constructor(
24
26
  address _ACTION_CORE,
25
27
  address _ACTION_YT,
26
28
  address _ACTION_CALLBACK,
29
+ address _ACTION_REDEEM,
27
30
  address _governanceManager
28
31
  ) PermissionsV2Upg(_governanceManager) initializer {
32
+ require(
33
+ _ACTION_CORE != address(0) &&
34
+ _ACTION_YT != address(0) &&
35
+ _ACTION_CALLBACK != address(0) &&
36
+ _ACTION_REDEEM != address(0),
37
+ "zero address"
38
+ );
29
39
  ACTION_CORE = _ACTION_CORE;
30
40
  ACTION_YT = _ACTION_YT;
31
41
  ACTION_CALLBACK = _ACTION_CALLBACK;
42
+ ACTION_REDEEM = _ACTION_REDEEM;
32
43
  }
33
44
 
34
45
  function initialize() external initializer {
@@ -40,16 +51,16 @@ contract PendleRouter is Proxy, Initializable, UUPSUpgradeable, PermissionsV2Upg
40
51
  if (
41
52
  sig == IPActionCore.mintScyFromRawToken.selector ||
42
53
  sig == IPActionCore.redeemScyToRawToken.selector ||
43
- sig == IPActionCore.mintYoFromRawToken.selector ||
44
- sig == IPActionCore.redeemYoToRawToken.selector ||
54
+ sig == IPActionCore.mintPyFromRawToken.selector ||
55
+ sig == IPActionCore.redeemPyToRawToken.selector ||
45
56
  sig == IPActionCore.addLiquidity.selector ||
46
57
  sig == IPActionCore.removeLiquidity.selector ||
47
- sig == IPActionCore.swapExactOtForScy.selector ||
48
- sig == IPActionCore.swapOtForExactScy.selector ||
49
- sig == IPActionCore.swapScyForExactOt.selector ||
50
- sig == IPActionCore.swapExactScyForOt.selector ||
51
- sig == IPActionCore.swapExactRawTokenForOt.selector ||
52
- sig == IPActionCore.swapExactOtForRawToken.selector
58
+ sig == IPActionCore.swapExactPtForScy.selector ||
59
+ sig == IPActionCore.swapPtForExactScy.selector ||
60
+ sig == IPActionCore.swapScyForExactPt.selector ||
61
+ sig == IPActionCore.swapExactScyForPt.selector ||
62
+ sig == IPActionCore.swapExactRawTokenForPt.selector ||
63
+ sig == IPActionCore.swapExactPtForRawToken.selector
53
64
  ) {
54
65
  return ACTION_CORE;
55
66
  } else if (
@@ -57,9 +68,12 @@ contract PendleRouter is Proxy, Initializable, UUPSUpgradeable, PermissionsV2Upg
57
68
  sig == IPActionYT.swapScyForExactYt.selector ||
58
69
  sig == IPActionYT.swapExactScyForYt.selector ||
59
70
  sig == IPActionYT.swapExactRawTokenForYt.selector ||
60
- sig == IPActionYT.swapExactYtForRawToken.selector
71
+ sig == IPActionYT.swapExactYtForRawToken.selector ||
72
+ sig == IPActionYT.swapYtForExactScy.selector
61
73
  ) {
62
74
  return ACTION_YT;
75
+ } else if (sig == IPActionRedeem.redeemDueIncome.selector) {
76
+ return ACTION_REDEEM;
63
77
  } else if (sig == IPMarketSwapCallback.swapCallback.selector) {
64
78
  return ACTION_CALLBACK;
65
79
  }