Diff
checker
Testo
Testo
Immagini
Documenti
Excel
Cartelle
Legal
Enterprise
Applicazione per desktop
Prezzi
Accedi
Scarica Diffchecker Desktop
Confronta il testo
Trova la differenza tra due file di testo
Strumenti
Cronologia
Editor live
Comprimi invariate
Senza a capo
Layout
Diviso
Unificato
Livello di dettaglio
Intelligente
Parola
Carattere
Evidenziazione sintassi
Scegli sintassi
Ignora
Trasforma testo
Vai alla prima modifica
Modifica input
Diffchecker Desktop
Il modo più sicuro per usare Diffchecker. Ottieni l'app Diffchecker Desktop: i tuoi diff non lasciano mai il tuo computer!
Ottieni Desktop
TombBasedTreasury
Creato
4 anni fa
Il diff non scade mai
Eliminare
Esporta
Condividere
Spiegare
271 rimozioni
Linee
Totale
Rimosso
Caratteri
Totale
Rimosso
Per continuare a utilizzare questa funzione, aggiorna a
Diff
checker
Pro
Visualizza prezzi
576 linee
Copia tutti
300 aggiunte
Linee
Totale
Aggiunto
Caratteri
Totale
Aggiunto
Per continuare a utilizzare questa funzione, aggiorna a
Diff
checker
Pro
Visualizza prezzi
597 linee
Copia tutti
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
Copia
Copiato
Copia
Copiato
pragma solidity
0.6.12
;
pragma solidity
^0.8.0
;
Copia
Copiato
Copia
Copiato
import "@openzeppelin/contracts/
math/Math.sol";
import "@openzeppelin/contracts/
utils/
math/Math.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
Copia
Copiato
Copia
Copiato
import "@openzeppelin/contracts/token/ERC20/
SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/
utils/
SafeERC20.sol";
import "@openzeppelin/contracts/
utils
/ReentrancyGuard.sol";
import "@openzeppelin/contracts/
security
/ReentrancyGuard.sol";
import "./lib/Babylonian.sol";
import "./lib/Babylonian.sol";
import "./owner/Operator.sol";
import "./owner/Operator.sol";
import "./utils/ContractGuard.sol";
import "./utils/ContractGuard.sol";
import "./interfaces/IBasisAsset.sol";
import "./interfaces/IBasisAsset.sol";
import "./interfaces/IOracle.sol";
import "./interfaces/IOracle.sol";
Copia
Copiato
Copia
Copiato
import "./interfaces/
IMasonry
.sol";
import "./interfaces/
IAcropolis
.sol";
/*
/*
Copia
Copiato
Copia
Copiato
______
__
_______
____
______
.___ ____
_______
.__
/
_
__/
___ ____
___ / /
_
/
____
(_)
___ ____
____
_
____
____
\______ \____
_
___
___ ____
__| _/ \
_
____
_/|__| _
___ ____
_
____
____
____
/
/
/ __ \
/ __
`__ \/
__
\ /
/_ /
/ __ \
/ __
`/
__ \
/
___/
_ \
| | _/\__ \
/
___/_
/ __ \
/ __
| | __) | | / \ \
__
\ /
\ _/ ___\_
/ __ \
/ /
/ /_/
/ / / / / / /_/ /
/ __
/ / / / / / /_/ / / / /
/__
/ __/
| | \
/ __
\_ \_
__ \
\
___/
/ /_/
| | \ | || | \
/ __
\_| | \\ \___\ ___/
/_/
\____
/_/ /_/ /_/_.
___
/
/
_/
/_/_/ /_/\__,_/_/ /_/
\___
/
\___
/
|______ /(____ /
/__
__ > \___ >
\____
| \
___
/
|__||___| /(____ /|___| /
\___
>
\___
>
\/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/
http://tomb.finance
*/
*/
contract Treasury is ContractGuard {
contract Treasury is ContractGuard {
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;
/* ========== STATE VARIABLES ========== */
/* ========== STATE VARIABLES ========== */
// governance
// governance
address public operator;
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;
Copia
Copiato
Copia
Copiato
//
exclusions from total supply
//
=================================================================//
exclusions from total supply
address[] public excludedFromTotalSupply = [
address[] public excludedFromTotalSupply = [
Copia
Copiato
Copia
Copiato
address(
0x9A896d3c54D7e45B558BD5fFf26bF1E8C031F93b),
//
Tomb
GenesisPool
address(
0x9Ec66B9409d4cD8D4a4C90950Ff0fd26bB39ad84)
//
Based
GenesisPool
address(0xa7b9123f4b15fE0fF01F469ff5Eab2b41296dC0E), // new TombRewardPool
address(0xA7B16703470055881e7EE093e9b0bF537f29CD4d) // old TombRewardPool
];
];
// core components
// core components
Copia
Copiato
Copia
Copiato
address public
tomb
;
address public
based
;
address public
t
bond;
address public
b
bond;
address public
t
share;
address public
b
share;
Copia
Copiato
Copia
Copiato
address public
masonry
;
address public
acropolis
;
address public
tomb
Oracle;
address public
based
Oracle;
// price
// price
Copia
Copiato
Copia
Copiato
uint256 public
tomb
PriceOne;
uint256 public
based
PriceOne;
uint256 public
tomb
PriceCeiling;
uint256 public
based
PriceCeiling;
uint256 public seigniorageSaved;
uint256 public seigniorageSaved;
uint256[] public supplyTiers;
uint256[] public supplyTiers;
uint256[] public maxExpansionTiers;
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;
Copia
Copiato
Copia
Copiato
//
28
first epochs (
1
week) with 4.5% expansion regardless of
TOMB
price
//
14
first epochs (
0.5
week) with 4.5% expansion regardless of
BASED
price
uint256 public bootstrapEpochs;
uint256 public bootstrapEpochs;
uint256 public bootstrapSupplyExpansionPercent;
uint256 public bootstrapSupplyExpansionPercent;
/* =================== Added variables =================== */
/* =================== Added variables =================== */
Copia
Copiato
Copia
Copiato
uint256 public previousEpoch
Tomb
Price;
uint256 public previousEpoch
Based
Price;
uint256 public maxDiscountRate; // when purchasing bond
uint256 public maxDiscountRate; // when purchasing bond
Copia
Copiato
Copia
Copiato
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;
Copia
Copiato
Copia
Copiato
uint256 public mintingFactorForPayingDebt; // print extra
TOMB
during debt phase
uint256 public mintingFactorForPayingDebt; // print extra
BASED
during debt phase
address public daoFund;
address public daoFund;
uint256 public daoFundSharedPercent;
uint256 public daoFundSharedPercent;
Copia
Copiato
Copia
Copiato
//=================================================//
address public devFund;
address public devFund;
uint256 public devFundSharedPercent;
uint256 public devFundSharedPercent;
Copia
Copiato
Copia
Copiato
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);
Copia
Copiato
Copia
Copiato
event RedeemedBonds(address indexed from, uint256
tomb
Amount, uint256 bondAmount);
event RedeemedBonds(address indexed from, uint256
based
Amount, uint256 bondAmount);
event BoughtBonds(address indexed from, uint256
tomb
Amount, uint256 bondAmount);
event BoughtBonds(address indexed from, uint256
based
Amount, uint256 bondAmount);
event TreasuryFunded(uint256 timestamp, uint256 seigniorage);
event TreasuryFunded(uint256 timestamp, uint256 seigniorage);
Copia
Copiato
Copia
Copiato
event
MasonryFunded
(uint256 timestamp, uint256 seigniorage);
event
AcropolisFunded
(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);
Copia
Copiato
Copia
Copiato
event TeamFundFunded(uint256 timestamp, uint256 seigniorage);
/* =================== Modifier =================== */
/* =================== Modifier =================== */
modifier onlyOperator() {
modifier onlyOperator() {
require(operator == msg.sender, "Treasury: caller is not the operator");
require(operator == msg.sender, "Treasury: caller is not the operator");
_;
_;
}
}
modifier checkCondition {
modifier checkCondition {
Copia
Copiato
Copia
Copiato
require(
now
>= startTime, "Treasury: not started yet");
require(
block.timestamp
>= startTime, "Treasury: not started yet");
_;
_;
}
}
modifier checkEpoch {
modifier checkEpoch {
Copia
Copiato
Copia
Copiato
require(
now
>= nextEpochPoint(), "Treasury: not opened yet");
require(
block.timestamp
>= nextEpochPoint(), "Treasury: not opened yet");
_;
_;
epoch = epoch.add(1);
epoch = epoch.add(1);
Copia
Copiato
Copia
Copiato
epochSupplyContractionLeft = (
getTombPrice
() >
tomb
PriceCeiling) ? 0 : get
Tomb
CirculatingSupply().mul(maxSupplyContractionPercent).div(10000);
epochSupplyContractionLeft = (
getBasedPrice
() >
based
PriceCeiling) ? 0 : get
Based
CirculatingSupply().mul(maxSupplyContractionPercent).div(10000);
}
}
modifier checkOperator {
modifier checkOperator {
require(
require(
Copia
Copiato
Copia
Copiato
IBasisAsset(
tomb
).operator() == address(this) &&
IBasisAsset(
based
).operator() == address(this) &&
IBasisAsset(
t
bond).operator() == address(this) &&
IBasisAsset(
b
bond).operator() == address(this) &&
IBasisAsset(
t
share).operator() == address(this) &&
IBasisAsset(
b
share).operator() == address(this) &&
Operator(
masonry
).operator() == address(this),
Operator(
acropolis
).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
Copia
Copiato
Copia
Copiato
function
getTombPrice
() public view returns (uint256
tombPrice
) {
function
getBasedPrice
() public view returns (uint256
basedPrice
) {
try IOracle(
tomb
Oracle).consult(
tomb
, 1e18) returns (uint144 price) {
try IOracle(
based
Oracle).consult(
based
, 1e18) returns (uint144 price) {
return uint256(price);
return uint256(price);
} catch {
} catch {
Copia
Copiato
Copia
Copiato
revert("Treasury: failed to consult
TOMB
price from the oracle");
revert("Treasury: failed to consult
BASED
price from the oracle");
}
}
}
}
Copia
Copiato
Copia
Copiato
function get
Tomb
UpdatedPrice() public view returns (uint256 _
tombPrice
) {
function get
Based
UpdatedPrice() public view returns (uint256 _
basedPrice
) {
try IOracle(
tomb
Oracle).twap(
tomb
, 1e18) returns (uint144 price) {
try IOracle(
based
Oracle).twap(
based
, 1e18) returns (uint144 price) {
return uint256(price);
return uint256(price);
} catch {
} catch {
Copia
Copiato
Copia
Copiato
revert("Treasury: failed to consult
TOMB
price from the oracle");
revert("Treasury: failed to consult
BASED
price from the oracle");
}
}
}
}
// budget
// budget
function getReserve() public view returns (uint256) {
function getReserve() public view returns (uint256) {
return seigniorageSaved;
return seigniorageSaved;
}
}
Copia
Copiato
Copia
Copiato
function getBurnable
Tomb
Left() public view returns (uint256 _burnable
Tomb
Left) {
function getBurnable
Based
Left() public view returns (uint256 _burnable
Based
Left) {
uint256 _
tombPrice
=
getTombPrice
();
uint256 _
basedPrice
=
getBasedPrice
();
if (_
tombPrice
<=
tomb
PriceOne) {
if (_
basedPrice
<=
based
PriceOne) {
uint256 _
tomb
Supply = get
Tomb
CirculatingSupply();
uint256 _
based
Supply = get
Based
CirculatingSupply();
uint256 _bondMaxSupply = _
tomb
Supply.mul(maxDebtRatioPercent).div(10000);
uint256 _bondMaxSupply = _
based
Supply.mul(maxDebtRatioPercent).div(10000);
uint256 _bondSupply = IERC20(
t
bond).totalSupply();
uint256 _bondSupply = IERC20(
b
bond).totalSupply();
if (_bondMaxSupply > _bondSupply) {
if (_bondMaxSupply > _bondSupply) {
uint256 _maxMintableBond = _bondMaxSupply.sub(_bondSupply);
uint256 _maxMintableBond = _bondMaxSupply.sub(_bondSupply);
Copia
Copiato
Copia
Copiato
uint256 _maxBurnable
Tomb
= _maxMintableBond.mul(_
tombPrice
).div(1e18);
uint256 _maxBurnable
Based
= _maxMintableBond.mul(_
basedPrice
).div(1e18);
_burnable
Tomb
Left = Math.min(epochSupplyContractionLeft, _maxBurnable
Tomb
);
_burnable
Based
Left = Math.min(epochSupplyContractionLeft, _maxBurnable
Based
);
}
}
}
}
}
}
function getRedeemableBonds() public view returns (uint256 _redeemableBonds) {
function getRedeemableBonds() public view returns (uint256 _redeemableBonds) {
Copia
Copiato
Copia
Copiato
uint256 _
tombPrice
=
getTombPrice
();
uint256 _
basedPrice
=
getBasedPrice
();
if (_
tombPrice
>
tomb
PriceCeiling) {
if (_
basedPrice
>
based
PriceCeiling) {
uint256 _
totalTomb
= IERC20(
tomb
).balanceOf(address(this));
uint256 _
totalBased
= IERC20(
based
).balanceOf(address(this));
uint256 _rate = getBondPremiumRate();
uint256 _rate = getBondPremiumRate();
if (_rate > 0) {
if (_rate > 0) {
Copia
Copiato
Copia
Copiato
_redeemableBonds = _
totalTomb
.mul(1e18).div(_rate);
_redeemableBonds = _
totalBased
.mul(1e18).div(_rate);
}
}
}
}
}
}
function getBondDiscountRate() public view returns (uint256 _rate) {
function getBondDiscountRate() public view returns (uint256 _rate) {
Copia
Copiato
Copia
Copiato
uint256 _
tombPrice
=
getTombPrice
();
uint256 _
basedPrice
=
getBasedPrice
();
if (_
tombPrice
<=
tomb
PriceOne) {
if (_
basedPrice
<=
based
PriceOne) {
if (discountPercent == 0) {
if (discountPercent == 0) {
// no discount
// no discount
Copia
Copiato
Copia
Copiato
_rate =
tomb
PriceOne;
_rate =
based
PriceOne;
} else {
} else {
Copia
Copiato
Copia
Copiato
uint256 _bondAmount =
tomb
PriceOne.mul(1e18).div(_
tombPrice
); // to burn 1
TOMB
uint256 _bondAmount =
based
PriceOne.mul(1e18).div(_
basedPrice
); // to burn 1
BASED
uint256 _discountAmount = _bondAmount.sub(
tomb
PriceOne).mul(discountPercent).div(10000);
uint256 _discountAmount = _bondAmount.sub(
based
PriceOne).mul(discountPercent).div(10000);
_rate =
tomb
PriceOne.add(_discountAmount);
_rate =
based
PriceOne.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) {
Copia
Copiato
Copia
Copiato
uint256 _
tombPrice
=
getTombPrice
();
uint256 _
basedPrice
=
getBasedPrice
();
if (_
tombPrice
>
tomb
PriceCeiling) {
if (_
basedPrice
>
based
PriceCeiling) {
uint256 _
tomb
PricePremiumThreshold =
tomb
PriceOne.mul(premiumThreshold).div(100);
uint256 _
based
PricePremiumThreshold =
based
PriceOne.mul(premiumThreshold).div(100);
if (_
tombPrice
>= _
tomb
PricePremiumThreshold) {
if (_
basedPrice
>= _
based
PricePremiumThreshold) {
//Price > 1.10
//Price > 1.10
Copia
Copiato
Copia
Copiato
uint256 _premiumAmount = _
tombPrice
.sub(
tomb
PriceOne).mul(premiumPercent).div(10000);
uint256 _premiumAmount = _
basedPrice
.sub(
based
PriceOne).mul(premiumPercent).div(10000);
_rate =
tomb
PriceOne.add(_premiumAmount);
_rate =
based
PriceOne.add(_premiumAmount);
if (maxPremiumRate > 0 && _rate > maxPremiumRate) {
if (maxPremiumRate > 0 && _rate > maxPremiumRate) {
_rate = maxPremiumRate;
_rate = maxPremiumRate;
}
}
} else {
} else {
// no premium bonus
// no premium bonus
Copia
Copiato
Copia
Copiato
_rate =
tomb
PriceOne;
_rate =
based
PriceOne;
}
}
}
}
}
}
/* ========== GOVERNANCE ========== */
/* ========== GOVERNANCE ========== */
function initialize(
function initialize(
Copia
Copiato
Copia
Copiato
address _
tomb
,
address _
based
,
address _
t
bond,
address _
b
bond,
address _
t
share,
address _
b
share,
address _
tomb
Oracle,
address _
based
Oracle,
address _
masonry
,
address _
acropolis
,
uint256 _startTime
uint256 _startTime
) public notInitialized {
) public notInitialized {
Copia
Copiato
Copia
Copiato
tomb
= _
tomb
;
based
= _
based
;
t
bond = _
t
bond;
b
bond = _
b
bond;
t
share = _
t
share;
b
share = _
b
share;
tomb
Oracle = _
tomb
Oracle;
based
Oracle = _
based
Oracle;
masonry
= _
masonry
;
acropolis
= _
acropolis
;
startTime = _startTime;
startTime = _startTime;
Copia
Copiato
Copia
Copiato
tomb
PriceOne = 10**18;
based
PriceOne = 10**18;
tomb
PriceCeiling =
tomb
PriceOne.mul(101).div(100);
based
PriceCeiling =
based
PriceOne.mul(101).div(100);
// Dynamic max expansion percent
// Dynamic max expansion percent
Copia
Copiato
Copia
Copiato
supplyTiers = [0 ether,
500
000 ether,
1000000
ether,
150
0000 ether,
20
00000 ether, 5000000 ether, 10000000 ether
, 20000000 ether, 50000000 ether
];
supplyTiers = [0 ether,
206
000 ether,
386000
ether,
53
0000 ether,
13
00000 ether, 5000000 ether, 10000000 ether
];
maxExpansionTiers = [
450, 400,
350, 300, 250,
200, 1
5
0,
125, 100
];
maxExpansionTiers = [
600, 500,
450, 400,
200, 1
0
0,
50
];
Copia
Copiato
Copia
Copiato
maxSupplyExpansionPercent =
4
00; // Upto 4.0% supply for expansion
maxSupplyExpansionPercent =
6
00; // Upto 4.0% supply for expansion
bondDepletionFloorPercent = 10000; // 100% of Bond supply for depletion floor
bondDepletionFloorPercent = 10000; // 100% of Bond supply for depletion floor
Copia
Copiato
Copia
Copiato
seigniorageExpansionFloorPercent = 3500; // At least 35% of expansion reserved for
masonry
seigniorageExpansionFloorPercent = 3500; // At least 35% of expansion reserved for
acropolis
maxSupplyContractionPercent = 300; // Upto 3.0% supply for contraction (to burn
TOMB
and mint
t
BOND)
maxSupplyContractionPercent = 300; // Upto 3.0% supply for contraction (to burn
BASED
and mint
b
BOND)
maxDebtRatioPercent = 3500; // Upto 35% supply of
t
BOND to purchase
maxDebtRatioPercent = 3500; // Upto 35% supply of
b
BOND to purchase
premiumThreshold = 110;
premiumThreshold = 110;
premiumPercent = 7000;
premiumPercent = 7000;
// First 28 epochs with 4.5% expansion
// First 28 epochs with 4.5% expansion
Copia
Copiato
Copia
Copiato
bootstrapEpochs =
28
;
bootstrapEpochs =
14
;
bootstrapSupplyExpansionPercent =
450
;
bootstrapSupplyExpansionPercent =
600
;
// set seigniorageSaved to it's balance
// set seigniorageSaved to it's balance
Copia
Copiato
Copia
Copiato
seigniorageSaved = IERC20(
tomb
).balanceOf(address(this));
seigniorageSaved = IERC20(
based
).balanceOf(address(this));
initialized = true;
initialized = true;
operator = msg.sender;
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;
operator = _operator;
}
}
Copia
Copiato
Copia
Copiato
function
setMasonry
(address _
masonry
) external onlyOperator {
function
setAcropolis
(address _
acropolis
) external onlyOperator {
masonry
= _
masonry
;
acropolis
= _
acropolis
;
}
}
Copia
Copiato
Copia
Copiato
function set
Tomb
Oracle(address _
tomb
Oracle) external onlyOperator {
function set
Based
Oracle(address _
based
Oracle) external onlyOperator {
tomb
Oracle = _
tomb
Oracle;
based
Oracle = _
based
Oracle;
}
}
Copia
Copiato
Copia
Copiato
function set
Tomb
PriceCeiling(uint256 _
tomb
PriceCeiling) external onlyOperator {
function set
Based
PriceCeiling(uint256 _
based
PriceCeiling) external onlyOperator {
require(_
tomb
PriceCeiling >=
tomb
PriceOne && _
tomb
PriceCeiling <=
tomb
PriceOne.mul(120).div(100), "out of range"); // [$1.0, $1.2]
require(_
based
PriceCeiling >=
based
PriceOne && _
based
PriceCeiling <=
based
PriceOne.mul(120).div(100), "out of range"); // [$1.0, $1.2]
tomb
PriceCeiling = _
tomb
PriceCeiling;
based
PriceCeiling = _
based
PriceCeiling;
}
}
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 <= 1000, "_maxSupplyExpansionPercent: out of range"); // [0.1%, 10%]
maxSupplyExpansionPercent = _maxSupplyExpansionPercent;
maxSupplyExpansionPercent = _maxSupplyExpansionPercent;
}
}
Copia
Copiato
Copia
Copiato
// =================== ALTER THE NUMBERS IN LOGIC!!!! =================== //
function setSupplyTiersEntry(uint8 _index, uint256 _value) external onlyOperator returns (bool) {
function setSupplyTiersEntry(uint8 _index, uint256 _value) external onlyOperator returns (bool) {
require(_index >= 0, "Index has to be higher than 0");
require(_index >= 0, "Index has to be higher than 0");
Copia
Copiato
Copia
Copiato
require(_index <
9
, "Index has to be lower than count of tiers");
require(_index <
7
, "Index has to be lower than count of tiers");
if (_index > 0) {
if (_index > 0) {
require(_value > supplyTiers[_index - 1]);
require(_value > supplyTiers[_index - 1]);
}
}
Copia
Copiato
Copia
Copiato
if (_index <
8
) {
if (_index <
6
) {
require(_value < supplyTiers[_index + 1]);
require(_value < supplyTiers[_index + 1]);
}
}
supplyTiers[_index] = _value;
supplyTiers[_index] = _value;
return true;
return true;
}
}
function setMaxExpansionTiersEntry(uint8 _index, uint256 _value) external onlyOperator returns (bool) {
function setMaxExpansionTiersEntry(uint8 _index, uint256 _value) external onlyOperator returns (bool) {
require(_index >= 0, "Index has to be higher than 0");
require(_index >= 0, "Index has to be higher than 0");
Copia
Copiato
Copia
Copiato
require(_index <
9
, "Index has to be lower than count of tiers");
require(_index <
7
, "Index has to be lower than count of tiers");
require(_value >= 10 && _value <= 1000, "_value: out of range"); // [0.1%, 10%]
require(_value >= 10 && _value <= 1000, "_value: out of range"); // [0.1%, 10%]
maxExpansionTiers[_index] = _value;
maxExpansionTiers[_index] = _value;
return true;
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 <= 10000, "out of range"); // [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 <= 1500, "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 <= 10000, "out of range"); // [10%, 100%]
maxDebtRatioPercent = _maxDebtRatioPercent;
maxDebtRatioPercent = _maxDebtRatioPercent;
}
}
function setBootstrap(uint256 _bootstrapEpochs, uint256 _bootstrapSupplyExpansionPercent) external onlyOperator {
function setBootstrap(uint256 _bootstrapEpochs, uint256 _bootstrapSupplyExpansionPercent) external onlyOperator {
require(_bootstrapEpochs <= 120, "_bootstrapEpochs: out of range"); // <= 1 month
require(_bootstrapEpochs <= 120, "_bootstrapEpochs: out of range"); // <= 1 month
require(_bootstrapSupplyExpansionPercent >= 100 && _bootstrapSupplyExpansionPercent <= 1000, "_bootstrapSupplyExpansionPercent: out of range"); // [1%, 10%]
require(_bootstrapSupplyExpansionPercent >= 100 && _bootstrapSupplyExpansionPercent <= 1000, "_bootstrapSupplyExpansionPercent: out of range"); // [1%, 10%]
bootstrapEpochs = _bootstrapEpochs;
bootstrapEpochs = _bootstrapEpochs;
bootstrapSupplyExpansionPercent = _bootstrapSupplyExpansionPercent;
bootstrapSupplyExpansionPercent = _bootstrapSupplyExpansionPercent;
}
}
Copia
Copiato
Copia
Copiato
//===============================================================================================================================================
function setExtraFunds(
function setExtraFunds(
Copia
Copiato
Copia
Copiato
// DAO FUND - 12%
// DEVS WALLET - 3%
// TEAM WALLET - 5%
address _daoFund,
address _daoFund,
uint256 _daoFundSharedPercent,
uint256 _daoFundSharedPercent,
address _devFund,
address _devFund,
Copia
Copiato
Copia
Copiato
uint256 _devFundSharedPercent
uint256 _devFundSharedPercent
,
address _teamFund,
uint256 _teamFundSharedPercent
) external onlyOperator {
) external onlyOperator {
require(_daoFund != address(0), "zero");
require(_daoFund != address(0), "zero");
Copia
Copiato
Copia
Copiato
require(_daoFundSharedPercent <=
3000
, "out of range"); // <=
30
%
require(_daoFundSharedPercent <=
1500
, "out of range"); // <=
15
%
require(_devFund != address(0), "zero");
require(_devFund != address(0), "zero");
Copia
Copiato
Copia
Copiato
require(_devFundSharedPercent <=
1000
, "out of range"); // <=
10%
require(_devFundSharedPercent <=
350, "out of range"); // <= 3.5%
require(_teamFund != address(0), "zero");
require(_teamFundSharedPercent <= 550
, "out of range"); // <=
5.5%
daoFund = _daoFund;
daoFund = _daoFund;
daoFundSharedPercent = _daoFundSharedPercent;
daoFundSharedPercent = _daoFundSharedPercent;
devFund = _devFund;
devFund = _devFund;
devFundSharedPercent = _devFundSharedPercent;
devFundSharedPercent = _devFundSharedPercent;
Copia
Copiato
Copia
Copiato
teamFund = _teamFund;
teamFundSharedPercent = _teamFundSharedPercent;
}
}
function setMaxDiscountRate(uint256 _maxDiscountRate) external onlyOperator {
function setMaxDiscountRate(uint256 _maxDiscountRate) external onlyOperator {
maxDiscountRate = _maxDiscountRate;
maxDiscountRate = _maxDiscountRate;
}
}
function setMaxPremiumRate(uint256 _maxPremiumRate) external onlyOperator {
function setMaxPremiumRate(uint256 _maxPremiumRate) external onlyOperator {
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 <= 20000, "_discountPercent is over 200%");
discountPercent = _discountPercent;
discountPercent = _discountPercent;
}
}
function setPremiumThreshold(uint256 _premiumThreshold) external onlyOperator {
function setPremiumThreshold(uint256 _premiumThreshold) external onlyOperator {
Copia
Copiato
Copia
Copiato
require(_premiumThreshold >=
tomb
PriceCeiling, "_premiumThreshold exceeds
tomb
PriceCeiling");
require(_premiumThreshold >=
based
PriceCeiling, "_premiumThreshold exceeds
based
PriceCeiling");
require(_premiumThreshold <= 150, "_premiumThreshold is higher than 1.5");
require(_premiumThreshold <= 150, "_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 <= 20000, "_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 >= 10000 && _mintingFactorForPayingDebt <= 20000, "_mintingFactorForPayingDebt: out of range"); // [100%, 200%]
mintingFactorForPayingDebt = _mintingFactorForPayingDebt;
mintingFactorForPayingDebt = _mintingFactorForPayingDebt;
}
}
/* ========== MUTABLE FUNCTIONS ========== */
/* ========== MUTABLE FUNCTIONS ========== */
Copia
Copiato
Copia
Copiato
function _update
Tomb
Price() internal {
function _update
Based
Price() internal {
try IOracle(
tomb
Oracle).update() {} catch {}
try IOracle(
based
Oracle).update() {} catch {}
}
}
Copia
Copiato
Copia
Copiato
function get
Tomb
CirculatingSupply() public view returns (uint256) {
function get
Based
CirculatingSupply() public view returns (uint256) {
IERC20
tombErc20
= IERC20(
tomb
);
IERC20
basedErc20
= IERC20(
based
);
uint256 totalSupply =
tombErc20
.totalSupply();
uint256 totalSupply =
basedErc20
.totalSupply();
uint256 balanceExcluded = 0;
uint256 balanceExcluded = 0;
for (uint8 entryId = 0; entryId < excludedFromTotalSupply.length; ++entryId) {
for (uint8 entryId = 0; entryId < excludedFromTotalSupply.length; ++entryId) {
Copia
Copiato
Copia
Copiato
balanceExcluded = balanceExcluded.add(
tombErc20
.balanceOf(excludedFromTotalSupply[entryId]));
balanceExcluded = balanceExcluded.add(
basedErc20
.balanceOf(excludedFromTotalSupply[entryId]));
}
}
return totalSupply.sub(balanceExcluded);
return totalSupply.sub(balanceExcluded);
}
}
Copia
Copiato
Copia
Copiato
function buyBonds(uint256 _
tomb
Amount, uint256 targetPrice) external onlyOneBlock checkCondition checkOperator {
function buyBonds(uint256 _
based
Amount, uint256 targetPrice) external onlyOneBlock checkCondition checkOperator {
require(_
tomb
Amount > 0, "Treasury: cannot purchase bonds with zero amount");
require(_
based
Amount > 0, "Treasury: cannot purchase bonds with zero amount");
Copia
Copiato
Copia
Copiato
uint256
tombPrice
=
getTombPrice
();
uint256
basedPrice
=
getBasedPrice
();
require(
tombPrice
== targetPrice, "Treasury:
TOMB
price moved");
require(
basedPrice
== targetPrice, "Treasury:
BASED
price moved");
require(
require(
Copia
Copiato
Copia
Copiato
tombPrice
<
tomb
PriceOne, // price < $1
basedPrice
<
based
PriceOne, // price < $1
"Treasury:
tombPrice
not eligible for bond purchase"
"Treasury:
basedPrice
not eligible for bond purchase"
);
);
Copia
Copiato
Copia
Copiato
require(_
tomb
Amount <= epochSupplyContractionLeft, "Treasury: not enough bond left to purchase");
require(_
based
Amount <= 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");
Copia
Copiato
Copia
Copiato
uint256 _bondAmount = _
tomb
Amount.mul(_rate).div(1e18);
uint256 _bondAmount = _
based
Amount.mul(_rate).div(1e18);
uint256
tomb
Supply = get
Tomb
CirculatingSupply();
uint256
based
Supply = get
Based
CirculatingSupply();
uint256 newBondSupply = IERC20(
t
bond).totalSupply().add(_bondAmount);
uint256 newBondSupply = IERC20(
b
bond).totalSupply().add(_bondAmount);
require(newBondSupply <=
tomb
Supply.mul(maxDebtRatioPercent).div(10000), "over max debt ratio");
require(newBondSupply <=
based
Supply.mul(maxDebtRatioPercent).div(10000), "over max debt ratio");
Copia
Copiato
Copia
Copiato
IBasisAsset(
tomb
).burnFrom(msg.sender, _
tomb
Amount);
IBasisAsset(
based
).burnFrom(msg.sender, _
based
Amount);
IBasisAsset(
t
bond).mint(msg.sender, _bondAmount);
IBasisAsset(
b
bond).mint(msg.sender, _bondAmount);
Copia
Copiato
Copia
Copiato
epochSupplyContractionLeft = epochSupplyContractionLeft.sub(_
tomb
Amount);
epochSupplyContractionLeft = epochSupplyContractionLeft.sub(_
based
Amount);
_update
Tomb
Price();
_update
Based
Price();
Copia
Copiato
Copia
Copiato
emit BoughtBonds(msg.sender, _
tomb
Amount, _bondAmount);
emit BoughtBonds(msg.sender, _
based
Amount, _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");
Copia
Copiato
Copia
Copiato
uint256
tombPrice
=
getTombPrice
();
uint256
basedPrice
=
getBasedPrice
();
require(
tombPrice
== targetPrice, "Treasury:
TOMB
price moved");
require(
basedPrice
== targetPrice, "Treasury:
BASED
price moved");
require(
require(
Copia
Copiato
Copia
Copiato
tombPrice
>
tomb
PriceCeiling, // price > $1.01
basedPrice
>
based
PriceCeiling, // price > $1.01
"Treasury:
tombPrice
not eligible for bond purchase"
"Treasury:
basedPrice
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");
Copia
Copiato
Copia
Copiato
uint256 _
tomb
Amount = _bondAmount.mul(_rate).div(1e18);
uint256 _
based
Amount = _bondAmount.mul(_rate).div(1e18);
require(IERC20(
tomb
).balanceOf(address(this)) >= _
tomb
Amount, "Treasury: treasury has no more budget");
require(IERC20(
based
).balanceOf(address(this)) >= _
based
Amount, "Treasury: treasury has no more budget");
Copia
Copiato
Copia
Copiato
seigniorageSaved = seigniorageSaved.sub(Math.min(seigniorageSaved, _
tomb
Amount));
seigniorageSaved = seigniorageSaved.sub(Math.min(seigniorageSaved, _
based
Amount));
Copia
Copiato
Copia
Copiato
IBasisAsset(
t
bond).burnFrom(msg.sender, _bondAmount);
IBasisAsset(
b
bond).burnFrom(msg.sender, _bondAmount);
IERC20(
tomb
).safeTransfer(msg.sender, _
tomb
Amount);
IERC20(
based
).safeTransfer(msg.sender, _
based
Amount);
Copia
Copiato
Copia
Copiato
_update
Tomb
Price();
_update
Based
Price();
Copia
Copiato
Copia
Copiato
emit RedeemedBonds(msg.sender, _
tomb
Amount, _bondAmount);
emit RedeemedBonds(msg.sender, _
based
Amount, _bondAmount);
}
}
Copia
Copiato
Copia
Copiato
function _
sendToMasonry
(uint256 _amount) internal {
function _
sendToAcropolis
(uint256 _amount) internal {
IBasisAsset(
tomb
).mint(address(this), _amount);
IBasisAsset(
based
).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(10000);
Copia
Copiato
Copia
Copiato
IERC20(
tomb
).transfer(daoFund, _daoFundSharedAmount);
IERC20(
based
).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(10000);
Copia
Copiato
Copia
Copiato
IERC20(
tomb
).transfer(devFund, _devFundSharedAmount);
IERC20(
based
).transfer(devFund, _devFundSharedAmount);
emit DevFundFunded(
now
, _devFundSharedAmount);
emit DevFundFunded(
block.timestamp
, _devFundSharedAmount);
}
}
Copia
Copiato
Copia
Copiato
_amount = _amount.sub(_daoFundSharedAmount).sub(_devFundSharedAmount);
uint256 _teamFundSharedAmount = 0;
if (teamFundSharedPercent > 0) {
_teamFundSharedAmount = _amount.mul(teamFundSharedPercent).div(10000);
IERC20(based).transfer(teamFund, _teamFundSharedAmount);
emit TeamFundFunded(block.timestamp, _teamFundSharedAmount);
}
Copia
Copiato
Copia
Copiato
IERC20(
tomb
).safeApprove(
masonry
, 0);
_amount = _amount.sub(_daoFundSharedAmount).sub(_devFundSharedAmount).sub(_teamFundSharedAmount);
IERC20(
tomb
).safeApprove(
masonry
, _amount);
IMasonry(masonry
).allocateSeigniorage(_amount);
IERC20(
based
).safeApprove(
acropolis
, 0);
emit
MasonryFunded(now
, _amount);
IERC20(
based
).safeApprove(
acropolis
, _amount);
IAcropolis(acropolis
).allocateSeigniorage(_amount);
emit
AcropolisFunded(block.timestamp
, _amount);
}
}
Copia
Copiato
Copia
Copiato
function _calculateMaxSupplyExpansionPercent(uint256 _
tomb
Supply) internal returns (uint256) {
function _calculateMaxSupplyExpansionPercent(uint256 _
based
Supply) internal returns (uint256) {
for (uint8 tierId =
8
; tierId >= 0; --tierId) {
for (uint8 tierId =
6
; tierId >= 0; --tierId) {
if (_
tomb
Supply >= supplyTiers[tierId]) {
if (_
based
Supply >= supplyTiers[tierId]) {
maxSupplyExpansionPercent = maxExpansionTiers[tierId];
maxSupplyExpansionPercent = maxExpansionTiers[tierId];
break;
break;
}
}
}
}
return maxSupplyExpansionPercent;
return maxSupplyExpansionPercent;
}
}
function allocateSeigniorage() external onlyOneBlock checkCondition checkEpoch checkOperator {
function allocateSeigniorage() external onlyOneBlock checkCondition checkEpoch checkOperator {
Copia
Copiato
Copia
Copiato
_update
Tomb
Price();
_update
Based
Price();
previousEpoch
Tomb
Price =
getTombPrice
();
previousEpoch
Based
Price =
getBasedPrice
();
uint256
tomb
Supply = get
Tomb
CirculatingSupply().sub(seigniorageSaved);
uint256
based
Supply = get
Based
CirculatingSupply().sub(seigniorageSaved);
if (epoch < bootstrapEpochs) {
if (epoch < bootstrapEpochs) {
Copia
Copiato
Copia
Copiato
//
28
first epochs with
4.5
% expansion
//
14
first epochs with
6
% expansion
_
sendToMasonry(tombSupply
.mul(bootstrapSupplyExpansionPercent).div(10000));
_
sendToAcropolis(basedSupply
.mul(bootstrapSupplyExpansionPercent).div(10000));
} else {
} else {
Copia
Copiato
Copia
Copiato
if (previousEpoch
Tomb
Price >
tomb
PriceCeiling) {
if (previousEpoch
Based
Price >
based
PriceCeiling) {
// Expansion ($
TOMB
Price > 1 $FTM): there is some seigniorage to be allocated
// Expansion ($
BASED
Price > 1 $FTM): there is some seigniorage to be allocated
uint256 bondSupply = IERC20(
t
bond).totalSupply();
uint256 bondSupply = IERC20(
b
bond).totalSupply();
uint256 _percentage = previousEpoch
Tomb
Price.sub(
tomb
PriceOne);
uint256 _percentage = previousEpoch
Based
Price.sub(
based
PriceOne);
uint256 _savedForBond;
uint256 _savedForBond;
Copia
Copiato
Copia
Copiato
uint256 _
savedForMasonry
;
uint256 _
savedForAcropolis
;
uint256 _mse = _calculateMaxSupplyExpansionPercent(
tomb
Supply).mul(1e14);
uint256 _mse = _calculateMaxSupplyExpansionPercent(
based
Supply).mul(1e14);
if (_percentage > _mse) {
if (_percentage > _mse) {
_percentage = _mse;
_percentage = _mse;
}
}
if (seigniorageSaved >= bondSupply.mul(bondDepletionFloorPercent).div(10000)) {
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
Copia
Copiato
Copia
Copiato
_
savedForMasonry
=
tomb
Supply.mul(_percentage).div(1e18);
_
savedForAcropolis
=
based
Supply.mul(_percentage).div(1e18);
} else {
} else {
// have not saved enough to pay debt, mint more
// have not saved enough to pay debt, mint more
Copia
Copiato
Copia
Copiato
uint256 _seigniorage =
tomb
Supply.mul(_percentage).div(1e18);
uint256 _seigniorage =
based
Supply.mul(_percentage).div(1e18);
_
savedForMasonry
= _seigniorage.mul(seigniorageExpansionFloorPercent).div(10000);
_
savedForAcropolis
= _seigniorage.mul(seigniorageExpansionFloorPercent).div(10000);
_savedForBond = _seigniorage.sub(_
savedForMasonry
);
_savedForBond = _seigniorage.sub(_
savedForAcropolis
);
if (mintingFactorForPayingDebt > 0) {
if (mintingFactorForPayingDebt > 0) {
_savedForBond = _savedForBond.mul(mintingFactorForPayingDebt).div(10000);
_savedForBond = _savedForBond.mul(mintingFactorForPayingDebt).div(10000);
}
}
}
}
Copia
Copiato
Copia
Copiato
if (_
savedForMasonry
> 0) {
if (_
savedForAcropolis
> 0) {
_
sendToMasonry(_savedForMasonry
);
_
sendToAcropolis(_savedForAcropolis
);
}
}
if (_savedForBond > 0) {
if (_savedForBond > 0) {
seigniorageSaved = seigniorageSaved.add(_savedForBond);
seigniorageSaved = seigniorageSaved.add(_savedForBond);
Copia
Copiato
Copia
Copiato
IBasisAsset(
tomb
).mint(address(this), _savedForBond);
IBasisAsset(
based
).mint(address(this), _savedForBond);
emit TreasuryFunded(
now
, _savedForBond);
emit TreasuryFunded(
block.timestamp
, _savedForBond);
}
}
}
}
}
}
}
}
Copia
Copiato
Copia
Copiato
//===================================================================================================================================
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
Copia
Copiato
Copia
Copiato
require(address(_token) != address(
tomb
), "
tomb
");
require(address(_token) != address(
based
), "
based
");
require(address(_token) != address(
t
bond), "bond");
require(address(_token) != address(
b
bond), "bond");
require(address(_token) != address(
t
share), "share");
require(address(_token) != address(
b
share), "share");
_token.safeTransfer(_to, _amount);
_token.safeTransfer(_to, _amount);
}
}
Copia
Copiato
Copia
Copiato
function
masonry
SetOperator(address _operator) external onlyOperator {
function
acropolis
SetOperator(address _operator) external onlyOperator {
IMasonry(masonry
).setOperator(_operator);
IAcropolis(acropolis
).setOperator(_operator);
}
}
Copia
Copiato
Copia
Copiato
function
masonrySetLockUp
(uint256 _withdrawLockupEpochs, uint256 _rewardLockupEpochs) external onlyOperator {
function
acropolisSetLockUp
(uint256 _withdrawLockupEpochs, uint256 _rewardLockupEpochs) external onlyOperator {
IMasonry(masonry
).setLockUp(_withdrawLockupEpochs, _rewardLockupEpochs);
IAcropolis(acropolis
).setLockUp(_withdrawLockupEpochs, _rewardLockupEpochs);
}
}
Copia
Copiato
Copia
Copiato
function
masonry
AllocateSeigniorage(uint256 amount) external onlyOperator {
function
acropolis
AllocateSeigniorage(uint256 amount) external onlyOperator {
IMasonry(masonry
).allocateSeigniorage(amount);
IAcropolis(acropolis
).allocateSeigniorage(amount);
}
}
Copia
Copiato
Copia
Copiato
function
masonry
GovernanceRecoverUnsupported(
function
acropolis
GovernanceRecoverUnsupported(
address _token,
address _token,
uint256 _amount,
uint256 _amount,
address _to
address _to
) external onlyOperator {
) external onlyOperator {
Copia
Copiato
Copia
Copiato
IMasonry(masonry
).governanceRecoverUnsupported(_token, _amount, _to);
IAcropolis(acropolis
).governanceRecoverUnsupported(_token, _amount, _to);
}
}
}
}
Diff salvati
Testo originale
Apri file
// 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); } 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); } } } } 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); } }
Testo modificato
Apri file
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/utils/math/Math.sol"; import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "./lib/Babylonian.sol"; import "./owner/Operator.sol"; import "./utils/ContractGuard.sol"; import "./interfaces/IBasisAsset.sol"; import "./interfaces/IOracle.sol"; import "./interfaces/IAcropolis.sol"; /* __________ .___ ___________.__ \______ \_____ ______ ____ __| _/ \_ _____/|__| ____ _____ ____ ____ ____ | | _/\__ \ / ___/_/ __ \ / __ | | __) | | / \ \__ \ / \ _/ ___\_/ __ \ | | \ / __ \_ \___ \ \ ___/ / /_/ | | \ | || | \ / __ \_| | \\ \___\ ___/ |______ /(____ //____ > \___ >\____ | \___ / |__||___| /(____ /|___| / \___ >\___ > \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ */ 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(0x9Ec66B9409d4cD8D4a4C90950Ff0fd26bB39ad84) // BasedGenesisPool ]; // core components address public based; address public bbond; address public bshare; address public acropolis; address public basedOracle; // price uint256 public basedPriceOne; uint256 public basedPriceCeiling; uint256 public seigniorageSaved; uint256[] public supplyTiers; uint256[] public maxExpansionTiers; uint256 public maxSupplyExpansionPercent; uint256 public bondDepletionFloorPercent; uint256 public seigniorageExpansionFloorPercent; uint256 public maxSupplyContractionPercent; uint256 public maxDebtRatioPercent; // 14 first epochs (0.5 week) with 4.5% expansion regardless of BASED price uint256 public bootstrapEpochs; uint256 public bootstrapSupplyExpansionPercent; /* =================== Added variables =================== */ uint256 public previousEpochBasedPrice; 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 BASED during debt phase address public daoFund; uint256 public daoFundSharedPercent; //=================================================// address public devFund; uint256 public devFundSharedPercent; address public teamFund; uint256 public teamFundSharedPercent; /* =================== Events =================== */ event Initialized(address indexed executor, uint256 at); event BurnedBonds(address indexed from, uint256 bondAmount); event RedeemedBonds(address indexed from, uint256 basedAmount, uint256 bondAmount); event BoughtBonds(address indexed from, uint256 basedAmount, uint256 bondAmount); event TreasuryFunded(uint256 timestamp, uint256 seigniorage); event AcropolisFunded(uint256 timestamp, uint256 seigniorage); event DaoFundFunded(uint256 timestamp, uint256 seigniorage); event DevFundFunded(uint256 timestamp, uint256 seigniorage); event TeamFundFunded(uint256 timestamp, uint256 seigniorage); /* =================== Modifier =================== */ modifier onlyOperator() { require(operator == msg.sender, "Treasury: caller is not the operator"); _; } modifier checkCondition { require(block.timestamp >= startTime, "Treasury: not started yet"); _; } modifier checkEpoch { require(block.timestamp >= nextEpochPoint(), "Treasury: not opened yet"); _; epoch = epoch.add(1); epochSupplyContractionLeft = (getBasedPrice() > basedPriceCeiling) ? 0 : getBasedCirculatingSupply().mul(maxSupplyContractionPercent).div(10000); } modifier checkOperator { require( IBasisAsset(based).operator() == address(this) && IBasisAsset(bbond).operator() == address(this) && IBasisAsset(bshare).operator() == address(this) && Operator(acropolis).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 getBasedPrice() public view returns (uint256 basedPrice) { try IOracle(basedOracle).consult(based, 1e18) returns (uint144 price) { return uint256(price); } catch { revert("Treasury: failed to consult BASED price from the oracle"); } } function getBasedUpdatedPrice() public view returns (uint256 _basedPrice) { try IOracle(basedOracle).twap(based, 1e18) returns (uint144 price) { return uint256(price); } catch { revert("Treasury: failed to consult BASED price from the oracle"); } } // budget function getReserve() public view returns (uint256) { return seigniorageSaved; } function getBurnableBasedLeft() public view returns (uint256 _burnableBasedLeft) { uint256 _basedPrice = getBasedPrice(); if (_basedPrice <= basedPriceOne) { uint256 _basedSupply = getBasedCirculatingSupply(); uint256 _bondMaxSupply = _basedSupply.mul(maxDebtRatioPercent).div(10000); uint256 _bondSupply = IERC20(bbond).totalSupply(); if (_bondMaxSupply > _bondSupply) { uint256 _maxMintableBond = _bondMaxSupply.sub(_bondSupply); uint256 _maxBurnableBased = _maxMintableBond.mul(_basedPrice).div(1e18); _burnableBasedLeft = Math.min(epochSupplyContractionLeft, _maxBurnableBased); } } } function getRedeemableBonds() public view returns (uint256 _redeemableBonds) { uint256 _basedPrice = getBasedPrice(); if (_basedPrice > basedPriceCeiling) { uint256 _totalBased = IERC20(based).balanceOf(address(this)); uint256 _rate = getBondPremiumRate(); if (_rate > 0) { _redeemableBonds = _totalBased.mul(1e18).div(_rate); } } } function getBondDiscountRate() public view returns (uint256 _rate) { uint256 _basedPrice = getBasedPrice(); if (_basedPrice <= basedPriceOne) { if (discountPercent == 0) { // no discount _rate = basedPriceOne; } else { uint256 _bondAmount = basedPriceOne.mul(1e18).div(_basedPrice); // to burn 1 BASED uint256 _discountAmount = _bondAmount.sub(basedPriceOne).mul(discountPercent).div(10000); _rate = basedPriceOne.add(_discountAmount); if (maxDiscountRate > 0 && _rate > maxDiscountRate) { _rate = maxDiscountRate; } } } } function getBondPremiumRate() public view returns (uint256 _rate) { uint256 _basedPrice = getBasedPrice(); if (_basedPrice > basedPriceCeiling) { uint256 _basedPricePremiumThreshold = basedPriceOne.mul(premiumThreshold).div(100); if (_basedPrice >= _basedPricePremiumThreshold) { //Price > 1.10 uint256 _premiumAmount = _basedPrice.sub(basedPriceOne).mul(premiumPercent).div(10000); _rate = basedPriceOne.add(_premiumAmount); if (maxPremiumRate > 0 && _rate > maxPremiumRate) { _rate = maxPremiumRate; } } else { // no premium bonus _rate = basedPriceOne; } } } /* ========== GOVERNANCE ========== */ function initialize( address _based, address _bbond, address _bshare, address _basedOracle, address _acropolis, uint256 _startTime ) public notInitialized { based = _based; bbond = _bbond; bshare = _bshare; basedOracle = _basedOracle; acropolis = _acropolis; startTime = _startTime; basedPriceOne = 10**18; basedPriceCeiling = basedPriceOne.mul(101).div(100); // Dynamic max expansion percent supplyTiers = [0 ether, 206000 ether, 386000 ether, 530000 ether, 1300000 ether, 5000000 ether, 10000000 ether]; maxExpansionTiers = [600, 500, 450, 400, 200, 100, 50]; maxSupplyExpansionPercent = 600; // Upto 4.0% supply for expansion bondDepletionFloorPercent = 10000; // 100% of Bond supply for depletion floor seigniorageExpansionFloorPercent = 3500; // At least 35% of expansion reserved for acropolis maxSupplyContractionPercent = 300; // Upto 3.0% supply for contraction (to burn BASED and mint bBOND) maxDebtRatioPercent = 3500; // Upto 35% supply of bBOND to purchase premiumThreshold = 110; premiumPercent = 7000; // First 28 epochs with 4.5% expansion bootstrapEpochs = 14; bootstrapSupplyExpansionPercent = 600; // set seigniorageSaved to it's balance seigniorageSaved = IERC20(based).balanceOf(address(this)); initialized = true; operator = msg.sender; emit Initialized(msg.sender, block.number); } function setOperator(address _operator) external onlyOperator { operator = _operator; } function setAcropolis(address _acropolis) external onlyOperator { acropolis = _acropolis; } function setBasedOracle(address _basedOracle) external onlyOperator { basedOracle = _basedOracle; } function setBasedPriceCeiling(uint256 _basedPriceCeiling) external onlyOperator { require(_basedPriceCeiling >= basedPriceOne && _basedPriceCeiling <= basedPriceOne.mul(120).div(100), "out of range"); // [$1.0, $1.2] basedPriceCeiling = _basedPriceCeiling; } function setMaxSupplyExpansionPercents(uint256 _maxSupplyExpansionPercent) external onlyOperator { require(_maxSupplyExpansionPercent >= 10 && _maxSupplyExpansionPercent <= 1000, "_maxSupplyExpansionPercent: out of range"); // [0.1%, 10%] maxSupplyExpansionPercent = _maxSupplyExpansionPercent; } // =================== ALTER THE NUMBERS IN LOGIC!!!! =================== // function setSupplyTiersEntry(uint8 _index, uint256 _value) external onlyOperator returns (bool) { require(_index >= 0, "Index has to be higher than 0"); require(_index <7, "Index has to be lower than count of tiers"); if (_index > 0) { require(_value > supplyTiers[_index - 1]); } if (_index < 6) { 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 < 7, "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( // DAO FUND - 12% // DEVS WALLET - 3% // TEAM WALLET - 5% address _daoFund, uint256 _daoFundSharedPercent, address _devFund, uint256 _devFundSharedPercent, address _teamFund, uint256 _teamFundSharedPercent ) external onlyOperator { require(_daoFund != address(0), "zero"); require(_daoFundSharedPercent <= 1500, "out of range"); // <= 15% require(_devFund != address(0), "zero"); require(_devFundSharedPercent <= 350, "out of range"); // <= 3.5% require(_teamFund != address(0), "zero"); require(_teamFundSharedPercent <= 550, "out of range"); // <= 5.5% daoFund = _daoFund; daoFundSharedPercent = _daoFundSharedPercent; devFund = _devFund; devFundSharedPercent = _devFundSharedPercent; teamFund = _teamFund; teamFundSharedPercent = _teamFundSharedPercent; } 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 >= basedPriceCeiling, "_premiumThreshold exceeds basedPriceCeiling"); 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 _updateBasedPrice() internal { try IOracle(basedOracle).update() {} catch {} } function getBasedCirculatingSupply() public view returns (uint256) { IERC20 basedErc20 = IERC20(based); uint256 totalSupply = basedErc20.totalSupply(); uint256 balanceExcluded = 0; for (uint8 entryId = 0; entryId < excludedFromTotalSupply.length; ++entryId) { balanceExcluded = balanceExcluded.add(basedErc20.balanceOf(excludedFromTotalSupply[entryId])); } return totalSupply.sub(balanceExcluded); } function buyBonds(uint256 _basedAmount, uint256 targetPrice) external onlyOneBlock checkCondition checkOperator { require(_basedAmount > 0, "Treasury: cannot purchase bonds with zero amount"); uint256 basedPrice = getBasedPrice(); require(basedPrice == targetPrice, "Treasury: BASED price moved"); require( basedPrice < basedPriceOne, // price < $1 "Treasury: basedPrice not eligible for bond purchase" ); require(_basedAmount <= epochSupplyContractionLeft, "Treasury: not enough bond left to purchase"); uint256 _rate = getBondDiscountRate(); require(_rate > 0, "Treasury: invalid bond rate"); uint256 _bondAmount = _basedAmount.mul(_rate).div(1e18); uint256 basedSupply = getBasedCirculatingSupply(); uint256 newBondSupply = IERC20(bbond).totalSupply().add(_bondAmount); require(newBondSupply <= basedSupply.mul(maxDebtRatioPercent).div(10000), "over max debt ratio"); IBasisAsset(based).burnFrom(msg.sender, _basedAmount); IBasisAsset(bbond).mint(msg.sender, _bondAmount); epochSupplyContractionLeft = epochSupplyContractionLeft.sub(_basedAmount); _updateBasedPrice(); emit BoughtBonds(msg.sender, _basedAmount, _bondAmount); } function redeemBonds(uint256 _bondAmount, uint256 targetPrice) external onlyOneBlock checkCondition checkOperator { require(_bondAmount > 0, "Treasury: cannot redeem bonds with zero amount"); uint256 basedPrice = getBasedPrice(); require(basedPrice == targetPrice, "Treasury: BASED price moved"); require( basedPrice > basedPriceCeiling, // price > $1.01 "Treasury: basedPrice not eligible for bond purchase" ); uint256 _rate = getBondPremiumRate(); require(_rate > 0, "Treasury: invalid bond rate"); uint256 _basedAmount = _bondAmount.mul(_rate).div(1e18); require(IERC20(based).balanceOf(address(this)) >= _basedAmount, "Treasury: treasury has no more budget"); seigniorageSaved = seigniorageSaved.sub(Math.min(seigniorageSaved, _basedAmount)); IBasisAsset(bbond).burnFrom(msg.sender, _bondAmount); IERC20(based).safeTransfer(msg.sender, _basedAmount); _updateBasedPrice(); emit RedeemedBonds(msg.sender, _basedAmount, _bondAmount); } function _sendToAcropolis(uint256 _amount) internal { IBasisAsset(based).mint(address(this), _amount); uint256 _daoFundSharedAmount = 0; if (daoFundSharedPercent > 0) { _daoFundSharedAmount = _amount.mul(daoFundSharedPercent).div(10000); IERC20(based).transfer(daoFund, _daoFundSharedAmount); emit DaoFundFunded(block.timestamp, _daoFundSharedAmount); } uint256 _devFundSharedAmount = 0; if (devFundSharedPercent > 0) { _devFundSharedAmount = _amount.mul(devFundSharedPercent).div(10000); IERC20(based).transfer(devFund, _devFundSharedAmount); emit DevFundFunded(block.timestamp, _devFundSharedAmount); } uint256 _teamFundSharedAmount = 0; if (teamFundSharedPercent > 0) { _teamFundSharedAmount = _amount.mul(teamFundSharedPercent).div(10000); IERC20(based).transfer(teamFund, _teamFundSharedAmount); emit TeamFundFunded(block.timestamp, _teamFundSharedAmount); } _amount = _amount.sub(_daoFundSharedAmount).sub(_devFundSharedAmount).sub(_teamFundSharedAmount); IERC20(based).safeApprove(acropolis, 0); IERC20(based).safeApprove(acropolis, _amount); IAcropolis(acropolis).allocateSeigniorage(_amount); emit AcropolisFunded(block.timestamp, _amount); } function _calculateMaxSupplyExpansionPercent(uint256 _basedSupply) internal returns (uint256) { for (uint8 tierId = 6; tierId >= 0; --tierId) { if (_basedSupply >= supplyTiers[tierId]) { maxSupplyExpansionPercent = maxExpansionTiers[tierId]; break; } } return maxSupplyExpansionPercent; } function allocateSeigniorage() external onlyOneBlock checkCondition checkEpoch checkOperator { _updateBasedPrice(); previousEpochBasedPrice = getBasedPrice(); uint256 basedSupply = getBasedCirculatingSupply().sub(seigniorageSaved); if (epoch < bootstrapEpochs) { // 14 first epochs with 6% expansion _sendToAcropolis(basedSupply.mul(bootstrapSupplyExpansionPercent).div(10000)); } else { if (previousEpochBasedPrice > basedPriceCeiling) { // Expansion ($BASED Price > 1 $FTM): there is some seigniorage to be allocated uint256 bondSupply = IERC20(bbond).totalSupply(); uint256 _percentage = previousEpochBasedPrice.sub(basedPriceOne); uint256 _savedForBond; uint256 _savedForAcropolis; uint256 _mse = _calculateMaxSupplyExpansionPercent(basedSupply).mul(1e14); if (_percentage > _mse) { _percentage = _mse; } if (seigniorageSaved >= bondSupply.mul(bondDepletionFloorPercent).div(10000)) { // saved enough to pay debt, mint as usual rate _savedForAcropolis = basedSupply.mul(_percentage).div(1e18); } else { // have not saved enough to pay debt, mint more uint256 _seigniorage = basedSupply.mul(_percentage).div(1e18); _savedForAcropolis = _seigniorage.mul(seigniorageExpansionFloorPercent).div(10000); _savedForBond = _seigniorage.sub(_savedForAcropolis); if (mintingFactorForPayingDebt > 0) { _savedForBond = _savedForBond.mul(mintingFactorForPayingDebt).div(10000); } } if (_savedForAcropolis > 0) { _sendToAcropolis(_savedForAcropolis); } if (_savedForBond > 0) { seigniorageSaved = seigniorageSaved.add(_savedForBond); IBasisAsset(based).mint(address(this), _savedForBond); emit TreasuryFunded(block.timestamp, _savedForBond); } } } } //=================================================================================================================================== function governanceRecoverUnsupported( IERC20 _token, uint256 _amount, address _to ) external onlyOperator { // do not allow to drain core tokens require(address(_token) != address(based), "based"); require(address(_token) != address(bbond), "bond"); require(address(_token) != address(bshare), "share"); _token.safeTransfer(_to, _amount); } function acropolisSetOperator(address _operator) external onlyOperator { IAcropolis(acropolis).setOperator(_operator); } function acropolisSetLockUp(uint256 _withdrawLockupEpochs, uint256 _rewardLockupEpochs) external onlyOperator { IAcropolis(acropolis).setLockUp(_withdrawLockupEpochs, _rewardLockupEpochs); } function acropolisAllocateSeigniorage(uint256 amount) external onlyOperator { IAcropolis(acropolis).allocateSeigniorage(amount); } function acropolisGovernanceRecoverUnsupported( address _token, uint256 _amount, address _to ) external onlyOperator { IAcropolis(acropolis).governanceRecoverUnsupported(_token, _amount, _to); } }
Trovare la differenza