Diff
checker
文本
文本
圖像
文檔
Excel
文件夾
Legal
Enterprise
桌面版
定價
登入
下載 Diffchecker 桌面版
比較文本
尋找兩個文字檔案之間的差異
工具
歷史
即時編輯器
摺疊未變更行
關閉換行
檢視
拆分
統一
比對精度
智能
單詞
字符
語法突出顯示
選擇語法
忽略
文字轉換
前往第一個差異
編輯輸入
Diffchecker Desktop
執行Diffchecker最安全的方式。取得Diffchecker桌面應用程式:您的差異永遠不會離開您的電腦!
取得桌面版
TombBasedTreasury
建立於
4 年前
差異永不過期
清除
匯出
分享
解釋
271 刪除
行
總計
刪除
字符
總計
刪除
要繼續使用此功能,請升級到
Diff
checker
Pro
查看價格
576 行
全部複製
300 新增
行
總計
新增
字符
總計
新增
要繼續使用此功能,請升級到
Diff
checker
Pro
查看價格
597 行
全部複製
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
複製
已複製
複製
已複製
pragma solidity
0.6.12
;
pragma solidity
^0.8.0
;
複製
已複製
複製
已複製
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";
複製
已複製
複製
已複製
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";
複製
已複製
複製
已複製
import "./interfaces/
IMasonry
.sol";
import "./interfaces/
IAcropolis
.sol";
/*
/*
複製
已複製
複製
已複製
______
__
_______
____
______
.___ ____
_______
.__
/
_
__/
___ ____
___ / /
_
/
____
(_)
___ ____
____
_
____
____
\______ \____
_
___
___ ____
__| _/ \
_
____
_/|__| _
___ ____
_
____
____
____
/
/
/ __ \
/ __
`__ \/
__
\ /
/_ /
/ __ \
/ __
`/
__ \
/
___/
_ \
| | _/\__ \
/
___/_
/ __ \
/ __
| | __) | | / \ \
__
\ /
\ _/ ___\_
/ __ \
/ /
/ /_/
/ / / / / / /_/ /
/ __
/ / / / / / /_/ / / / /
/__
/ __/
| | \
/ __
\_ \_
__ \
\
___/
/ /_/
| | \ | || | \
/ __
\_| | \\ \___\ ___/
/_/
\____
/_/ /_/ /_/_.
___
/
/
_/
/_/_/ /_/\__,_/_/ /_/
\___
/
\___
/
|______ /(____ /
/__
__ > \___ >
\____
| \
___
/
|__||___| /(____ /|___| /
\___
>
\___
>
\/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/
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;
複製
已複製
複製
已複製
//
exclusions from total supply
//
=================================================================//
exclusions from total supply
address[] public excludedFromTotalSupply = [
address[] public excludedFromTotalSupply = [
複製
已複製
複製
已複製
address(
0x9A896d3c54D7e45B558BD5fFf26bF1E8C031F93b),
//
Tomb
GenesisPool
address(
0x9Ec66B9409d4cD8D4a4C90950Ff0fd26bB39ad84)
//
Based
GenesisPool
address(0xa7b9123f4b15fE0fF01F469ff5Eab2b41296dC0E), // new TombRewardPool
address(0xA7B16703470055881e7EE093e9b0bF537f29CD4d) // old TombRewardPool
];
];
// core components
// core components
複製
已複製
複製
已複製
address public
tomb
;
address public
based
;
address public
t
bond;
address public
b
bond;
address public
t
share;
address public
b
share;
複製
已複製
複製
已複製
address public
masonry
;
address public
acropolis
;
address public
tomb
Oracle;
address public
based
Oracle;
// price
// price
複製
已複製
複製
已複製
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;
複製
已複製
複製
已複製
//
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 =================== */
複製
已複製
複製
已複製
uint256 public previousEpoch
Tomb
Price;
uint256 public previousEpoch
Based
Price;
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
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;
複製
已複製
複製
已複製
//=================================================//
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
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);
複製
已複製
複製
已複製
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);
複製
已複製
複製
已複製
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 {
複製
已複製
複製
已複製
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
() >
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(
複製
已複製
複製
已複製
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
複製
已複製
複製
已複製
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 {
複製
已複製
複製
已複製
revert("Treasury: failed to consult
TOMB
price from the oracle");
revert("Treasury: failed to consult
BASED
price from the oracle");
}
}
}
}
複製
已複製
複製
已複製
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 {
複製
已複製
複製
已複製
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;
}
}
複製
已複製
複製
已複製
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);
複製
已複製
複製
已複製
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) {
複製
已複製
複製
已複製
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) {
複製
已複製
複製
已複製
_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) {
複製
已複製
複製
已複製
uint256 _
tombPrice
=
getTombPrice
();
uint256 _
basedPrice
=
getBasedPrice
();
if (_
tombPrice
<=
tomb
PriceOne) {
if (_
basedPrice
<=
based
PriceOne) {
if (discountPercent == 0) {
if (discountPercent == 0) {
// no discount
// no discount
複製
已複製
複製
已複製
_rate =
tomb
PriceOne;
_rate =
based
PriceOne;
} else {
} else {
複製
已複製
複製
已複製
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) {
複製
已複製
複製
已複製
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
複製
已複製
複製
已複製
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
複製
已複製
複製
已複製
_rate =
tomb
PriceOne;
_rate =
based
PriceOne;
}
}
}
}
}
}
/* ========== GOVERNANCE ========== */
/* ========== GOVERNANCE ========== */
function initialize(
function initialize(
複製
已複製
複製
已複製
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 {
複製
已複製
複製
已複製
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;
複製
已複製
複製
已複製
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
複製
已複製
複製
已複製
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
];
複製
已複製
複製
已複製
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
複製
已複製
複製
已複製
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
複製
已複製
複製
已複製
bootstrapEpochs =
28
;
bootstrapEpochs =
14
;
bootstrapSupplyExpansionPercent =
450
;
bootstrapSupplyExpansionPercent =
600
;
// set seigniorageSaved to it's balance
// set seigniorageSaved to it's balance
複製
已複製
複製
已複製
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;
}
}
複製
已複製
複製
已複製
function
setMasonry
(address _
masonry
) external onlyOperator {
function
setAcropolis
(address _
acropolis
) external onlyOperator {
masonry
= _
masonry
;
acropolis
= _
acropolis
;
}
}
複製
已複製
複製
已複製
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;
}
}
複製
已複製
複製
已複製
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;
}
}
複製
已複製
複製
已複製
// =================== 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");
複製
已複製
複製
已複製
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]);
}
}
複製
已複製
複製
已複製
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");
複製
已複製
複製
已複製
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;
}
}
複製
已複製
複製
已複製
//===============================================================================================================================================
function setExtraFunds(
function setExtraFunds(
複製
已複製
複製
已複製
// DAO FUND - 12%
// DEVS WALLET - 3%
// TEAM WALLET - 5%
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 <=
1500
, "out of range"); // <=
15
%
require(_devFund != address(0), "zero");
require(_devFund != address(0), "zero");
複製
已複製
複製
已複製
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;
複製
已複製
複製
已複製
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 {
複製
已複製
複製
已複製
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 ========== */
複製
已複製
複製
已複製
function _update
Tomb
Price() internal {
function _update
Based
Price() internal {
try IOracle(
tomb
Oracle).update() {} catch {}
try IOracle(
based
Oracle).update() {} catch {}
}
}
複製
已複製
複製
已複製
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) {
複製
已複製
複製
已複製
balanceExcluded = balanceExcluded.add(
tombErc20
.balanceOf(excludedFromTotalSupply[entryId]));
balanceExcluded = balanceExcluded.add(
basedErc20
.balanceOf(excludedFromTotalSupply[entryId]));
}
}
return totalSupply.sub(balanceExcluded);
return totalSupply.sub(balanceExcluded);
}
}
複製
已複製
複製
已複製
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");
複製
已複製
複製
已複製
uint256
tombPrice
=
getTombPrice
();
uint256
basedPrice
=
getBasedPrice
();
require(
tombPrice
== targetPrice, "Treasury:
TOMB
price moved");
require(
basedPrice
== targetPrice, "Treasury:
BASED
price moved");
require(
require(
複製
已複製
複製
已複製
tombPrice
<
tomb
PriceOne, // price < $1
basedPrice
<
based
PriceOne, // price < $1
"Treasury:
tombPrice
not eligible for bond purchase"
"Treasury:
basedPrice
not eligible for bond purchase"
);
);
複製
已複製
複製
已複製
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");
複製
已複製
複製
已複製
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");
複製
已複製
複製
已複製
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);
複製
已複製
複製
已複製
epochSupplyContractionLeft = epochSupplyContractionLeft.sub(_
tomb
Amount);
epochSupplyContractionLeft = epochSupplyContractionLeft.sub(_
based
Amount);
_update
Tomb
Price();
_update
Based
Price();
複製
已複製
複製
已複製
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");
複製
已複製
複製
已複製
uint256
tombPrice
=
getTombPrice
();
uint256
basedPrice
=
getBasedPrice
();
require(
tombPrice
== targetPrice, "Treasury:
TOMB
price moved");
require(
basedPrice
== targetPrice, "Treasury:
BASED
price moved");
require(
require(
複製
已複製
複製
已複製
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");
複製
已複製
複製
已複製
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");
複製
已複製
複製
已複製
seigniorageSaved = seigniorageSaved.sub(Math.min(seigniorageSaved, _
tomb
Amount));
seigniorageSaved = seigniorageSaved.sub(Math.min(seigniorageSaved, _
based
Amount));
複製
已複製
複製
已複製
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);
複製
已複製
複製
已複製
_update
Tomb
Price();
_update
Based
Price();
複製
已複製
複製
已複製
emit RedeemedBonds(msg.sender, _
tomb
Amount, _bondAmount);
emit RedeemedBonds(msg.sender, _
based
Amount, _bondAmount);
}
}
複製
已複製
複製
已複製
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);
複製
已複製
複製
已複製
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);
複製
已複製
複製
已複製
IERC20(
tomb
).transfer(devFund, _devFundSharedAmount);
IERC20(
based
).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(10000);
IERC20(based).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);
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);
}
}
複製
已複製
複製
已複製
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 {
複製
已複製
複製
已複製
_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) {
複製
已複製
複製
已複製
//
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 {
複製
已複製
複製
已複製
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;
複製
已複製
複製
已複製
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
複製
已複製
複製
已複製
_
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
複製
已複製
複製
已複製
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);
}
}
}
}
複製
已複製
複製
已複製
if (_
savedForMasonry
> 0) {
if (_
savedForAcropolis
> 0) {
_
sendToMasonry(_savedForMasonry
);
_
sendToAcropolis(_savedForAcropolis
);
}
}
if (_savedForBond > 0) {
if (_savedForBond > 0) {
seigniorageSaved = seigniorageSaved.add(_savedForBond);
seigniorageSaved = seigniorageSaved.add(_savedForBond);
複製
已複製
複製
已複製
IBasisAsset(
tomb
).mint(address(this), _savedForBond);
IBasisAsset(
based
).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(
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);
}
}
複製
已複製
複製
已複製
function
masonry
SetOperator(address _operator) external onlyOperator {
function
acropolis
SetOperator(address _operator) external onlyOperator {
IMasonry(masonry
).setOperator(_operator);
IAcropolis(acropolis
).setOperator(_operator);
}
}
複製
已複製
複製
已複製
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);
}
}
複製
已複製
複製
已複製
function
masonry
AllocateSeigniorage(uint256 amount) external onlyOperator {
function
acropolis
AllocateSeigniorage(uint256 amount) external onlyOperator {
IMasonry(masonry
).allocateSeigniorage(amount);
IAcropolis(acropolis
).allocateSeigniorage(amount);
}
}
複製
已複製
複製
已複製
function
masonry
GovernanceRecoverUnsupported(
function
acropolis
GovernanceRecoverUnsupported(
address _token,
address _token,
uint256 _amount,
uint256 _amount,
address _to
address _to
) external onlyOperator {
) external onlyOperator {
複製
已複製
複製
已複製
IMasonry(masonry
).governanceRecoverUnsupported(_token, _amount, _to);
IAcropolis(acropolis
).governanceRecoverUnsupported(_token, _amount, _to);
}
}
}
}
已保存差異
原始文本
開啟檔案
// 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); } }
更改後文本
開啟檔案
// 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); } }
尋找差異