This repository has been archived by the owner on May 13, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
LibUnripe.sol
240 lines (216 loc) · 9.74 KB
/
LibUnripe.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
// SPDX-License-Identifier: MIT
pragma solidity =0.7.6;
pragma experimental ABIEncoderV2;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
import {IBean} from "../interfaces/IBean.sol";
import {AppStorage, LibAppStorage} from "./LibAppStorage.sol";
import {C} from "../C.sol";
import {LibWell} from "./Well/LibWell.sol";
import {Call, IWell} from "contracts/interfaces/basin/IWell.sol";
import {IWellFunction} from "contracts/interfaces/basin/IWellFunction.sol";
import {LibLockedUnderlying} from "./LibLockedUnderlying.sol";
/**
* @title LibUnripe
* @author Publius
* @notice Library for handling functionality related to Unripe Tokens and their Ripe Tokens.
*/
library LibUnripe {
using SafeMath for uint256;
event ChangeUnderlying(address indexed token, int256 underlying);
event SwitchUnderlyingToken(address indexed token, address indexed underlyingToken);
uint256 constant DECIMALS = 1e6;
/**
* @notice Returns the percentage that Unripe Beans have been recapitalized.
*/
function percentBeansRecapped() internal view returns (uint256 percent) {
AppStorage storage s = LibAppStorage.diamondStorage();
return
s.u[C.UNRIPE_BEAN].balanceOfUnderlying.mul(DECIMALS).div(C.unripeBean().totalSupply());
}
/**
* @notice Returns the percentage that Unripe LP have been recapitalized.
*/
function percentLPRecapped() internal view returns (uint256 percent) {
AppStorage storage s = LibAppStorage.diamondStorage();
return C.unripeLPPerDollar().mul(s.recapitalized).div(C.unripeLP().totalSupply());
}
/**
* @notice Increments the underlying balance of an Unripe Token.
* @param token The address of the unripe token.
* @param amount The amount of the of the unripe token to be added to the storage reserves
*/
function incrementUnderlying(address token, uint256 amount) internal {
AppStorage storage s = LibAppStorage.diamondStorage();
s.u[token].balanceOfUnderlying = s.u[token].balanceOfUnderlying.add(amount);
emit ChangeUnderlying(token, int256(amount));
}
/**
* @notice Decrements the underlying balance of an Unripe Token.
* @param token The address of the Unripe Token.
* @param amount The amount of the of the Unripe Token to be removed from storage reserves
*/
function decrementUnderlying(address token, uint256 amount) internal {
AppStorage storage s = LibAppStorage.diamondStorage();
s.u[token].balanceOfUnderlying = s.u[token].balanceOfUnderlying.sub(amount);
emit ChangeUnderlying(token, -int256(amount));
}
/**
* @notice Calculates the amount of Ripe Tokens that underly a given amount of Unripe Tokens.
* @param unripeToken The address of the Unripe Token
* @param unripe The amount of Unripe Tokens.
* @return underlying The amount of Ripe Tokens that underly the Unripe Tokens.
*/
function unripeToUnderlying(
address unripeToken,
uint256 unripe,
uint256 supply
) internal view returns (uint256 underlying) {
AppStorage storage s = LibAppStorage.diamondStorage();
underlying = s.u[unripeToken].balanceOfUnderlying.mul(unripe).div(supply);
}
/**
* @notice Calculates the amount of Unripe Tokens that are underlaid by a given amount of Ripe Tokens.
* @param unripeToken The address of the Unripe Tokens.
* @param underlying The amount of Ripe Tokens.
* @return unripe The amount of the of the Unripe Tokens that are underlaid by the Ripe Tokens.
*/
function underlyingToUnripe(
address unripeToken,
uint256 underlying
) internal view returns (uint256 unripe) {
AppStorage storage s = LibAppStorage.diamondStorage();
unripe = IBean(unripeToken).totalSupply().mul(underlying).div(
s.u[unripeToken].balanceOfUnderlying
);
}
/**
* @notice Adds Ripe Tokens to an Unripe Token. Also, increments the recapitalized
* amount proportionally if the Unripe Token is Unripe LP.
* @param token The address of the Unripe Token to add Ripe Tokens to.
* @param underlying The amount of the of the underlying token to be taken as input.
*/
function addUnderlying(address token, uint256 underlying) internal {
AppStorage storage s = LibAppStorage.diamondStorage();
if (token == C.UNRIPE_LP) {
uint256 recapped = underlying.mul(s.recapitalized).div(
s.u[C.UNRIPE_LP].balanceOfUnderlying
);
s.recapitalized = s.recapitalized.add(recapped);
}
incrementUnderlying(token, underlying);
}
/**
* @notice Removes Ripe Tokens from an Unripe Token. Also, decrements the recapitalized
* amount proportionally if the Unripe Token is Unripe LP.
* @param token The address of the unripe token to be removed.
* @param underlying The amount of the of the underlying token to be removed.
*/
function removeUnderlying(address token, uint256 underlying) internal {
AppStorage storage s = LibAppStorage.diamondStorage();
if (token == C.UNRIPE_LP) {
uint256 recapped = underlying.mul(s.recapitalized).div(
s.u[C.UNRIPE_LP].balanceOfUnderlying
);
s.recapitalized = s.recapitalized.sub(recapped);
}
decrementUnderlying(token, underlying);
}
/**
* @dev Switches the underlying token of an unripe token.
* Should only be called if `s.u[unripeToken].balanceOfUnderlying == 0`.
*/
function switchUnderlyingToken(address unripeToken, address newUnderlyingToken) internal {
AppStorage storage s = LibAppStorage.diamondStorage();
s.u[unripeToken].underlyingToken = newUnderlyingToken;
emit SwitchUnderlyingToken(unripeToken, newUnderlyingToken);
}
function _getPenalizedUnderlying(
address unripeToken,
uint256 amount,
uint256 supply
) internal view returns (uint256 redeem) {
require(isUnripe(unripeToken), "not vesting");
uint256 sharesBeingRedeemed = getRecapPaidPercentAmount(amount);
redeem = _getUnderlying(unripeToken, sharesBeingRedeemed, supply);
}
/**
* @notice Calculates the the amount of Ripe Tokens that would be paid out if
* all Unripe Tokens were Chopped at the current Chop Rate.
*/
function _getTotalPenalizedUnderlying(
address unripeToken
) internal view returns (uint256 redeem) {
require(isUnripe(unripeToken), "not vesting");
uint256 supply = IERC20(unripeToken).totalSupply();
redeem = _getUnderlying(unripeToken, getRecapPaidPercentAmount(supply), supply);
}
/**
* @notice Returns the amount of beans that are locked in the unripe token.
* @dev Locked beans are the beans that are forfeited if the unripe token is chopped.
* @param reserves the reserves of the LP that underly the unripe token.
* @dev reserves are used as a parameter for gas effiency purposes (see LibEvaluate.calcLPToSupplyRatio}.
*/
function getLockedBeans(
uint256[] memory reserves
) internal view returns (uint256 lockedAmount) {
lockedAmount = LibLockedUnderlying
.getLockedUnderlying(C.UNRIPE_BEAN, getRecapPaidPercentAmount(1e6))
.add(getLockedBeansFromLP(reserves));
}
/**
* @notice Returns the amount of beans that are locked in the unripeLP token.
* @param reserves the reserves of the LP that underly the unripe token.
*/
function getLockedBeansFromLP(
uint256[] memory reserves
) internal view returns (uint256 lockedBeanAmount) {
AppStorage storage s = LibAppStorage.diamondStorage();
// if reserves return 0, then skip calculations.
if (reserves[0] == 0) return 0;
uint256 lockedLpAmount = LibLockedUnderlying.getLockedUnderlying(
C.UNRIPE_LP,
getRecapPaidPercentAmount(1e6)
);
address underlying = s.u[C.UNRIPE_LP].underlyingToken;
uint256 beanIndex = LibWell.getBeanIndexFromWell(underlying);
// lpTokenSupply is calculated rather than calling totalSupply(),
// because the Well's lpTokenSupply is not MEV resistant.
Call memory wellFunction = IWell(underlying).wellFunction();
uint lpTokenSupply = IWellFunction(wellFunction.target).calcLpTokenSupply(
reserves,
wellFunction.data
);
lockedBeanAmount = lockedLpAmount.mul(reserves[beanIndex]).div(lpTokenSupply);
}
/**
* @notice Calculates the penalized amount based the amount of Sprouts that are Rinsable
* or Rinsed (Fertilized).
* @param amount The amount of the Unripe Tokens.
* @return penalizedAmount The penalized amount of the Ripe Tokens received from Chopping.
*/
function getRecapPaidPercentAmount(
uint256 amount
) internal view returns (uint256 penalizedAmount) {
AppStorage storage s = LibAppStorage.diamondStorage();
return s.fertilizedIndex.mul(amount).div(s.unfertilizedIndex);
}
/**
* @notice Returns true if the token is unripe.
*/
function isUnripe(address unripeToken) internal view returns (bool unripe) {
AppStorage storage s = LibAppStorage.diamondStorage();
unripe = s.u[unripeToken].underlyingToken != address(0);
}
/**
* @notice Returns the underlying token amount of the unripe token.
*/
function _getUnderlying(
address unripeToken,
uint256 amount,
uint256 supply
) internal view returns (uint256 redeem) {
AppStorage storage s = LibAppStorage.diamondStorage();
redeem = s.u[unripeToken].balanceOfUnderlying.mul(amount).div(supply);
}
}