Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add gnosis chain with sushiswap, curve, and swapr strategies #187

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
264 changes: 264 additions & 0 deletions src/strategies/gnosis/strategy-base-v2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
pragma solidity ^0.6.7;

import "../../lib/erc20.sol";
import "../../lib/safe-math.sol";

import "../../interfaces/jar.sol";
import "../../interfaces/staking-rewards.sol";
import "../../interfaces/masterchef.sol";
import "../../interfaces/uniswapv2.sol";
import "../../interfaces/controller.sol";

// Strategy Contract Basics

abstract contract StrategyBase {
using SafeERC20 for IERC20;
using Address for address;
using SafeMath for uint256;

// Perfomance fees - start with 20%
uint256 public performanceTreasuryFee = 420;
uint256 public constant performanceTreasuryMax = 10000;

uint256 public performanceDevFee = 0;
uint256 public constant performanceDevMax = 10000;

// Withdrawal fee 0%
// - 0% to treasury
// - 0% to dev fund
uint256 public withdrawalTreasuryFee = 0;
uint256 public constant withdrawalTreasuryMax = 100000;

uint256 public withdrawalDevFundFee = 0;
uint256 public constant withdrawalDevFundMax = 100000;

// Tokens
address public want;
address public constant xdai = 0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d;
address public constant native = xdai;

// User accounts
address public governance;
address public controller;
address public strategist;
address public timelock;

// Dex
address public uniV2Router = 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506;

mapping(address => bool) public harvesters;

constructor(
address _want,
address _governance,
address _strategist,
address _controller,
address _timelock
) public {
require(_want != address(0));
require(_governance != address(0));
require(_strategist != address(0));
require(_controller != address(0));
require(_timelock != address(0));

want = _want;
governance = _governance;
strategist = _strategist;
controller = _controller;
timelock = _timelock;
}

// **** Modifiers **** //

modifier onlyBenevolent() {
require(harvesters[msg.sender] || msg.sender == governance || msg.sender == strategist);
_;
}

// **** Views **** //

function balanceOfWant() public view returns (uint256) {
return IERC20(want).balanceOf(address(this));
}

function balanceOfPool() public view virtual returns (uint256);

function balanceOf() public view returns (uint256) {
return balanceOfWant().add(balanceOfPool());
}

function getName() external pure virtual returns (string memory);

function getHarvestable() external view virtual returns (address[] memory, uint256[] memory);

// **** Setters **** //

function whitelistHarvesters(address[] calldata _harvesters) external {
require(msg.sender == governance || msg.sender == strategist || harvesters[msg.sender], "not authorized");

for (uint256 i = 0; i < _harvesters.length; i++) {
harvesters[_harvesters[i]] = true;
}
}

function revokeHarvesters(address[] calldata _harvesters) external {
require(msg.sender == governance || msg.sender == strategist, "not authorized");

for (uint256 i = 0; i < _harvesters.length; i++) {
harvesters[_harvesters[i]] = false;
}
}

function setWithdrawalDevFundFee(uint256 _withdrawalDevFundFee) external {
require(msg.sender == timelock, "!timelock");
withdrawalDevFundFee = _withdrawalDevFundFee;
}

function setWithdrawalTreasuryFee(uint256 _withdrawalTreasuryFee) external {
require(msg.sender == timelock, "!timelock");
withdrawalTreasuryFee = _withdrawalTreasuryFee;
}

function setPerformanceDevFee(uint256 _performanceDevFee) external {
require(msg.sender == timelock, "!timelock");
performanceDevFee = _performanceDevFee;
}

function setPerformanceTreasuryFee(uint256 _performanceTreasuryFee) external {
require(msg.sender == timelock, "!timelock");
performanceTreasuryFee = _performanceTreasuryFee;
}

function setStrategist(address _strategist) external {
require(msg.sender == governance, "!governance");
strategist = _strategist;
}

function setGovernance(address _governance) external {
require(msg.sender == governance, "!governance");
governance = _governance;
}

function setTimelock(address _timelock) external {
require(msg.sender == timelock, "!timelock");
timelock = _timelock;
}

function setController(address _controller) external {
require(msg.sender == timelock, "!timelock");
controller = _controller;
}

// **** State mutations **** //
function deposit() public virtual;

// Controller only function for creating additional rewards from dust
function withdraw(IERC20 _asset) external returns (uint256 balance) {
require(msg.sender == controller, "!controller");
require(want != address(_asset), "want");
balance = _asset.balanceOf(address(this));
_asset.safeTransfer(controller, balance);
}

// Withdraw partial funds, normally used with a jar withdrawal
function withdraw(uint256 _amount) external {
require(msg.sender == controller, "!controller");
uint256 _balance = IERC20(want).balanceOf(address(this));
if (_balance < _amount) {
_amount = _withdrawSome(_amount.sub(_balance));
_amount = _amount.add(_balance);
}

uint256 _feeDev = _amount.mul(withdrawalDevFundFee).div(withdrawalDevFundMax);
IERC20(want).safeTransfer(IController(controller).devfund(), _feeDev);

uint256 _feeTreasury = _amount.mul(withdrawalTreasuryFee).div(withdrawalTreasuryMax);
IERC20(want).safeTransfer(IController(controller).treasury(), _feeTreasury);

address _jar = IController(controller).jars(address(want));
require(_jar != address(0), "!jar"); // additional protection so we don't burn the funds

IERC20(want).safeTransfer(_jar, _amount.sub(_feeDev).sub(_feeTreasury));
}

// Withdraw funds, used to swap between strategies
function withdrawForSwap(uint256 _amount) external returns (uint256 balance) {
require(msg.sender == controller, "!controller");
_withdrawSome(_amount);

balance = IERC20(want).balanceOf(address(this));

address _jar = IController(controller).jars(address(want));
require(_jar != address(0), "!jar");
IERC20(want).safeTransfer(_jar, balance);
}

// Withdraw all funds, normally used when migrating strategies
function withdrawAll() external returns (uint256 balance) {
require(msg.sender == controller, "!controller");
_withdrawAll();

balance = IERC20(want).balanceOf(address(this));

address _jar = IController(controller).jars(address(want));
require(_jar != address(0), "!jar"); // additional protection so we don't burn the funds
IERC20(want).safeTransfer(_jar, balance);
}

function _withdrawAll() internal {
_withdrawSome(balanceOfPool());
}

function _withdrawSome(uint256 _amount) internal virtual returns (uint256);

function harvest() public virtual;

// **** Emergency functions ****

function execute(address _target, bytes memory _data) public payable returns (bytes memory response) {
require(msg.sender == timelock, "!timelock");
require(_target != address(0), "!target");

// call contract in current context
assembly {
let succeeded := delegatecall(sub(gas(), 5000), _target, add(_data, 0x20), mload(_data), 0, 0)
let size := returndatasize()

response := mload(0x40)
mstore(0x40, add(response, and(add(add(size, 0x20), 0x1f), not(0x1f))))
mstore(response, size)
returndatacopy(add(response, 0x20), 0, size)

switch iszero(succeeded)
case 1 {
// throw if delegatecall failed
revert(add(response, 0x20), size)
}
}
}

function _swapDefaultWithPath(address[] memory path, uint256 _amount) internal {
require(path[1] != address(0));
UniswapRouterV2(uniV2Router).swapExactTokensForTokens(_amount, 0, path, address(this), now.add(60));
}

function _swapWithPath(
address router,
address[] memory path,
uint256 _amount
) internal {
require(path[1] != address(0));
UniswapRouterV2(router).swapExactTokensForTokens(_amount, 0, path, address(this), now.add(60));
}

function _distributePerformanceFeesNative() internal {
uint256 _native = IERC20(native).balanceOf(address(this));
if (_native > 0) {
// Treasury fees
IERC20(native).safeTransfer(
IController(controller).treasury(),
_native.mul(performanceTreasuryFee).div(performanceTreasuryMax)
);
}
}
}
Loading