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

[New Operator] StakeDAO + Yearn (curve pools) #119

Merged
merged 27 commits into from
Jun 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1ac223b
feat: Add stakeDAO operator deposit
Yashiru May 11, 2022
07b94db
doc: add StakeDAO curve strategy deposit doc
Yashiru May 12, 2022
031ea13
feat: add StakeDAO operator withdraw
Yashiru May 13, 2022
6a83a61
doc: improve StakeDAO operator doc
Yashiru May 13, 2022
69149d7
fix: improve StakeDAO operator gas consumption
Yashiru May 13, 2022
63513aa
fix: StakeDAO operator typo
Yashiru May 13, 2022
2ca6204
feat: improved StakeDAO operator logic
Yashiru May 13, 2022
bb95cd3
fix: readability comments
Yashiru May 15, 2022
03fcb29
Doc: improve the returned values doc
Yashiru May 15, 2022
e7f5fed
refactor: optimize and refactor StakeDAO operator
Yashiru May 16, 2022
bcb574f
feat: add StakeDAO curve 2/4pool
Yashiru May 18, 2022
d99bd3c
feat: add curve compatibilities (ETH, 128 & 256)
May 27, 2022
bc0c615
feat: adds YarnCurveVaultOperator
obatirou May 27, 2022
2f5281f
fix: @obatirou code review fixes
Yashiru May 31, 2022
94d6170
fix: @obatirou code review fixes
Yashiru Jun 1, 2022
710f1dd
fix: change SPDX licence yearnVaulOperator
obatirou Jun 3, 2022
10cb5fa
fix: typo notice depositETH
obatirou Jun 3, 2022
7346b93
fix: remove unecessary variable YearnCurveHelpers
obatirou Jun 3, 2022
4b6889f
fix: adds natspec receive NestedFactory
obatirou Jun 6, 2022
2650ece
fix: minor code review fixes
Yashiru Jun 6, 2022
c2ef000
fix: typo code review fixes
Yashiru Jun 6, 2022
b91944f
fix: adds comments on EURT decimals and withdraw parameter
obatirou Jun 6, 2022
b1b2abd
fix: opti code review fixes
Yashiru Jun 7, 2022
fe27ecc
Merge branch 'feature/sc-80-operator-proposal-yearn-curve-vaults' int…
Yashiru Jun 7, 2022
48ad782
fix: @obatirou code review fixes
Yashiru Jun 9, 2022
ba008d7
fix: @maximebrugel code review fixes
Yashiru Jun 9, 2022
14d51ea
Merge branch 'master' into feature/sc-98-operator-proposal-stakedao-y…
Yashiru Jun 10, 2022
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
11 changes: 7 additions & 4 deletions contracts/NestedFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ import "./NestedAsset.sol";
import "./NestedRecords.sol";
import "./Withdrawer.sol";

import "hardhat/console.sol";

/// @title Creates, updates and destroys NestedAssets (portfolios).
/// @notice Responsible for the business logic of the protocol and interaction with operators
contract NestedFactory is INestedFactory, ReentrancyGuard, OwnableProxyDelegation, MixinOperatorResolver {
Expand Down Expand Up @@ -83,9 +81,14 @@ contract NestedFactory is INestedFactory, ReentrancyGuard, OwnableProxyDelegatio
withdrawer = _withdrawer;
}

/// @dev Receive function
/// @dev Receive function that will wrap the ether if
/// an address other than the withdrawer sends ether to
/// to the contract. The factory cannot handle ether but
/// has functions to withdraw ERC20 tokens if needed.
receive() external payable {
require(msg.sender == address(withdrawer), "NF: ETH_SENDER_NOT_WITHDRAWER");
if (msg.sender != address(withdrawer)) {
weth.deposit{ value: msg.value }();
}
}

/* ------------------------------ MODIFIERS ---------------------------- */
Expand Down
9 changes: 9 additions & 0 deletions contracts/interfaces/external/ICurvePool/ICurvePool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.11;

/// @title Curve pool interface
interface ICurvePool {
function token() external view returns (address);

function coins(uint256 index) external view returns (address);
}
16 changes: 16 additions & 0 deletions contracts/interfaces/external/ICurvePool/ICurvePoolETH.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.11;

import "./ICurvePool.sol";

/// @title ETH Curve pool interface
/// @notice The difference with non-ETH pools is that ETH pools must have
/// a payable add_liquidity function to allow direct sending of
/// ETH in order to add liquidity with ETH and not an ERC20.
interface ICurvePoolETH is ICurvePool {
function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount) external payable;

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

function add_liquidity(uint256[4] calldata amounts, uint256 min_mint_amount) external payable;
}
16 changes: 16 additions & 0 deletions contracts/interfaces/external/ICurvePool/ICurvePoolNonETH.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.11;

import "./ICurvePool.sol";

/// @title non-ETH Curve pool interface
/// @notice The difference with ETH pools is that ETH pools must have
/// a payable add_liquidity function to allow direct sending of
/// ETH in order to add liquidity with ETH and not an ERC20.
interface ICurvePoolNonETH is ICurvePool {
function add_liquidity(uint256[2] calldata amounts, uint256 min_mint_amount) external;

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

function add_liquidity(uint256[4] calldata amounts, uint256 min_mint_amount) external;
}
14 changes: 14 additions & 0 deletions contracts/interfaces/external/IStakingVault/IStakeDaoStrategy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.11;

import "./IStakingVault.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title StakeDAO strategy interface
/// @dev In the deployed code of StakeDAO, the token() function
/// allows to retrieve the LP token to stake.
/// Note : In the StakeDAO repository, this function has
/// been replaced by want().
interface IStakeDaoStrategy is IStakingVault {
function token() external view returns (IERC20);
}
9 changes: 9 additions & 0 deletions contracts/interfaces/external/IStakingVault/IStakingVault.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.11;

