BunnyMinterV2.sol

Created Diff never expires
81 removals
313 lines
18 additions
251 lines
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma solidity ^0.6.12;


/*
/*
___ _ _
___ _ _
| _ )_ _ _ _ _ _ _ _ | | | |
| _ )_ _ _ _ _ _ _ _ | | | |
| _ \ || | ' \| ' \ || | |_| |_|
| _ \ || | ' \| ' \ || | |_| |_|
|___/\_,_|_||_|_||_\_, | (_) (_)
|___/\_,_|_||_|_||_\_, | (_) (_)
|__/
|__/


*
*
* MIT License
* MIT License
* ===========
* ===========
*
*
* Copyright (c) 2020 BunnyFinance
* Copyright (c) 2020 BunnyFinance
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in all
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/
*/


import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/BEP20.sol";
import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/BEP20.sol";
import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/SafeBEP20.sol";
import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/SafeBEP20.sol";
import "@pancakeswap/pancake-swap-lib/contracts/math/SafeMath.sol";
import "@pancakeswap/pancake-swap-lib/contracts/math/SafeMath.sol";


import "../interfaces/IBunnyMinterV2.sol";
import "../interfaces/IBunnyMinterV2.sol";
import "../interfaces/IStakingRewards.sol";
import "../interfaces/IStakingRewards.sol";
import "../dashboard/calculator/PriceCalculatorBSC.sol";
import "../interfaces/IPriceCalculator.sol";

import "../zap/ZapBSC.sol";
import "../zap/ZapBSC.sol";
import "../library/SafeToken.sol";


