Skip to content

Commit

Permalink
updates following review from strategists and security on #8
Browse files Browse the repository at this point in the history
  • Loading branch information
pata.eth committed May 27, 2022
1 parent 54fca6b commit aeb14c7
Show file tree
Hide file tree
Showing 21 changed files with 152 additions and 292 deletions.
20 changes: 10 additions & 10 deletions brownie-config.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
# use Ganache's forked mainnet mode as the default network
# NOTE: You don't *have* to do this, but it is often helpful for testing
networks:
default: ftm-main-fork
default: ftm-main-fork

# automatically fetch contract sources from Etherscan
autofetch_sources: True

# require OpenZepplin Contracts
dependencies:
- yearn/[email protected]
- OpenZeppelin/[email protected]
- yearn/[email protected]-1
- OpenZeppelin/[email protected]

# path remapping to support imports from GitHub/NPM
compiler:
solc:
version:
remappings:
- "@yearnvaults=yearn/[email protected]"
- "@openzeppelin=OpenZeppelin/[email protected]"
solc:
version:
remappings:
- "@yearnvaults=yearn/[email protected]-1"
- "@openzeppelin=OpenZeppelin/[email protected]"

reports:
exclude_contracts:
- SafeMath
exclude_contracts:
- SafeMath
113 changes: 43 additions & 70 deletions contracts/StrategyCurveGeist.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,9 @@ import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/math/Math.sol";

import "./interfaces/curve.sol";
import "./interfaces/yearn.sol";
import {IGauge, IGaugeFactory, ICurveFi} from "./interfaces/curve.sol";
import {IUniswapV2Router02} from "./interfaces/uniswap.sol";
import {
BaseStrategy,
StrategyParams
} from "@yearnvaults/contracts/BaseStrategy.sol";

interface IBaseFee {
function isCurrentBaseFeeAcceptable() external view returns (bool);
}

interface IUniV3 {
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}

function exactInput(ExactInputParams calldata params)
external
payable
returns (uint256 amountOut);
}
import {BaseStrategy, StrategyParams} from "@yearnvaults/contracts/BaseStrategy.sol";

