-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathTreasury.sol
586 lines (480 loc) · 22.5 KB
/
Treasury.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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
import "@openzeppelin/contracts/math/Math.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./lib/Babylonian.sol";
import "./owner/Operator.sol";
import "./utils/ContractGuard.sol";
import "./interfaces/IBasisAsset.sol";
import "./interfaces/IOracle.sol";
import "./interfaces/IMasonry.sol";
/*
______ __ _______
/_ __/___ ____ ___ / /_ / ____(_)___ ____ _____ ________
/ / / __ \/ __ `__ \/ __ \ / /_ / / __ \/ __ `/ __ \/ ___/ _ \
/ / / /_/ / / / / / / /_/ / / __/ / / / / / /_/ / / / / /__/ __/
/_/ \____/_/ /_/ /_/_.___/ /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/
http://tomb.finance
*/
contract Treasury is ContractGuard {
using SafeERC20 for IERC20;
using Address for address;
using SafeMath for uint256;
/* ========= CONSTANT VARIABLES ======== */
uint256 public constant PERIOD = 6 hours;
/* ========== STATE VARIABLES ========== */
// governance
address public operator;
// flags
bool public initialized = false;
// epoch
uint256 public startTime;
uint256 public epoch = 0;
uint256 public epochSupplyContractionLeft = 0;
// exclusions from total supply
address[] public excludedFromTotalSupply = [
address(0x9A896d3c54D7e45B558BD5fFf26bF1E8C031F93b), // TombGenesisPool
address(0xa7b9123f4b15fE0fF01F469ff5Eab2b41296dC0E), // new TombRewardPool
address(0xA7B16703470055881e7EE093e9b0bF537f29CD4d) // old TombRewardPool
];
// core components
address public tomb;
address public tbond;
address public tshare;
address public masonry;
address public tombOracle;
// price
uint256 public tombPriceOne;
uint256 public tombPriceCeiling;
uint256 public seigniorageSaved;
uint256[] public supplyTiers;
uint256[] public maxExpansionTiers;
uint256 public maxSupplyExpansionPercent;
uint256 public bondDepletionFloorPercent;
uint256 public seigniorageExpansionFloorPercent;
uint256 public maxSupplyContractionPercent;
uint256 public maxDebtRatioPercent;
// 28 first epochs (1 week) with 4.5% expansion regardless of TOMB price
uint256 public bootstrapEpochs;
uint256 public bootstrapSupplyExpansionPercent;
/* =================== Added variables =================== */
uint256 public previousEpochTombPrice;
uint256 public maxDiscountRate; // when purchasing bond
uint256 public maxPremiumRate; // when redeeming bond
uint256 public discountPercent;
uint256 public premiumThreshold;
uint256 public premiumPercent;
uint256 public mintingFactorForPayingDebt; // print extra TOMB during debt phase
address public daoFund;
uint256 public daoFundSharedPercent;
address public devFund;
uint256 public devFundSharedPercent;
/* =================== Events =================== */
event Initialized(address indexed executor, uint256 at);
event BurnedBonds(address indexed from, uint256 bondAmount);
event RedeemedBonds(address indexed from, uint256 tombAmount, uint256 bondAmount);
event BoughtBonds(address indexed from, uint256 tombAmount, uint256 bondAmount);
event TreasuryFunded(uint256 timestamp, uint256 seigniorage);
event MasonryFunded(uint256 timestamp, uint256 seigniorage);
event DaoFundFunded(uint256 timestamp, uint256 seigniorage);
event DevFundFunded(uint256 timestamp, uint256 seigniorage);
/* =================== Modifier =================== */
modifier onlyOperator() {
require(operator == msg.sender, "Treasury: caller is not the operator");
_;
}
modifier checkCondition {
require(now >= startTime, "Treasury: not started yet");
_;
}
modifier checkEpoch {
require(now >= nextEpochPoint(), "Treasury: not opened yet");
_;
epoch = epoch.add(1);
epochSupplyContractionLeft = (getTombPrice() > tombPriceCeiling) ? 0 : getTombCirculatingSupply().mul(maxSupplyContractionPercent).div(10000);
}
modifier checkOperator {
require(
IBasisAsset(tomb).operator() == address(this) &&
IBasisAsset(tbond).operator() == address(this) &&
IBasisAsset(tshare).operator() == address(this) &&
Operator(masonry).operator() == address(this),
"Treasury: need more permission"
);
_;
}
modifier notInitialized {
require(!initialized, "Treasury: already initialized");
_;
}
/* ========== VIEW FUNCTIONS ========== */
function isInitialized() public view returns (bool) {
return initialized;
}
// epoch
function nextEpochPoint() public view returns (uint256) {
return startTime.add(epoch.mul(PERIOD));
}
// oracle
function getTombPrice() public view returns (uint256 tombPrice) {
try IOracle(tombOracle).consult(tomb, 1e18) returns (uint144 price) {
return uint256(price);
} catch {
revert("Treasury: failed to consult TOMB price from the oracle");
}
}
function getTombUpdatedPrice() public view returns (uint256 _tombPrice) {
try IOracle(tombOracle).twap(tomb, 1e18) returns (uint144 price) {
return uint256(price);
} catch {
revert("Treasury: failed to consult TOMB price from the oracle");
}
}
// budget
function getReserve() public view returns (uint256) {
return seigniorageSaved;
}
function getBurnableTombLeft() public view returns (uint256 _burnableTombLeft) {
uint256 _tombPrice = getTombPrice();
if (_tombPrice <= tombPriceOne) {
uint256 _tombSupply = getTombCirculatingSupply();
uint256 _bondMaxSupply = _tombSupply.mul(maxDebtRatioPercent).div(10000);
uint256 _bondSupply = IERC20(tbond).totalSupply();
if (_bondMaxSupply > _bondSupply) {
uint256 _maxMintableBond = _bondMaxSupply.sub(_bondSupply);
uint256 _maxBurnableTomb = _maxMintableBond.mul(_tombPrice).div(1e18);
_burnableTombLeft = Math.min(epochSupplyContractionLeft, _maxBurnableTomb);
}
}
}
function getRedeemableBonds() public view returns (uint256 _redeemableBonds) {
uint256 _tombPrice = getTombPrice();
if (_tombPrice > tombPriceCeiling) {
uint256 _totalTomb = IERC20(tomb).balanceOf(address(this));
uint256 _rate = getBondPremiumRate();
if (_rate > 0) {
_redeemableBonds = _totalTomb.mul(1e18).div(_rate);
}
}
}
function getBondDiscountRate() public view returns (uint256 _rate) {
uint256 _tombPrice = getTombPrice();
if (_tombPrice <= tombPriceOne) {
if (discountPercent == 0) {
// no discount
_rate = tombPriceOne;
} else {
uint256 _bondAmount = tombPriceOne.mul(1e18).div(_tombPrice); // to burn 1 TOMB
uint256 _discountAmount = _bondAmount.sub(tombPriceOne).mul(discountPercent).div(10000);
_rate = tombPriceOne.add(_discountAmount);
if (maxDiscountRate > 0 && _rate > maxDiscountRate) {
_rate = maxDiscountRate;
}
}
}
}
function getBondPremiumRate() public view returns (uint256 _rate) {
uint256 _tombPrice = getTombPrice();
if (_tombPrice > tombPriceCeiling) {
uint256 _tombPricePremiumThreshold = tombPriceOne.mul(premiumThreshold).div(100);
if (_tombPrice >= _tombPricePremiumThreshold) {
//Price > 1.10
uint256 _premiumAmount = _tombPrice.sub(tombPriceOne).mul(premiumPercent).div(10000);
_rate = tombPriceOne.add(_premiumAmount);
if (maxPremiumRate > 0 && _rate > maxPremiumRate) {
_rate = maxPremiumRate;
}
} else {
// no premium bonus
_rate = tombPriceOne;
}
}
}
/* ========== GOVERNANCE ========== */
function initialize(
address _tomb,
address _tbond,
address _tshare,
address _tombOracle,
address _masonry,
uint256 _startTime
) public notInitialized {
tomb = _tomb;
tbond = _tbond;
tshare = _tshare;
tombOracle = _tombOracle;
masonry = _masonry;
startTime = _startTime;
tombPriceOne = 10**18;
tombPriceCeiling = tombPriceOne.mul(101).div(100);
// Dynamic max expansion percent
supplyTiers = [0 ether, 500000 ether, 1000000 ether, 1500000 ether, 2000000 ether, 5000000 ether, 10000000 ether, 20000000 ether, 50000000 ether];
maxExpansionTiers = [450, 400, 350, 300, 250, 200, 150, 125, 100];
maxSupplyExpansionPercent = 400; // Upto 4.0% supply for expansion
bondDepletionFloorPercent = 10000; // 100% of Bond supply for depletion floor
seigniorageExpansionFloorPercent = 3500; // At least 35% of expansion reserved for masonry
maxSupplyContractionPercent = 300; // Upto 3.0% supply for contraction (to burn TOMB and mint tBOND)
maxDebtRatioPercent = 3500; // Upto 35% supply of tBOND to purchase
premiumThreshold = 110;
premiumPercent = 7000;
// First 28 epochs with 4.5% expansion
bootstrapEpochs = 28;
bootstrapSupplyExpansionPercent = 450;
// set seigniorageSaved to it's balance
seigniorageSaved = IERC20(tomb).balanceOf(address(this));
initialized = true;
operator = msg.sender;
emit Initialized(msg.sender, block.number);
}
// Test helper
function setepochSupplyContractionLeft(uint num) public {
epochSupplyContractionLeft = num;
}
function setOperator(address _operator) external onlyOperator {
operator = _operator;
}
function setMasonry(address _masonry) external onlyOperator {
masonry = _masonry;
}
function setTombOracle(address _tombOracle) external onlyOperator {
tombOracle = _tombOracle;
}
function setTombPriceCeiling(uint256 _tombPriceCeiling) external onlyOperator {
require(_tombPriceCeiling >= tombPriceOne && _tombPriceCeiling <= tombPriceOne.mul(120).div(100), "out of range"); // [$1.0, $1.2]
tombPriceCeiling = _tombPriceCeiling;
}
function setMaxSupplyExpansionPercents(uint256 _maxSupplyExpansionPercent) external onlyOperator {
require(_maxSupplyExpansionPercent >= 10 && _maxSupplyExpansionPercent <= 1000, "_maxSupplyExpansionPercent: out of range"); // [0.1%, 10%]
maxSupplyExpansionPercent = _maxSupplyExpansionPercent;
}
function setSupplyTiersEntry(uint8 _index, uint256 _value) external onlyOperator returns (bool) {
require(_index >= 0, "Index has to be higher than 0");
require(_index < 9, "Index has to be lower than count of tiers");
if (_index > 0) {
require(_value > supplyTiers[_index - 1]);
}
if (_index < 8) {
require(_value < supplyTiers[_index + 1]);
}
supplyTiers[_index] = _value;
return true;
}
function setMaxExpansionTiersEntry(uint8 _index, uint256 _value) external onlyOperator returns (bool) {
require(_index >= 0, "Index has to be higher than 0");
require(_index < 9, "Index has to be lower than count of tiers");
require(_value >= 10 && _value <= 1000, "_value: out of range"); // [0.1%, 10%]
maxExpansionTiers[_index] = _value;
return true;
}
function setBondDepletionFloorPercent(uint256 _bondDepletionFloorPercent) external onlyOperator {
require(_bondDepletionFloorPercent >= 500 && _bondDepletionFloorPercent <= 10000, "out of range"); // [5%, 100%]
bondDepletionFloorPercent = _bondDepletionFloorPercent;
}
function setMaxSupplyContractionPercent(uint256 _maxSupplyContractionPercent) external onlyOperator {
require(_maxSupplyContractionPercent >= 100 && _maxSupplyContractionPercent <= 1500, "out of range"); // [0.1%, 15%]
maxSupplyContractionPercent = _maxSupplyContractionPercent;
}
function setMaxDebtRatioPercent(uint256 _maxDebtRatioPercent) external onlyOperator {
require(_maxDebtRatioPercent >= 1000 && _maxDebtRatioPercent <= 10000, "out of range"); // [10%, 100%]
maxDebtRatioPercent = _maxDebtRatioPercent;
}
function setBootstrap(uint256 _bootstrapEpochs, uint256 _bootstrapSupplyExpansionPercent) external onlyOperator {
require(_bootstrapEpochs <= 120, "_bootstrapEpochs: out of range"); // <= 1 month
require(_bootstrapSupplyExpansionPercent >= 100 && _bootstrapSupplyExpansionPercent <= 1000, "_bootstrapSupplyExpansionPercent: out of range"); // [1%, 10%]
bootstrapEpochs = _bootstrapEpochs;
bootstrapSupplyExpansionPercent = _bootstrapSupplyExpansionPercent;
}
function setExtraFunds(
address _daoFund,
uint256 _daoFundSharedPercent,
address _devFund,
uint256 _devFundSharedPercent
) external onlyOperator {
require(_daoFund != address(0), "zero");
require(_daoFundSharedPercent <= 3000, "out of range"); // <= 30%
require(_devFund != address(0), "zero");
require(_devFundSharedPercent <= 1000, "out of range"); // <= 10%
daoFund = _daoFund;
daoFundSharedPercent = _daoFundSharedPercent;
devFund = _devFund;
devFundSharedPercent = _devFundSharedPercent;
}
function setMaxDiscountRate(uint256 _maxDiscountRate) external onlyOperator {
maxDiscountRate = _maxDiscountRate;
}
function setMaxPremiumRate(uint256 _maxPremiumRate) external onlyOperator {
maxPremiumRate = _maxPremiumRate;
}
function setDiscountPercent(uint256 _discountPercent) external onlyOperator {
require(_discountPercent <= 20000, "_discountPercent is over 200%");
discountPercent = _discountPercent;
}
function setPremiumThreshold(uint256 _premiumThreshold) external onlyOperator {
require(_premiumThreshold >= tombPriceCeiling, "_premiumThreshold exceeds tombPriceCeiling");
require(_premiumThreshold <= 150, "_premiumThreshold is higher than 1.5");
premiumThreshold = _premiumThreshold;
}
function setPremiumPercent(uint256 _premiumPercent) external onlyOperator {
require(_premiumPercent <= 20000, "_premiumPercent is over 200%");
premiumPercent = _premiumPercent;
}
function setMintingFactorForPayingDebt(uint256 _mintingFactorForPayingDebt) external onlyOperator {
require(_mintingFactorForPayingDebt >= 10000 && _mintingFactorForPayingDebt <= 20000, "_mintingFactorForPayingDebt: out of range"); // [100%, 200%]
mintingFactorForPayingDebt = _mintingFactorForPayingDebt;
}
/* ========== MUTABLE FUNCTIONS ========== */
function _updateTombPrice() internal {
try IOracle(tombOracle).update() {} catch {}
}
function getTombCirculatingSupply() public view returns (uint256) {
IERC20 tombErc20 = IERC20(tomb);
uint256 totalSupply = tombErc20.totalSupply();
uint256 balanceExcluded = 0;
for (uint8 entryId = 0; entryId < excludedFromTotalSupply.length; ++entryId) {
balanceExcluded = balanceExcluded.add(tombErc20.balanceOf(excludedFromTotalSupply[entryId]));
}
return totalSupply.sub(balanceExcluded);
}
function buyBonds(uint256 _tombAmount, uint256 targetPrice) external onlyOneBlock checkCondition checkOperator {
require(_tombAmount > 0, "Treasury: cannot purchase bonds with zero amount");
uint256 tombPrice = getTombPrice();
require(tombPrice == targetPrice, "Treasury: TOMB price moved");
require(
tombPrice < tombPriceOne, // price < $1
"Treasury: tombPrice not eligible for bond purchase"
);
require(_tombAmount <= epochSupplyContractionLeft, "Treasury: not enough bond left to purchase");
uint256 _rate = getBondDiscountRate();
require(_rate > 0, "Treasury: invalid bond rate");
uint256 _bondAmount = _tombAmount.mul(_rate).div(1e18);
uint256 tombSupply = getTombCirculatingSupply();
uint256 newBondSupply = IERC20(tbond).totalSupply().add(_bondAmount);
require(newBondSupply <= tombSupply.mul(maxDebtRatioPercent).div(10000), "over max debt ratio");
IBasisAsset(tomb).burnFrom(msg.sender, _tombAmount);
IBasisAsset(tbond).mint(msg.sender, _bondAmount);
epochSupplyContractionLeft = epochSupplyContractionLeft.sub(_tombAmount);
_updateTombPrice();
emit BoughtBonds(msg.sender, _tombAmount, _bondAmount);
}
function redeemBonds(uint256 _bondAmount, uint256 targetPrice) external onlyOneBlock checkCondition checkOperator {
require(_bondAmount > 0, "Treasury: cannot redeem bonds with zero amount");
uint256 tombPrice = getTombPrice();
require(tombPrice == targetPrice, "Treasury: TOMB price moved");
require(
tombPrice > tombPriceCeiling, // price > $1.01
"Treasury: tombPrice not eligible for bond purchase"
);
uint256 _rate = getBondPremiumRate();
require(_rate > 0, "Treasury: invalid bond rate");
uint256 _tombAmount = _bondAmount.mul(_rate).div(1e18);
require(IERC20(tomb).balanceOf(address(this)) >= _tombAmount, "Treasury: treasury has no more budget");
seigniorageSaved = seigniorageSaved.sub(Math.min(seigniorageSaved, _tombAmount));
IBasisAsset(tbond).burnFrom(msg.sender, _bondAmount);
IERC20(tomb).safeTransfer(msg.sender, _tombAmount);
_updateTombPrice();
emit RedeemedBonds(msg.sender, _tombAmount, _bondAmount);
}
function _sendToMasonry(uint256 _amount) internal {
IBasisAsset(tomb).mint(address(this), _amount);
uint256 _daoFundSharedAmount = 0;
if (daoFundSharedPercent > 0) {
_daoFundSharedAmount = _amount.mul(daoFundSharedPercent).div(10000);
IERC20(tomb).transfer(daoFund, _daoFundSharedAmount);
emit DaoFundFunded(now, _daoFundSharedAmount);
}
uint256 _devFundSharedAmount = 0;
if (devFundSharedPercent > 0) {
_devFundSharedAmount = _amount.mul(devFundSharedPercent).div(10000);
IERC20(tomb).transfer(devFund, _devFundSharedAmount);
emit DevFundFunded(now, _devFundSharedAmount);
}
_amount = _amount.sub(_daoFundSharedAmount).sub(_devFundSharedAmount);
IERC20(tomb).safeApprove(masonry, 0);
IERC20(tomb).safeApprove(masonry, _amount);
IMasonry(masonry).allocateSeigniorage(_amount);
emit MasonryFunded(now, _amount);
}
function _calculateMaxSupplyExpansionPercent(uint256 _tombSupply) internal returns (uint256) {
for (uint8 tierId = 8; tierId >= 0; --tierId) {
if (_tombSupply >= supplyTiers[tierId]) {
maxSupplyExpansionPercent = maxExpansionTiers[tierId];
break;
}
}
return maxSupplyExpansionPercent;
}
function allocateSeigniorage() external onlyOneBlock checkCondition checkEpoch checkOperator {
_updateTombPrice();
previousEpochTombPrice = getTombPrice();
uint256 tombSupply = getTombCirculatingSupply().sub(seigniorageSaved);
if (epoch < bootstrapEpochs) {
// 28 first epochs with 4.5% expansion
_sendToMasonry(tombSupply.mul(bootstrapSupplyExpansionPercent).div(10000));
} else {
if (previousEpochTombPrice > tombPriceCeiling) {
// Expansion ($TOMB Price > 1 $FTM): there is some seigniorage to be allocated
uint256 bondSupply = IERC20(tbond).totalSupply();
uint256 _percentage = previousEpochTombPrice.sub(tombPriceOne);
uint256 _savedForBond;
uint256 _savedForMasonry;
uint256 _mse = _calculateMaxSupplyExpansionPercent(tombSupply).mul(1e14);
if (_percentage > _mse) {
_percentage = _mse;
}
if (seigniorageSaved >= bondSupply.mul(bondDepletionFloorPercent).div(10000)) {
// saved enough to pay debt, mint as usual rate
_savedForMasonry = tombSupply.mul(_percentage).div(1e18);
} else {
// have not saved enough to pay debt, mint more
uint256 _seigniorage = tombSupply.mul(_percentage).div(1e18);
_savedForMasonry = _seigniorage.mul(seigniorageExpansionFloorPercent).div(10000);
_savedForBond = _seigniorage.sub(_savedForMasonry);
if (mintingFactorForPayingDebt > 0) {
_savedForBond = _savedForBond.mul(mintingFactorForPayingDebt).div(10000);
}
}
if (_savedForMasonry > 0) {
_sendToMasonry(_savedForMasonry);
}
if (_savedForBond > 0) {
seigniorageSaved = seigniorageSaved.add(_savedForBond);
IBasisAsset(tomb).mint(address(this), _savedForBond);
emit TreasuryFunded(now, _savedForBond);
}
}
}
}
// Used for testing
function moveEpoch(uint skipNum) public {
epoch = epoch.add(skipNum);
}
function governanceRecoverUnsupported(
IERC20 _token,
uint256 _amount,
address _to
) external onlyOperator {
// do not allow to drain core tokens
require(address(_token) != address(tomb), "tomb");
require(address(_token) != address(tbond), "bond");
require(address(_token) != address(tshare), "share");
_token.safeTransfer(_to, _amount);
}
function masonrySetOperator(address _operator) external onlyOperator {
IMasonry(masonry).setOperator(_operator);
}
function masonrySetLockUp(uint256 _withdrawLockupEpochs, uint256 _rewardLockupEpochs) external onlyOperator {
IMasonry(masonry).setLockUp(_withdrawLockupEpochs, _rewardLockupEpochs);
}
function masonryAllocateSeigniorage(uint256 amount) external onlyOperator {
IMasonry(masonry).allocateSeigniorage(amount);
}
function masonryGovernanceRecoverUnsupported(
address _token,
uint256 _amount,
address _to
) external onlyOperator {
IMasonry(masonry).governanceRecoverUnsupported(_token, _amount, _to);
}
}