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
8 changes: 7 additions & 1 deletion contracts/MorphoAaveStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pragma experimental ABIEncoderV2;
import "./MorphoStrategy.sol";

contract MorphoAaveStrategy is MorphoStrategy {
// TODO: change the reward token (last param) if needed, current is AAVE
constructor(
address _vault,
address _poolToken,
Expand All @@ -19,7 +20,12 @@ contract MorphoAaveStrategy is MorphoStrategy {
_poolToken,
_strategyName,
0x777777c9898D384F785Ee44Acfe945efDFf5f3E0,
0x507fA343d0A90786d86C7cd885f5C49263A91FF4
0x507fA343d0A90786d86C7cd885f5C49263A91FF4,
0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9
)
{}

function claimRewardToken() internal override {
// TODO: implement function for claiming rewards when added to Morpho Aave
}
}
130 changes: 5 additions & 125 deletions contracts/MorphoCompoundStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,8 @@ pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import "./MorphoStrategy.sol";
import "../interfaces/IUniswapV2Router01.sol";
import "../interfaces/ySwap/ITradeFactory.sol";

contract MorphoCompoundStrategy is MorphoStrategy {
// ySwap TradeFactory:
address public tradeFactory;
// Router used for swapping reward token (COMP)
IUniswapV2Router01 public currentV2Router;
// Minimum amount of COMP to be claimed or sold
uint256 public minCompToClaimOrSell = 0.1 ether;

address private constant COMP = 0xc00e94Cb662C3520282E6f5717214004A7f26888;
address private constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
IUniswapV2Router01 private constant UNI_V2_ROUTER =
IUniswapV2Router01(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
IUniswapV2Router01 private constant SUSHI_V2_ROUTER =
IUniswapV2Router01(0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F);

constructor(
address _vault,
address _poolToken,
Expand All @@ -35,124 +19,20 @@ contract MorphoCompoundStrategy is MorphoStrategy {
_poolToken,
_strategyName,
0x8888882f8f843896699869179fB6E4f7e3B58888,
0x930f1b46e1D081Ec1524efD95752bE3eCe51EF67
0x930f1b46e1D081Ec1524efD95752bE3eCe51EF67,
0xc00e94Cb662C3520282E6f5717214004A7f26888
)
{
currentV2Router = SUSHI_V2_ROUTER;
IERC20 comp = IERC20(COMP);
// COMP max allowance is uint96
comp.safeApprove(address(SUSHI_V2_ROUTER), type(uint96).max);
comp.safeApprove(address(UNI_V2_ROUTER), type(uint96).max);
}

// ---------------------- MorphoStrategy overriden contract function ----------------
function prepareReturn(uint256 _debtOutstanding)
internal
override
returns (
uint256 _profit,
uint256 _loss,
uint256 _debtPayment
)
{
claimComp();
sellComp();

return super.prepareReturn(_debtOutstanding);
}
{}

function prepareMigration(address _newStrategy) internal override {
super.prepareMigration(_newStrategy);

claimComp();
IERC20 comp = IERC20(COMP);
comp.safeTransfer(_newStrategy, comp.balanceOf(address(this)));
}

// ---------------------- functions for claiming reward token COMP ------------------
function claimComp() internal {
function claimRewardToken() internal override {
address[] memory pools = new address[](1);
pools[0] = poolToken;
if (
lens.getUserUnclaimedRewards(pools, address(this)) >
minCompToClaimOrSell
minRewardToClaimOrSell
) {
// claim the underlying pool's rewards, currently COMP token
morpho.claimRewards(pools, false);
}
}

// ---------------------- functions for selling reward token COMP -------------------
/**
* @notice
* Set toggle v2 swap router between sushiv2 and univ2
*/
function setToggleV2Router() external onlyAuthorized {
currentV2Router = currentV2Router == SUSHI_V2_ROUTER
? UNI_V2_ROUTER
: SUSHI_V2_ROUTER;
}

/**
* @notice
* Set the minimum amount of compount token need to claim or sell it for `want` token.
*/
function setMinCompToClaimOrSell(uint256 _minCompToClaimOrSell)
external
onlyAuthorized
{
minCompToClaimOrSell = _minCompToClaimOrSell;
}

function sellComp() internal {
if (tradeFactory == address(0)) {
uint256 compBalance = IERC20(COMP).balanceOf(address(this));
if (compBalance > minCompToClaimOrSell) {
currentV2Router.swapExactTokensForTokens(
compBalance,
0,
getTokenOutPathV2(COMP, address(want)),
address(this),
block.timestamp
);
}
}
}

function getTokenOutPathV2(address _tokenIn, address _tokenOut)
internal
pure
returns (address[] memory _path)
{
bool isWeth = _tokenIn == address(WETH) || _tokenOut == address(WETH);
_path = new address[](isWeth ? 2 : 3);
_path[0] = _tokenIn;

if (isWeth) {
_path[1] = _tokenOut;
} else {
_path[1] = address(WETH);
_path[2] = _tokenOut;
}
}

// ---------------------- YSWAPS FUNCTIONS ----------------------
function setTradeFactory(address _tradeFactory) external onlyGovernance {
if (tradeFactory != address(0)) {
_removeTradeFactoryPermissions();
}
IERC20(COMP).safeApprove(_tradeFactory, type(uint96).max);
ITradeFactory tf = ITradeFactory(_tradeFactory);
tf.enable(COMP, address(want));
tradeFactory = _tradeFactory;
}

function removeTradeFactoryPermissions() external onlyEmergencyAuthorized {
_removeTradeFactoryPermissions();
}

function _removeTradeFactoryPermissions() internal {
IERC20(COMP).safeApprove(tradeFactory, 0);
tradeFactory = address(0);
}
}
111 changes: 110 additions & 1 deletion contracts/MorphoStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,27 @@ import "@openzeppelin/contracts/math/Math.sol";

import "../interfaces/IMorpho.sol";
import "../interfaces/ILens.sol";
import "../interfaces/IUniswapV2Router01.sol";
import "../interfaces/ySwap/ITradeFactory.sol";

abstract contract MorphoStrategy is BaseStrategy {
using SafeERC20 for IERC20;
using Address for address;
using SafeMath for uint256;

address private constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
IUniswapV2Router01 private constant UNI_V2_ROUTER =
IUniswapV2Router01(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
IUniswapV2Router01 private constant SUSHI_V2_ROUTER =
IUniswapV2Router01(0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F);

// ySwap TradeFactory:
address public tradeFactory;
// Router used for swapping reward token (COMP)
IUniswapV2Router01 public currentV2Router;
// Minimum amount of COMP to be claimed or sold
uint256 public minRewardToClaimOrSell = 0.1 ether;
address public rewardToken;
// Morpho is a contract to handle interaction with the protocol
IMorpho public immutable morpho;
// Lens is a contract to fetch data about Morpho protocol
Expand All @@ -41,15 +56,24 @@ abstract contract MorphoStrategy is BaseStrategy {
address _poolToken,
string memory _strategyName,
address _morpho,
address _lens
address _lens,
address _rewardToken
) public BaseStrategy(_vault) {
poolToken = _poolToken;
strategyName = _strategyName;
lens = ILens(_lens);
morpho = IMorpho(_morpho);
want.safeApprove(_morpho, type(uint256).max);

currentV2Router = SUSHI_V2_ROUTER;
rewardToken = _rewardToken;
IERC20 iRewardToken = IERC20(_rewardToken);
iRewardToken.safeApprove(address(SUSHI_V2_ROUTER), type(uint256).max);
iRewardToken.safeApprove(address(UNI_V2_ROUTER), type(uint256).max);
}

function claimRewardToken() internal virtual;

// ******** BaseStrategy overriden contract function ************

function name() external view override returns (string memory) {
Expand All @@ -72,6 +96,9 @@ abstract contract MorphoStrategy is BaseStrategy {
uint256 _debtPayment
)
{
claimRewardToken();
sellRewardToken();

uint256 totalDebt = vault.strategies(address(this)).totalDebt;
uint256 totalAssetsAfterProfit = estimatedTotalAssets();
_profit = totalAssetsAfterProfit > totalDebt
Expand Down Expand Up @@ -141,6 +168,13 @@ abstract contract MorphoStrategy is BaseStrategy {
// NOTE: Can override `tendTrigger` and `harvestTrigger` if necessary
function prepareMigration(address _newStrategy) internal virtual override {
liquidateAllPositions();

claimRewardToken();
IERC20 iRewardToken = IERC20(rewardToken);
iRewardToken.safeTransfer(
_newStrategy,
iRewardToken.balanceOf(address(this))
);
}

function protectedTokens()
Expand Down Expand Up @@ -205,4 +239,79 @@ abstract contract MorphoStrategy is BaseStrategy {
{
maxGasForMatching = _maxGasForMatching;
}

// ---------------------- functions for selling reward token COMP -------------------
/**
* @notice
* Set toggle v2 swap router between sushiv2 and univ2
*/
function setToggleV2Router() external onlyAuthorized {
currentV2Router = currentV2Router == SUSHI_V2_ROUTER
? UNI_V2_ROUTER
: SUSHI_V2_ROUTER;
}

/**
* @notice
* Set the minimum amount of compount token need to claim or sell it for `want` token.
*/
function setMinRewardToClaimOrSell(uint256 _minRewardToClaimOrSell)
external
onlyAuthorized
{
minRewardToClaimOrSell = _minRewardToClaimOrSell;
}

function sellRewardToken() internal {
if (tradeFactory == address(0)) {
uint256 rewardTokenBalance =
IERC20(rewardToken).balanceOf(address(this));
if (rewardTokenBalance > minRewardToClaimOrSell) {
currentV2Router.swapExactTokensForTokens(
rewardTokenBalance,
0,
getTokenOutPathV2(rewardToken, address(want)),
address(this),
block.timestamp
);
}
}
}

function getTokenOutPathV2(address _tokenIn, address _tokenOut)
internal
pure
returns (address[] memory _path)
{
bool isWeth = _tokenIn == address(WETH) || _tokenOut == address(WETH);
_path = new address[](isWeth ? 2 : 3);
_path[0] = _tokenIn;

if (isWeth) {
_path[1] = _tokenOut;
} else {
_path[1] = address(WETH);
_path[2] = _tokenOut;
}
}

// ---------------------- YSWAPS FUNCTIONS ----------------------
function setTradeFactory(address _tradeFactory) external onlyGovernance {
if (tradeFactory != address(0)) {
_removeTradeFactoryPermissions();
}
IERC20(rewardToken).safeApprove(_tradeFactory, type(uint256).max);
ITradeFactory tf = ITradeFactory(_tradeFactory);
tf.enable(rewardToken, address(want));
tradeFactory = _tradeFactory;
}

function removeTradeFactoryPermissions() external onlyEmergencyAuthorized {
_removeTradeFactoryPermissions();
}

function _removeTradeFactoryPermissions() internal {
IERC20(rewardToken).safeApprove(tradeFactory, 0);
tradeFactory = address(0);
}
}
Loading