Comparing sensitive data, confidential files or internal emails?

Most legal and privacy policies prohibit uploading sensitive data online. Diffchecker Desktop ensures your confidential information never leaves your computer. Work offline and compare documents securely.

VaultSushiFlipToFlip.sol

Created Diff never expires
0 removals
310 lines
17 additions
327 lines
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;
pragma experimental ABIEncoderV2;


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


*
*
* 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/SafeBEP20.sol";
import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/SafeBEP20.sol";
import "@openzeppelin/contracts/math/Math.sol";
import "@openzeppelin/contracts/math/Math.sol";


import {PoolConstant} from "../library/PoolConstant.sol";
import {PoolConstant} from "../library/PoolConstant.sol";
import "../interfaces/IUniswapV2Pair.sol";
import "../interfaces/IUniswapV2Pair.sol";
import "../interfaces/IUniswapV2Factory.sol";
import "../interfaces/IUniswapV2Factory.sol";
import "../interfaces/IStrategy.sol";
import "../interfaces/IStrategy.sol";
import "../interfaces/ISushiMiniChefV2.sol";
import "../interfaces/ISushiMiniChefV2.sol";
import "../interfaces/IZap.sol";
import "../interfaces/IZap.sol";


import "./VaultController.sol";
import "./VaultController.sol";