/// @title Generic staking vault interface
interface IStakingVault {
function deposit(uint256 _amount) external;

function withdraw(uint256 _shares) external;
}
13 changes: 13 additions & 0 deletions contracts/interfaces/external/IStakingVault/IYearnVault.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.11;

import "./IStakingVault.sol";

/// @dev Yearn vault interface
interface IYearnVault is IStakingVault {
function withdraw(
uint256 _shares,
address _recipient,
uint256 _maxLoss
) external returns (uint256);
}
2 changes: 2 additions & 0 deletions contracts/interfaces/external/IWETH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ interface IWETH {

function transfer(address recipient, uint256 amount) external returns (bool);

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

function allowance(address owner, address spender) external view returns (uint256);

function approve(address spender, uint256 amount) external returns (bool);
Expand Down
94 changes: 94 additions & 0 deletions contracts/libraries/CurveHelpers.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.11;

import "./../interfaces/external/ICurvePool/ICurvePool.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/// @notice Library for Curve deposit/withdraw
library CurveHelpers {
using SafeERC20 for IERC20;

/// @dev Get the array of token amount to send to a
/// Curve 2pool to add liquidity
/// @param pool The curve 2pool
/// @param token The token to remove from the pool
/// @param amount The amount of token to remove from the pool
/// @return amounts Array of 2 token amounts sorted by Curve pool token indexes
function getAmounts2Coins(
ICurvePool pool,
address token,
uint256 amount
) internal view returns (uint256[2] memory amounts) {
for (uint256 i; i < 2; i++) {
if (token == pool.coins(i)) {
amounts[i] = amount;
return amounts;
}
}
revert("CH: INVALID_INPUT_TOKEN");
}

/// @dev Get the array of token amount to send to a
/// Curve 3pool to add liquidity
/// @param pool The curve 3pool
/// @param token The token to remove from the pool
/// @param amount The amount of token to remove from the pool
/// @return amounts Array of 3 token amounts sorted by Curve pool token indexes
function getAmounts3Coins(
ICurvePool pool,
address token,
uint256 amount
) internal view returns (uint256[3] memory amounts) {
for (uint256 i; i < 3; i++) {
if (token == pool.coins(i)) {
amounts[i] = amount;
return amounts;
}
}
revert("CH: INVALID_INPUT_TOKEN");
}

/// @dev Get the array of token amount to send to a
/// Curve 4pool to add liquidity
/// @param pool The curve 4pool
/// @param token The token to remove from the pool
/// @param amount The amount of token to remove from the pool
/// @return amounts Array of 4 token amounts sorted by Curve pool token indexes
function getAmounts4Coins(
ICurvePool pool,
address token,
uint256 amount
) internal view returns (uint256[4] memory amounts) {
for (uint256 i; i < 4; i++) {
if (token == pool.coins(i)) {
amounts[i] = amount;
return amounts;
}
}
revert("CH: INVALID_INPUT_TOKEN");
}

/// @dev Remove liquidity from a Curve pool
/// @param pool The Curve pool to remove liquidity from
/// @param amount The Curve pool LP token to withdraw
/// @param outputToken One of the Curve pool token
/// @param poolCoinAmount The amount of token in the Curve pool
/// @param signature The signature of the remove_liquidity_one_coin
/// function to be used to call to the Curve pool
/// @return success If the call to remove liquidity succeeded
function removeLiquidityOneCoin(
ICurvePool pool,
uint256 amount,
address outputToken,
uint256 poolCoinAmount,
bytes4 signature
) internal returns (bool success) {
for (uint256 i; i < poolCoinAmount; i++) {
if (outputToken == pool.coins(i)) {
(success, ) = address(pool).call(abi.encodeWithSelector(signature, amount, i, 0));
return success;
}
}
revert("CH: INVALID_OUTPUT_TOKEN");
}
}
46 changes: 46 additions & 0 deletions contracts/libraries/OperatorHelpers.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.11;

import "./../interfaces/external/ICurvePool/ICurvePool.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/// @notice Library for all operators
library OperatorHelpers {
using SafeERC20 for IERC20;

/// @dev Get the arrays of obtained token and spent token
/// @param inputToken The token spent
/// @param inputTokenBalanceBefore The input token balance before
/// @param expectedInputAmount The expected amount of input token spent
/// @param outputToken The token obtained
/// @param outputTokenBalanceBefore The output token balance before
/// @param minAmountOut The minimum of output token expected
function getOutputAmounts(
IERC20 inputToken,
uint256 inputTokenBalanceBefore,
uint256 expectedInputAmount,
IERC20 outputToken,
uint256 outputTokenBalanceBefore,
uint256 minAmountOut
) internal view returns (uint256[] memory amounts, address[] memory tokens) {
require(
inputTokenBalanceBefore - inputToken.balanceOf(address(this)) == expectedInputAmount,
"OH: INVALID_AMOUNT_WITHDRAWED"
);

uint256 tokenAmount = outputToken.balanceOf(address(this)) - outputTokenBalanceBefore;
require(tokenAmount != 0, "OH: INVALID_AMOUNT_RECEIVED");
require(tokenAmount >= minAmountOut, "OH: INVALID_AMOUNT_RECEIVED");

amounts = new uint256[](2);
tokens = new address[](2);

// Output amounts
amounts[0] = tokenAmount;
amounts[1] = expectedInputAmount;

// Output token
tokens[0] = address(outputToken);
tokens[1] = address(inputToken);
}
}
Loading