Diff
checker
텍스트
텍스트
이미지
문서
Excel
폴더
Legal
Enterprise
데스크톱
요금제
로그인
데스크톱 앱 다운로드
텍스트 비교
두 텍스트 파일의 차이점을 찾아보세요
도구
기록
실시간 편집
변경 없는 행 숨기기
줄바꿈 비활성화
레이아웃
나란히 보기
합쳐 보기
비교 단위
스마트
단어
글자
구문 강조
언어 선택
제외
텍스트 변환
첫 변경으로
수정
Diffchecker Desktop
가장 안전하게 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); } }
비교하기