contract VaultSushiFlipToFlip is VaultController, IStrategy {
contract VaultSushiFlipToFlip is VaultController, IStrategy {
using SafeBEP20 for IBEP20;
using SafeBEP20 for IBEP20;
using SafeMath for uint256;
using SafeMath for uint256;


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


IBEP20 private constant SUSHI = IBEP20(0x0b3F868E0BE5597D5DB7fEB59E1CADBb0fdDa50a);
IBEP20 private constant SUSHI = IBEP20(0x0b3F868E0BE5597D5DB7fEB59E1CADBb0fdDa50a);
IBEP20 private constant WMATIC = IBEP20(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270);
IBEP20 private constant WMATIC = IBEP20(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270);
PoolConstant.PoolTypes public constant override poolType = PoolConstant.PoolTypes.FlipToFlip;
PoolConstant.PoolTypes public constant override poolType = PoolConstant.PoolTypes.FlipToFlip;
ISushiMiniChefV2 private constant SUSHI_MINI_CHEF = ISushiMiniChefV2(0x0769fd68dFb93167989C6f7254cd0D766Fb2841F);
ISushiMiniChefV2 private constant SUSHI_MINI_CHEF = ISushiMiniChefV2(0x0769fd68dFb93167989C6f7254cd0D766Fb2841F);
IZap private constant zap = IZap(0x93bCE7E49E26AF0f87b74583Ba6551DF5E4867B7);
IZap private constant zap = IZap(0x93bCE7E49E26AF0f87b74583Ba6551DF5E4867B7);


uint private constant DUST = 1000;
uint private constant DUST = 1000;


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


uint public override pid;
uint public override pid;


address private _token0;
address private _token0;
address private _token1;
address private _token1;


uint public totalShares;
uint public totalShares;
mapping (address => uint) private _shares;
mapping (address => uint) private _shares;
mapping (address => uint) private _principal;
mapping (address => uint) private _principal;
mapping (address => uint) private _depositedAt;
mapping (address => uint) private _depositedAt;


uint public sushiHarvested;
uint public sushiHarvested;
uint public wmaticHarvested;
uint public wmaticHarvested;


uint public totalBalance;


/* ========== MODIFIER ========== */
/* ========== MODIFIER ========== */


modifier updateSushiHarvested {
modifier updateSushiHarvested {
uint _before = SUSHI.balanceOf(address(this));
uint _before = SUSHI.balanceOf(address(this));
uint _beforeWmatic = WMATIC.balanceOf(address(this));
uint _beforeWmatic = WMATIC.balanceOf(address(this));
_;
_;
uint _after = SUSHI.balanceOf(address(this));
uint _after = SUSHI.balanceOf(address(this));
uint _afterWmatic = WMATIC.balanceOf(address(this));
uint _afterWmatic = WMATIC.balanceOf(address(this));
sushiHarvested = sushiHarvested.add(_after).sub(_before);
sushiHarvested = sushiHarvested.add(_after).sub(_before);
wmaticHarvested = wmaticHarvested.add(_afterWmatic).sub(_beforeWmatic);
wmaticHarvested = wmaticHarvested.add(_afterWmatic).sub(_beforeWmatic);
}
}


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


function initialize(uint _pid, address _token) external initializer {
function initialize(uint _pid, address _token) external initializer {
__VaultController_init(IBEP20(_token));
__VaultController_init(IBEP20(_token));
_stakingToken.safeApprove(address(SUSHI_MINI_CHEF), uint(- 1));
_stakingToken.safeApprove(address(SUSHI_MINI_CHEF), uint(- 1));
pid = _pid;
pid = _pid;


SUSHI.safeApprove(address(zap), uint(- 1));
SUSHI.safeApprove(address(zap), uint(- 1));
WMATIC.safeApprove(address(zap), uint(- 1));
WMATIC.safeApprove(address(zap), uint(- 1));
}
}


/* ========== VIEW FUNCTIONS ========== */
/* ========== VIEW FUNCTIONS ========== */


function totalSupply() external view override returns (uint) {
function totalSupply() external view override returns (uint) {
return totalShares;
return totalShares;
}
}


function balance() public view override returns (uint amount) {
function balance() public view override returns (uint amount) {
(amount,) = SUSHI_MINI_CHEF.userInfo(pid, address(this));
(amount,) = SUSHI_MINI_CHEF.userInfo(pid, address(this));
amount = Math.min(amount, totalBalance);
}
}


function balanceOf(address account) public view override returns(uint) {
function balanceOf(address account) public view override returns(uint) {
if (totalShares == 0) return 0;
if (totalShares == 0) return 0;
return balance().mul(sharesOf(account)).div(totalShares);
return balance().mul(sharesOf(account)).div(totalShares);
}
}


function withdrawableBalanceOf(address account) public view override returns (uint) {
function withdrawableBalanceOf(address account) public view override returns (uint) {
return balanceOf(account);
return balanceOf(account);
}
}


function sharesOf(address account) public view override returns (uint) {
function sharesOf(address account) public view override returns (uint) {
return _shares[account];
return _shares[account];
}
}


function principalOf(address account) public view override returns (uint) {
function principalOf(address account) public view override returns (uint) {
return _principal[account];
return _principal[account];
}
}


function earned(address account) public view override returns (uint) {
function earned(address account) public view override returns (uint) {
if (balanceOf(account) >= principalOf(account) + DUST) {
if (balanceOf(account) >= principalOf(account) + DUST) {
return balanceOf(account).sub(principalOf(account));
return balanceOf(account).sub(principalOf(account));
} else {
} else {
return 0;
return 0;
}
}
}
}


function depositedAt(address account) external view override returns (uint) {
function depositedAt(address account) external view override returns (uint) {
return _depositedAt[account];
return _depositedAt[account];
}
}


function rewardsToken() external view override returns (address) {
function rewardsToken() external view override returns (address) {
return address(_stakingToken);
return address(_stakingToken);
}
}


function priceShare() external view override returns(uint) {
function priceShare() external view override returns(uint) {
if (totalShares == 0) return 1e18;
if (totalShares == 0) return 1e18;
return balance().mul(1e18).div(totalShares);
return balance().mul(1e18).div(totalShares);
}
}


/* ========== MUTATIVE FUNCTIONS ========== */
/* ========== MUTATIVE FUNCTIONS ========== */


function deposit(uint _amount) public override {
function deposit(uint _amount) public override {
_depositTo(_amount, msg.sender);
_depositTo(_amount, msg.sender);
}
}


function depositAll() external override {
function depositAll() external override {
deposit(_stakingToken.balanceOf(msg.sender));
deposit(_stakingToken.balanceOf(msg.sender));
}
}


function withdrawAll() external override {
function withdrawAll() external override {
uint amount = balanceOf(msg.sender);
uint amount = balanceOf(msg.sender);
uint principal = principalOf(msg.sender);
uint principal = principalOf(msg.sender);
uint depositTimestamp = _depositedAt[msg.sender];
uint depositTimestamp = _depositedAt[msg.sender];


totalBalance = totalBalance.sub(amount);
totalShares = totalShares.sub(_shares[msg.sender]);
totalShares = totalShares.sub(_shares[msg.sender]);
delete _shares[msg.sender];
delete _shares[msg.sender];
delete _principal[msg.sender];
delete _principal[msg.sender];
delete _depositedAt[msg.sender];
delete _depositedAt[msg.sender];


amount = _withdrawTokenWithCorrection(amount);
amount = _withdrawTokenWithCorrection(amount);
uint profit = amount > principal ? amount.sub(principal) : 0;
uint profit = amount > principal ? amount.sub(principal) : 0;


uint withdrawalFee = canMint() ? _minter.withdrawalFee(principal, depositTimestamp) : 0;
uint withdrawalFee = canMint() ? _minter.withdrawalFee(principal, depositTimestamp) : 0;
uint performanceFee = canMint() ? _minter.performanceFee(profit) : 0;
uint performanceFee = canMint() ? _minter.performanceFee(profit) : 0;
if (withdrawalFee.add(performanceFee) > DUST) {
if (withdrawalFee.add(performanceFee) > DUST) {
_minter.mintForV2(address(_stakingToken), withdrawalFee, performanceFee, msg.sender, depositTimestamp);
_minter.mintForV2(address(_stakingToken), withdrawalFee, performanceFee, msg.sender, depositTimestamp);


if (performanceFee > 0) {
if (performanceFee > 0) {
emit ProfitPaid(msg.sender, profit, performanceFee);
emit ProfitPaid(msg.sender, profit, performanceFee);
}
}
amount = amount.sub(withdrawalFee).sub(performanceFee);
amount = amount.sub(withdrawalFee).sub(performanceFee);
}
}


_stakingToken.safeTransfer(msg.sender, amount);
_stakingToken.safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount, withdrawalFee);
emit Withdrawn(msg.sender, amount, withdrawalFee);
}
}


function harvest() external override onlyKeeper {
function harvest() external override onlyKeeper {
_harvest();
_harvest();


uint before = _stakingToken.balanceOf(address(this));
uint before = _stakingToken.balanceOf(address(this));
zap.zapInToken(address(SUSHI), sushiHarvested, address(_stakingToken));
zap.zapInToken(address(SUSHI), sushiHarvested, address(_stakingToken));
zap.zapInToken(address(WMATIC), wmaticHarvested, address(_stakingToken));
zap.zapInToken(address(WMATIC), wmaticHarvested, address(_stakingToken));
uint harvested = _stakingToken.balanceOf(address(this)).sub(before);
uint harvested = _stakingToken.balanceOf(address(this)).sub(before);


totalBalance = totalBalance.add(harvested);
SUSHI_MINI_CHEF.deposit(pid, harvested, address(this));
SUSHI_MINI_CHEF.deposit(pid, harvested, address(this));
emit Harvested(harvested);
emit Harvested(harvested);


sushiHarvested = 0;
sushiHarvested = 0;
wmaticHarvested = 0;
wmaticHarvested = 0;
}
}


function _harvest() private updateSushiHarvested {
function _harvest() private updateSushiHarvested {
SUSHI_MINI_CHEF.harvest(pid, address(this));
SUSHI_MINI_CHEF.harvest(pid, address(this));
}
}


function withdraw(uint shares) external override onlyWhitelisted {
function withdraw(uint shares) external override onlyWhitelisted {
uint amount = balance().mul(shares).div(totalShares);
uint amount = balance().mul(shares).div(totalShares);
totalBalance = totalBalance.sub(amount);
totalShares = totalShares.sub(shares);
totalShares = totalShares.sub(shares);
_shares[msg.sender] = _shares[msg.sender].sub(shares);
_shares[msg.sender] = _shares[msg.sender].sub(shares);


amount = _withdrawTokenWithCorrection(amount);
amount = _withdrawTokenWithCorrection(amount);
_stakingToken.safeTransfer(msg.sender, amount);
_stakingToken.safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount, 0);
emit Withdrawn(msg.sender, amount, 0);
}
}


