Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ reports/
# Node/npm
node_modules/
.DS_Store
.env.local
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,22 @@ interface ChefLike {
returns (uint256 amount, uint256 rewardDebt);
}

contract Strategy0xDAOStaker is BaseStrategy {
contract GenericMasterChefStrategy is BaseStrategy {
using SafeERC20 for IERC20;
using Address for address;
using SafeMath for uint256;

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

ChefLike public constant masterchef =
ChefLike(0xa7821C3e9fC1bF961e280510c471031120716c3d);
IERC20 public constant emissionToken =
IERC20(0xc165d941481e68696f43EE6E99BFB2B23E0E3114); // the token we receive for staking, 0XD
ChefLike public masterchef;
IERC20 public emissionToken;
IERC20 public poolTwoSecondToken;

// swap stuff
address internal constant spookyRouter =
0xF491e7B69E4244ad4002BC14e878a34207E38c29;
address internal constant spiritRouter =
0xF491e7B69E4244ad4002BC14e878a34207E38c29;
ICurveFi internal constant mimPool =
ICurveFi(0x2dd7C9371965472E5A5fD28fbE165007c61439E1); // Curve's MIM-USDC-USDT pool
ICurveFi internal constant daiPool =
Expand All @@ -106,6 +107,12 @@ contract Strategy0xDAOStaker is BaseStrategy {
IERC20 internal constant mim =
IERC20(0x82f0B8B456c1A451378467398982d4834b6829c1);

bool public autoSell;
uint256 public maxSell; //set to zero for unlimited

bool public useSpiritPartOne;
bool public useSpiritPartTwo;

uint256 public pid; // the pool ID we are staking for

string internal stratName; // we use this for our strategy's name on cloning
Expand All @@ -119,9 +126,13 @@ contract Strategy0xDAOStaker is BaseStrategy {
constructor(
address _vault,
uint256 _pid,
string memory _name
string memory _name,
address _masterchef,
address _emissionToken,
address _poolTwoSecondToken,
bool _autoSell
) public BaseStrategy(_vault) {
_initializeStrat(_pid, _name);
_initializeStrat(_pid, _name, _masterchef, _emissionToken, _poolTwoSecondToken, _autoSell);
}

/* ========== CLONING ========== */
Expand All @@ -135,7 +146,11 @@ contract Strategy0xDAOStaker is BaseStrategy {
address _rewards,
address _keeper,
uint256 _pid,
string memory _name
string memory _name,
address _masterchef,
address _emissionToken,
address _poolTwoSecondToken,
bool _autoSell
) external returns (address newStrategy) {
require(isOriginal);
// Copied from https://github.com/optionality/clone-factory/blob/master/contracts/CloneFactory.sol
Expand All @@ -155,13 +170,13 @@ contract Strategy0xDAOStaker is BaseStrategy {
newStrategy := create(0, clone_code, 0x37)
}

Strategy0xDAOStaker(newStrategy).initialize(
GenericMasterChefStrategy(newStrategy).initialize(
_vault,
_strategist,
_rewards,
_keeper,
_pid,
_name
_name, _masterchef, _emissionToken, _poolTwoSecondToken, _autoSell
);

emit Cloned(newStrategy);
Expand All @@ -174,21 +189,31 @@ contract Strategy0xDAOStaker is BaseStrategy {
address _rewards,
address _keeper,
uint256 _pid,
string memory _name
string memory _name,
address _masterchef,
address _emissionToken,
address _poolTwoSecondToken,
bool _autoSell
) public {
_initialize(_vault, _strategist, _rewards, _keeper);
_initializeStrat(_pid, _name);
_initializeStrat(_pid, _name, _masterchef, _emissionToken, _poolTwoSecondToken, _autoSell);
}

// this is called by our original strategy, as well as any clones
function _initializeStrat(uint256 _pid, string memory _name) internal {
function _initializeStrat(uint256 _pid, string memory _name, address _masterchef, address _emissionToken, address _poolTwoSecondToken, bool _autoSell) internal {

masterchef = ChefLike(_masterchef);
emissionToken = IERC20(_emissionToken);
poolTwoSecondToken = IERC20(_poolTwoSecondToken);
// initialize variables
maxReportDelay = 43200; // 1/2 day in seconds, if we hit this then harvestTrigger = True
healthCheck = address(0xf13Cd6887C62B5beC145e30c38c4938c5E627fe0); // Fantom common health check

// set our strategy's name
stratName = _name;

autoSell = _autoSell;

// make sure that we used the correct pid
pid = _pid;
(address poolToken, , , ) = masterchef.poolInfo(pid);
Expand All @@ -198,11 +223,12 @@ contract Strategy0xDAOStaker is BaseStrategy {
minHarvestCredit = type(uint256).max;

// add approvals on all tokens
usdc.approve(spookyRouter, type(uint256).max);
usdc.approve(address(mimPool), type(uint256).max);
usdc.approve(address(daiPool), type(uint256).max);
want.approve(address(masterchef), type(uint256).max);

emissionToken.approve(spookyRouter, type(uint256).max);
poolTwoSecondToken.approve(spookyRouter, type(uint256).max);
emissionToken.approve(spiritRouter, type(uint256).max);
poolTwoSecondToken.approve(spiritRouter, type(uint256).max);
want.approve(address(masterchef), type(uint256).max);
}

/* ========== VIEWS ========== */
Expand Down Expand Up @@ -242,7 +268,7 @@ contract Strategy0xDAOStaker is BaseStrategy {

// if we have emissionToken to sell, then sell some of it
uint256 emissionTokenBalance = emissionToken.balanceOf(address(this));
if (emissionTokenBalance > 0) {
if (emissionTokenBalance > 0 && autoSell) {
// sell our emissionToken
_sell(emissionTokenBalance);
}
Expand All @@ -264,7 +290,7 @@ contract Strategy0xDAOStaker is BaseStrategy {
return (_profit, _loss, _debtPayment);
}

if (assets > debt) {
if (assets >= debt) {
_debtPayment = _debtOutstanding;
_profit = assets - debt;

Expand Down Expand Up @@ -348,86 +374,82 @@ contract Strategy0xDAOStaker is BaseStrategy {
}

// send our claimed emissionToken to the new strategy
emissionToken.safeTransfer(
_newStrategy,
emissionToken.balanceOf(address(this))
);
uint256 emis = emissionToken.balanceOf(address(this));
if(emis > 0){
emissionToken.safeTransfer(
_newStrategy,
emis
);
}

}

///@notice Only do this if absolutely necessary; as assets will be withdrawn but rewards won't be claimed.
function emergencyWithdraw() external onlyEmergencyAuthorized {
masterchef.emergencyWithdraw(pid);
}

function manualSell(uint256 _amount) external onlyEmergencyAuthorized {
_sell(_amount);
}

// sell from reward token to want
function _sell(uint256 _amount) internal {
// sell our emission token for usdc

if(maxSell > 0){
_amount = Math.min(maxSell, _amount);
}


// sell our emission token for pool two second token
address[] memory emissionTokenPath = new address[](2);
emissionTokenPath[0] = address(emissionToken);
emissionTokenPath[1] = address(usdc);
emissionTokenPath[1] = address(poolTwoSecondToken);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might this ever need to be swapped via weth/wftm? In the case where address(want) == address(poolTwoSecondToken)?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree, one of the vaults is WFTM and the pool 2 is with WFTM, so we shouldn't need the second sell for it

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, this should be handled just fine as-is

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

telegram-cloud-photo-size-4-6037309243520038344-x
if want == secondtoken then we dont do the second


IUniswapV2Router02(spookyRouter).swapExactTokensForTokens(
address router = useSpiritPartOne? spiritRouter: spookyRouter;

IUniswapV2Router02(router).swapExactTokensForTokens(
_amount,
uint256(0),
emissionTokenPath,
address(this),
block.timestamp
);

if (address(want) == address(usdc)) {
if (address(want) == address(poolTwoSecondToken)) {
return;
}

// sell our USDC for want
uint256 usdcBalance = usdc.balanceOf(address(this));
if (address(want) == address(wftm)) {
// sell our usdc for want with spooky
address[] memory usdcSwapPath = new address[](2);
usdcSwapPath[0] = address(usdc);
usdcSwapPath[1] = address(want);
_amount = poolTwoSecondToken.balanceOf(address(this));

if(address(poolTwoSecondToken) != address(wftm)){
emissionTokenPath[0] = address(poolTwoSecondToken);
emissionTokenPath[1] = address(wftm);
IUniswapV2Router02(spookyRouter).swapExactTokensForTokens(
usdcBalance,
_amount,
uint256(0),
usdcSwapPath,
emissionTokenPath,
address(this),
block.timestamp
);
} else if (address(want) == address(weth)) {
// sell our usdc for want with spooky
address[] memory usdcSwapPath = new address[](3);
usdcSwapPath[0] = address(usdc);
usdcSwapPath[1] = address(wftm);
usdcSwapPath[2] = address(weth);
if (address(want) == address(wftm)) {
return;
}
_amount = wftm.balanceOf(address(this));
}


IUniswapV2Router02(spookyRouter).swapExactTokensForTokens(
usdcBalance,
uint256(0),
usdcSwapPath,
address(this),
block.timestamp
);
} else if (address(want) == address(dai)) {
// sell our usdc for want with curve
daiPool.exchange(1, 0, usdcBalance, 0);
} else if (address(want) == address(mim)) {
// sell our usdc for want with curve
mimPool.exchange(2, 0, usdcBalance, 0);
} else if (address(want) == address(wbtc)) {
// sell our usdc for want with spooky
address[] memory usdcSwapPath = new address[](3);
usdcSwapPath[0] = address(usdc);
usdcSwapPath[1] = address(wftm);
usdcSwapPath[2] = address(wbtc);
router = useSpiritPartTwo? spiritRouter: spookyRouter;
emissionTokenPath[0] = address(wftm);
emissionTokenPath[1] = address(want);

IUniswapV2Router02(spookyRouter).swapExactTokensForTokens(
usdcBalance,
uint256(0),
usdcSwapPath,
address(this),
block.timestamp
);
}
IUniswapV2Router02(router).swapExactTokensForTokens(
_amount,
uint256(0),
emissionTokenPath,
address(this),
block.timestamp
);
}

function protectedTokens()
Expand Down Expand Up @@ -489,4 +511,34 @@ contract Strategy0xDAOStaker is BaseStrategy {
{
minHarvestCredit = _minHarvestCredit;
}

///@notice autosell if pools are liquid enough
function setAutoSell(bool _autoSell)
external
onlyEmergencyAuthorized
{
autoSell = _autoSell;
}

///@notice set a max sell for illiquid pools
function setMaxSell(uint256 _maxSell)
external
onlyEmergencyAuthorized
{
maxSell = _maxSell;
}

function setUseSpiritOne(bool _useSpirit)
external
onlyEmergencyAuthorized
{
useSpiritPartOne = _useSpirit;
}

function setUseSpiritTwo(bool _useSpirit)
external
onlyEmergencyAuthorized
{
useSpiritPartTwo = _useSpirit;
}
}
Loading