Treasury

Created Diff never expires
278 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
546 lines
249 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
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);
}
}
}
}