@pendle/core-v2 0.2.3 → 0.4.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 +15 -5
- package/contracts/SuperComposableYield/implementations/RewardManager.sol +2 -2
- package/contracts/SuperComposableYield/implementations/SCYBase.sol +23 -3
- package/contracts/SuperComposableYield/implementations/SCYBaseWithRewards.sol +4 -7
- package/contracts/core/PendleMarket.sol +24 -22
- package/contracts/core/PendleMarketFactory.sol +7 -6
- package/contracts/core/PendleSCYImpl/PendleBenQiErc20SCY.sol +1 -1
- package/contracts/core/PendleSCYImpl/PendleBtrflySCY.sol +2 -7
- package/contracts/core/PendleSCYImpl/PendleYearnVaultSCY.sol +2 -7
- package/contracts/core/PendleYieldContractFactory.sol +12 -7
- package/contracts/core/PendleYieldToken.sol +93 -55
- package/contracts/core/router/PendleRouterCoreUpg.sol +35 -21
- package/contracts/core/router/PendleRouterProxy.sol +12 -6
- package/contracts/core/router/PendleRouterStaticUpg.sol +43 -18
- package/contracts/core/router/PendleRouterYTUpg.sol +47 -13
- package/contracts/core/router/base/PendleRouterOTBaseUpg.sol +22 -32
- package/contracts/core/router/base/PendleRouterSCYAndForgeBaseUpg.sol +4 -4
- package/contracts/core/router/base/PendleRouterYTBaseUpg.sol +62 -43
- package/contracts/interfaces/IPMarket.sol +4 -2
- package/contracts/interfaces/{IPPermissionsV2.sol → IPPermissionsV2Upg.sol} +1 -1
- package/contracts/interfaces/IPRouterCore.sol +24 -21
- package/contracts/interfaces/IPRouterStatic.sol +10 -1
- package/contracts/interfaces/IPRouterYT.sol +16 -4
- package/contracts/interfaces/IPYieldContractFactory.sol +4 -0
- package/contracts/interfaces/IPYieldToken.sol +2 -4
- package/contracts/libraries/SCYIndex.sol +3 -3
- package/contracts/libraries/math/LogExpMath.sol +1 -90
- package/contracts/libraries/math/MarketApproxLib.sol +336 -94
- package/contracts/libraries/math/MarketMathAux.sol +101 -0
- package/contracts/libraries/math/MarketMathCore.sol +423 -0
- package/contracts/libraries/math/Math.sol +144 -0
- package/contracts/{core/misc → periphery}/PendleJoeSwapHelperUpg.sol +2 -2
- package/contracts/periphery/{PermissionsV2.sol → PermissionsV2Upg.sol} +2 -2
- package/package.json +1 -1
- package/typechain-types/IPMarket.ts +22 -11
- package/typechain-types/IPRouterCore.ts +68 -58
- package/typechain-types/IPRouterStatic.ts +52 -0
- package/typechain-types/IPRouterYT.ts +96 -12
- package/typechain-types/IPYieldContractFactory.ts +32 -0
- package/typechain-types/IPYieldToken.ts +14 -48
- package/typechain-types/IPermissionsV2Upg.ts +87 -0
- package/typechain-types/ISuperComposableYield.ts +98 -11
- package/typechain-types/OwnableUpgradeable.ts +165 -0
- package/typechain-types/PendleAaveV3SCY.ts +98 -11
- package/typechain-types/PendleBenQiErc20SCY.ts +98 -11
- package/typechain-types/PendleBtrflyScy.ts +98 -11
- package/typechain-types/PendleMarket.ts +22 -11
- package/typechain-types/PendleRouterCoreUpg.ts +66 -48
- package/typechain-types/PendleRouterProxy.ts +15 -118
- package/typechain-types/PendleRouterStaticUpg.ts +92 -0
- package/typechain-types/PendleRouterYTUpg.ts +96 -12
- package/typechain-types/PendleYearnVaultScy.ts +98 -11
- package/typechain-types/PendleYieldContractFactory.ts +40 -113
- package/typechain-types/PendleYieldToken.ts +106 -7
- package/typechain-types/PermissionsV2Upg.ts +87 -0
- package/typechain-types/SCYBase.ts +98 -11
- package/typechain-types/SCYBaseWithRewards.ts +98 -11
- package/typechain-types/factories/IPMarket__factory.ts +8 -2
- package/typechain-types/factories/IPRouterCore__factory.ts +38 -23
- package/typechain-types/factories/IPRouterStatic__factory.ts +29 -0
- package/typechain-types/factories/IPRouterYT__factory.ts +65 -6
- package/typechain-types/factories/IPYieldContractFactory__factory.ts +38 -0
- package/typechain-types/factories/IPYieldToken__factory.ts +1 -20
- package/typechain-types/factories/IPermissionsV2Upg__factory.ts +39 -0
- package/typechain-types/factories/ISuperComposableYield__factory.ts +65 -2
- package/typechain-types/factories/OwnableUpgradeable__factory.ts +78 -0
- package/typechain-types/factories/PendleAaveV3SCY__factory.ts +66 -3
- package/typechain-types/factories/PendleBenQiErc20SCY__factory.ts +66 -3
- package/typechain-types/factories/PendleBtrflyScy__factory.ts +66 -3
- package/typechain-types/factories/PendleMarketFactory__factory.ts +1 -1
- package/typechain-types/factories/PendleMarket__factory.ts +9 -3
- package/typechain-types/factories/PendleRouterCoreUpg__factory.ts +26 -11
- package/typechain-types/factories/PendleRouterProxy__factory.ts +14 -67
- package/typechain-types/factories/PendleRouterStaticUpg__factory.ts +63 -17
- package/typechain-types/factories/PendleRouterYTUpg__factory.ts +66 -7
- package/typechain-types/factories/PendleYearnVaultScy__factory.ts +66 -3
- package/typechain-types/factories/PendleYieldContractFactory__factory.ts +39 -54
- package/typechain-types/factories/PendleYieldToken__factory.ts +53 -2
- package/typechain-types/factories/PermissionsV2Upg__factory.ts +39 -0
- package/typechain-types/factories/SCYBaseWithRewards__factory.ts +65 -2
- package/typechain-types/factories/SCYBase__factory.ts +65 -2
- package/typechain-types/hardhat.d.ts +26 -53
- package/typechain-types/index.ts +8 -14
- package/contracts/core/misc/BoringOwnable.sol +0 -62
- package/contracts/core/misc/BoringOwnableUpg.sol +0 -64
- package/contracts/libraries/math/FixedPoint.sol +0 -205
- package/contracts/libraries/math/MarketMathLib.sol +0 -614
|
@@ -25,15 +25,29 @@ pragma solidity 0.8.9;
|
|
|
25
25
|
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
|
|
26
26
|
|
|
27
27
|
interface ISuperComposableYield is IERC20Metadata {
|
|
28
|
+
function mintNoPull(
|
|
29
|
+
address receiver,
|
|
30
|
+
address baseTokenIn,
|
|
31
|
+
uint256 minAmountScyOut
|
|
32
|
+
) external returns (uint256 amountScyOut);
|
|
33
|
+
|
|
34
|
+
function redeemNoPull(
|
|
35
|
+
address receiver,
|
|
36
|
+
address baseTokenOut,
|
|
37
|
+
uint256 minAmountBaseOut
|
|
38
|
+
) external returns (uint256 amountBaseOut);
|
|
39
|
+
|
|
28
40
|
function mint(
|
|
29
41
|
address receiver,
|
|
30
42
|
address baseTokenIn,
|
|
43
|
+
uint256 amountBaseToPull,
|
|
31
44
|
uint256 minAmountScyOut
|
|
32
45
|
) external returns (uint256 amountScyOut);
|
|
33
46
|
|
|
34
47
|
function redeem(
|
|
35
48
|
address receiver,
|
|
36
49
|
address baseTokenOut,
|
|
50
|
+
uint256 amountScyToPull,
|
|
37
51
|
uint256 minAmountBaseOut
|
|
38
52
|
) external returns (uint256 amountBaseOut);
|
|
39
53
|
|
|
@@ -41,11 +55,7 @@ interface ISuperComposableYield is IERC20Metadata {
|
|
|
41
55
|
|
|
42
56
|
function updateUserReward(address user) external;
|
|
43
57
|
|
|
44
|
-
|
|
45
|
-
/// else, receiver must be msg.sender
|
|
46
|
-
function redeemReward(address user, address receiver)
|
|
47
|
-
external
|
|
48
|
-
returns (uint256[] memory outAmounts);
|
|
58
|
+
function redeemReward(address user) external returns (uint256[] memory outAmounts);
|
|
49
59
|
|
|
50
60
|
/**
|
|
51
61
|
* @notice scyIndexCurrent.mulDown(scyBalance) must return the asset balance of the account
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
pragma solidity 0.8.9;
|
|
3
3
|
import "../ISuperComposableYield.sol";
|
|
4
4
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
5
|
-
import "../../libraries/math/
|
|
5
|
+
import "../../libraries/math/Math.sol";
|
|
6
6
|
|
|
7
7
|
abstract contract RewardManager {
|
|
8
8
|
using SafeERC20 for IERC20;
|
|
9
|
-
using
|
|
9
|
+
using Math for uint256;
|
|
10
10
|
|
|
11
11
|
struct GlobalReward {
|
|
12
12
|
uint256 index;
|
|
@@ -4,7 +4,7 @@ import "../ISuperComposableYield.sol";
|
|
|
4
4
|
import "./RewardManager.sol";
|
|
5
5
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
6
6
|
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
7
|
-
import "../../libraries/math/
|
|
7
|
+
import "../../libraries/math/Math.sol";
|
|
8
8
|
import "../SCYUtils.sol";
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -15,7 +15,7 @@ satisfy this restriction is AaveV2's aToken
|
|
|
15
15
|
*/
|
|
16
16
|
abstract contract SCYBase is ERC20, ISuperComposableYield {
|
|
17
17
|
using SafeERC20 for IERC20;
|
|
18
|
-
using
|
|
18
|
+
using Math for uint256;
|
|
19
19
|
|
|
20
20
|
uint8 private immutable _scyDecimals;
|
|
21
21
|
uint8 private immutable _assetDecimals;
|
|
@@ -37,6 +37,26 @@ abstract contract SCYBase is ERC20, ISuperComposableYield {
|
|
|
37
37
|
//////////////////////////////////////////////////////////////*/
|
|
38
38
|
|
|
39
39
|
function mint(
|
|
40
|
+
address receiver,
|
|
41
|
+
address baseTokenIn,
|
|
42
|
+
uint256 amountBaseToPull,
|
|
43
|
+
uint256 minAmountScyOut
|
|
44
|
+
) external returns (uint256 amountScyOut) {
|
|
45
|
+
IERC20(baseTokenIn).safeTransferFrom(msg.sender, address(this), amountBaseToPull);
|
|
46
|
+
amountScyOut = mintNoPull(receiver, baseTokenIn, minAmountScyOut);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function redeem(
|
|
50
|
+
address receiver,
|
|
51
|
+
address baseTokenOut,
|
|
52
|
+
uint256 amountScyToPull,
|
|
53
|
+
uint256 minAmountBaseOut
|
|
54
|
+
) external returns (uint256 amountBaseOut) {
|
|
55
|
+
transferFrom(msg.sender, address(this), amountScyToPull);
|
|
56
|
+
amountBaseOut = redeemNoPull(receiver, baseTokenOut, minAmountBaseOut);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function mintNoPull(
|
|
40
60
|
address receiver,
|
|
41
61
|
address baseTokenIn,
|
|
42
62
|
uint256 minAmountScyOut
|
|
@@ -52,7 +72,7 @@ abstract contract SCYBase is ERC20, ISuperComposableYield {
|
|
|
52
72
|
_mint(receiver, amountScyOut);
|
|
53
73
|
}
|
|
54
74
|
|
|
55
|
-
function
|
|
75
|
+
function redeemNoPull(
|
|
56
76
|
address receiver,
|
|
57
77
|
address baseTokenOut,
|
|
58
78
|
uint256 minAmountBaseOut
|
|
@@ -5,7 +5,7 @@ import "./RewardManager.sol";
|
|
|
5
5
|
import "./SCYBase.sol";
|
|
6
6
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
7
7
|
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
8
|
-
import "../../libraries/math/
|
|
8
|
+
import "../../libraries/math/Math.sol";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
# CONDITIONS TO USE THIS PRESET:
|
|
@@ -15,7 +15,7 @@ satisfy this restriction is AaveV2's aToken
|
|
|
15
15
|
*/
|
|
16
16
|
abstract contract SCYBaseWithRewards is SCYBase, RewardManager {
|
|
17
17
|
using SafeERC20 for IERC20;
|
|
18
|
-
using
|
|
18
|
+
using Math for uint256;
|
|
19
19
|
|
|
20
20
|
constructor(
|
|
21
21
|
string memory _name,
|
|
@@ -34,17 +34,14 @@ abstract contract SCYBaseWithRewards is SCYBase, RewardManager {
|
|
|
34
34
|
REWARDS-RELATED
|
|
35
35
|
//////////////////////////////////////////////////////////////*/
|
|
36
36
|
|
|
37
|
-
function redeemReward(address user
|
|
37
|
+
function redeemReward(address user)
|
|
38
38
|
public
|
|
39
39
|
virtual
|
|
40
40
|
override
|
|
41
41
|
returns (uint256[] memory outAmounts)
|
|
42
42
|
{
|
|
43
|
-
if (user != msg.sender) require(receiver == user, "invalid receiver");
|
|
44
|
-
else require(receiver != address(0), "zero address");
|
|
45
|
-
|
|
46
43
|
_updateUserReward(user, balanceOf(user), totalSupply());
|
|
47
|
-
outAmounts = _doTransferOutRewardsForUser(user,
|
|
44
|
+
outAmounts = _doTransferOutRewardsForUser(user, user);
|
|
48
45
|
}
|
|
49
46
|
|
|
50
47
|
function updateGlobalReward() public virtual override {
|
|
@@ -10,19 +10,20 @@ import "../interfaces/IPMarketSwapCallback.sol";
|
|
|
10
10
|
import "../interfaces/IPMarketAddRemoveCallback.sol";
|
|
11
11
|
|
|
12
12
|
import "../libraries/math/LogExpMath.sol";
|
|
13
|
-
import "../libraries/math/
|
|
14
|
-
import "../libraries/math/
|
|
13
|
+
import "../libraries/math/Math.sol";
|
|
14
|
+
import "../libraries/math/MarketMathAux.sol";
|
|
15
15
|
|
|
16
16
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
17
17
|
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
|
18
18
|
|
|
19
19
|
// solhint-disable reason-string
|
|
20
20
|
contract PendleMarket is PendleBaseToken, IPMarket, ReentrancyGuard {
|
|
21
|
-
using
|
|
22
|
-
using
|
|
23
|
-
using
|
|
21
|
+
using Math for uint256;
|
|
22
|
+
using Math for int256;
|
|
23
|
+
using Math for uint128;
|
|
24
24
|
using LogExpMath for uint256;
|
|
25
|
-
using
|
|
25
|
+
using MarketMathAux for MarketState;
|
|
26
|
+
using MarketMathCore for MarketState;
|
|
26
27
|
using SafeERC20 for IERC20;
|
|
27
28
|
|
|
28
29
|
string private constant NAME = "Pendle Market";
|
|
@@ -75,14 +76,15 @@ contract PendleMarket is PendleBaseToken, IPMarket, ReentrancyGuard {
|
|
|
75
76
|
uint256 otUsed
|
|
76
77
|
)
|
|
77
78
|
{
|
|
78
|
-
|
|
79
|
+
MarketState memory market = readState(true);
|
|
79
80
|
SCYIndex index = SCYIndexLib.newIndex(SCY);
|
|
80
81
|
|
|
81
82
|
uint256 lpToReserve;
|
|
82
83
|
(lpToReserve, lpToAccount, scyUsed, otUsed) = market.addLiquidity(
|
|
83
84
|
index,
|
|
84
85
|
scyDesired,
|
|
85
|
-
otDesired
|
|
86
|
+
otDesired,
|
|
87
|
+
true
|
|
86
88
|
);
|
|
87
89
|
|
|
88
90
|
// initializing the market
|
|
@@ -113,9 +115,9 @@ contract PendleMarket is PendleBaseToken, IPMarket, ReentrancyGuard {
|
|
|
113
115
|
uint256 lpToRemove,
|
|
114
116
|
bytes calldata data
|
|
115
117
|
) external nonReentrant returns (uint256 scyToAccount, uint256 otToAccount) {
|
|
116
|
-
|
|
118
|
+
MarketState memory market = readState(true);
|
|
117
119
|
|
|
118
|
-
(scyToAccount, otToAccount) = market.removeLiquidity(lpToRemove);
|
|
120
|
+
(scyToAccount, otToAccount) = market.removeLiquidity(lpToRemove, true);
|
|
119
121
|
|
|
120
122
|
IERC20(SCY).safeTransfer(receiver, scyToAccount);
|
|
121
123
|
IERC20(OT).safeTransfer(receiver, otToAccount);
|
|
@@ -142,12 +144,13 @@ contract PendleMarket is PendleBaseToken, IPMarket, ReentrancyGuard {
|
|
|
142
144
|
) external nonReentrant returns (uint256 netScyOut, uint256 netScyToReserve) {
|
|
143
145
|
require(block.timestamp < expiry, "MARKET_EXPIRED");
|
|
144
146
|
|
|
145
|
-
|
|
147
|
+
MarketState memory market = readState(true);
|
|
146
148
|
|
|
147
149
|
(netScyOut, netScyToReserve) = market.swapExactOtForScy(
|
|
148
150
|
SCYIndexLib.newIndex(SCY),
|
|
149
151
|
exactOtIn,
|
|
150
|
-
block.timestamp
|
|
152
|
+
block.timestamp,
|
|
153
|
+
true
|
|
151
154
|
);
|
|
152
155
|
require(netScyOut >= minScyOut, "insufficient scy out");
|
|
153
156
|
IERC20(SCY).safeTransfer(receiver, netScyOut);
|
|
@@ -170,12 +173,13 @@ contract PendleMarket is PendleBaseToken, IPMarket, ReentrancyGuard {
|
|
|
170
173
|
) external nonReentrant returns (uint256 netScyIn, uint256 netScyToReserve) {
|
|
171
174
|
require(block.timestamp < expiry, "MARKET_EXPIRED");
|
|
172
175
|
|
|
173
|
-
|
|
176
|
+
MarketState memory market = readState(true);
|
|
174
177
|
|
|
175
178
|
(netScyIn, netScyToReserve) = market.swapScyForExactOt(
|
|
176
179
|
SCYIndexLib.newIndex(SCY),
|
|
177
180
|
exactOtOut,
|
|
178
|
-
block.timestamp
|
|
181
|
+
block.timestamp,
|
|
182
|
+
true
|
|
179
183
|
);
|
|
180
184
|
require(netScyIn <= maxScyIn, "scy in exceed limit");
|
|
181
185
|
IERC20(OT).safeTransfer(receiver, exactOtOut);
|
|
@@ -193,14 +197,10 @@ contract PendleMarket is PendleBaseToken, IPMarket, ReentrancyGuard {
|
|
|
193
197
|
/// @dev this function is just a place holder. Later on the rewards will be transferred to the liquidity minining
|
|
194
198
|
/// instead
|
|
195
199
|
function redeemScyReward() external returns (uint256[] memory outAmounts) {
|
|
196
|
-
outAmounts = ISuperComposableYield(SCY).redeemReward(
|
|
197
|
-
address(this),
|
|
198
|
-
IPMarketFactory(factory).treasury()
|
|
199
|
-
);
|
|
200
|
+
outAmounts = ISuperComposableYield(SCY).redeemReward(address(this));
|
|
200
201
|
}
|
|
201
202
|
|
|
202
|
-
|
|
203
|
-
function readState() public view returns (MarketParameters memory market) {
|
|
203
|
+
function readState(bool updateRateOracle) public view returns (MarketState memory market) {
|
|
204
204
|
MarketStorage storage store = marketStorage;
|
|
205
205
|
market.totalOt = store.totalOt;
|
|
206
206
|
market.totalScy = store.totalScy;
|
|
@@ -216,10 +216,12 @@ contract PendleMarket is PendleBaseToken, IPMarket, ReentrancyGuard {
|
|
|
216
216
|
market.lastImpliedRate = store.lastImpliedRate;
|
|
217
217
|
market.lastTradeTime = store.lastTradeTime;
|
|
218
218
|
|
|
219
|
-
|
|
219
|
+
if (updateRateOracle) {
|
|
220
|
+
market.updateNewRateOracle(block.timestamp);
|
|
221
|
+
}
|
|
220
222
|
}
|
|
221
223
|
|
|
222
|
-
function _writeState(
|
|
224
|
+
function _writeState(MarketState memory market) internal {
|
|
223
225
|
MarketStorage storage store = marketStorage;
|
|
224
226
|
|
|
225
227
|
store.totalOt = market.totalOt.Int128();
|
|
@@ -4,18 +4,19 @@ import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
|
|
|
4
4
|
import "../interfaces/IPMarket.sol";
|
|
5
5
|
import "../interfaces/IPYieldContractFactory.sol";
|
|
6
6
|
import "../interfaces/IPMarketFactory.sol";
|
|
7
|
-
import "../periphery/
|
|
7
|
+
import "../periphery/PermissionsV2Upg.sol";
|
|
8
8
|
import "./PendleMarket.sol";
|
|
9
9
|
|
|
10
|
-
contract PendleMarketFactory is
|
|
10
|
+
contract PendleMarketFactory is PermissionsV2Upg, IPMarketFactory {
|
|
11
11
|
using EnumerableSet for EnumerableSet.AddressSet;
|
|
12
12
|
|
|
13
|
-
mapping(address => EnumerableSet.AddressSet) internal
|
|
13
|
+
mapping(address => EnumerableSet.AddressSet) internal markets;
|
|
14
|
+
|
|
14
15
|
address public immutable yieldContractFactory;
|
|
15
16
|
address public treasury;
|
|
16
17
|
|
|
17
18
|
constructor(address _governanceManager, address _yieldContractFactory)
|
|
18
|
-
|
|
19
|
+
PermissionsV2Upg(_governanceManager)
|
|
19
20
|
{
|
|
20
21
|
yieldContractFactory = _yieldContractFactory;
|
|
21
22
|
}
|
|
@@ -46,7 +47,7 @@ contract PendleMarketFactory is PermissionsV2, IPMarketFactory {
|
|
|
46
47
|
reserveFeePercent
|
|
47
48
|
)
|
|
48
49
|
);
|
|
49
|
-
|
|
50
|
+
markets[OT].add(market);
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
function setTreasury(address newTreasury) external onlyGovernance {
|
|
@@ -55,6 +56,6 @@ contract PendleMarketFactory is PermissionsV2, IPMarketFactory {
|
|
|
55
56
|
|
|
56
57
|
function isValidMarket(address market) external view returns (bool) {
|
|
57
58
|
address OT = IPMarket(market).OT();
|
|
58
|
-
return
|
|
59
|
+
return markets[OT].contains(market);
|
|
59
60
|
}
|
|
60
61
|
}
|
|
@@ -80,7 +80,7 @@ contract PendleBenQiErc20SCY is SCYBaseWithRewards {
|
|
|
80
80
|
//////////////////////////////////////////////////////////////*/
|
|
81
81
|
|
|
82
82
|
function scyIndexCurrent() public virtual override returns (uint256 res) {
|
|
83
|
-
res =
|
|
83
|
+
res = Math.max(lastScyIndex, IQiToken(qiToken).exchangeRateCurrent());
|
|
84
84
|
lastScyIndex = res;
|
|
85
85
|
return res;
|
|
86
86
|
}
|
|
@@ -74,7 +74,7 @@ contract PendleBtrflyScy is SCYBase {
|
|
|
74
74
|
//////////////////////////////////////////////////////////////*/
|
|
75
75
|
|
|
76
76
|
function scyIndexCurrent() public virtual override returns (uint256 res) {
|
|
77
|
-
res =
|
|
77
|
+
res = Math.max(lastScyIndex, IWXBTRFLY(wxBTRFLY).xBTRFLYValue(Math.ONE));
|
|
78
78
|
lastScyIndex = res;
|
|
79
79
|
return res;
|
|
80
80
|
}
|
|
@@ -103,12 +103,7 @@ contract PendleBtrflyScy is SCYBase {
|
|
|
103
103
|
//////////////////////////////////////////////////////////////*/
|
|
104
104
|
|
|
105
105
|
//solhint-disable-next-line no-empty-blocks
|
|
106
|
-
function redeemReward(address user
|
|
107
|
-
public
|
|
108
|
-
virtual
|
|
109
|
-
override
|
|
110
|
-
returns (uint256[] memory)
|
|
111
|
-
{}
|
|
106
|
+
function redeemReward(address user) public virtual override returns (uint256[] memory) {}
|
|
112
107
|
|
|
113
108
|
//solhint-disable-next-line no-empty-blocks
|
|
114
109
|
function updateGlobalReward() public virtual override {}
|
|
@@ -66,7 +66,7 @@ contract PendleYearnVaultScy is SCYBase {
|
|
|
66
66
|
//////////////////////////////////////////////////////////////*/
|
|
67
67
|
|
|
68
68
|
function scyIndexCurrent() public virtual override returns (uint256 res) {
|
|
69
|
-
res =
|
|
69
|
+
res = Math.max(lastScyIndex, IYearnVault(yvToken).pricePerShare());
|
|
70
70
|
lastScyIndex = res;
|
|
71
71
|
return res;
|
|
72
72
|
}
|
|
@@ -93,12 +93,7 @@ contract PendleYearnVaultScy is SCYBase {
|
|
|
93
93
|
//////////////////////////////////////////////////////////////*/
|
|
94
94
|
|
|
95
95
|
//solhint-disable-next-line no-empty-blocks
|
|
96
|
-
function redeemReward(address user
|
|
97
|
-
public
|
|
98
|
-
virtual
|
|
99
|
-
override
|
|
100
|
-
returns (uint256[] memory)
|
|
101
|
-
{}
|
|
96
|
+
function redeemReward(address user) public virtual override returns (uint256[] memory) {}
|
|
102
97
|
|
|
103
98
|
//solhint-disable-next-line no-empty-blocks
|
|
104
99
|
function updateGlobalReward() public virtual override {}
|
|
@@ -26,13 +26,13 @@ pragma solidity 0.8.9;
|
|
|
26
26
|
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|
27
27
|
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
|
|
28
28
|
import "../libraries/helpers/ExpiryUtilsLib.sol";
|
|
29
|
-
import "
|
|
29
|
+
import "../periphery/PermissionsV2Upg.sol";
|
|
30
30
|
import "../interfaces/IPYieldContractFactory.sol";
|
|
31
31
|
|
|
32
32
|
import "./PendleOwnershipToken.sol";
|
|
33
33
|
import "./PendleYieldToken.sol";
|
|
34
34
|
|
|
35
|
-
contract PendleYieldContractFactory is
|
|
35
|
+
contract PendleYieldContractFactory is PermissionsV2Upg, IPYieldContractFactory {
|
|
36
36
|
using ExpiryUtils for string;
|
|
37
37
|
|
|
38
38
|
string public constant OT_PREFIX = "OT";
|
|
@@ -45,12 +45,15 @@ contract PendleYieldContractFactory is BoringOwnable, IPYieldContractFactory {
|
|
|
45
45
|
// SCY => expiry => address
|
|
46
46
|
mapping(address => mapping(uint256 => address)) public getOT;
|
|
47
47
|
mapping(address => mapping(uint256 => address)) public getYT;
|
|
48
|
+
mapping(address => bool) public isOT;
|
|
49
|
+
mapping(address => bool) public isYT;
|
|
48
50
|
|
|
49
51
|
constructor(
|
|
50
52
|
uint256 _expiryDivisor,
|
|
51
53
|
uint256 _interestFeeRate,
|
|
52
|
-
address _treasury
|
|
53
|
-
|
|
54
|
+
address _treasury,
|
|
55
|
+
address _governanceManager
|
|
56
|
+
) PermissionsV2Upg(_governanceManager) {
|
|
54
57
|
expiryDivisor = _expiryDivisor;
|
|
55
58
|
interestFeeRate = _interestFeeRate;
|
|
56
59
|
treasury = _treasury;
|
|
@@ -93,17 +96,19 @@ contract PendleYieldContractFactory is BoringOwnable, IPYieldContractFactory {
|
|
|
93
96
|
|
|
94
97
|
getOT[SCY][expiry] = OT;
|
|
95
98
|
getYT[SCY][expiry] = YT;
|
|
99
|
+
isOT[OT] = true;
|
|
100
|
+
isYT[YT] = true;
|
|
96
101
|
}
|
|
97
102
|
|
|
98
|
-
function setExpiryDivisor(uint256 newExpiryDivisor) external
|
|
103
|
+
function setExpiryDivisor(uint256 newExpiryDivisor) external onlyGovernance {
|
|
99
104
|
expiryDivisor = newExpiryDivisor;
|
|
100
105
|
}
|
|
101
106
|
|
|
102
|
-
function setInterestFeeRate(uint256 newInterestFeeRate) external
|
|
107
|
+
function setInterestFeeRate(uint256 newInterestFeeRate) external onlyGovernance {
|
|
103
108
|
interestFeeRate = newInterestFeeRate;
|
|
104
109
|
}
|
|
105
110
|
|
|
106
|
-
function setTreasury(address newTreasury) external
|
|
111
|
+
function setTreasury(address newTreasury) external onlyGovernance {
|
|
107
112
|
treasury = newTreasury;
|
|
108
113
|
}
|
|
109
114
|
}
|
|
@@ -5,15 +5,22 @@ import "./PendleBaseToken.sol";
|
|
|
5
5
|
import "../SuperComposableYield/ISuperComposableYield.sol";
|
|
6
6
|
import "../interfaces/IPYieldToken.sol";
|
|
7
7
|
import "../interfaces/IPOwnershipToken.sol";
|
|
8
|
-
import "../libraries/math/
|
|
8
|
+
import "../libraries/math/Math.sol";
|
|
9
9
|
import "../interfaces/IPYieldContractFactory.sol";
|
|
10
|
-
import "@openzeppelin/contracts/utils/math/Math.sol";
|
|
11
10
|
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
12
11
|
import "../SuperComposableYield/SCYUtils.sol";
|
|
13
12
|
import "../SuperComposableYield/implementations/RewardManager.sol";
|
|
14
13
|
|
|
14
|
+
/*
|
|
15
|
+
With YT yielding more SCYs overtime, which is allowed to be redeemed by users, the reward distribution should
|
|
16
|
+
be based on the amount of SCYs that their YT currently represent, plus with their dueInterest.
|
|
17
|
+
|
|
18
|
+
It has been proven and tested that impliedScyBalance will not change over time, unless users redeem their interest or redeemYO.
|
|
19
|
+
|
|
20
|
+
Due to this, it is required to update users' accruedReward STRICTLY BEFORE redeeming their interest.
|
|
21
|
+
*/
|
|
15
22
|
contract PendleYieldToken is PendleBaseToken, IPYieldToken, RewardManager {
|
|
16
|
-
using
|
|
23
|
+
using Math for uint256;
|
|
17
24
|
using SafeERC20 for IERC20;
|
|
18
25
|
|
|
19
26
|
struct UserData {
|
|
@@ -77,39 +84,58 @@ contract PendleYieldToken is PendleBaseToken, IPYieldToken, RewardManager {
|
|
|
77
84
|
_afterTransferOutSCY();
|
|
78
85
|
}
|
|
79
86
|
|
|
87
|
+
function redeemDueInterestAndRewards(address user)
|
|
88
|
+
public
|
|
89
|
+
returns (uint256 interestOut, uint256[] memory rewardsOut)
|
|
90
|
+
{
|
|
91
|
+
// redeemDueRewards before redeemDueInterest
|
|
92
|
+
updateUserReward(user);
|
|
93
|
+
updateUserInterest(user);
|
|
94
|
+
rewardsOut = _doTransferOutRewardsForUser(user, user);
|
|
95
|
+
interestOut = _doTransferOutDueInterest(user);
|
|
96
|
+
}
|
|
97
|
+
|
|
80
98
|
function redeemDueInterest(address user) public returns (uint256 interestOut) {
|
|
81
|
-
|
|
99
|
+
updateUserReward(user); /// strictly required, see above for explanation
|
|
100
|
+
updateUserInterest(user);
|
|
101
|
+
interestOut = _doTransferOutDueInterest(user);
|
|
102
|
+
}
|
|
82
103
|
|
|
83
|
-
|
|
84
|
-
|
|
104
|
+
function redeemDueRewards(address user) public returns (uint256[] memory rewardsOut) {
|
|
105
|
+
updateUserReward(user);
|
|
106
|
+
rewardsOut = _doTransferOutRewardsForUser(user, user);
|
|
107
|
+
}
|
|
85
108
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
109
|
+
function updateGlobalReward() external {
|
|
110
|
+
address[] memory rewardTokens = getRewardTokens();
|
|
111
|
+
_updateGlobalReward(rewardTokens, IERC20(SCY).balanceOf(address(this)));
|
|
112
|
+
}
|
|
89
113
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
114
|
+
function updateUserReward(address user) public {
|
|
115
|
+
uint256 impliedScyBalance = getImpliedScyBalance(user);
|
|
116
|
+
uint256 totalScy = IERC20(SCY).balanceOf(address(this));
|
|
117
|
+
_updateUserReward(user, impliedScyBalance, totalScy);
|
|
93
118
|
}
|
|
94
119
|
|
|
95
|
-
function
|
|
96
|
-
|
|
97
|
-
returns (uint256[] memory rewardsOut)
|
|
98
|
-
{
|
|
99
|
-
if (user != msg.sender) require(receiver == user, "invalid receiver");
|
|
100
|
-
else require(receiver != address(0), "zero address");
|
|
120
|
+
function updateUserInterest(address user) public {
|
|
121
|
+
uint256 prevIndex = data[user].lastScyIndex;
|
|
101
122
|
|
|
102
|
-
|
|
103
|
-
rewardsOut = _doTransferOutRewardsForUser(user, receiver);
|
|
104
|
-
}
|
|
123
|
+
uint256 currentIndex = getScyIndexBeforeExpiry();
|
|
105
124
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
125
|
+
if (prevIndex == currentIndex) return;
|
|
126
|
+
if (prevIndex == 0) {
|
|
127
|
+
data[user].lastScyIndex = currentIndex;
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
uint256 principal = balanceOf(user);
|
|
132
|
+
|
|
133
|
+
uint256 interestFromYT = (principal * (currentIndex - prevIndex)).divDown(
|
|
134
|
+
prevIndex * currentIndex
|
|
135
|
+
);
|
|
110
136
|
|
|
111
|
-
|
|
112
|
-
|
|
137
|
+
data[user].dueInterest += interestFromYT;
|
|
138
|
+
data[user].lastScyIndex = currentIndex;
|
|
113
139
|
}
|
|
114
140
|
|
|
115
141
|
function _updateGlobalReward(address[] memory rewardTokens, uint256 totalSupply)
|
|
@@ -138,8 +164,21 @@ contract PendleYieldToken is PendleBaseToken, IPYieldToken, RewardManager {
|
|
|
138
164
|
}
|
|
139
165
|
}
|
|
140
166
|
|
|
167
|
+
function _doTransferOutDueInterest(address user) internal returns (uint256 interestOut) {
|
|
168
|
+
uint256 interestPreFee = data[user].dueInterest;
|
|
169
|
+
data[user].dueInterest = 0;
|
|
170
|
+
|
|
171
|
+
uint256 feeRate = IPYieldContractFactory(factory).interestFeeRate();
|
|
172
|
+
uint256 feeAmount = interestPreFee.mulDown(feeRate);
|
|
173
|
+
totalProtocolFee += feeAmount;
|
|
174
|
+
|
|
175
|
+
interestOut = interestPreFee - feeAmount;
|
|
176
|
+
IERC20(SCY).safeTransfer(user, interestOut);
|
|
177
|
+
_afterTransferOutSCY();
|
|
178
|
+
}
|
|
179
|
+
|
|
141
180
|
function _redeemExternalReward() internal virtual override {
|
|
142
|
-
ISuperComposableYield(SCY).redeemReward(address(this)
|
|
181
|
+
ISuperComposableYield(SCY).redeemReward(address(this));
|
|
143
182
|
}
|
|
144
183
|
|
|
145
184
|
function getRewardTokens()
|
|
@@ -158,8 +197,19 @@ contract PendleYieldToken is PendleBaseToken, IPYieldToken, RewardManager {
|
|
|
158
197
|
lastScyIndexBeforeExpiry = res;
|
|
159
198
|
}
|
|
160
199
|
|
|
200
|
+
/**
|
|
201
|
+
* In case the pool is expired and there is left some SCY not yet redeemed from the contract, the rewards should
|
|
202
|
+
* be claimed before withdrawing to treasury.
|
|
203
|
+
*
|
|
204
|
+
* And since the reward distribution (which based on users' dueInterest) stopped at the scyIndexBeforeExpiry, it is not
|
|
205
|
+
* necessary to updateDueInterest along with reward here.
|
|
206
|
+
*/
|
|
161
207
|
function withdrawFeeToTreasury() public {
|
|
162
|
-
address[] memory rewardTokens =
|
|
208
|
+
address[] memory rewardTokens = getRewardTokens();
|
|
209
|
+
if (isExpired()) {
|
|
210
|
+
_updateGlobalReward(rewardTokens, IERC20(SCY).balanceOf(address(this))); // as refered to the doc above
|
|
211
|
+
}
|
|
212
|
+
|
|
163
213
|
uint256 length = rewardTokens.length;
|
|
164
214
|
address treasury = IPYieldContractFactory(factory).treasury();
|
|
165
215
|
|
|
@@ -177,27 +227,6 @@ contract PendleYieldToken is PendleBaseToken, IPYieldToken, RewardManager {
|
|
|
177
227
|
}
|
|
178
228
|
}
|
|
179
229
|
|
|
180
|
-
function _updateDueInterest(address user) internal {
|
|
181
|
-
uint256 prevIndex = data[user].lastScyIndex;
|
|
182
|
-
|
|
183
|
-
uint256 currentIndex = getScyIndexBeforeExpiry();
|
|
184
|
-
|
|
185
|
-
if (prevIndex == currentIndex) return;
|
|
186
|
-
if (prevIndex == 0) {
|
|
187
|
-
data[user].lastScyIndex = currentIndex;
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
uint256 principal = balanceOf(user);
|
|
192
|
-
|
|
193
|
-
uint256 interestFromYT = (principal * (currentIndex - prevIndex)).divDown(
|
|
194
|
-
prevIndex * currentIndex
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
data[user].dueInterest += interestFromYT;
|
|
198
|
-
data[user].lastScyIndex = currentIndex;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
230
|
function _calcAmountToMint(uint256 amount) internal returns (uint256) {
|
|
202
231
|
return SCYUtils.scyToAsset(getScyIndexBeforeExpiry(), amount);
|
|
203
232
|
}
|
|
@@ -217,20 +246,29 @@ contract PendleYieldToken is PendleBaseToken, IPYieldToken, RewardManager {
|
|
|
217
246
|
lastScyBalance = IERC20(SCY).balanceOf(address(this));
|
|
218
247
|
}
|
|
219
248
|
|
|
249
|
+
function getImpliedScyBalance(address user) public view returns (uint256) {
|
|
250
|
+
uint256 scyIndex = data[user].lastScyIndex;
|
|
251
|
+
if (scyIndex == 0) return 0;
|
|
252
|
+
return SCYUtils.assetToScy(scyIndex, balanceOf(user)) + data[user].dueInterest;
|
|
253
|
+
}
|
|
254
|
+
|
|
220
255
|
function _beforeTokenTransfer(
|
|
221
256
|
address from,
|
|
222
257
|
address to,
|
|
223
258
|
uint256
|
|
224
259
|
) internal override {
|
|
225
260
|
address[] memory rewardTokens = getRewardTokens();
|
|
226
|
-
_updateGlobalReward(rewardTokens,
|
|
261
|
+
_updateGlobalReward(rewardTokens, IERC20(SCY).balanceOf(address(this)));
|
|
262
|
+
|
|
263
|
+
// Before the change in YT balance, users' impliedScyBalance is kept unchanged from last time
|
|
264
|
+
// Therefore, both updating due interest before or after due reward work the same.
|
|
227
265
|
if (from != address(0) && from != address(this)) {
|
|
228
|
-
|
|
229
|
-
_updateUserRewardSkipGlobal(rewardTokens, from,
|
|
266
|
+
updateUserInterest(from);
|
|
267
|
+
_updateUserRewardSkipGlobal(rewardTokens, from, getImpliedScyBalance(from));
|
|
230
268
|
}
|
|
231
269
|
if (to != address(0) && to != address(this)) {
|
|
232
|
-
|
|
233
|
-
_updateUserRewardSkipGlobal(rewardTokens, to,
|
|
270
|
+
updateUserInterest(to);
|
|
271
|
+
_updateUserRewardSkipGlobal(rewardTokens, to, getImpliedScyBalance(to));
|
|
234
272
|
}
|
|
235
273
|
}
|
|
236
274
|
}
|