The Treasury Contract determines the expansion and contraction status of the round and plays a central role like the central bank, such as the issuance of new KAI and redemption of bKAI.
[Main role]
Determination of expansion/contraction status according to the price of KAI provided by PriceOracle
Determination of airdrop ratio based on expansion/contraction/boost result
New issuance and distribution of KAI in the expanded state
bHolding and paying KAI for bKAI redemption
The following is the code to mint for additional KAI issuance in the expanded state.
function allocateSeigniorage() external onlyOneBlock checkCondition checkEpoch checkOperator {
_updateKAIPrice();
previousRoundKAIPrice = getKAIPrice(); // KAI 가격을 얻어옴
uint256 kaiSupply = IERC20(kai).totalSupply().sub(seigniorageSaved);
if (round < bootstrapRounds) { // 초기 21라운드는 3% 고정
_sendToBoardRoom(kaiSupply.mul(bootstrapSupplyExpansionPercent).div(10000));
} else {
if (previousRoundKAIPrice > kaiPriceOne) { // KAI 가격이 $1 초과하면 확장
uint256 bkaiSupply = IERC20(bkai).totalSupply();
uint256 _percentage = previousRoundKAIPrice.sub(kaiPriceOne).mul(seigniorageExpansionRate).div(10000);
uint256 _savedForBKAI;
uint256 _savedForBoardRoom;
// bKAI 의 발행량 이상으로 Treasury 가 KAI 를 보유한 경우 일반적인 확장 진행
if (seigniorageSaved >= bkaiSupply.mul(bkaiDepletionFloorPercent).div(10000))
uint256 _mse = maxSupplyExpansionPercent.mul(1e14);
if (_percentage > _mse) {
_percentage = _mse;
}
_savedForBoardRoom = kaiSupply.mul(_percentage).div(1e18);
} else {
// bKAI 의 발행량 만큼 Treasury 가 보유하고 있지 않다면, 이는 Debt Phase 로 전환되어 더 많은 KAI 가 Treasury 에 적립됨
_percentage = maxSupplyExpansionPercentInDebtPhase.mul(1e14);
uint256 _seigniorage = kaiSupply.mul(_percentage).div(1e18);
_savedForBoardRoom = _seigniorage.mul(seigniorageExpansionFloorPercent).div(10000);
_savedForBKAI = _seigniorage.sub(_savedForBoardRoom);
if (mintingFactorForPayingDebt > 0) {
_savedForBKAI = _savedForBKAI.mul(mintingFactorForPayingDebt).div(10000);
}
}
if (_savedForBoardRoom > 0) {
// 보드룸으로 KAI 생성 요청
_sendToBoardRoom(_savedForBoardRoom);
}
if (_savedForBKAI > 0) {
seigniorageSaved = seigniorageSaved.add(_savedForBKAI);
IKAIAsset(kai).mint(address(this), _savedForBKAI);
emit TreasuryFunded(now, _savedForBKAI);
}
}
}
// 바이백 펀드 KAI 생성
if (previousRoundKAIPrice > kaiPriceOne) {
uint256 _buyBackRate = previousRoundKAIPrice.sub(kaiPriceOne).mul(buyBackFundExpansionRate).div(10000);
uint256 _maxBuyBackRate = maxBuyBackFundExpansion.mul(1e14);
if (_buyBackRate > _maxBuyBackRate) {
_buyBackRate = _maxBuyBackRate;
}
uint256 _savedForBuyBackFund = kaiSupply.mul(_buyBackRate).div(1e18);
if (_savedForBuyBackFund > 0) {
IKAIAsset(kai).mint(address(buyBackFund), _savedForBuyBackFund);
emit BuyBackFunded(now, _savedForBuyBackFund);
}
}
if (allocateSeigniorageSalary > 0) {
IKAIAsset(kai).mint(address(admin), allocateSeigniorageSalary);
}
}
After mint, KAI is distributed to Boardroom Contract and Team Fund through the _sendToBoardRoom function.
function _sendToBoardRoom(uint256 _amount) internal {
IKAIAsset(kai).mint(address(this), _amount);
// 10%의 물량은 팀펀드로 적립되며, 해당 물량은 마케팅 및 운영비로 사용됨
if (teamFundSharedPercent > 0) {
uint256 _teamFundSharedAmount = _amount.mul(teamFundSharedPercent).div(10000);
IERC20(kai).transfer(teamFund, _teamFundSharedAmount);
emit TeamFundFunded(now, _teamFundSharedAmount);
_amount = _amount.sub(_teamFundSharedAmount);
}
IERC20(kai).safeApprove(boardroom, 0);
IERC20(kai).safeApprove(boardroom, _amount);
// 보드룸에 나머지 물량을 보내서 단일예치자들에게 배분
IBoardroom(boardroom).allocateSeigniorage(_amount);
emit BoardroomFunded(now, _amount);
}
Boardroom Contract
0x931579Fa23580CB2214fBE89aB487f6AEDE8a352
Boardroom Contract acts like a general bank that manages sKAI deposit(Stake) and distributes KAI received from Treasury Contract to sKAI deposit users in the expanded state.
[Main role]
Stake sKAI
Distribution of additional minted KAI
Distributed minted vKAI
BBFund Contract - Buyback Fund
0x451033434Fb739a538DA0a77d3DcA23Bf0801255
A complementary device to KAI's $1 pegging algorithm through bKAI can stabilize the price by backing up KAI when the estimated price (TWAP) for the next round is less than $1. Depending on KAI distribution and the status of buyback funds, buyback KAI may be incinerated.
The creation of the buyback fund will be carried out through KAI and vKAI, and it will be converted into KUSDT and KDAI and accumulated.
[Main role]
KAI -> KUSDT or KAI -> KDAI exchange when KAI price is over $1
KUSDT -> KAI or KDAI -> KAI exchange when KAI price is less than $1
The Buyback Fund Contract does not have a transfer function and consists only of Sell, Buy, and Burn functions to maintain the KAI price.
function setKAIPriceToSell(uint256 _priceToSell) external onlyStrategist {
require(_priceToSell > 1.0 ether, "out of range"); // 1불을 초과해야 매도 가능
kaiPriceToSell = _priceToSell;
}
function setKAIPriceToBuy(uint256 _priceToBuy) external onlyStrategist {
require(_priceToBuy < 1.0 ether, "out of range"); // 1 불 미만일 때 매수 가능
kaiPriceToBuy = _priceToBuy;
}
function forceSell(address _buyingToken, uint256 _amount) external onlyStrategist {
require(getKAIUpdatedPrice() >= kaiPriceToSell, "price is too low to sell");
_swapToken(kai, _buyingToken, _amount);
}
function forceBuy(address _sellingToken, uint256 _amount) external onlyStrategist {
require(getKAIUpdatedPrice() <= kaiPriceToBuy, "price is too high to buy");
_swapToken(_sellingToken, kai, _amount);
}
function _swapToken(address _inputToken, address _outputToken, uint256 _amount) internal {
if (_amount == 0) return;
uint256 _maxAmount = maxAmountToTrade[_inputToken];
if (_maxAmount > 0 && _maxAmount < _amount) {
_amount = _maxAmount;
}
address[] memory _path;
IERC20(_inputToken).safeIncreaseAllowance(address(klayswapFactory), _amount);
IKlayswapFactory(klayswapFactory).exchangeKctPos(_inputToken, _amount, _outputToken, 1, _path);
}