contract BunnyMinterV2 is IBunnyMinterV2, OwnableUpgradeable {
contract BunnyMinterV2 is IBunnyMinterV2, OwnableUpgradeable {
using SafeMath for uint;
using SafeMath for uint;
using SafeBEP20 for IBEP20;
using SafeBEP20 for IBEP20;


/* ========== CONSTANTS ============= */
/* ========== CONSTANTS ============= */


address public constant WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;
address public constant WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;
address public constant BUNNY = 0xC9849E6fdB743d08fAeE3E34dd2D1bc69EA11a51;
address public constant BUNNY = 0xC9849E6fdB743d08fAeE3E34dd2D1bc69EA11a51;
address public constant BUNNY_BNB = 0x7Bb89460599Dbf32ee3Aa50798BBcEae2A5F7f6a;
address public constant BUNNY_POOL = 0xCADc8CB26c8C7cB46500E61171b5F27e9bd7889D;
address public constant BUNNY_POOL = 0xCADc8CB26c8C7cB46500E61171b5F27e9bd7889D;


address public constant DEPLOYER = 0xe87f02606911223C2Cf200398FFAF353f60801F7;
address public constant TREASURY = 0x0989091F27708Bc92ea4cA60073e03592B94C0eE;
address private constant TIMELOCK = 0x85c9162A51E03078bdCd08D4232Bab13ed414cC3;
address private constant TIMELOCK = 0x85c9162A51E03078bdCd08D4232Bab13ed414cC3;
address private constant DEAD = 0x000000000000000000000000000000000000dEaD;
address private constant DEAD = 0x000000000000000000000000000000000000dEaD;


uint public constant FEE_MAX = 10000;
uint public constant FEE_MAX = 10000;


ZapBSC public constant zapBSC = ZapBSC(0xCBEC8e7AB969F6Eb873Df63d04b4eAFC353574b1);
IPriceCalculator public constant priceCalculator = IPriceCalculator(0xF5BF8A9249e3cc4cB684E3f23db9669323d4FB7d);
ZapBSC public constant zapBSCV2 = ZapBSC(0xdC2bBB0D33E0e7Dea9F5b98F46EDBaC823586a0C);
PriceCalculatorBSC public constant priceCalculator = PriceCalculatorBSC(0x542c06a5dc3f27e0fbDc9FB7BC6748f26d54dDb0); // should be V1 address

IPancakeRouter02 private constant routerV1 = IPancakeRouter02(0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F);
IPancakeRouter02 private constant routerV2 = IPancakeRouter02(0x10ED43C718714eb63d5aA57B78B54704E256024E);


/* ========== STATE VARIABLES ========== */
/* ========== STATE VARIABLES ========== */


address public bunnyChef;
address public bunnyChef;
mapping(address => bool) private _minters;
mapping(address => bool) private _minters;
address public _deprecated_helper; // deprecated
address public _deprecated_helper; // deprecated


uint public PERFORMANCE_FEE;
uint public PERFORMANCE_FEE;
uint public override WITHDRAWAL_FEE_FREE_PERIOD;
uint public override WITHDRAWAL_FEE_FREE_PERIOD;
uint public override WITHDRAWAL_FEE;
uint public override WITHDRAWAL_FEE;


uint public override bunnyPerProfitBNB;
uint public override bunnyPerProfitBNB;
uint public bunnyPerBunnyBNBFlip; // will be deprecated
uint public bunnyPerBunnyBNBFlip; // will be deprecated


/* ========== MODIFIERS ========== */
/* ========== MODIFIERS ========== */


modifier onlyMinter {
modifier onlyMinter {
require(isMinter(msg.sender) == true, "BunnyMinterV2: caller is not the minter");
require(isMinter(msg.sender) == true, "BunnyMinterV2: caller is not the minter");
_;
_;
}
}


modifier onlyBunnyChef {
modifier onlyBunnyChef {
require(msg.sender == bunnyChef, "BunnyMinterV2: caller not the bunny chef");
require(msg.sender == bunnyChef, "BunnyMinterV2: caller not the bunny chef");
_;
_;
}
}


receive() external payable {}
receive() external payable {}


/* ========== INITIALIZER ========== */
/* ========== INITIALIZER ========== */


function initialize() external initializer {
function initialize() external initializer {
WITHDRAWAL_FEE_FREE_PERIOD = 3 days;
WITHDRAWAL_FEE_FREE_PERIOD = 3 days;
WITHDRAWAL_FEE = 50;
WITHDRAWAL_FEE = 50;
PERFORMANCE_FEE = 3000;
PERFORMANCE_FEE = 3000;


bunnyPerProfitBNB = 5e18;
bunnyPerProfitBNB = 5e18;
bunnyPerBunnyBNBFlip = 6e18;
bunnyPerBunnyBNBFlip = 6e18;


IBEP20(BUNNY).approve(BUNNY_POOL, uint(- 1));
IBEP20(BUNNY).approve(BUNNY_POOL, uint(- 1));
}
}


/* ========== RESTRICTED FUNCTIONS ========== */
/* ========== RESTRICTED FUNCTIONS ========== */


function transferBunnyOwner(address _owner) external onlyOwner {
function transferBunnyOwner(address _owner) external onlyOwner {
Ownable(BUNNY).transferOwnership(_owner);
Ownable(BUNNY).transferOwnership(_owner);
}
}


function setWithdrawalFee(uint _fee) external onlyOwner {
function setWithdrawalFee(uint _fee) external onlyOwner {
require(_fee < 500, "wrong fee");
require(_fee < 500, "wrong fee");
// less 5%
// less 5%
WITHDRAWAL_FEE = _fee;
WITHDRAWAL_FEE = _fee;
}
}


function setPerformanceFee(uint _fee) external onlyOwner {
function setPerformanceFee(uint _fee) external onlyOwner {
require(_fee < 5000, "wrong fee");
require(_fee < 5000, "wrong fee");
PERFORMANCE_FEE = _fee;
PERFORMANCE_FEE = _fee;
}
}


function setWithdrawalFeeFreePeriod(uint _period) external onlyOwner {
function setWithdrawalFeeFreePeriod(uint _period) external onlyOwner {
WITHDRAWAL_FEE_FREE_PERIOD = _period;
WITHDRAWAL_FEE_FREE_PERIOD = _period;
}
}


function setMinter(address minter, bool canMint) external override onlyOwner {
function setMinter(address minter, bool canMint) external override onlyOwner {
if (canMint) {
if (canMint) {
_minters[minter] = canMint;
_minters[minter] = canMint;
} else {
} else {
delete _minters[minter];
delete _minters[minter];
}
}
}
}


function setBunnyPerProfitBNB(uint _ratio) external onlyOwner {
function setBunnyPerProfitBNB(uint _ratio) external onlyOwner {
bunnyPerProfitBNB = _ratio;
bunnyPerProfitBNB = _ratio;
}
}


function setBunnyPerBunnyBNBFlip(uint _bunnyPerBunnyBNBFlip) external onlyOwner {
function setBunnyPerBunnyBNBFlip(uint _bunnyPerBunnyBNBFlip) external onlyOwner {
bunnyPerBunnyBNBFlip = _bunnyPerBunnyBNBFlip;
bunnyPerBunnyBNBFlip = _bunnyPerBunnyBNBFlip;
}
}


function setBunnyChef(address _bunnyChef) external onlyOwner {
function setBunnyChef(address _bunnyChef) external onlyOwner {
require(bunnyChef == address(0), "BunnyMinterV2: setBunnyChef only once");
require(bunnyChef == address(0), "BunnyMinterV2: setBunnyChef only once");
bunnyChef = _bunnyChef;
bunnyChef = _bunnyChef;
}
}


/* ========== VIEWS ========== */
/* ========== VIEWS ========== */


function isMinter(address account) public view override returns (bool) {
function isMinter(address account) public view override returns (bool) {
if (IBEP20(BUNNY).getOwner() != address(this)) {
if (IBEP20(BUNNY).getOwner() != address(this)) {
return false;
return false;
}
}
return _minters[account];
return _minters[account];
}
}


function amountBunnyToMint(uint bnbProfit) public view override returns (uint) {
function amountBunnyToMint(uint bnbProfit) public view override returns (uint) {
return bnbProfit.mul(bunnyPerProfitBNB).div(1e18);
return bnbProfit.mul(bunnyPerProfitBNB).div(1e18);
}
}


function amountBunnyToMintForBunnyBNB(uint amount, uint duration) public view override returns (uint) {
function amountBunnyToMintForBunnyBNB(uint amount, uint duration) public view override returns (uint) {
return amount.mul(bunnyPerBunnyBNBFlip).mul(duration).div(365 days).div(1e18);
return amount.mul(bunnyPerBunnyBNBFlip).mul(duration).div(365 days).div(1e18);
}
}


function withdrawalFee(uint amount, uint depositedAt) external view override returns (uint) {
function withdrawalFee(uint amount, uint depositedAt) external view override returns (uint) {
if (depositedAt.add(WITHDRAWAL_FEE_FREE_PERIOD) > block.timestamp) {
if (depositedAt.add(WITHDRAWAL_FEE_FREE_PERIOD) > block.timestamp) {
return amount.mul(WITHDRAWAL_FEE).div(FEE_MAX);
return amount.mul(WITHDRAWAL_FEE).div(FEE_MAX);
}
}
return 0;
return 0;
}
}


function performanceFee(uint profit) public view override returns (uint) {
function performanceFee(uint profit) public view override returns (uint) {
return profit.mul(PERFORMANCE_FEE).div(FEE_MAX);
return profit.mul(PERFORMANCE_FEE).div(FEE_MAX);
}
}


/* ========== V1 FUNCTIONS ========== */
/* ========== V1 FUNCTIONS ========== */


function mintFor(address asset, uint _withdrawalFee, uint _performanceFee, address to, uint) external payable override onlyMinter {
function mintFor(address asset, uint _withdrawalFee, uint _performanceFee, address to, uint) public payable override onlyMinter {
uint feeSum = _performanceFee.add(_withdrawalFee);
uint feeSum = _performanceFee.add(_withdrawalFee);
_transferAsset(asset, feeSum);
_transferAsset(asset, feeSum);


if (asset == BUNNY) {
if (asset == BUNNY) {
IBEP20(BUNNY).safeTransfer(DEAD, feeSum);
IBEP20(BUNNY).safeTransfer(DEAD, feeSum);
return;
return;
}
}


uint bunnyBNBAmount = _zapAssetsToBunnyBNB(asset, feeSum, false);
if (asset == address(0)) { // means BNB
if (bunnyBNBAmount == 0) return;
SafeToken.safeTransferETH(TREASURY, feeSum);

} else {
IBEP20(BUNNY_BNB).safeTransfer(BUNNY_POOL, bunnyBNBAmount);
IBEP20(asset).safeTransfer(TREASURY, feeSum);
IStakingRewards(BUNNY_POOL).notifyRewardAmount(bunnyBNBAmount);
}


(uint valueInBNB,) = priceCalculator.valueOfAsset(BUNNY_BNB, bunnyBNBAmount);
(uint contribution, ) = priceCalculator.valueOfAsset(asset, _performanceFee);
uint contribution = valueInBNB.mul(_performanceFee).div(feeSum);
uint mintBunny = amountBunnyToMint(contribution);
uint mintBunny = amountBunnyToMint(contribution);
if (mintBunny == 0) return;
if (mintBunny == 0) return;
_mint(mintBunny, to);
_mint(mintBunny, to);
}
}


/* ========== PancakeSwap V2 FUNCTIONS ========== */
/* ========== PancakeSwap V2 FUNCTIONS ========== */


function mintForV2(address asset, uint _withdrawalFee, uint _performanceFee, address to, uint) external payable override onlyMinter {
function mintForV2(address asset, uint _withdrawalFee, uint _performanceFee, address to, uint timestamp) external payable override onlyMinter {
uint feeSum = _performanceFee.add(_withdrawalFee);
mintFor(asset, _withdrawalFee, _performanceFee, to, timestamp);
_transferAsset(asset, feeSum);

if (asset == BUNNY) {
IBEP20(BUNNY).safeTransfer(DEAD, feeSum);
return;
}

uint bunnyBNBAmount = _zapAssetsToBunnyBNB(asset, feeSum, true);
if (bunnyBNBAmount == 0) return;

IBEP20(BUNNY_BNB).safeTransfer(BUNNY_POOL, bunnyBNBAmount);
IStakingRewards(BUNNY_POOL).notifyRewardAmount(bunnyBNBAmount);

(uint valueInBNB,) = priceCalculator.valueOfAsset(BUNNY_BNB, bunnyBNBAmount);
uint contribution = valueInBNB.mul(_performanceFee).div(feeSum);
uint mintBunny = amountBunnyToMint(contribution);
if (mintBunny == 0) return;
_mint(mintBunny, to);
}
}


/* ========== BunnyChef FUNCTIONS ========== */
/* ========== BunnyChef FUNCTIONS ========== */


function mint(uint amount) external override onlyBunnyChef {
function mint(uint amount) external override onlyBunnyChef {
if (amount == 0) return;
if (amount == 0) return;
_mint(amount, address(this));
_mint(amount, address(this));
}
}


function safeBunnyTransfer(address _to, uint _amount) external override onlyBunnyChef {
function safeBunnyTransfer(address _to, uint _amount) external override onlyBunnyChef {
if (_amount == 0) return;
if (_amount == 0) return;


uint bal = IBEP20(BUNNY).balanceOf(address(this));
uint bal = IBEP20(BUNNY).balanceOf(address(this));
if (_amount <= bal) {
if (_amount <= bal) {
IBEP20(BUNNY).safeTransfer(_to, _amount);
IBEP20(BUNNY).safeTransfer(_to, _amount);
} else {
} else {
IBEP20(BUNNY).safeTransfer(_to, bal);
IBEP20(BUNNY).safeTransfer(_to, bal);
}
}
}
}


// @dev should be called when determining mint in governance. Bunny is transferred to the timelock contract.
// @dev should be called when determining mint in governance. Bunny is transferred to the timelock contract.
function mintGov(uint amount) external override onlyOwner {
function mintGov(uint amount) external override onlyOwner {
if (amount == 0) return;
if (amount == 0) return;
_mint(amount, TIMELOCK);
_mint(amount, TIMELOCK);
}
}


/* ========== PRIVATE FUNCTIONS ========== */
/* ========== PRIVATE FUNCTIONS ========== */


function _transferAsset(address asset, uint amount) private {
function _transferAsset(address asset, uint amount) private {
if (asset == address(0)) {
if (asset == address(0)) {
// case) transferred BNB
// case) transferred BNB
require(msg.value >= amount);
require(msg.value >= amount);
} else {
} else {
IBEP20(asset).safeTransferFrom(msg.sender, address(this), amount);
IBEP20(asset).safeTransferFrom(msg.sender, address(this), amount);
}
}
}
}


function _zapAssetsToBunnyBNB(address asset, uint amount, bool fromV2) private returns (uint bunnyBNBAmount) {
uint _initBunnyBNBAmount = IBEP20(BUNNY_BNB).balanceOf(address(this));

if (asset == address(0)) {
zapBSC.zapIn{ value : amount }(BUNNY_BNB);
}
else if (keccak256(abi.encodePacked(IPancakePair(asset).symbol())) == keccak256("Cake-LP")) {
IPancakeRouter02 router = fromV2 ? routerV2 : routerV1;

if (IBEP20(asset).allowance(address(this), address(router)) == 0) {
IBEP20(asset).safeApprove(address(router), uint(- 1));
}

IPancakePair pair = IPancakePair(asset);
address token0 = pair.token0();
address token1 = pair.token1();

(uint amountToken0, uint amountToken1) = router.removeLiquidity(token0, token1, amount, 0, 0, address(this), block.timestamp);

if (IBEP20(token0).allowance(address(this), address(zapBSC)) == 0) {
IBEP20(token0).safeApprove(address(zapBSC), uint(- 1));
}
if (IBEP20(token1).allowance(address(this), address(zapBSC)) == 0) {
IBEP20(token1).safeApprove(address(zapBSC), uint(- 1));
}

zapBSC.zapInToken(token0, amountToken0, BUNNY_BNB);
zapBSC.zapInToken(token1, amountToken1, BUNNY_BNB);
}
else {
if (IBEP20(asset).allowance(address(this), address(zapBSC)) == 0) {
IBEP20(asset).safeApprove(address(zapBSC), uint(- 1));
}

zapBSC.zapInToken(asset, amount, BUNNY_BNB);
}

bunnyBNBAmount = IBEP20(BUNNY_BNB).balanceOf(address(this)).sub(_initBunnyBNBAmount);
}

function _mint(uint amount, address to) private {
function _mint(uint amount, address to) private {
BEP20 tokenBUNNY = BEP20(BUNNY);
BEP20 tokenBUNNY = BEP20(BUNNY);


tokenBUNNY.mint(amount);
tokenBUNNY.mint(amount);
if (to != address(this)) {
if (to != address(this)) {
tokenBUNNY.transfer(to, amount);
tokenBUNNY.transfer(to, amount);
}
}


uint bunnyForDev = amount.mul(15).div(100);
uint bunnyForDev = amount.mul(15).div(100);
tokenBUNNY.mint(bunnyForDev);
tokenBUNNY.mint(bunnyForDev);
IStakingRewards(BUNNY_POOL).stakeTo(bunnyForDev, DEPLOYER);
tokenBUNNY.transfer(TREASURY, bunnyForDev);
}
}
}
}