abstract contract StrategyCurveBase is BaseStrategy {
using SafeERC20 for IERC20;
Expand All @@ -46,10 +23,10 @@ abstract contract StrategyCurveBase is BaseStrategy {

// Curve stuff
IGauge public constant gauge =
IGauge(0xF7b9c402c4D6c2eDbA04a7a515b53D11B1E9b2cc); // Curve gauge contract, most are tokenized, held by strategy
IGauge(0xF7b9c402c4D6c2eDbA04a7a515b53D11B1E9b2cc); // Curve gauge contract, most are tokenized, held by strategy

IGaugeFactory public constant gaugeFactory =
IGaugeFactory(0xabC000d88f23Bb45525E447528DBF656A9D55bf5);
IGaugeFactory(0xabC000d88f23Bb45525E447528DBF656A9D55bf5);

// keepCRV stuff
uint256 public keepCRV; // the percentage of CRV we re-lock for boost (in basis points)
Expand Down Expand Up @@ -133,7 +110,23 @@ abstract contract StrategyCurveBase is BaseStrategy {
return balanceOfWant();
}

function _claimRewards() internal {
// Claims any pending CRV
//
// Mints claimable CRV from the factory gauge. Reward tokens are sent to `msg.sender`
gaugeFactory.mint(address(gauge));

// harvest third-party rewards from the gauge, if any
gauge.claim_rewards();
}

function claimRewards() external onlyVaultManagers {
_claimRewards();
}

function prepareMigration(address _newStrategy) internal override {
// Withdraw LP tokens from the gauge. The transfer to the new strategy is done
// by migrate() in BaseStrategy.sol
uint256 _stakedBal = stakedBalance();
if (_stakedBal > 0) {
gauge.withdraw(_stakedBal);
Expand Down Expand Up @@ -204,10 +197,8 @@ contract StrategyCurveGeist is StrategyCurveBase {
address spirit = 0x16327E3FbDaCA3bcF7E38F5Af2599D2DDc33aE52;
want.approve(address(gauge), type(uint256).max);
crv.approve(spooky, type(uint256).max);
wftm.approve(spooky, type(uint256).max);
geist.approve(spooky, type(uint256).max);
crv.approve(spirit, type(uint256).max);
wftm.approve(spirit, type(uint256).max);
geist.approve(spirit, type(uint256).max);

// set our strategy's name
Expand All @@ -216,10 +207,12 @@ contract StrategyCurveGeist is StrategyCurveBase {
// these are our approvals and path specific to this contract
dai.approve(address(curve), type(uint256).max);
usdc.approve(address(curve), type(uint256).max);
fusdt.safeApprove(address(curve), type(uint256).max);
fusdt.approve(address(curve), type(uint256).max);

// start off with fusdt
targetToken = address(fusdt);
//'targetToken' is the token with the least impact on the curve pool at the time of deposit
// or the one with the biggest bonus. 'targetToken' is updated by yearn when granted by
// market conditions. We start off with usdc.
targetToken = address(usdc);
}

/* ========== MUTATIVE FUNCTIONS ========== */
Expand All @@ -234,17 +227,14 @@ contract StrategyCurveGeist is StrategyCurveBase {
uint256 _debtPayment
)
{
// Mint CRV from the gauge factory. CRV is then transfered to the strategy (msg.sender)
gaugeFactory.mint(address(gauge));
// Claim and get a fresh snapshot of the strategy's CRV and GEIST balance
_claimRewards();

uint256 crvBalance = crv.balanceOf(address(this));

// harvest third-party rewards from the gauge, if any
gauge.claim_rewards();

uint256 wftmBalance = wftm.balanceOf(address(this));
uint256 geistBalance = geist.balanceOf(address(this));
// if we claimed any CRV, then sell it

// Sell CRV if we have any
if (crvBalance > 0) {
// keep some of our CRV to increase our boost
uint256 sendToVoter = crvBalance.mul(keepCRV).div(FEE_DENOMINATOR);
Expand All @@ -260,12 +250,8 @@ contract StrategyCurveGeist is StrategyCurveBase {
_sellToken(address(crv), crvBalance);
}
}
// sell WFTM if we have any
if (wftmBalance > 0) {
_sellToken(address(wftm), wftmBalance);
}

// sell the rest of our CRV
// Sell GEIST if we have any
if (geistBalance > 0) {
_sellToken(address(geist), geistBalance);
}
Expand Down Expand Up @@ -316,32 +302,19 @@ contract StrategyCurveGeist is StrategyCurveBase {
forceHarvestTriggerOnce = false;
}

// Sells our CRV, WFTM, or GEIST for our target token
// Sells our CRV or GEIST for our target token
function _sellToken(address token, uint256 _amount) internal {
if (token == address(wftm)) {
address[] memory tokenPath = new address[](2);
tokenPath[0] = address(wftm);
tokenPath[1] = address(targetToken);
IUniswapV2Router02(router).swapExactTokensForTokens(
_amount,
uint256(0),
tokenPath,
address(this),
block.timestamp
);
} else {
address[] memory tokenPath = new address[](3);
tokenPath[0] = address(token);
tokenPath[1] = address(wftm);
tokenPath[2] = address(targetToken);
IUniswapV2Router02(router).swapExactTokensForTokens(
_amount,
uint256(0),
tokenPath,
address(this),
block.timestamp
);
}
address[] memory tokenPath = new address[](3);
tokenPath[0] = address(token);
tokenPath[1] = address(wftm);
tokenPath[2] = address(targetToken);
IUniswapV2Router02(router).swapExactTokensForTokens(
_amount,
uint256(0),
tokenPath,
address(this),
block.timestamp
);
}

/* ========== KEEP3RS ========== */
Expand Down Expand Up @@ -382,7 +355,7 @@ contract StrategyCurveGeist is StrategyCurveBase {

// These functions are useful for setting parameters of the strategy that may need to be adjusted.
// Set optimal token to sell harvested funds for depositing to Curve.
// Default is fUSDT, but can be set to USDC or DAI as needed by strategist or governance.
// Default is USDC, but can be set to fUSDT or DAI as needed by strategist or governance.
function setOptimal(uint256 _optimal) external onlyAuthorized {
if (_optimal == 0) {
targetToken = address(dai);
Expand Down
120 changes: 8 additions & 112 deletions contracts/interfaces/curve.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,135 +2,31 @@
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";

interface IGauge {
function deposit(uint256) external;

function balanceOf(address) external view returns (uint256);

function withdraw(uint256) external;

function claim_rewards() external;

function withdraw(uint256) external;
// CRV
function claimable_tokens(address) external returns (uint256);

// Third-party tokens only
function claimable_reward(address, address) external view returns (uint256);
}

interface IGaugeFactory {
function mint(address _gauge) external;
function mint(address _gauge) external;
}

interface ICurveFi {
function get_virtual_price() external view returns (uint256);

function add_liquidity(
// EURt
uint256[2] calldata amounts,
uint256 min_mint_amount
) external payable;

function add_liquidity(
// Compound, sAave
uint256[2] calldata amounts,
uint256 min_mint_amount,
bool _use_underlying
) external payable returns (uint256);

function add_liquidity(
// Iron Bank, Aave
uint256[3] calldata amounts,
uint256 min_mint_amount,
bool _use_underlying
) external payable returns (uint256);

function add_liquidity(
// 3Crv Metapools
address pool,
uint256[4] calldata amounts,
uint256 min_mint_amount
) external;

function add_liquidity(
// Y and yBUSD
uint256[4] calldata amounts,
uint256 min_mint_amount,
bool _use_underlying
) external payable returns (uint256);

function add_liquidity(
// 3pool
uint256[3] calldata amounts,
uint256 min_mint_amount
) external payable;

function add_liquidity(
// sUSD
uint256[4] calldata amounts,
uint256 min_mint_amount
) external payable;

function remove_liquidity_imbalance(
uint256[2] calldata amounts,
uint256 max_burn_amount
) external;

function remove_liquidity(uint256 _amount, uint256[2] calldata amounts)
external;

function remove_liquidity_one_coin(
uint256 _token_amount,
int128 i,
uint256 min_amount
) external;

function exchange(
int128 from,
int128 to,
uint256 _from_amount,
uint256 _min_to_amount
) external;

function balances(uint256) external view returns (uint256);

function get_dy(
int128 from,
int128 to,
uint256 _from_amount
) external view returns (uint256);

// EURt
function calc_token_amount(uint256[2] calldata _amounts, bool _is_deposit)
external
view
returns (uint256);

// 3Crv Metapools
function calc_token_amount(
address _pool,
uint256[4] calldata _amounts,
bool _is_deposit
) external view returns (uint256);

// sUSD, Y pool, etc
function calc_token_amount(uint256[4] calldata _amounts, bool _is_deposit)
external
view
returns (uint256);

// 3pool, Iron Bank, etc
function calc_token_amount(uint256[3] calldata _amounts, bool _is_deposit)
external
view
returns (uint256);

function calc_withdraw_one_coin(uint256 amount, int128 i)
external
view
returns (uint256);
}

interface ICrvV3 is IERC20 {
function minter() external view returns (address);
}

interface IMinter {
function mint(address) external;
}
Loading

0 comments on commit aeb14c7

Please sign in to comment.