BaseStrategy

Created Diff never expires
10 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
295 lines
23 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
306 lines
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT


pragma solidity 0.6.12;
pragma solidity 0.6.12;


import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.1.0/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.1.0/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.1.0/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.1.0/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";


import "./libs/IStrategyFish.sol";
import "./libs/IStrategyFish.sol";
import "./libs/IUniPair.sol";
import "./libs/IUniPair.sol";
import "./libs/IUniRouter02.sol";
import "./libs/IUniRouter02.sol";


abstract contract BaseStrategy is Ownable, ReentrancyGuard, Pausable {
abstract contract BaseStrategy is Ownable, ReentrancyGuard, Pausable {
using SafeMath for uint256;
using SafeMath for uint256;
using SafeERC20 for IERC20;
using SafeERC20 for IERC20;


address public wantAddress;
address public wantAddress;
address public token0Address;
address public token0Address;
address public token1Address;
address public token1Address;
address public earnedAddress;
address public earnedAddress;
address public uniRouterAddress;
address public uniRouterAddress;
address public constant usdcAddress = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174;
address public constant usdcAddress = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174;
address public constant fishAddress = 0x3a3Df212b7AA91Aa0402B9035b098891d276572B;
address public constant fishAddress = 0x76bF0C28e604CC3fE9967c83b3C3F31c213cfE64;
address public constant rewardAddress = 0x917FB15E8aAA12264DCBdC15AFef7cD3cE76BA39;
address public constant rewardAddress = 0x917FB15E8aAA12264DCBdC15AFef7cD3cE76BA39;
address public constant withdrawFeeAddress = 0x4879712c5D1A98C0B88Fb700daFF5c65d12Fd729;
address public constant withdrawFeeAddress = 0x5386881b46C37CdD30A748f7771CF95D7B213637;
address public constant feeAddress = 0x1cb757f1eB92F25A917CE9a92ED88c1aC0734334;
address public constant feeAddress = 0x5386881b46C37CdD30A748f7771CF95D7B213637;
address public vaultChefAddress;
address public vaultChefAddress;
address public govAddress;
address public govAddress;


uint256 public lastEarnBlock = block.number;
uint256 public lastEarnBlock = block.number;
uint256 public sharesTotal = 0;
uint256 public sharesTotal = 0;


address public constant buyBackAddress = 0x000000000000000000000000000000000000dEaD;
address public constant buyBackAddress = 0x000000000000000000000000000000000000dEaD;
uint256 public controllerFee = 50;
uint256 public controllerFee = 50;
uint256 public rewardRate = 0;
uint256 public rewardRate = 0;
uint256 public buyBackRate = 450;
uint256 public buyBackRate = 450;
uint256 public constant feeMaxTotal = 1000;
uint256 public constant feeMaxTotal = 1000;
uint256 public constant feeMax = 10000; // 100 = 1%
uint256 public constant feeMax = 10000; // 100 = 1%


uint256 public withdrawFeeFactor = 10000; // 0% withdraw fee
uint256 public withdrawFeeFactor = 9990; // 0.1% withdraw fee
uint256 public constant withdrawFeeFactorMax = 10000;
uint256 public constant withdrawFeeFactorMax = 10000;
uint256 public constant withdrawFeeFactorLL = 9900;
uint256 public constant withdrawFeeFactorLL = 9900;


uint256 public slippageFactor = 950; // 5% default slippage tolerance
uint256 public slippageFactor = 950; // 5% default slippage tolerance
uint256 public constant slippageFactorUL = 995;
uint256 public constant slippageFactorUL = 995;


// Frontend variables
uint256 public tolerance;
uint256 public burnedAmount;

address[] public earnedToWmaticPath;
address[] public earnedToWmaticPath;
address[] public earnedToUsdcPath;
address[] public earnedToUsdcPath;
address[] public earnedToFishPath;
address[] public earnedToFishPath;
address[] public earnedToToken0Path;
address[] public earnedToToken0Path;
address[] public earnedToToken1Path;
address[] public earnedToToken1Path;
address[] public token0ToEarnedPath;
address[] public token0ToEarnedPath;
address[] public token1ToEarnedPath;
address[] public token1ToEarnedPath;
event SetSettings(
event SetSettings(
uint256 _controllerFee,
uint256 _controllerFee,
uint256 _rewardRate,
uint256 _rewardRate,
uint256 _buyBackRate,
uint256 _buyBackRate,
uint256 _withdrawFeeFactor,
uint256 _withdrawFeeFactor,
uint256 _slippageFactor,
uint256 _slippageFactor,
uint256 _tolerance,
address _uniRouterAddress
address _uniRouterAddress
);
);
modifier onlyGov() {
modifier onlyGov() {
require(msg.sender == govAddress, "!gov");
require(msg.sender == govAddress, "!gov");
_;
_;
}
}


function _vaultDeposit(uint256 _amount) internal virtual;
function _vaultDeposit(uint256 _amount) internal virtual;
function _vaultWithdraw(uint256 _amount) internal virtual;
function _vaultWithdraw(uint256 _amount) internal virtual;
function earn() external virtual;
function earn() external virtual;
function vaultSharesTotal() public virtual view returns (uint256);
function vaultSharesTotal() public virtual view returns (uint256);
function wantLockedTotal() public virtual view returns (uint256);
function wantLockedTotal() public virtual view returns (uint256);
function _resetAllowances() internal virtual;
function _resetAllowances() internal virtual;
function _emergencyVaultWithdraw() internal virtual;
function _emergencyVaultWithdraw() internal virtual;
function deposit(address _userAddress, uint256 _wantAmt) external onlyOwner nonReentrant whenNotPaused returns (uint256) {
function deposit(address _userAddress, uint256 _wantAmt) external onlyOwner nonReentrant whenNotPaused returns (uint256) {
// Call must happen before transfer
// Call must happen before transfer
uint256 wantLockedBefore = wantLockedTotal();
uint256 wantLockedBefore = wantLockedTotal();


IERC20(wantAddress).safeTransferFrom(
IERC20(wantAddress).safeTransferFrom(
address(msg.sender),
address(msg.sender),
address(this),
address(this),
_wantAmt
_wantAmt
);
);


// Proper deposit amount for tokens with fees, or vaults with deposit fees
// Proper deposit amount for tokens with fees, or vaults with deposit fees
uint256 sharesAdded = _farm();
uint256 sharesAdded = _farm();
if (sharesTotal > 0) {
if (sharesTotal > 0) {
sharesAdded = sharesAdded.mul(sharesTotal).div(wantLockedBefore);
sharesAdded = sharesAdded.mul(sharesTotal).div(wantLockedBefore);
}
}
sharesTotal = sharesTotal.add(sharesAdded);
sharesTotal = sharesTotal.add(sharesAdded);


return sharesAdded;
return sharesAdded;
}
}


function _farm() internal returns (uint256) {
function _farm() internal returns (uint256) {
uint256 wantAmt = IERC20(wantAddress).balanceOf(address(this));
uint256 wantAmt = IERC20(wantAddress).balanceOf(address(this));
if (wantAmt == 0) return 0;
if (wantAmt == 0) return 0;
uint256 sharesBefore = vaultSharesTotal();
uint256 sharesBefore = vaultSharesTotal();
_vaultDeposit(wantAmt);
_vaultDeposit(wantAmt);
uint256 sharesAfter = vaultSharesTotal();
uint256 sharesAfter = vaultSharesTotal();
return sharesAfter.sub(sharesBefore);
return sharesAfter.sub(sharesBefore);
}
}


function withdraw(address _userAddress, uint256 _wantAmt) external onlyOwner nonReentrant returns (uint256) {
function withdraw(address _userAddress, uint256 _wantAmt) external onlyOwner nonReentrant returns (uint256) {
require(_wantAmt > 0, "_wantAmt is 0");
require(_wantAmt > 0, "_wantAmt is 0");
uint256 wantAmt = IERC20(wantAddress).balanceOf(address(this));
uint256 wantAmt = IERC20(wantAddress).balanceOf(address(this));
// Check if strategy has tokens from panic
// Check if strategy has tokens from panic
if (_wantAmt > wantAmt) {
if (_wantAmt > wantAmt) {
_vaultWithdraw(_wantAmt.sub(wantAmt));
_vaultWithdraw(_wantAmt.sub(wantAmt));
wantAmt = IERC20(wantAddress).balanceOf(address(this));
wantAmt = IERC20(wantAddress).balanceOf(address(this));
}
}


if (_wantAmt > wantAmt) {
if (_wantAmt > wantAmt) {
_wantAmt = wantAmt;
_wantAmt = wantAmt;
}
}


if (_wantAmt > wantLockedTotal()) {
if (_wantAmt > wantLockedTotal()) {
_wantAmt = wantLockedTotal();
_wantAmt = wantLockedTotal();
}
}


uint256 sharesRemoved = _wantAmt.mul(sharesTotal).div(wantLockedTotal());
uint256 sharesRemoved = _wantAmt.mul(sharesTotal).div(wantLockedTotal());
if (sharesRemoved > sharesTotal) {
if (sharesRemoved > sharesTotal) {
sharesRemoved = sharesTotal;
sharesRemoved = sharesTotal;
}
}
sharesTotal = sharesTotal.sub(sharesRemoved);
sharesTotal = sharesTotal.sub(sharesRemoved);
// Withdraw fee
// Withdraw fee
uint256 withdrawFee = _wantAmt
uint256 withdrawFee = _wantAmt
.mul(withdrawFeeFactorMax.sub(withdrawFeeFactor))
.mul(withdrawFeeFactorMax.sub(withdrawFeeFactor))
.div(withdrawFeeFactorMax);
.div(withdrawFeeFactorMax);
if (withdrawFee > 0) {
if (withdrawFee > 0) {
IERC20(wantAddress).safeTransfer(withdrawFeeAddress, withdrawFee);
IERC20(wantAddress).safeTransfer(withdrawFeeAddress, withdrawFee);
}
}
_wantAmt = _wantAmt.sub(withdrawFee);
_wantAmt = _wantAmt.sub(withdrawFee);


IERC20(wantAddress).safeTransfer(vaultChefAddress, _wantAmt);
IERC20(wantAddress).safeTransfer(vaultChefAddress, _wantAmt);


return sharesRemoved;
return sharesRemoved;
}
}


// To pay for earn function
// To pay for earn function
function distributeFees(uint256 _earnedAmt) internal returns (uint256) {
function distributeFees(uint256 _earnedAmt) internal returns (uint256) {
if (controllerFee > 0) {
if (controllerFee > 0) {
uint256 fee = _earnedAmt.mul(controllerFee).div(feeMax);
uint256 fee = _earnedAmt.mul(controllerFee).div(feeMax);
_safeSwapWmatic(
_safeSwapWmatic(
fee,
fee,
earnedToWmaticPath,
earnedToWmaticPath,
feeAddress
feeAddress
);
);
_earnedAmt = _earnedAmt.sub(fee);
_earnedAmt = _earnedAmt.sub(fee);
}
}


return _earnedAmt;
return _earnedAmt;
}
}


function distributeRewards(uint256 _earnedAmt) internal returns (uint256) {
function distributeRewards(uint256 _earnedAmt) internal returns (uint256) {
if (rewardRate > 0) {
if (rewardRate > 0) {
uint256 fee = _earnedAmt.mul(rewardRate).div(feeMax);
uint256 fee = _earnedAmt.mul(rewardRate).div(feeMax);
uint256 usdcBefore = IERC20(usdcAddress).balanceOf(address(this));
uint256 usdcBefore = IERC20(usdcAddress).balanceOf(address(this));
_safeSwap(
_safeSwap(
fee,
fee,
earnedToUsdcPath,
earnedToUsdcPath,
address(this)
address(this)
);
);
uint256 usdcAfter = IERC20(usdcAddress).balanceOf(address(this)).sub(usdcBefore);
uint256 usdcAfter = IERC20(usdcAddress).balanceOf(address(this)).sub(usdcBefore);
IStrategyFish(rewardAddress).depositReward(usdcAfter);
IStrategyFish(rewardAddress).depositReward(usdcAfter);
_earnedAmt = _earnedAmt.sub(fee);
_earnedAmt = _earnedAmt.sub(fee);
}
}


return _earnedAmt;
return _earnedAmt;
}
}


function buyBack(uint256 _earnedAmt) internal virtual returns (uint256) {
function buyBack(uint256 _earnedAmt) internal virtual returns (uint256) {
if (buyBackRate > 0) {
if (buyBackRate > 0) {
uint256 buyBackAmt = _earnedAmt.mul(buyBackRate).div(feeMax);
uint256 buyBackAmt = _earnedAmt.mul(buyBackRate).div(feeMax);
_safeSwap(
_safeSwap(
buyBackAmt,
buyBackAmt,
earnedToFishPath,
earnedToFishPath,
buyBackAddress
buyBackAddress
);
);


_earnedAmt = _earnedAmt.sub(buyBackAmt);
_earnedAmt = _earnedAmt.sub(buyBackAmt);
}
}
return _earnedAmt;
return _earnedAmt;
}
}


function resetAllowances() external onlyGov {
function resetAllowances() external onlyGov {
_resetAllowances();
_resetAllowances();
}
}


function pause() external onlyGov {
function pause() external onlyGov {
_pause();
_pause();
}
}


function unpause() external onlyGov {
function unpause() external onlyGov {
_unpause();
_unpause();
_resetAllowances();
_resetAllowances();
}
}


function panic() external onlyGov {
function panic() external onlyGov {
_pause();
_pause();
_emergencyVaultWithdraw();
_emergencyVaultWithdraw();
}
}


function unpanic() external onlyGov {
function unpanic() external onlyGov {
_unpause();
_unpause();
_farm();
_farm();
}
}


function setGov(address _govAddress) external onlyGov {
function setGov(address _govAddress) external onlyGov {
govAddress = _govAddress;
govAddress = _govAddress;
}
}
function setSettings(
function setSettings(
uint256 _controllerFee,
uint256 _controllerFee,
uint256 _rewardRate,
uint256 _rewardRate,
uint256 _buyBackRate,
uint256 _buyBackRate,
uint256 _withdrawFeeFactor,
uint256 _withdrawFeeFactor,
uint256 _slippageFactor,
uint256 _slippageFactor,
uint256 _tolerance,
address _uniRouterAddress
address _uniRouterAddress
) external onlyGov {
) external onlyGov {
require(_controllerFee.add(_rewardRate).add(_buyBackRate) <= feeMaxTotal, "Max fee of 10%");
require(_controllerFee.add(_rewardRate).add(_buyBackRate) <= feeMaxTotal, "Max fee of 10%");
require(_withdrawFeeFactor >= withdrawFeeFactorLL, "_withdrawFeeFactor too low");
require(_withdrawFeeFactor >= withdrawFeeFactorLL, "_withdrawFeeFactor too low");
require(_withdrawFeeFactor <= withdrawFeeFactorMax, "_withdrawFeeFactor too high");
require(_withdrawFeeFactor <= withdrawFeeFactorMax, "_withdrawFeeFactor too high");
require(_slippageFactor <= slippageFactorUL, "_slippageFactor too high");
require(_slippageFactor <= slippageFactorUL, "_slippageFactor too high");
controllerFee = _controllerFee;
controllerFee = _controllerFee;
rewardRate = _rewardRate;
rewardRate = _rewardRate;
buyBackRate = _buyBackRate;
buyBackRate = _buyBackRate;
withdrawFeeFactor = _withdrawFeeFactor;
withdrawFeeFactor = _withdrawFeeFactor;
slippageFactor = _slippageFactor;
slippageFactor = _slippageFactor;
tolerance = _tolerance;
uniRouterAddress = _uniRouterAddress;
uniRouterAddress = _uniRouterAddress;


emit SetSettings(
emit SetSettings(
_controllerFee,
_controllerFee,
_rewardRate,
_rewardRate,
_buyBackRate,
_buyBackRate,
_withdrawFeeFactor,
_withdrawFeeFactor,
_slippageFactor,
_slippageFactor,
_tolerance,
_uniRouterAddress
_uniRouterAddress
);
);
}
}
function _safeSwap(
function _safeSwap(
uint256 _amountIn,
uint256 _amountIn,
address[] memory _path,
address[] memory _path,
address _to
address _to
) internal {
) internal {
uint256[] memory amounts = IUniRouter02(uniRouterAddress).getAmountsOut(_amountIn, _path);
uint256[] memory amounts = IUniRouter02(uniRouterAddress).getAmountsOut(_amountIn, _path);
uint256 amountOut = amounts[amounts.length.sub(1)];
uint256 amountOut = amounts[amounts.length.sub(1)];
if (_path[_path.length.sub(1)] == fishAddress && _to == buyBackAddress) {
burnedAmount = burnedAmount.add(amountOut);
}


IUniRouter02(uniRouterAddress).swapExactTokensForTokens(
IUniRouter02(uniRouterAddress).swapExactTokensForTokens(
_amountIn,
_amountIn,
amountOut.mul(slippageFactor).div(1000),
amountOut.mul(slippageFactor).div(1000),
_path,
_path,
_to,
_to,
now.add(600)
now.add(600)
);
);
}
}
function _safeSwapWmatic(
function _safeSwapWmatic(
uint256 _amountIn,
uint256 _amountIn,
address[] memory _path,
address[] memory _path,
address _to
address _to
) internal {
) internal {
uint256[] memory amounts = IUniRouter02(uniRouterAddress).getAmountsOut(_amountIn, _path);
uint256[] memory amounts = IUniRouter02(uniRouterAddress).getAmountsOut(_amountIn, _path);
uint256 amountOut = amounts[amounts.length.sub(1)];
uint256 amountOut = amounts[amounts.length.sub(1)];


IUniRouter02(uniRouterAddress).swapExactTokensForETH(
IUniRouter02(uniRouterAddress).swapExactTokensForETH(
_amountIn,
_amountIn,
amountOut.mul(slippageFactor).div(1000),
amountOut.mul(slippageFactor).div(1000),
_path,
_path,
_to,
_to,
now.add(600)
now.add(600)
);
);
}
}
}
}