Treasury

Created Diff never expires
278 removals
Words removed579
Total words1814
Words removed (%)31.92
546 lines
249 additions
Words added414
Total words1649
Words added (%)25.11
518 lines
contract Treasury is ContractGuard {
contract Treasury is ContractGuard, Operator {
using SafeERC20 for IERC20;
using SafeERC20 for IERC20;
using Address for address;
using Address for address;
using SafeMath for uint256;
using SafeMath for uint256;


/* ========= CONSTANT VARIABLES ======== */
/* ========= CONSTANT VARIABLES ======== */


uint256 public constant PERIOD = 6 hours;
uint256 public constant PERIOD = 6 hours;
uint256 public constant BASIS_DIVISOR = 100000; // 100%


/* ========== STATE VARIABLES ========== */
/* ========== STATE VARIABLES ========== */


// governance
address public operator;

// flags
// flags
bool public initialized = false;
bool public initialized = false;


// epoch
// epoch
uint256 public startTime;
uint256 public startTime;
uint256 public epoch = 0;
uint256 public epoch = 0;
uint256 public epochSupplyContractionLeft = 0;
uint256 public epochSupplyContractionLeft = 0;


//=================================================================// exclusions from total supply
address[] public excludedFromTotalSupply = [
address(0x2E585B96A2Ef1661508110E41c005bE86b63fc34) // HogGenesisRewardPool
];

// core components
// core components
address public tomb;
address public hog;
address public tbond;
address public bhog;
address public tshare;
address public ghog;


address public masonry;
address public masonry;
address public tombOracle;
address public hogOracle;


// price
// price
uint256 public tombPriceOne;
uint256 public hogPriceOne;
uint256 public tombPriceCeiling;
uint256 public hogPriceCeiling;


uint256 public seigniorageSaved;
uint256 public seigniorageSaved;


uint256[] public supplyTiers;
uint256[] public maxExpansionTiers;

uint256 public maxSupplyExpansionPercent;
uint256 public maxSupplyExpansionPercent;
uint256 public bondDepletionFloorPercent;
uint256 public bondDepletionFloorPercent;
uint256 public seigniorageExpansionFloorPercent;
uint256 public seigniorageExpansionFloorPercent;
uint256 public maxSupplyContractionPercent;
uint256 public maxSupplyContractionPercent;
uint256 public maxDebtRatioPercent;
uint256 public maxDebtRatioPercent;


// 28 first epochs (1 week) with 4.5% expansion regardless of SCARAB price
uint256 public bootstrapEpochs;
uint256 public bootstrapSupplyExpansionPercent;

/* =================== Added variables =================== */
/* =================== Added variables =================== */
uint256 public previousEpochTombPrice;
uint256 public previousEpochHogPrice;
uint256 public maxDiscountRate; // when purchasing bond
uint256 public maxDiscountRate; // when purchasing bond
uint256 public maxPremiumRate; // when redeeming bond
uint256 public maxPremiumRate; // when redeeming bond
uint256 public discountPercent;
uint256 public discountPercent;
uint256 public premiumThreshold;
uint256 public premiumThreshold;
uint256 public premiumPercent;
uint256 public premiumPercent;
uint256 public mintingFactorForPayingDebt; // print extra SCARAB during debt phase
uint256 public mintingFactorForPayingDebt; // print extra HOG during debt phase


address public daoFund;
address public daoFund;
uint256 public daoFundSharedPercent;
uint256 public daoFundSharedPercent;


//=================================================//

address public devFund;
address public devFund;
uint256 public devFundSharedPercent;
uint256 public devFundSharedPercent;
address public teamFund;
uint256 public teamFundSharedPercent;


/* =================== Events =================== */
/* =================== Events =================== */


event Initialized(address indexed executor, uint256 at);
event Initialized(address indexed executor, uint256 at);
event BurnedBonds(address indexed from, uint256 bondAmount);
event BurnedBonds(address indexed from, uint256 bondAmount);
event RedeemedBonds(address indexed from, uint256 tombAmount, uint256 bondAmount);
event RedeemedBonds(address indexed from, uint256 hogAmount, uint256 bondAmount);
event BoughtBonds(address indexed from, uint256 tombAmount, uint256 bondAmount);
event BoughtBonds(address indexed from, uint256 hogAmount, uint256 bondAmount);
event TreasuryFunded(uint256 timestamp, uint256 seigniorage);
event TreasuryFunded(uint256 timestamp, uint256 seigniorage);
event MasonryFunded(uint256 timestamp, uint256 seigniorage);
event MasonryFunded(uint256 timestamp, uint256 seigniorage);
event DaoFundFunded(uint256 timestamp, uint256 seigniorage);
event DaoFundFunded(uint256 timestamp, uint256 seigniorage);
event DevFundFunded(uint256 timestamp, uint256 seigniorage);
event DevFundFunded(uint256 timestamp, uint256 seigniorage);
event TeamFundFunded(uint256 timestamp, uint256 seigniorage);


/* =================== Modifier =================== */
/* =================== Modifier =================== */


modifier onlyOperator() {
require(operator == msg.sender, "Treasury: caller is not the operator");
_;
}

modifier checkCondition {
modifier checkCondition {
require(now >= startTime, "Treasury: not started yet");
require(block.timestamp >= startTime, "Treasury: not started yet");


_;
_;
}
}


modifier checkEpoch {
modifier checkEpoch {
require(now >= nextEpochPoint(), "Treasury: not opened yet");
require(block.timestamp >= nextEpochPoint(), "Treasury: not opened yet");


_;
_;


epoch = epoch.add(1);
epoch = epoch.add(1);
epochSupplyContractionLeft = (getTombPrice() > tombPriceCeiling) ? 0 : IERC20(tomb).totalSupply().mul(maxSupplyContractionPercent).div(10000);
epochSupplyContractionLeft = (getHogPrice() > hogPriceCeiling) ? 0 : getHogCirculatingSupply().mul(maxSupplyContractionPercent).div(BASIS_DIVISOR);
}
}


modifier checkOperator {
modifier checkOperator {
require(
require(
IBasisAsset(tomb).operator() == address(this) &&
IBasisAsset(hog).operator() == address(this) &&
IBasisAsset(tbond).operator() == address(this) &&
IBasisAsset(bhog).operator() == address(this) &&
IBasisAsset(tshare).operator() == address(this) &&
IBasisAsset(ghog).operator() == address(this) &&
Operator(masonry).operator() == address(this),
Operator(masonry).operator() == address(this),
"Treasury: need more permission"
"Treasury: need more permission"
);
);


_;
_;
}
}


modifier notInitialized {
modifier notInitialized {
require(!initialized, "Treasury: already initialized");
require(!initialized, "Treasury: already initialized");


_;
_;
}
}


/* ========== VIEW FUNCTIONS ========== */
/* ========== VIEW FUNCTIONS ========== */


function isInitialized() public view returns (bool) {
function isInitialized() public view returns (bool) {
return initialized;
return initialized;
}
}


// epoch
// epoch
function nextEpochPoint() public view returns (uint256) {
function nextEpochPoint() public view returns (uint256) {
return startTime.add(epoch.mul(PERIOD));
return startTime.add(epoch.mul(PERIOD));
}
}


// oracle
// oracle
function getTombPrice() public view returns (uint256 tombPrice) {
function getHogPrice() public view returns (uint256 hogPrice) {
try IOracle(tombOracle).consult(tomb, 1e18) returns (uint144 price) {
try IOracle(hogOracle).consult(hog, 1e18) returns (uint256 price) {
return uint256(price);
return uint256(price);
} catch {
} catch {
revert("Treasury: failed to consult SCARAB price from the oracle");
revert("Treasury: failed to consult HOG price from the oracle");
}
}
}
}


function getTombUpdatedPrice() public view returns (uint256 _tombPrice) {
function getHogUpdatedPrice() public view returns (uint256 _hogPrice) {
try IOracle(tombOracle).twap(tomb, 1e18) returns (uint144 price) {
try IOracle(hogOracle).twap(hog, 1e18) returns (uint256 price) {
return uint256(price);
return uint256(price);
} catch {
} catch {
revert("Treasury: failed to consult SCARAB price from the oracle");
revert("Treasury: failed to consult HOG price from the oracle");
}
}
}
}


// budget
// budget
function getReserve() public view returns (uint256) {
function getReserve() public view returns (uint256) {
return seigniorageSaved;
return seigniorageSaved;
}
}


function getBurnableTombLeft() public view returns (uint256 _burnableTombLeft) {
function getBurnableHogLeft() public view returns (uint256 _burnableHogLeft) {
uint256 _tombPrice = getTombPrice();
uint256 _hogPrice = getHogPrice();
if (_tombPrice <= tombPriceOne) {
if (_hogPrice <= hogPriceOne) {
uint256 _tombSupply = IERC20(tomb).totalSupply();
uint256 _hogSupply = getHogCirculatingSupply();
uint256 _bondMaxSupply = _tombSupply.mul(maxDebtRatioPercent).div(10000);
uint256 _bondMaxSupply = _hogSupply.mul(maxDebtRatioPercent).div(BASIS_DIVISOR);
uint256 _bondSupply = IERC20(tbond).totalSupply();
uint256 _bondSupply = IERC20(bhog).totalSupply();
if (_bondMaxSupply > _bondSupply) {
if (_bondMaxSupply > _bondSupply) {
uint256 _maxMintableBond = _bondMaxSupply.sub(_bondSupply);
uint256 _maxMintableBond = _bondMaxSupply.sub(_bondSupply);
uint256 _maxBurnableTomb = _maxMintableBond.mul(_tombPrice).div(1e18);
uint256 _maxBurnableHog = _maxMintableBond.mul(_hogPrice).div(1e18);
_burnableTombLeft = Math.min(epochSupplyContractionLeft, _maxBurnableTomb);
_burnableHogLeft = Math.min(epochSupplyContractionLeft, _maxBurnableHog);
}
}
}
}
}
}


function getRedeemableBonds() public view returns (uint256 _redeemableBonds) {
function getRedeemableBonds() public view returns (uint256 _redeemableBonds) {
uint256 _tombPrice = getTombPrice();
uint256 _hogPrice = getHogPrice();
if (_tombPrice > tombPriceCeiling) {
if (_hogPrice > hogPriceCeiling) {
uint256 _totalTomb = IERC20(tomb).balanceOf(address(this));
uint256 _totalHog = IERC20(hog).balanceOf(address(this));
uint256 _rate = getBondPremiumRate();
uint256 _rate = getBondPremiumRate();
if (_rate > 0) {
if (_rate > 0) {
_redeemableBonds = _totalTomb.mul(1e18).div(_rate);
_redeemableBonds = _totalHog.mul(1e18).div(_rate);
}
}
}
}
}
}


function getBondDiscountRate() public view returns (uint256 _rate) {
function getBondDiscountRate() public view returns (uint256 _rate) {
uint256 _tombPrice = getTombPrice();
uint256 _hogPrice = getHogPrice();
if (_tombPrice <= tombPriceOne) {
if (_hogPrice <= hogPriceOne) {
if (discountPercent == 0) {
if (discountPercent == 0) {
// no discount
// no discount
_rate = tombPriceOne;
_rate = hogPriceOne;
} else {
} else {
uint256 _bondAmount = tombPriceOne.mul(1e18).div(_tombPrice); // to burn 1 SCARAB
uint256 _bondAmount = hogPriceOne.mul(1e18).div(_hogPrice); // to burn 1 HOG
uint256 _discountAmount = _bondAmount.sub(tombPriceOne).mul(discountPercent).div(10000);
uint256 _discountAmount = _bondAmount.sub(hogPriceOne).mul(discountPercent).div(BASIS_DIVISOR);
_rate = tombPriceOne.add(_discountAmount);
_rate = hogPriceOne.add(_discountAmount);
if (maxDiscountRate > 0 && _rate > maxDiscountRate) {
if (maxDiscountRate > 0 && _rate > maxDiscountRate) {
_rate = maxDiscountRate;
_rate = maxDiscountRate;
}
}
}
}
}
}
}
}


function getBondPremiumRate() public view returns (uint256 _rate) {
function getBondPremiumRate() public view returns (uint256 _rate) {
uint256 _tombPrice = getTombPrice();
uint256 _hogPrice = getHogPrice();
if (_tombPrice > tombPriceCeiling) {
if (_hogPrice > hogPriceCeiling) {
uint256 _tombPricePremiumThreshold = tombPriceOne.mul(premiumThreshold).div(100);
uint256 _hogPricePremiumThreshold = hogPriceOne.mul(premiumThreshold).div(100);
if (_tombPrice >= _tombPricePremiumThreshold) {
if (_hogPrice >= _hogPricePremiumThreshold) {
//Price > 1.10
//Price > 1.10
uint256 _premiumAmount = _tombPrice.sub(tombPriceOne).mul(premiumPercent).div(10000);
uint256 _premiumAmount = _hogPrice.sub(hogPriceOne).mul(premiumPercent).div(BASIS_DIVISOR);
_rate = tombPriceOne.add(_premiumAmount);
_rate = hogPriceOne.add(_premiumAmount);
if (maxPremiumRate > 0 && _rate > maxPremiumRate) {
if (maxPremiumRate > 0 && _rate > maxPremiumRate) {
_rate = maxPremiumRate;
_rate = maxPremiumRate;
}
}
} else {
} else {
// no premium bonus
// no premium bonus
_rate = tombPriceOne;
_rate = hogPriceOne;
}
}
}
}
}
}


/* ========== GOVERNANCE ========== */
/* ========== GOVERNANCE ========== */


function initialize(
function initialize(
address _tomb,
address _hog,
address _tbond,
address _bhog,
address _tshare,
address _ghog,
address _tombOracle,
address _hogOracle,
address _masonry,
address _masonry,
uint256 _startTime
uint256 _startTime
) public notInitialized {
) public notInitialized onlyOperator {
tomb = _tomb;
hog = _hog;
tbond = _tbond;
bhog = _bhog;
tshare = _tshare;
ghog = _ghog;
tombOracle = _tombOracle;
hogOracle = _hogOracle;
masonry = _masonry;
masonry = _masonry;
startTime = _startTime;
startTime = _startTime;


tombPriceOne = 10**18;
hogPriceOne = 10 ** 18;
tombPriceCeiling = tombPriceOne.mul(101).div(100);
// hogPriceCeiling = 1000300000000000000; // 1.003 as its stable pool

hogPriceCeiling = hogPriceOne.mul(101).div(100); // even if its stable we aim to get 1.01
// 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 SCARAB and mint sBOND)
maxDebtRatioPercent = 3500; // Upto 35% supply of sBOND to purchase


premiumThreshold = 110;
maxSupplyExpansionPercent = 150; // 0.15%
premiumPercent = 7000;


// First 28 epochs with 4.5% expansion
bondDepletionFloorPercent = 100000; // 100% of Bond supply for depletion floor
bootstrapEpochs = 28;
seigniorageExpansionFloorPercent = 35000; // At least 35% of expansion reserved for masonry
bootstrapSupplyExpansionPercent = 450;
maxSupplyContractionPercent = 10000; // Upto 10.0% supply for contraction (to burn HOG and mint bhog)
maxDebtRatioPercent = 35000; // Upto 35% supply of bhog to purchase


// set seigniorageSaved to it's balance
// set seigniorageSaved to it's balance
seigniorageSaved = IERC20(tomb).balanceOf(address(this));
seigniorageSaved = IERC20(hog).balanceOf(address(this));


initialized = true;
initialized = true;
operator = msg.sender;
emit Initialized(msg.sender, block.number);
emit Initialized(msg.sender, block.number);
}
}


function setOperator(address _operator) external onlyOperator {
function setOperator(address _operator) external onlyOperator {
operator = _operator;
transferOperator(_operator);
}

function renounceOperator() external onlyOperator {
_renounceOperator();
}
}


function setMasonry(address _masonry) external onlyOperator {
function setMasonry(address _masonry) external onlyOperator {
masonry = _masonry;
masonry = _masonry;
}
}


function setTombOracle(address _tombOracle) external onlyOperator {
function setHogOracle(address _hogOracle) external onlyOperator {
tombOracle = _tombOracle;
hogOracle = _hogOracle;
}
}


function setTombPriceCeiling(uint256 _tombPriceCeiling) external onlyOperator {
function setHogPriceCeiling(uint256 _hogPriceCeiling) external onlyOperator {
require(_tombPriceCeiling >= tombPriceOne && _tombPriceCeiling <= tombPriceOne.mul(120).div(100), "out of range"); // [$1.0, $1.2]
require(_hogPriceCeiling >= hogPriceOne && _hogPriceCeiling <= hogPriceOne.mul(120).div(100), "out of range"); // [$1.0, $1.2]
tombPriceCeiling = _tombPriceCeiling;
hogPriceCeiling = _hogPriceCeiling;
}
}


function setMaxSupplyExpansionPercents(uint256 _maxSupplyExpansionPercent) external onlyOperator {
function setMaxSupplyExpansionPercents(uint256 _maxSupplyExpansionPercent) external onlyOperator {
require(_maxSupplyExpansionPercent >= 10 && _maxSupplyExpansionPercent <= 1000, "_maxSupplyExpansionPercent: out of range"); // [0.1%, 10%]
require(_maxSupplyExpansionPercent >= 10 && _maxSupplyExpansionPercent <= 10000, "_maxSupplyExpansionPercent: out of range"); // [0.01%, 10%]
maxSupplyExpansionPercent = _maxSupplyExpansionPercent;
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 {
function setBondDepletionFloorPercent(uint256 _bondDepletionFloorPercent) external onlyOperator {
require(_bondDepletionFloorPercent >= 500 && _bondDepletionFloorPercent <= 10000, "out of range"); // [5%, 100%]
require(_bondDepletionFloorPercent >= 500 && _bondDepletionFloorPercent <= BASIS_DIVISOR, "out of range"); // [0.5%, 100%]
bondDepletionFloorPercent = _bondDepletionFloorPercent;
bondDepletionFloorPercent = _bondDepletionFloorPercent;
}
}


function setMaxSupplyContractionPercent(uint256 _maxSupplyContractionPercent) external onlyOperator {
function setMaxSupplyContractionPercent(uint256 _maxSupplyContractionPercent) external onlyOperator {
require(_maxSupplyContractionPercent >= 100 && _maxSupplyContractionPercent <= 1500, "out of range"); // [0.1%, 15%]
require(_maxSupplyContractionPercent >= 100 && _maxSupplyContractionPercent <= 15000, "out of range"); // [0.1%, 15%]
maxSupplyContractionPercent = _maxSupplyContractionPercent;
maxSupplyContractionPercent = _maxSupplyContractionPercent;
}
}


function setMaxDebtRatioPercent(uint256 _maxDebtRatioPercent) external onlyOperator {
function setMaxDebtRatioPercent(uint256 _maxDebtRatioPercent) external onlyOperator {
require(_maxDebtRatioPercent >= 1000 && _maxDebtRatioPercent <= 10000, "out of range"); // [10%, 100%]
require(_maxDebtRatioPercent >= 1000 && _maxDebtRatioPercent <= BASIS_DIVISOR, "out of range"); // [1%, 100%]
maxDebtRatioPercent = _maxDebtRatioPercent;
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(
function setExtraFunds(
address _daoFund,
address _daoFund,
uint256 _daoFundSharedPercent,
uint256 _daoFundSharedPercent,
address _devFund,
address _devFund,
uint256 _devFundSharedPercent
uint256 _devFundSharedPercent,
address _teamFund,
uint256 _teamFundSharedPercent
) external onlyOperator {
) external onlyOperator {
require(_daoFund != address(0), "zero");
require(_daoFund != address(0), "zero");
require(_daoFundSharedPercent <= 3000, "out of range"); // <= 30%
require(_daoFundSharedPercent <= 15000, "out of range");
require(_devFund != address(0), "zero");
require(_devFund != address(0), "zero");
require(_devFundSharedPercent <= 1000, "out of range"); // <= 10%
require(_devFundSharedPercent <= 3500, "out of range");
require(_teamFund != address(0), "zero");
require(_teamFundSharedPercent <= 5500, "out of range");

daoFund = _daoFund;
daoFund = _daoFund;
daoFundSharedPercent = _daoFundSharedPercent;
daoFundSharedPercent = _daoFundSharedPercent;
devFund = _devFund;
devFund = _devFund;
devFundSharedPercent = _devFundSharedPercent;
devFundSharedPercent = _devFundSharedPercent;
teamFund = _teamFund;
teamFundSharedPercent = _teamFundSharedPercent;
}
}


function setMaxDiscountRate(uint256 _maxDiscountRate) external onlyOperator {
function setMaxDiscountRate(uint256 _maxDiscountRate) external onlyOperator {
require(_maxDiscountRate <= 200000, "_maxDiscountRate is over 200%");
maxDiscountRate = _maxDiscountRate;
maxDiscountRate = _maxDiscountRate;
}
}


function setMaxPremiumRate(uint256 _maxPremiumRate) external onlyOperator {
function setMaxPremiumRate(uint256 _maxPremiumRate) external onlyOperator {
require(_maxPremiumRate <= 200000, "_maxPremiumRate is over 200%");
maxPremiumRate = _maxPremiumRate;
maxPremiumRate = _maxPremiumRate;
}
}


function setDiscountPercent(uint256 _discountPercent) external onlyOperator {
function setDiscountPercent(uint256 _discountPercent) external onlyOperator {
require(_discountPercent <= 20000, "_discountPercent is over 200%");
require(_discountPercent <= 200000, "_discountPercent is over 200%");
discountPercent = _discountPercent;
discountPercent = _discountPercent;
}
}


function setPremiumThreshold(uint256 _premiumThreshold) external onlyOperator {
function setPremiumThreshold(uint256 _premiumThreshold) external onlyOperator {
require(_premiumThreshold >= tombPriceCeiling, "_premiumThreshold exceeds tombPriceCeiling");
require(_premiumThreshold >= hogPriceCeiling, "_premiumThreshold exceeds hogPriceCeiling");
require(_premiumThreshold <= 150, "_premiumThreshold is higher than 1.5");
require(_premiumThreshold <= 1500, "_premiumThreshold is higher than 1.5");
premiumThreshold = _premiumThreshold;
premiumThreshold = _premiumThreshold;
}
}


function setPremiumPercent(uint256 _premiumPercent) external onlyOperator {
function setPremiumPercent(uint256 _premiumPercent) external onlyOperator {
require(_premiumPercent <= 20000, "_premiumPercent is over 200%");
require(_premiumPercent <= 200000, "_premiumPercent is over 200%");
premiumPercent = _premiumPercent;
premiumPercent = _premiumPercent;
}
}


function setMintingFactorForPayingDebt(uint256 _mintingFactorForPayingDebt) external onlyOperator {
function setMintingFactorForPayingDebt(uint256 _mintingFactorForPayingDebt) external onlyOperator {
require(_mintingFactorForPayingDebt >= 10000 && _mintingFactorForPayingDebt <= 20000, "_mintingFactorForPayingDebt: out of range"); // [100%, 200%]
require(_mintingFactorForPayingDebt >= BASIS_DIVISOR && _mintingFactorForPayingDebt <= 200000, "_mintingFactorForPayingDebt: out of range"); // [100%, 200%]
mintingFactorForPayingDebt = _mintingFactorForPayingDebt;
mintingFactorForPayingDebt = _mintingFactorForPayingDebt;
}
}


/* ========== MUTABLE FUNCTIONS ========== */
/* ========== MUTABLE FUNCTIONS ========== */


function _updateTombPrice() internal {
function _updateHogPrice() internal {
try IOracle(tombOracle).update() {} catch {}
try IOracle(hogOracle).update() {} catch {}
}
}


function buyBonds(uint256 _tombAmount, uint256 targetPrice) external onlyOneBlock checkCondition checkOperator {
function getHogCirculatingSupply() public view returns (uint256) {
require(_tombAmount > 0, "Treasury: cannot purchase bonds with zero amount");
IERC20 hogErc20 = IERC20(hog);
uint256 totalSupply = hogErc20.totalSupply();
uint256 balanceExcluded = 0;
for (uint8 entryId = 0; entryId < excludedFromTotalSupply.length; ++entryId) {
balanceExcluded = balanceExcluded.add(hogErc20.balanceOf(excludedFromTotalSupply[entryId]));
}
return totalSupply.sub(balanceExcluded);
}


uint256 tombPrice = getTombPrice();
function buyBonds(uint256 _hogAmount, uint256 targetPrice) external onlyOneBlock checkCondition checkOperator {
require(tombPrice == targetPrice, "Treasury: SCARAB price moved");
require(_hogAmount > 0, "Treasury: cannot purchase bonds with zero amount");

uint256 hogPrice = getHogPrice();
require(hogPrice == targetPrice, "Treasury: HOG price moved");
require(
require(
tombPrice < tombPriceOne, // price < $1
hogPrice < hogPriceOne, // price < $1
"Treasury: tombPrice not eligible for bond purchase"
"Treasury: hogPrice not eligible for bond purchase"
);
);


require(_tombAmount <= epochSupplyContractionLeft, "Treasury: not enough bond left to purchase");
require(_hogAmount <= epochSupplyContractionLeft, "Treasury: not enough bond left to purchase");


uint256 _rate = getBondDiscountRate();
uint256 _rate = getBondDiscountRate();
require(_rate > 0, "Treasury: invalid bond rate");
require(_rate > 0, "Treasury: invalid bond rate");


uint256 _bondAmount = _tombAmount.mul(_rate).div(1e18);
uint256 _bondAmount = _hogAmount.mul(_rate).div(1e18);
uint256 tombSupply = IERC20(tomb).totalSupply();
uint256 hogSupply = getHogCirculatingSupply();
uint256 newBondSupply = IERC20(tbond).totalSupply().add(_bondAmount);
uint256 newBondSupply = IERC20(bhog).totalSupply().add(_bondAmount);
require(newBondSupply <= tombSupply.mul(maxDebtRatioPercent).div(10000), "over max debt ratio");
require(newBondSupply <= hogSupply.mul(maxDebtRatioPercent).div(BASIS_DIVISOR), "over max debt ratio");


IBasisAsset(tomb).burnFrom(msg.sender, _tombAmount);
IBasisAsset(hog).burnFrom(msg.sender, _hogAmount);
IBasisAsset(tbond).mint(msg.sender, _bondAmount);
IBasisAsset(bhog).mint(msg.sender, _bondAmount);


epochSupplyContractionLeft = epochSupplyContractionLeft.sub(_tombAmount);
epochSupplyContractionLeft = epochSupplyContractionLeft.sub(_hogAmount);
_updateTombPrice();
_updateHogPrice();


emit BoughtBonds(msg.sender, _tombAmount, _bondAmount);
emit BoughtBonds(msg.sender, _hogAmount, _bondAmount);
}
}


function redeemBonds(uint256 _bondAmount, uint256 targetPrice) external onlyOneBlock checkCondition checkOperator {
function redeemBonds(uint256 _bondAmount, uint256 targetPrice) external onlyOneBlock checkCondition checkOperator {
require(_bondAmount > 0, "Treasury: cannot redeem bonds with zero amount");
require(_bondAmount > 0, "Treasury: cannot redeem bonds with zero amount");


uint256 tombPrice = getTombPrice();
uint256 hogPrice = getHogPrice();
require(tombPrice == targetPrice, "Treasury: SCARAB price moved");
require(hogPrice == targetPrice, "Treasury: HOG price moved");
require(
require(
tombPrice > tombPriceCeiling, // price > $1.01
hogPrice > hogPriceCeiling, // price > $1.01
"Treasury: tombPrice not eligible for bond purchase"
"Treasury: hogPrice not eligible for bond purchase"
);
);


uint256 _rate = getBondPremiumRate();
uint256 _rate = getBondPremiumRate();
require(_rate > 0, "Treasury: invalid bond rate");
require(_rate > 0, "Treasury: invalid bond rate");


uint256 _tombAmount = _bondAmount.mul(_rate).div(1e18);
uint256 _hogAmount = _bondAmount.mul(_rate).div(1e18);
require(IERC20(tomb).balanceOf(address(this)) >= _tombAmount, "Treasury: treasury has no more budget");
require(IERC20(hog).balanceOf(address(this)) >= _hogAmount, "Treasury: treasury has no more budget");


seigniorageSaved = seigniorageSaved.sub(Math.min(seigniorageSaved, _tombAmount));
seigniorageSaved = seigniorageSaved.sub(Math.min(seigniorageSaved, _hogAmount));


IBasisAsset(tbond).burnFrom(msg.sender, _bondAmount);
IBasisAsset(bhog).burnFrom(msg.sender, _bondAmount);
IERC20(tomb).safeTransfer(msg.sender, _tombAmount);
IERC20(hog).safeTransfer(msg.sender, _hogAmount);


_updateTombPrice();
_updateHogPrice();


emit RedeemedBonds(msg.sender, _tombAmount, _bondAmount);
emit RedeemedBonds(msg.sender, _hogAmount, _bondAmount);
}
}


function _sendToMasonry(uint256 _amount) internal {
function _sendToMasonry(uint256 _amount) internal {
IBasisAsset(tomb).mint(address(this), _amount);
IBasisAsset(hog).mint(address(this), _amount);


uint256 _daoFundSharedAmount = 0;
uint256 _daoFundSharedAmount = 0;
if (daoFundSharedPercent > 0) {
if (daoFundSharedPercent > 0) {
_daoFundSharedAmount = _amount.mul(daoFundSharedPercent).div(10000);
_daoFundSharedAmount = _amount.mul(daoFundSharedPercent).div(BASIS_DIVISOR);
IERC20(tomb).transfer(daoFund, _daoFundSharedAmount);
IERC20(hog).transfer(daoFund, _daoFundSharedAmount);
emit DaoFundFunded(now, _daoFundSharedAmount);
emit DaoFundFunded(block.timestamp, _daoFundSharedAmount);
}
}


uint256 _devFundSharedAmount = 0;
uint256 _devFundSharedAmount = 0;
if (devFundSharedPercent > 0) {
if (devFundSharedPercent > 0) {
_devFundSharedAmount = _amount.mul(devFundSharedPercent).div(10000);
_devFundSharedAmount = _amount.mul(devFundSharedPercent).div(BASIS_DIVISOR);
IERC20(tomb).transfer(devFund, _devFundSharedAmount);
IERC20(hog).transfer(devFund, _devFundSharedAmount);
emit DevFundFunded(now, _devFundSharedAmount);
emit DevFundFunded(block.timestamp, _devFundSharedAmount);
}
}


_amount = _amount.sub(_daoFundSharedAmount).sub(_devFundSharedAmount);
uint256 _teamFundSharedAmount = 0;
if (teamFundSharedPercent > 0) {
_teamFundSharedAmount = _amount.mul(teamFundSharedPercent).div(BASIS_DIVISOR);
IERC20(hog).transfer(teamFund, _teamFundSharedAmount);
emit TeamFundFunded(block.timestamp, _teamFundSharedAmount);
}


IERC20(tomb).safeApprove(masonry, 0);
_amount = _amount.sub(_daoFundSharedAmount).sub(_devFundSharedAmount).sub(_teamFundSharedAmount);
IERC20(tomb).safeApprove(masonry, _amount);

IERC20(hog).safeApprove(masonry, 0);
IERC20(hog).safeApprove(masonry, _amount);
IMasonry(masonry).allocateSeigniorage(_amount);
IMasonry(masonry).allocateSeigniorage(_amount);
emit MasonryFunded(now, _amount);
emit MasonryFunded(block.timestamp, _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 {
function allocateSeigniorage() external onlyOneBlock checkCondition checkEpoch checkOperator {
_updateTombPrice();
_updateHogPrice();
previousEpochTombPrice = getTombPrice();
previousEpochHogPrice = getHogPrice();
uint256 tombSupply = IERC20(tomb).totalSupply().sub(seigniorageSaved);
uint256 hogSupply = getHogCirculatingSupply().sub(seigniorageSaved);
if (epoch < bootstrapEpochs) {
if (previousEpochHogPrice > hogPriceCeiling) {
// 28 first epochs with 4.5% expansion
// Expansion ($HOG Price > 1 $OS): there is some seigniorage to be allocated
_sendToMasonry(tombSupply.mul(bootstrapSupplyExpansionPercent).div(10000));
uint256 bondSupply = IERC20(bhog).totalSupply();
} else {
uint256 _percentage = previousEpochHogPrice.sub(hogPriceOne);
if (previousEpochTombPrice > tombPriceCeiling) {
// Expansion ($SCARAB Price > 1 $FTM): there is some seigniorage to be allocated
uint256 bondSupply = IERC20(tbond).totalSupply();
uint256 _percentage = previousEpochTombPrice.sub(tombPriceOne);
uint256 _savedForBond;
uint256 _savedForBond;
uint256 _savedForMasonry;
uint256 _savedForMasonry;
uint256 _mse = _calculateMaxSupplyExpansionPercent(tombSupply).mul(1e14);
uint256 _mse = maxSupplyExpansionPercent.mul(1e13);
if (_percentage > _mse) {
_percentage = _mse;
_percentage = _mse;
}
if (seigniorageSaved >= bondSupply.mul(bondDepletionFloorPercent).div(BASIS_DIVISOR)) {
if (seigniorageSaved >= bondSupply.mul(bondDepletionFloorPercent).div(10000)) {
// saved enough to pay debt, mint as usual rate
// saved enough to pay debt, mint as usual rate
_savedForMasonry = tombSupply.mul(_percentage).div(1e18);
_savedForMasonry = hogSupply.mul(_percentage).div(1e18);
} else {
} else {
// have not saved enough to pay debt, mint more
// have not saved enough to pay debt, mint more
uint256 _seigniorage = tombSupply.mul(_percentage).div(1e18);
uint256 _seigniorage = hogSupply.mul(_percentage).div(1e18);
_savedForMasonry = _seigniorage.mul(seigniorageExpansionFloorPercent).div(10000);
_savedForMasonry = _seigniorage.mul(seigniorageExpansionFloorPercent).div(BASIS_DIVISOR);
_savedForBond = _seigniorage.sub(_savedForMasonry);
_savedForBond = _seigniorage.sub(_savedForMasonry);
if (mintingFactorForPayingDebt > 0) {
if (mintingFactorForPayingDebt > 0) {
_savedForBond = _savedForBond.mul(mintingFactorForPayingDebt).div(10000);
_savedForBond = _savedForBond.mul(mintingFactorForPayingDebt).div(BASIS_DIVISOR);
}
}
}
}
if (_savedForMasonry > 0) {
if (_savedForMasonry > 0) {
_sendToMasonry(_savedForMasonry);
_sendToMasonry(_savedForMasonry);
}
}
if (_savedForBond > 0) {
if (_savedForBond > 0) {
seigniorageSaved = seigniorageSaved.add(_savedForBond);
seigniorageSaved = seigniorageSaved.add(_savedForBond);
IBasisAsset(tomb).mint(address(this), _savedForBond);
IBasisAsset(hog).mint(address(this), _savedForBond);
emit TreasuryFunded(now, _savedForBond);
emit TreasuryFunded(block.timestamp, _savedForBond);
}
}
}
}
}
}
}


function governanceRecoverUnsupported(
function governanceRecoverUnsupported(
IERC20 _token,
IERC20 _token,
uint256 _amount,
uint256 _amount,
address _to
address _to
) external onlyOperator {
) external onlyOperator {
// do not allow to drain core tokens
// do not allow to drain core tokens
require(address(_token) != address(tomb), "tomb");
require(address(_token) != address(hog), "hog");
require(address(_token) != address(tbond), "bond");
require(address(_token) != address(bhog), "bond");
require(address(_token) != address(tshare), "share");
require(address(_token) != address(ghog), "share");
_token.safeTransfer(_to, _amount);
_token.safeTransfer(_to, _amount);
}
}


function tombSetOperator(address _operator) external onlyOperator {
function hogSetOperator(address _operator) external onlyOperator {
IBasisAsset(tomb).transferOperator(_operator);
IBasisAsset(hog).transferOperator(_operator);
}
}


function tshareSetOperator(address _operator) external onlyOperator {
function ghogSetOperator(address _operator) external onlyOperator {
IBasisAsset(tshare).transferOperator(_operator);
IBasisAsset(ghog).transferOperator(_operator);
}
}


function tbondSetOperator(address _operator) external onlyOperator {
function bhogSetOperator(address _operator) external onlyOperator {
IBasisAsset(tbond).transferOperator(_operator);
IBasisAsset(bhog).transferOperator(_operator);
}
}


function masonrySetOperator(address _operator) external onlyOperator {
function masonrySetOperator(address _operator) external onlyOperator {
IMasonry(masonry).setOperator(_operator);
IMasonry(masonry).setOperator(_operator);
}
}


function masonrySetLockUp(uint256 _withdrawLockupEpochs, uint256 _rewardLockupEpochs) external onlyOperator {
function masonrySetLockUp(uint256 _withdrawLockupEpochs, uint256 _rewardLockupEpochs) external onlyOperator {
IMasonry(masonry).setLockUp(_withdrawLockupEpochs, _rewardLockupEpochs);
IMasonry(masonry).setLockUp(_withdrawLockupEpochs, _rewardLockupEpochs);
}
}


function masonryAllocateSeigniorage(uint256 amount) external onlyOperator {
function masonryAllocateSeigniorage(uint256 amount) external onlyOperator {
IMasonry(masonry).allocateSeigniorage(amount);
IMasonry(masonry).allocateSeigniorage(amount);
}
}


function masonryGovernanceRecoverUnsupported(
function masonryGovernanceRecoverUnsupported(
address _token,
address _token,
uint256 _amount,
uint256 _amount,
address _to
address _to
) external onlyOperator {
) external onlyOperator {
IMasonry(masonry).governanceRecoverUnsupported(_token, _amount, _to);
IMasonry(masonry).governanceRecoverUnsupported(_token, _amount, _to);
}
}
}
}