// @dev underlying only + withdrawal fee + no perf fee
// @dev underlying only + withdrawal fee + no perf fee
function withdrawUnderlying(uint _amount) external {
function withdrawUnderlying(uint _amount) external {
uint amount = Math.min(_amount, _principal[msg.sender]);
uint amount = Math.min(_amount, _principal[msg.sender]);
uint shares = Math.min(amount.mul(totalShares).div(balance()), _shares[msg.sender]);
uint shares = Math.min(amount.mul(totalShares).div(balance()), _shares[msg.sender]);

totalBalance = totalBalance.sub(amount);
totalShares = totalShares.sub(shares);
totalShares = totalShares.sub(shares);
_shares[msg.sender] = _shares[msg.sender].sub(shares);
_shares[msg.sender] = _shares[msg.sender].sub(shares);
_principal[msg.sender] = _principal[msg.sender].sub(amount);
_principal[msg.sender] = _principal[msg.sender].sub(amount);


amount = _withdrawTokenWithCorrection(amount);
amount = _withdrawTokenWithCorrection(amount);
uint depositTimestamp = _depositedAt[msg.sender];
uint depositTimestamp = _depositedAt[msg.sender];
uint withdrawalFee = canMint() ? _minter.withdrawalFee(amount, depositTimestamp) : 0;
uint withdrawalFee = canMint() ? _minter.withdrawalFee(amount, depositTimestamp) : 0;
if (withdrawalFee > DUST) {
if (withdrawalFee > DUST) {
_minter.mintForV2(address(_stakingToken), withdrawalFee, 0, msg.sender, depositTimestamp);
_minter.mintForV2(address(_stakingToken), withdrawalFee, 0, msg.sender, depositTimestamp);
amount = amount.sub(withdrawalFee);
amount = amount.sub(withdrawalFee);
}
}


_stakingToken.safeTransfer(msg.sender, amount);
_stakingToken.safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount, withdrawalFee);
emit Withdrawn(msg.sender, amount, withdrawalFee);
}
}


