-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSupplySchedule.sol
234 lines (186 loc) · 7.79 KB
/
SupplySchedule.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
// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;
import "ds-test/test.sol";
import {IERC20Upgradeable} from "openzeppelin-contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import {ERC20Upgradeable} from "openzeppelin-contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {MathUpgradeable} from "openzeppelin-contracts-upgradeable/utils/math/MathUpgradeable.sol";
import {AddressUpgradeable} from "openzeppelin-contracts-upgradeable/utils/AddressUpgradeable.sol";
import {PausableUpgradeable} from "openzeppelin-contracts-upgradeable/security/PausableUpgradeable.sol";
import {SafeERC20Upgradeable} from "openzeppelin-contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import {ReentrancyGuardUpgradeable} from "openzeppelin-contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import {IVault} from "./interfaces/badger/IVault.sol";
import "./lib/GlobalAccessControlManaged.sol";
/**
Supply schedules are defined in terms of Epochs
*/
contract SupplySchedule is GlobalAccessControlManaged, DSTest {
bytes32 public constant CONTRACT_GOVERNANCE_ROLE =
keccak256("CONTRACT_GOVERNANCE_ROLE");
uint256 public constant epochLength = 21 days;
uint256 public globalStartTimestamp;
/// epoch index * epoch length = start time
mapping(uint256 => uint256) public epochRate;
/// ==================
/// ===== Events =====
/// ==================
event MintingStartTimeSet(uint256 globalStartTimestamp);
event EpochSupplyRateSet(uint256 epoch, uint256 rate);
/// =======================
/// ===== Initializer =====
/// =======================
function initialize(address _gac) public initializer {
__GlobalAccessControlManaged_init(_gac);
_setEpochRates();
}
/// =======================
/// ===== Public view =====
/// =======================
// @dev duplicate of getMintable() with debug print added
// @dev this function is out of scope for reviews and audits
function getEpochAtTimestamp(uint256 _timestamp)
public
view
returns (uint256)
{
require(
globalStartTimestamp > 0,
"SupplySchedule: minting not started"
);
return (_timestamp - globalStartTimestamp) / epochLength;
}
function getCurrentEpoch() public view returns (uint256) {
return getEpochAtTimestamp(block.timestamp);
}
function getEmissionsForEpoch(uint256 _epoch)
public
view
returns (uint256)
{
return epochRate[_epoch] * epochLength;
}
function getEmissionsForCurrentEpoch() public view returns (uint256) {
uint256 epoch = getCurrentEpoch();
return getEmissionsForEpoch(epoch);
}
function getMintable(uint256 lastMintTimestamp)
external
view
returns (uint256)
{
uint256 cachedGlobalStartTimestamp = globalStartTimestamp;
require(
cachedGlobalStartTimestamp > 0,
"SupplySchedule: minting not started"
);
require(
block.timestamp > lastMintTimestamp,
"SupplySchedule: already minted up to current block"
);
if (lastMintTimestamp < cachedGlobalStartTimestamp) {
lastMintTimestamp = cachedGlobalStartTimestamp;
}
uint256 mintable = 0;
uint256 startingEpoch = (lastMintTimestamp - cachedGlobalStartTimestamp) /
epochLength;
uint256 endingEpoch = (block.timestamp - cachedGlobalStartTimestamp) /
epochLength;
for (uint256 i = startingEpoch; i <= endingEpoch; /** See below ++i */) {
uint256 rate = epochRate[i];
uint256 epochStartTime = cachedGlobalStartTimestamp + i * epochLength;
uint256 epochEndTime = cachedGlobalStartTimestamp + (i + 1) * epochLength;
uint256 time = MathUpgradeable.min(block.timestamp, epochEndTime) -
MathUpgradeable.max(lastMintTimestamp, epochStartTime);
mintable += rate * time;
unchecked { ++i; }
}
return mintable;
}
/// ==============================
/// ===== Governance actions =====
/// ==============================
function setMintingStart(uint256 _globalStartTimestamp)
external
onlyRole(CONTRACT_GOVERNANCE_ROLE)
gacPausable
{
require(
globalStartTimestamp == 0,
"SupplySchedule: minting already started"
);
require(
_globalStartTimestamp >= block.timestamp,
"SupplySchedule: minting must start at or after current time"
);
globalStartTimestamp = _globalStartTimestamp;
emit MintingStartTimeSet(_globalStartTimestamp);
}
function setEpochRate(uint256 _epoch, uint256 _rate)
external
onlyRole(CONTRACT_GOVERNANCE_ROLE)
gacPausable
{
require(
epochRate[_epoch] == 0,
"SupplySchedule: rate already set for given epoch"
);
// TODO: Require this epoch is in the future. What happens if no data is set? (It just fails to mint until set)
epochRate[_epoch] = _rate;
emit EpochSupplyRateSet(_epoch, _rate);
}
/// ========================
/// ===== Test actions =====
/// ========================
// @dev Set rates for the initial epochs
function _setEpochRates() internal {
epochRate[0] = 593962000000000000000000 / epochLength;
epochRate[1] = 591445000000000000000000 / epochLength;
epochRate[2] = 585021000000000000000000 / epochLength;
epochRate[3] = 574138000000000000000000 / epochLength;
epochRate[4] = 558275000000000000000000 / epochLength;
epochRate[5] = 536986000000000000000000 / epochLength;
}
function getMintableDebug(uint256 lastMintTimestamp) external {
require(
globalStartTimestamp > 0,
"SupplySchedule: minting not started"
);
require(
lastMintTimestamp > globalStartTimestamp,
"SupplySchedule: attempting to mint before start block"
);
require(
block.timestamp > lastMintTimestamp,
"SupplySchedule: already minted up to current block"
);
uint256 mintable = 0;
emit log_named_uint("mintable", mintable);
emit log_named_uint("block.timestamp", block.timestamp);
emit log_named_uint("lastMintTimestamp", lastMintTimestamp);
emit log_named_uint("globalStartTimestamp", globalStartTimestamp);
emit log_named_uint("epochLength", epochLength);
uint256 startingEpoch = (lastMintTimestamp - globalStartTimestamp) /
epochLength;
emit log_named_uint("startingEpoch", startingEpoch);
uint256 endingEpoch = (block.timestamp - globalStartTimestamp) /
epochLength;
emit log_named_uint("endingEpoch", endingEpoch);
for (uint256 i = startingEpoch; i <= endingEpoch; i++) {
uint256 rate = epochRate[i];
uint256 epochStartTime = globalStartTimestamp + i * epochLength;
uint256 epochEndTime = globalStartTimestamp + (i + 1) * epochLength;
emit log_named_uint("epoch iteration", i);
emit log_named_uint("epochStartTime", epochStartTime);
emit log_named_uint("epochEndTime", epochEndTime);
uint256 time = MathUpgradeable.min(block.timestamp, epochEndTime) -
MathUpgradeable.max(lastMintTimestamp, epochStartTime);
emit log_named_uint("time to mint over", time);
mintable += rate * time;
emit log_named_uint("mintable from this iteration", rate * time);
emit log_named_uint(
"total mintable after this iteration",
mintable
);
}
// return mintable;
}
}