Skip to content

Commit

Permalink
Resolve compiler issues
Browse files Browse the repository at this point in the history
  • Loading branch information
steve0xp committed Feb 8, 2023
1 parent 75f754f commit 0c3dde6
Show file tree
Hide file tree
Showing 13 changed files with 478 additions and 48 deletions.
3 changes: 1 addition & 2 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
@yearnvaults/=lib/yearn-vaults/
@openzeppelin/=lib/openzeppelin-contracts/
forge-std/=lib/forge-std/src/
@mellowvaults/=lib/mellow-vaults/
forge-std/=lib/forge-std/src/
86 changes: 47 additions & 39 deletions src/Strategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

pragma solidity ^0.8.12;

import {BaseStrategy, StrategyParams} from "@yearnvaults/contracts/BaseStrategy.sol";
import {BaseStrategy, StrategyParams, IBaseFee} from "@yearnvaults/contracts/BaseStrategy.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/interfaces/IERC20MetaData.sol";
import {IGearboxRootVault} from "./interfaces/Mellow/IGearboxRootVault.sol"; // specific GearboxRootVault for Fearless Gearbox Strategies

/// @title StrategyMellow-Gearbox_wETH
Expand All @@ -19,6 +21,11 @@ contract Strategy is BaseStrategy {
IGearboxRootVault public gearboxRootVault;
IERC20 public mellowLPT;
bool internal isOriginal = true;
address public tradeFactory = address(0);

// keeper vars
uint256 public harvestProfitMin; // minimum size in wETH (18 decimals) that we want to harvest
uint256 public harvestProfitMax; // maximum size in wETH (18 decimals) that we want to harvest

/// EVENTS

Expand Down Expand Up @@ -52,7 +59,7 @@ contract Strategy is BaseStrategy {
public
BaseStrategy(_vault)
{
_initializeStrategy(__mellowRootVault);
_initializeStrategy(_mellowRootVault);
}

/// SETTERS
Expand All @@ -64,7 +71,7 @@ contract Strategy is BaseStrategy {
address _rewards,
address _keeper,
address _mellowRootVault
) public override {
) public {
require(address(gearboxRootVault) == address(0)); // @note Only initialize once

_initialize(_vault, _strategist, _rewards, _keeper);
Expand All @@ -78,7 +85,7 @@ contract Strategy is BaseStrategy {
address _rewards,
address _keeper,
address _mellowRootVault
) external override returns (address payable newStrategy) {
) external returns (address payable newStrategy) {
require(isOriginal);

bytes20 addressBytes = bytes20(address(this));
Expand Down Expand Up @@ -128,7 +135,7 @@ contract Strategy is BaseStrategy {

/// @notice called when preparing return to have accounting of losses & gains from the last harvest(), and liquidates positions if rqd
/// @dev Part of Harvest 'workflow' - bot calls `harvest()`, it calls this function && `adjustPosition()`
/// @param debtOutstanding how much wantToken the vault requests
/// @param _debtOutstanding how much wantToken the vault requests
function prepareReturn(uint256 _debtOutstanding)
internal
override
Expand All @@ -141,7 +148,7 @@ contract Strategy is BaseStrategy {
{
// Run initial profit + loss calculations.

uint256 _totalAssets = estimatedTotalAssets(); // STEVE calculating based on gearbox values, good.
uint256 _totalAssets = estimatedTotalAssets();
uint256 _totalDebt = vault.strategies(address(this)).totalDebt;

if (_totalAssets >= _totalDebt) {
Expand Down Expand Up @@ -189,16 +196,19 @@ contract Strategy is BaseStrategy {
}

// Invest the rest of the want
uint256 _excessWETH = _WETHBal - _debtOutstanding;
uint256 lptMinimum = 0; // TODO - add minimum LP amount to receive
uint256[] memory _excessWETH = new uint256[](1);
_excessWETH[0] = _WETHBal - _debtOutstanding;
uint256[] memory _lptMinimum = new uint256[](1);
_lptMinimum[0] = 0; // TODO - add minimum LP amount to receive
uint256 lptMinimum = 0;

uint256 lpAmount = gearboxRootVault.deposit(
uint256[] memory lpAmount = gearboxRootVault.deposit(
_excessWETH,
lptMinimum,
""
); // TODO - instill checks to ensure we get at least a certain amount of LPTs back

assert(lpAmount >= lptMinimum);
assert(lpAmount[0] >= _lptMinimum[0]);
}

/// @notice Liquidate / Withdraw up to `_amountNeeded` of `want` of this strategy's positions, irregardless of slippage. Mellow Fearless Gearbox strategies have `periods` where wantTokens are locked up. If the Yearn strategy's tokens exist but are locked up, this function registers a withdraw to be called after `period` elapses and wantTokens from Gearbox credit account are freed up.
Expand Down Expand Up @@ -240,10 +250,13 @@ contract Strategy is BaseStrategy {
// Cannot withdraw more than withdrawable
_amountToWithdraw = Math.min(_primaryTokensToClaim, _amountToWithdraw);

// TODO - sort out what to put for vaultOptions
bytes[] memory vaultOptions = new bytes[](2);

if (_primaryTokensToClaim > 0) {
// gearboxRootVault doesn't allow amount specified for withdrawal
// TODO - put checks here to ensure we are getting a minimal slippage, if any upon redemption
try gearboxRootVault.withdraw(address(this), "") {
try gearboxRootVault.withdraw(address(this), vaultOptions) {
uint256 _newLiquidAssets = wantBalance();
_liquidatedAmount = Math.min(_newLiquidAssets, _amountNeeded);

Expand Down Expand Up @@ -273,7 +286,7 @@ contract Strategy is BaseStrategy {
}

/// @inheritdoc BaseStrategy
/// NOTE - does this transfer want and non-want tokens? I've read different things throughout my research
/// @dev NOTE - does this transfer want and non-want tokens? I've read different things throughout my research
function prepareMigration(address _newStrategy) internal override {
// from angle strategy: wantToken is transferred by the base contract's migrate function
// TODO - possibly transfer CRV
Expand Down Expand Up @@ -305,8 +318,8 @@ contract Strategy is BaseStrategy {

/// @inheritdoc BaseStrategy
/// @dev mellow fearless gearbox strategies have `periods` where wantToken is locked up in the strategy. Custom logic is required to check if there are `wantTokens` available to claim, or if totalPosition is locked up but has increased enough since initial `deposit`
/// TODO - Question - where does callCostinETH get used? How is it used to check that the gas cost of the call won't be too much?
function harvestTrigger(uint256 callCostinEth)
/// TODO - Question - where does callCostinWei get used? How is it used to check that the gas cost of the call won't be too much?
function harvestTrigger(uint256 callCostInWei)
public
view
override
Expand All @@ -327,13 +340,7 @@ contract Strategy is BaseStrategy {
return super.harvestTrigger(callCostInWei);
}

/// @inheritdoc BaseStrategy
/// TODO: not sure if this address is the one to use still
function isBaseFeeAcceptable() internal view returns (bool) {
return
IBaseFee(0xb5e1CAcB567d98faaDB60a1fD4820720141f064F)
.isCurrentBaseFeeAcceptable();
}
/// TODO: not sure if we use the BaseStrategy isBaseFeeAcceptable()

/// @notice The value in wETH that our claimable rewards are worth (18 decimals)
function claimableProfit() public view returns (uint256) {
Expand Down Expand Up @@ -402,22 +409,23 @@ contract Strategy is BaseStrategy {

// ---------------------- YSWAPS FUNCTIONS ----------------------

function setTradeFactory(address _tradeFactory) external onlyGovernance {
if (tradeFactory != address(0)) {
_removeTradeFactoryPermissions();
}
angleToken.safeApprove(_tradeFactory, type(uint256).max);
ITradeFactory tf = ITradeFactory(_tradeFactory);
tf.enable(address(angleToken), address(want));
tradeFactory = _tradeFactory;
}

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

function _removeTradeFactoryPermissions() internal {
angleToken.safeApprove(tradeFactory, 0);
tradeFactory = address(0);
}
// TODO - Sort these functions out for Mellow-Gearbox Strategy
// function setTradeFactory(address _tradeFactory) external onlyGovernance {
// if (tradeFactory != address(0)) {
// _removeTradeFactoryPermissions();
// }
// angleToken.safeApprove(_tradeFactory, type(uint256).max);
// ITradeFactory tf = ITradeFactory(_tradeFactory);
// tf.enable(address(angleToken), address(want));
// tradeFactory = _tradeFactory;
// }

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

// function _removeTradeFactoryPermissions() internal {
// angleToken.safeApprove(tradeFactory, 0);
// tradeFactory = address(0);
// }
}
2 changes: 1 addition & 1 deletion src/interfaces/Mellow/IAggregateVault.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
pragma solidity ^0.8.12;

import "./IVault.sol";
import "./IVaultRoot.sol";
Expand Down
16 changes: 16 additions & 0 deletions src/interfaces/Mellow/IDefaultAccessControl.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;

import "@openzeppelin/contracts/access/IAccessControlEnumerable.sol";

interface IDefaultAccessControl is IAccessControlEnumerable {
/// @notice Checks that the address is contract admin.
/// @param who Address to check
/// @return `true` if who is admin, `false` otherwise
function isAdmin(address who) external view returns (bool);

/// @notice Checks that the address is contract admin.
/// @param who Address to check
/// @return `true` if who is operator, `false` otherwise
function isOperator(address who) external view returns (bool);
}
20 changes: 20 additions & 0 deletions src/interfaces/Mellow/IERC1271.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;

interface IERC1271 {
/// @notice Verifies offchain signature.
/// @dev Should return whether the signature provided is valid for the provided hash
///
/// MUST return the bytes4 magic value 0x1626ba7e when function passes.
///
/// MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
///
/// MUST allow external calls
/// @param _hash Hash of the data to be signed
/// @param _signature Signature byte array associated with _hash
/// @return magicValue 0x1626ba7e if valid, 0xffffffff otherwise
function isValidSignature(bytes32 _hash, bytes memory _signature)
external
view
returns (bytes4 magicValue);
}
2 changes: 1 addition & 1 deletion src/interfaces/Mellow/IGearboxRootVault.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
pragma solidity ^0.8.12;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IAggregateVault.sol";
Expand Down
82 changes: 82 additions & 0 deletions src/interfaces/Mellow/IIntegrationVault.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;

import "./IERC1271.sol";
import "./IVault.sol";

interface IIntegrationVault is IVault, IERC1271 {
/// @notice Pushes tokens on the vault balance to the underlying protocol. For example, for Yearn this operation will take USDC from
/// the contract balance and convert it to yUSDC.
/// @dev Tokens **must** be a subset of Vault Tokens. However, the convention is that if tokenAmount == 0 it is the same as token is missing.
///
/// Also notice that this operation doesn't guarantee that tokenAmounts will be invested in full.
/// @param tokens Tokens to push
/// @param tokenAmounts Amounts of tokens to push
/// @param options Additional options that could be needed for some vaults. E.g. for Uniswap this could be `deadline` param. For the exact bytes structure see concrete vault descriptions
/// @return actualTokenAmounts The amounts actually invested. It could be less than tokenAmounts (but not higher)
function push(
address[] memory tokens,
uint256[] memory tokenAmounts,
bytes memory options
) external returns (uint256[] memory actualTokenAmounts);

/// @notice The same as `push` method above but transfers tokens to vault balance prior to calling push.
/// After the `push` it returns all the leftover tokens back (`push` method doesn't guarantee that tokenAmounts will be invested in full).
/// @param tokens Tokens to push
/// @param tokenAmounts Amounts of tokens to push
/// @param options Additional options that could be needed for some vaults. E.g. for Uniswap this could be `deadline` param. For the exact bytes structure see concrete vault descriptions
/// @return actualTokenAmounts The amounts actually invested. It could be less than tokenAmounts (but not higher)
function transferAndPush(
address from,
address[] memory tokens,
uint256[] memory tokenAmounts,
bytes memory options
) external returns (uint256[] memory actualTokenAmounts);

/// @notice Pulls tokens from the underlying protocol to the `to` address.
/// @dev Can only be called but Vault Owner or Strategy. Vault owner is the owner of NFT for this vault in VaultManager.
/// Strategy is approved address for the vault NFT.
/// When called by vault owner this method just pulls the tokens from the protocol to the `to` address
/// When called by strategy on vault other than zero vault it pulls the tokens to zero vault (required `to` == zero vault)
/// When called by strategy on zero vault it pulls the tokens to zero vault, pushes tokens on the `to` vault, and reclaims everything that's left.
/// Thus any vault other than zero vault cannot have any tokens on it
///
/// Tokens **must** be a subset of Vault Tokens. However, the convention is that if tokenAmount == 0 it is the same as token is missing.
///
/// Pull is fulfilled on the best effort basis, i.e. if the tokenAmounts overflows available funds it withdraws all the funds.
/// @param to Address to receive the tokens
/// @param tokens Tokens to pull
/// @param tokenAmounts Amounts of tokens to pull
/// @param options Additional options that could be needed for some vaults. E.g. for Uniswap this could be `deadline` param. For the exact bytes structure see concrete vault descriptions
/// @return actualTokenAmounts The amounts actually withdrawn. It could be less than tokenAmounts (but not higher)
function pull(
address to,
address[] memory tokens,
uint256[] memory tokenAmounts,
bytes memory options
) external returns (uint256[] memory actualTokenAmounts);

/// @notice Claim ERC20 tokens from vault balance to zero vault.
/// @dev Cannot be called from zero vault.
/// @param tokens Tokens to claim
/// @return actualTokenAmounts Amounts reclaimed
function reclaimTokens(address[] memory tokens)
external
returns (uint256[] memory actualTokenAmounts);

/// @notice Execute one of whitelisted calls.
/// @dev Can only be called by Vault Owner or Strategy. Vault owner is the owner of NFT for this vault in VaultManager.
/// Strategy is approved address for the vault NFT.
///
/// Since this method allows sending arbitrary transactions, the destinations of the calls
/// are whitelisted by Protocol Governance.
/// @param to Address of the reward pool
/// @param selector Selector of the call
/// @param data Abi encoded parameters to `to::selector`
/// @return result Result of execution of the call
function externalCall(
address to,
bytes4 selector,
bytes memory data
) external payable returns (bytes memory result);
}
Loading

0 comments on commit 0c3dde6

Please sign in to comment.