// @dev profits only (underlying + bunny) + no withdraw fee + perf fee
// @dev profits only (underlying + bunny) + no withdraw fee + perf fee
function getReward() external override {
function getReward() external override {
uint amount = earned(msg.sender);
uint amount = earned(msg.sender);
uint shares = Math.min(amount.mul(totalShares).div(balance()), _shares[msg.sender]);
uint shares = Math.min(amount.mul(totalShares).div(balance()), _shares[msg.sender]);
totalBalance = totalBalance.sub(amount);
totalShares = totalShares.sub(shares);
totalShares = totalShares.sub(shares);
_shares[msg.sender] = _shares[msg.sender].sub(shares);
_shares[msg.sender] = _shares[msg.sender].sub(shares);
_cleanupIfDustShares();
_cleanupIfDustShares();


amount = _withdrawTokenWithCorrection(amount);
amount = _withdrawTokenWithCorrection(amount);
uint depositTimestamp = _depositedAt[msg.sender];
uint depositTimestamp = _depositedAt[msg.sender];
uint performanceFee = canMint() ? _minter.performanceFee(amount) : 0;
uint performanceFee = canMint() ? _minter.performanceFee(amount) : 0;
if (performanceFee > DUST) {
if (performanceFee > DUST) {
_minter.mintForV2(address(_stakingToken), 0, performanceFee, msg.sender, depositTimestamp);
_minter.mintForV2(address(_stakingToken), 0, performanceFee, msg.sender, depositTimestamp);
amount = amount.sub(performanceFee);
amount = amount.sub(performanceFee);
}
}


_stakingToken.safeTransfer(msg.sender, amount);
_stakingToken.safeTransfer(msg.sender, amount);
emit ProfitPaid(msg.sender, amount, performanceFee);
emit ProfitPaid(msg.sender, amount, performanceFee);
}
}


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


