/
Silo.sol
165 lines (140 loc) · 5.97 KB
/
Silo.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
/**
* SPDX-License-Identifier: MIT
**/
pragma solidity =0.7.6;
pragma abicoder v2;
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "./SiloExit.sol";
/**
* @title Silo
* @author Publius, Pizzaman1337, Brean
* @notice Provides utility functions for claiming Silo rewards, including:
*
* - Grown Stalk (see "Mow")
* - Earned Beans, Earned Stalk (see "Plant")
* - 3CRV earned during a Flood (see "Flood")
*
* For backwards compatibility, a Flood is sometimes referred to by its old name
* "Season of Plenty".
*/
contract Silo is SiloExit {
using SafeMath for uint256;
using SafeERC20 for IERC20;
using LibSafeMath128 for uint128;
//////////////////////// EVENTS ////////////////////////
/**
* @notice Emitted when the deposit associated with the Earned Beans of
* `account` are Planted.
* @param account Owns the Earned Beans
* @param beans The amount of Earned Beans claimed by `account`.
*/
event Plant(
address indexed account,
uint256 beans
);
/**
* @notice Emitted when 3CRV paid to `account` during a Flood is Claimed.
* @param account Owns and receives the assets paid during a Flood.
* @param plenty The amount of 3CRV claimed by `account`. This is the amount
* that `account` has been paid since their last {ClaimPlenty}.
*
* @dev Flood was previously called a "Season of Plenty". For backwards
* compatibility, the event has not been changed. For more information on
* Flood, see: {Weather.sop}.
*/
event ClaimPlenty(
address indexed account,
uint256 plenty
);
/**
* @notice Emitted when `account` gains or loses Stalk.
* @param account The account that gained or lost Stalk.
* @param delta The change in Stalk.
* @param deltaRoots The change in Roots.
*
* @dev {StalkBalanceChanged} should be emitted anytime a Deposit is added, removed or transferred AND
* anytime an account Mows Grown Stalk.
* @dev BIP-24 included a one-time re-emission of {SeedsBalanceChanged} for accounts that had
* executed a Deposit transfer between the Replant and BIP-24 execution. For more, see:
* [BIP-24](https://github.com/BeanstalkFarms/Beanstalk-Governance-Proposals/blob/master/bip/bip-24-fungible-bdv-support.md)
* [Event-24-Event-Emission](https://github.com/BeanstalkFarms/Event-24-Event-Emission)
*/
event StalkBalanceChanged(
address indexed account,
int256 delta,
int256 deltaRoots
);
//////////////////////// INTERNAL: MOW ////////////////////////
/**
* @dev Claims the Grown Stalk for `msg.sender`. Requires token address to mow.
*/
modifier mowSender(address token) {
LibSilo._mow(msg.sender, token);
_;
}
//////////////////////// INTERNAL: VESTING ////////////////////////
/**
* @notice Verifies that the function is not called in the vesting period.
* @dev Added with ebip-13. Will be removed upon seed gauge BIP.
* This modifier is added to the following functions:
* {SiloFacet.withdrawDeposit(s)}
*/
modifier checkVesting() {
require(!LibSilo.inVestingPeriod(), "Silo: In vesting period");
_;
}
//////////////////////// INTERNAL: PLANT ////////////////////////
/**
* @dev Plants the Plantable BDV of `account` associated with its Earned
* Beans.
*
* For more info on Planting, see: {SiloFacet-plant}
*/
function _plant(address account) internal returns (uint256 beans, int96 stemTip) {
// Need to Mow for `account` before we calculate the balance of
// Earned Beans.
LibSilo._mow(account, C.BEAN);
uint256 accountStalk = s.a[account].s.stalk;
// Calculate balance of Earned Beans.
beans = _balanceOfEarnedBeans(account, accountStalk);
stemTip = LibTokenSilo.stemTipForToken(C.BEAN);
s.a[account].deltaRoots = 0; // must be 0'd, as calling balanceOfEarnedBeans would give a invalid amount of beans.
if (beans == 0) return (0,stemTip);
// Reduce the Silo's supply of Earned Beans.
// SafeCast unnecessary because beans is <= s.earnedBeans.
s.earnedBeans = s.earnedBeans.sub(uint128(beans));
// Deposit Earned Beans if there are any. Note that 1 Bean = 1 BDV.
LibTokenSilo.addDepositToAccount(
account,
C.BEAN,
stemTip,
beans, // amount
beans, // bdv
LibTokenSilo.Transfer.emitTransferSingle
);
s.a[account].deltaRoots = 0; // must be 0'd, as calling balanceOfEarnedBeans would give a invalid amount of beans.
// Earned Stalk associated with Earned Beans generate more Earned Beans automatically (i.e., auto compounding).
// Earned Stalk are minted when Earned Beans are minted during Sunrise. See {Sun.sol:rewardToSilo} for details.
// Similarly, `account` does not receive additional Roots from Earned Stalk during a Plant.
// The following lines allocate Earned Stalk that has already been minted to `account`.
// Constant is used here rather than s.ss[BEAN].stalkIssuedPerBdv
// for gas savings.
uint256 stalk = beans.mul(C.STALK_PER_BEAN);
s.a[account].s.stalk = accountStalk.add(stalk);
emit StalkBalanceChanged(account, int256(stalk), 0);
emit Plant(account, beans);
}
//////////////////////// INTERNAL: SEASON OF PLENTY ////////////////////////
/**
* @dev Gas optimization: An account can call `{SiloFacet:claimPlenty}` even
* if `s.a[account].sop.plenty == 0`. This would emit a ClaimPlenty event
* with an amount of 0.
*/
function _claimPlenty(address account) internal {
// Plenty is earned in the form of 3Crv.
uint256 plenty = s.a[account].sop.plenty;
C.threeCrv().safeTransfer(account, plenty);
delete s.a[account].sop.plenty;
emit ClaimPlenty(account, plenty);
}
}