function _depositTo(uint _amount, address _to) private notPaused updateSushiHarvested {
function _depositTo(uint _amount, address _to) private notPaused updateSushiHarvested {
uint _pool = balance();
uint _pool = balance();
uint _before = _stakingToken.balanceOf(address(this));
uint _before = _stakingToken.balanceOf(address(this));
_stakingToken.safeTransferFrom(msg.sender, address(this), _amount);
_stakingToken.safeTransferFrom(msg.sender, address(this), _amount);
uint _after = _stakingToken.balanceOf(address(this));
uint _after = _stakingToken.balanceOf(address(this));
_amount = _after.sub(_before); // Additional check for deflationary tokens
_amount = _after.sub(_before); // Additional check for deflationary tokens
uint shares = 0;
uint shares = 0;
if (totalShares == 0) {
if (totalShares == 0) {
shares = _amount;
shares = _amount;
} else {
} else {
shares = (_amount.mul(totalShares)).div(_pool);
shares = (_amount.mul(totalShares)).div(_pool);
}
}


totalBalance = totalBalance.add(_amount);
totalShares = totalShares.add(shares);
totalShares = totalShares.add(shares);
_shares[_to] = _shares[_to].add(shares);
_shares[_to] = _shares[_to].add(shares);
_principal[_to] = _principal[_to].add(_amount);
_principal[_to] = _principal[_to].add(_amount);
_depositedAt[_to] = block.timestamp;
_depositedAt[_to] = block.timestamp;


SUSHI_MINI_CHEF.deposit(pid, _amount, address(this));
SUSHI_MINI_CHEF.deposit(pid, _amount, address(this));
emit Deposited(_to, _amount);
emit Deposited(_to, _amount);
}
}


function _withdrawTokenWithCorrection(uint amount) private updateSushiHarvested returns (uint) {
function _withdrawTokenWithCorrection(uint amount) private updateSushiHarvested returns (uint) {
uint before = _stakingToken.balanceOf(address(this));
uint before = _stakingToken.balanceOf(address(this));
SUSHI_MINI_CHEF.withdraw(pid, amount, address(this));
SUSHI_MINI_CHEF.withdraw(pid, amount, address(this));
return _stakingToken.balanceOf(address(this)).sub(before);
return _stakingToken.balanceOf(address(this)).sub(before);
}
}


function _cleanupIfDustShares() private {
function _cleanupIfDustShares() private {
uint shares = _shares[msg.sender];
uint shares = _shares[msg.sender];
if (shares > 0 && shares < DUST) {
if (shares > 0 && shares < DUST) {
totalShares = totalShares.sub(shares);
totalShares = totalShares.sub(shares);
delete _shares[msg.sender];
delete _shares[msg.sender];
}
}
}
}


/* ========== SALVAGE PURPOSE ONLY ========== */
/* ========== SALVAGE PURPOSE ONLY ========== */


// @dev stakingToken must not remain balance in this contract. So dev should salvage staking token transferred by mistake.
// @dev stakingToken must not remain balance in this contract. So dev should salvage staking token transferred by mistake.
function recoverToken(address token, uint amount) external override onlyOwner {
function recoverToken(address token, uint amount) external override onlyOwner {
if (token == address(SUSHI)) {
if (token == address(SUSHI)) {
uint sushiBalance = SUSHI.balanceOf(address(this));
uint sushiBalance = SUSHI.balanceOf(address(this));
require(amount <= sushiBalance.sub(sushiHarvested), "VaultFlipToFlip: cannot recover lp's harvested sushi");
require(amount <= sushiBalance.sub(sushiHarvested), "VaultFlipToFlip: cannot recover lp's harvested sushi");
}
}
if (token == address(WMATIC)){
if (token == address(WMATIC)){
uint wmaticBalance = WMATIC.balanceOf(address(this));
uint wmaticBalance = WMATIC.balanceOf(address(this));
require(amount <= wmaticBalance.sub(wmaticHarvested));
require(amount <= wmaticBalance.sub(wmaticHarvested));
}
}


IBEP20(token).safeTransfer(owner(), amount);
IBEP20(token).safeTransfer(owner(), amount);
emit Recovered(token, amount);
emit Recovered(token, amount);
}
}

function setTotalBalance() external onlyOwner {
require(totalBalance == 0, "VaultSushiFlipToFlip: can't update totalBalance");
(uint amount,) = SUSHI_MINI_CHEF.userInfo(pid, address(this));
totalBalance = amount;
}

}
}