From 90b3ae8f568309bb50e19bab929da529d74f59e4 Mon Sep 17 00:00:00 2001 From: bergben Date: Sun, 3 Oct 2021 22:55:03 +0200 Subject: [PATCH 1/6] feat(1inch): add generic DAI_X base strategy and implement DAI_LDO strategy --- .../OneInchStrategyMainnet_1INCH_USDC.sol | 2 +- .../OneInchStrategyMainnet_1INCH_WBTC.sol | 2 +- .../1inch/OneInchStrategyMainnet_DAI_LDO.sol | 45 +++ .../1inch/OneInchStrategyMainnet_ETH_DAI.sol | 2 +- .../OneInchStrategyMainnet_ETH_ONEINCH.sol | 2 +- .../1inch/OneInchStrategyMainnet_ETH_USDC.sol | 2 +- .../1inch/OneInchStrategyMainnet_ETH_USDT.sol | 2 +- .../1inch/OneInchStrategyMainnet_ETH_WBTC.sol | 2 +- .../{ => base}/OneInchStrategy_1INCH_X.sol | 16 +- .../1inch/base/OneInchStrategy_DAI_X.sol | 272 ++++++++++++++++++ .../{ => base}/OneInchStrategy_ETH_X.sol | 14 +- .../{ => base}/OneInchStrategy_ETH_X_V2.sol | 14 +- .../convex/base/ConvexStrategyUL.sol | 16 +- 13 files changed, 355 insertions(+), 36 deletions(-) create mode 100644 contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol rename contracts/strategies/1inch/{ => base}/OneInchStrategy_1INCH_X.sol (95%) create mode 100644 contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol rename contracts/strategies/1inch/{ => base}/OneInchStrategy_ETH_X.sol (95%) rename contracts/strategies/1inch/{ => base}/OneInchStrategy_ETH_X_V2.sol (95%) diff --git a/contracts/strategies/1inch/OneInchStrategyMainnet_1INCH_USDC.sol b/contracts/strategies/1inch/OneInchStrategyMainnet_1INCH_USDC.sol index 27bf966..3dd248d 100644 --- a/contracts/strategies/1inch/OneInchStrategyMainnet_1INCH_USDC.sol +++ b/contracts/strategies/1inch/OneInchStrategyMainnet_1INCH_USDC.sol @@ -1,6 +1,6 @@ pragma solidity 0.5.16; -import "./OneInchStrategy_1INCH_X.sol"; +import "./base/OneInchStrategy_1INCH_X.sol"; /** diff --git a/contracts/strategies/1inch/OneInchStrategyMainnet_1INCH_WBTC.sol b/contracts/strategies/1inch/OneInchStrategyMainnet_1INCH_WBTC.sol index b08f590..86de1dc 100644 --- a/contracts/strategies/1inch/OneInchStrategyMainnet_1INCH_WBTC.sol +++ b/contracts/strategies/1inch/OneInchStrategyMainnet_1INCH_WBTC.sol @@ -1,6 +1,6 @@ pragma solidity 0.5.16; -import "./OneInchStrategy_1INCH_X.sol"; +import "./base/OneInchStrategy_1INCH_X.sol"; /** diff --git a/contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol b/contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol new file mode 100644 index 0000000..d31e3eb --- /dev/null +++ b/contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol @@ -0,0 +1,45 @@ +pragma solidity 0.5.16; + +import "./base/OneInchStrategy_DAI_X.sol"; + + +/** +* This strategy is for the DAI/LDO LP token on 1inch +*/ +contract OneInchStrategy_DAI_LDO is OneInchStrategy_DAI_X { + + address public dai_ldo_unused; // just a differentiator for the bytecode + + constructor() public {} + + function initializeStrategy( + address _storage, + address _vault + ) public initializer { + address underlying = address(0xC1A900Ae76dB21dC5aa8E418Ac0F4E888A4C7431); + address rewardPool = address(0xd7012cDeBF10d5B352c601563aA3A8D1795A3F52); + address oneInch = address(0x111111111117dC0aa78b770fA6A738034120C302); + address ldo = address(0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32); + address stEth = address(0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32); + bytes32 sushiDex = bytes32(0xcb2d20206d906069351c89a2cb7cdbd96c71998717cd5a82e724d955b654f67a); + bytes32 uniV3Dex = bytes32(0x8f78a54cb77f4634a5bf3dd452ed6a2e33432c73821be59208661199511cd94f); + OneInchStrategy_DAI_X.initializeBaseStrategy( + _storage, + underlying, + _vault, + rewardPool, //rewardPool + 300 // profit sharing numerator + ); + rewardTokens = [oneInch, ldo]; + storedLiquidationPaths[oneInch][weth] = [oneInch, weth]; + storedLiquidationDexes[oneInch][weth] = [uniV3Dex]; + storedLiquidationPaths[ldo][weth] = [ldo, weth]; + storedLiquidationDexes[ldo][weth] = [sushiDex]; + storedLiquidationPaths[weth][dai] = [weth, dai]; + storedLiquidationDexes[weth][dai] = [sushiDex]; + storedLiquidationPaths[weth][stEth] = [weth, dai]; + // TODO: stEth has liquidity almost exclusively on curve. + // Have to use curve here for buying the token + storedLiquidationDexes[weth][stEth] = [sushiDex]; + } +} diff --git a/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_DAI.sol b/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_DAI.sol index 726fcf1..c946618 100644 --- a/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_DAI.sol +++ b/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_DAI.sol @@ -1,6 +1,6 @@ pragma solidity 0.5.16; -import "./OneInchStrategy_ETH_X.sol"; +import "./base/OneInchStrategy_ETH_X.sol"; /** diff --git a/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_ONEINCH.sol b/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_ONEINCH.sol index 0476036..ee2e15b 100644 --- a/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_ONEINCH.sol +++ b/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_ONEINCH.sol @@ -1,6 +1,6 @@ pragma solidity 0.5.16; -import "./OneInchStrategy_ETH_X_V2.sol"; +import "./base/OneInchStrategy_ETH_X_V2.sol"; /** diff --git a/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_USDC.sol b/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_USDC.sol index b4e46d1..41b9671 100644 --- a/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_USDC.sol +++ b/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_USDC.sol @@ -1,6 +1,6 @@ pragma solidity 0.5.16; -import "./OneInchStrategy_ETH_X.sol"; +import "./base/OneInchStrategy_ETH_X.sol"; /** diff --git a/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_USDT.sol b/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_USDT.sol index a49a9b1..799e609 100644 --- a/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_USDT.sol +++ b/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_USDT.sol @@ -1,6 +1,6 @@ pragma solidity 0.5.16; -import "./OneInchStrategy_ETH_X.sol"; +import "./base/OneInchStrategy_ETH_X.sol"; /** diff --git a/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_WBTC.sol b/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_WBTC.sol index 999798c..844101b 100644 --- a/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_WBTC.sol +++ b/contracts/strategies/1inch/OneInchStrategyMainnet_ETH_WBTC.sol @@ -1,6 +1,6 @@ pragma solidity 0.5.16; -import "./OneInchStrategy_ETH_X.sol"; +import "./base/OneInchStrategy_ETH_X.sol"; /** diff --git a/contracts/strategies/1inch/OneInchStrategy_1INCH_X.sol b/contracts/strategies/1inch/base/OneInchStrategy_1INCH_X.sol similarity index 95% rename from contracts/strategies/1inch/OneInchStrategy_1INCH_X.sol rename to contracts/strategies/1inch/base/OneInchStrategy_1INCH_X.sol index bc7aae3..0b1d2eb 100644 --- a/contracts/strategies/1inch/OneInchStrategy_1INCH_X.sol +++ b/contracts/strategies/1inch/base/OneInchStrategy_1INCH_X.sol @@ -5,15 +5,15 @@ import "@openzeppelin/contracts/math/Math.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; -import "./interface/IMooniswap.sol"; -import "./interface/IFarmingRewardsV2.sol"; +import "../interface/IMooniswap.sol"; +import "../interface/IFarmingRewardsV2.sol"; -import "../../base/interface/uniswap/IUniswapV2Router02.sol"; -import "../../base/interface/IStrategy.sol"; -import "../../base/interface/IVault.sol"; -import "../../base/interface/weth/Weth9.sol"; +import "../../../base/interface/uniswap/IUniswapV2Router02.sol"; +import "../../../base/interface/IStrategy.sol"; +import "../../../base/interface/IVault.sol"; +import "../../../base/interface/weth/Weth9.sol"; -import "../../base/StrategyBase.sol"; +import "../../../base/StrategyBase.sol"; /** * This strategy is for 1INCH / X 1inch LP tokens @@ -42,7 +42,7 @@ contract OneInchStrategy_1INCH_X is StrategyBase { address public oneInchEthLP; address[] public uniswap_WETH2Token1; - // token0 is ETH + // token0 is 1inch address public token1; uint256 slippageNumerator = 9; diff --git a/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol b/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol new file mode 100644 index 0000000..772c57f --- /dev/null +++ b/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol @@ -0,0 +1,272 @@ +pragma solidity 0.5.16; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/math/Math.sol"; +import "@openzeppelin/contracts/math/SafeMath.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "../interface/IMooniswap.sol"; +import "../interface/IFarmingRewardsV2.sol"; + +import "../../../base/interface/uniswap/IUniswapV2Router02.sol"; +import "../../../base/interface/IStrategy.sol"; +import "../../../base/interface/IVault.sol"; +import "../../../base/interface/weth/Weth9.sol"; + +import "../../../base/StrategyBase.sol"; + +/** +* This strategy is for DAI / X 1inch LP tokens +* DAI must be token0, and the other token is denoted X +*/ +contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { + + using SafeERC20 for IERC20; + using SafeMath for uint256; + + address public dai = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); + address public weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + + uint256 maxUint = uint256(~0); + + // depositToken0 is DAI + address public depositToken1; + + address[] public rewardTokens; + + uint256 slippageNumerator = 9; + uint256 slippageDenominator = 10; + + constructor() public BaseUpgradeableStrategyUL() {} + + function initializeBaseStrategy( + address _storage, + address _underlying, + address _vault, + address _rewardPool, + uint256 _profitSharingNumerator + ) public initializer { + + BaseUpgradeableStrategyUL.initialize( + _storage, + _underlying, + _vault, + _rewardPool, + weth, + _profitSharingNumerator, // profit sharing numerator + 1000, // profit sharing denominator + true, // sell + 0, // sell floor + 12 hours, // implementation change delay + address(0x7882172921E99d590E097cD600554339fBDBc480) // UL Registry + ); + + require(IVault(_vault).underlying() == _underlying, "vault does not support the required LP token"); + depositToken1 = IMooniswap(_underlying).token1(); + require(depositToken1 != address(0), "token1 must be non-zero"); + require(IMooniswap(_underlying).token0() == dai, "token0 must be dai"); + + rewardTokens = new address[](0); + } + + function depositArbCheck() public view returns(bool) { + return true; + } + + /* + * Governance or Controller can claim coins that are somehow transferred into the contract + * Note that they cannot come in take away coins that are used and defined in the strategy itself + */ + function salvage(address recipient, address token, uint256 amount) external onlyControllerOrGovernance { + // To make sure that governance cannot come in and take away the coins + require(!unsalvagableTokens(token), "token is defined as not salvagable"); + IERC20(token).safeTransfer(recipient, amount); + } + + /* + * Resumes the ability to invest into the underlying reward pools + */ + function continueInvesting() public onlyGovernance { + _setPausedInvesting(false); + } + + function addRewardToken(address _token) public onlyGovernance { + rewardTokens.push(_token); + } + + /** + * Withdraws underlying from the investment rewardPool() that mints crops. + */ + function withdrawUnderlyingFromPool(uint256 amount) internal { + IFarmingRewardsV2(rewardPool()).withdraw( + Math.min(IFarmingRewardsV2(rewardPool()).balanceOf(address(this)), amount) + ); + } + + /** + * Withdraws the underlying tokens to the rewardPool() in the specified amount. + */ + function withdrawToVault(uint256 amountUnderlying) external restricted { + withdrawUnderlyingFromPool(amountUnderlying); + require(IERC20(underlying()).balanceOf(address(this)) >= amountUnderlying, "insufficient balance for the withdrawal"); + IERC20(underlying()).safeTransfer(vault, amountUnderlying); + } + + /** + * Withdraws all the underlying tokens to the rewardPool(). + */ + function withdrawAllToVault() external restricted { + claimAndLiquidate(); + withdrawUnderlyingFromPool(maxUint); + uint256 balance = IERC20(underlying()).balanceOf(address(this)); + IERC20(underlying()).safeTransfer(vault(), balance); + } + + /** + * Invests all the underlying into the rewardPool() that mints crops (1inch) + */ + function investAllUnderlying() public restricted { + uint256 underlyingBalance = IERC20(underlying()).balanceOf(address(this)); + if (underlyingBalance > 0) { + IERC20(underlying()).safeApprove(rewardPool(), 0); + IERC20(underlying()).safeApprove(rewardPool(), underlyingBalance); + IFarmingRewardsV2(rewardPool()).stake(underlyingBalance); + } + } + + function() external payable {} + + /** + * liquidates rewards via defined dexes such as uni / sushi and redeposits + * and converts accordingly for reinvesting underlying + */ + function liquidateReward() internal { + if (!sell()) { + // Profits can be disabled for possible simplified and rapid exit + emit ProfitsNotCollected(sell(), false); + return; + } + + // multiple reward tokens are supported -> liquidate all of them into common rewardToken (weth) + for(uint256 i = 0; i < rewardTokens.length; i++){ + address rewardToken = rewardTokens[i]; + uint256 rewardBalance = IERC20(rewardToken).balanceOf(address(this)); + if (rewardBalance == 0 || storedLiquidationDexes[rewardToken][weth].length < 1) { + continue; + } + + IERC20(rewardToken).safeApprove(universalLiquidator(), 0); + IERC20(rewardToken).safeApprove(universalLiquidator(), rewardBalance); + // we can accept 1 as the minimum because this will be called only by a trusted worker + ILiquidator(universalLiquidator()).swapTokenOnMultipleDEXes( + rewardBalance, + 1, + address(this), // target + storedLiquidationDexes[rewardToken][weth], + storedLiquidationPaths[rewardToken][weth] + ); + } + + uint256 rewardBalance = IERC20(weth).balanceOf(address(this)); + + // share 30% of the wrapped Ether as a profit sharing reward + notifyProfitInRewardToken(rewardBalance); + uint256 remainingRewardBalance = IERC20(rewardToken()).balanceOf(address(this)); + + if (remainingRewardBalance == 0) { + return; + } + + // convert half of the remaining rewardBalance back to the main deposit token DAI + IERC20(rewardToken()).safeApprove(universalLiquidator(), 0); + IERC20(rewardToken()).safeApprove(universalLiquidator(), remainingRewardBalance.div(2)); + // we can accept 1 as minimum because this is called only by a trusted role + ILiquidator(universalLiquidator()).swapTokenOnMultipleDEXes( + remainingRewardBalance.div(2), + 1, + address(this), // target + storedLiquidationDexes[weth][dai], + storedLiquidationPaths[weth][dai] + ); + + // the remaining half is converted into the second token + remainingRewardBalance = IERC20(rewardToken()).balanceOf(address(this)); + IERC20(rewardToken()).safeApprove(universalLiquidator(), 0); + IERC20(rewardToken()).safeApprove(universalLiquidator(), remainingRewardBalance); + // we can accept 1 as minimum because this is called only by a trusted role + ILiquidator(universalLiquidator()).swapTokenOnMultipleDEXes( + remainingRewardBalance, + 1, + address(this), // target + storedLiquidationDexes[weth][depositToken1], + storedLiquidationPaths[weth][depositToken1] + ); + + depositMooniSwap(); + } + + function depositMooniSwap() internal { + int256 token1Amount = IERC20(depositToken1).balanceOf(address(this)); + uint256 daiAmount = IERC20(dai).balanceOf(address(this)); + if (!(daiAmount > 0 && token1Amount > 0)) { + return; + } + + IERC20(depositToken1).safeApprove(underlying(), 0); + IERC20(depositToken1).safeApprove(underlying(), token1Amount); + + IERC20(oneInch).safeApprove(underlying(), 0); + IERC20(oneInch).safeApprove(underlying(), oneInchAmount); + + // adding liquidity: DAI + depositToken1 + IMooniswap(underlying()).deposit( + [daiAmount, token1Amount], + [ + daiAmount.mul(slippageNumerator).div(slippageDenominator), + token1Amount.mul(slippageNumerator).div(slippageDenominator) + ] + ); + } + + + /* + * Get the reward, sell it in exchange for underlying, invest what you got. + * It's not much, but it's honest work. + * + * Note that although `onlyNotPausedInvesting` is not added here, + * calling `investAllUnderlying()` affectively blocks the usage of `doHardWork` + * when the investing is being paused by governance. + */ + function doHardWork() external onlyNotPausedInvesting restricted { + IFarmingRewardsV2(rewardPool()).getAllRewards(); + liquidateReward(); + investAllUnderlying(); + } + + /** + * Investing all underlying. + */ + function investedUnderlyingBalance() public view returns (uint256) { + return IFarmingRewardsV2(rewardPool()).balanceOf(address(this)).add( + IERC20(underlying()).balanceOf(address(this)) + ); + } + + function unsalvagableTokens(address token) public view returns (bool) { + return (token == rewardToken() || token == underlying()); + } + + /** + * Can completely disable claiming UNI rewards and selling. Good for emergency withdraw in the + * simplest possible way. + */ + function setSell(bool s) public onlyGovernance { + _setSell(s); + } + + /** + * Sets the minimum amount of rewardToken() needed to trigger a sale. + */ + function setSellFloor(uint256 floor) public onlyGovernance { + _setSellFloor(floor); + } +} \ No newline at end of file diff --git a/contracts/strategies/1inch/OneInchStrategy_ETH_X.sol b/contracts/strategies/1inch/base/OneInchStrategy_ETH_X.sol similarity index 95% rename from contracts/strategies/1inch/OneInchStrategy_ETH_X.sol rename to contracts/strategies/1inch/base/OneInchStrategy_ETH_X.sol index a6407b1..b00bba6 100644 --- a/contracts/strategies/1inch/OneInchStrategy_ETH_X.sol +++ b/contracts/strategies/1inch/base/OneInchStrategy_ETH_X.sol @@ -5,15 +5,15 @@ import "@openzeppelin/contracts/math/Math.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; -import "./interface/IMooniswap.sol"; -import "./interface/IFarmingRewards.sol"; +import "../interface/IMooniswap.sol"; +import "../interface/IFarmingRewards.sol"; -import "../../base/interface/uniswap/IUniswapV2Router02.sol"; -import "../../base/interface/IStrategy.sol"; -import "../../base/interface/IVault.sol"; -import "../../base/interface/weth/Weth9.sol"; +import "../../../base/interface/uniswap/IUniswapV2Router02.sol"; +import "../../../base/interface/IStrategy.sol"; +import "../../../base/interface/IVault.sol"; +import "../../../base/interface/weth/Weth9.sol"; -import "../../base/StrategyBase.sol"; +import "../../../base/StrategyBase.sol"; /** * This strategy is for ETH / X 1inch LP tokens diff --git a/contracts/strategies/1inch/OneInchStrategy_ETH_X_V2.sol b/contracts/strategies/1inch/base/OneInchStrategy_ETH_X_V2.sol similarity index 95% rename from contracts/strategies/1inch/OneInchStrategy_ETH_X_V2.sol rename to contracts/strategies/1inch/base/OneInchStrategy_ETH_X_V2.sol index 29c389b..7538738 100644 --- a/contracts/strategies/1inch/OneInchStrategy_ETH_X_V2.sol +++ b/contracts/strategies/1inch/base/OneInchStrategy_ETH_X_V2.sol @@ -5,15 +5,15 @@ import "@openzeppelin/contracts/math/Math.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; -import "./interface/IMooniswap.sol"; -import "./interface/IFarmingRewardsV2.sol"; +import "../interface/IMooniswap.sol"; +import "../interface/IFarmingRewardsV2.sol"; -import "../../base/interface/uniswap/IUniswapV2Router02.sol"; -import "../../base/interface/IStrategy.sol"; -import "../../base/interface/IVault.sol"; -import "../../base/interface/weth/Weth9.sol"; +import "../../../base/interface/uniswap/IUniswapV2Router02.sol"; +import "../../../base/interface/IStrategy.sol"; +import "../../../base/interface/IVault.sol"; +import "../../../base/interface/weth/Weth9.sol"; -import "../../base/StrategyBase.sol"; +import "../../../base/StrategyBase.sol"; /** * This strategy is for ETH / X 1inch LP tokens diff --git a/contracts/strategies/convex/base/ConvexStrategyUL.sol b/contracts/strategies/convex/base/ConvexStrategyUL.sol index 8afd3cc..d835617 100644 --- a/contracts/strategies/convex/base/ConvexStrategyUL.sol +++ b/contracts/strategies/convex/base/ConvexStrategyUL.sol @@ -90,7 +90,7 @@ contract ConvexStrategyUL is IStrategy, BaseUpgradeableStrategyUL { true, // sell 0, // sell floor 12 hours, // implementation change delay - address(0x7882172921E99d590E097cD600554339fBDBc480) //UL Registry + address(0x7882172921E99d590E097cD600554339fBDBc480) // UL Registry ); address _lpt; @@ -210,14 +210,20 @@ contract ConvexStrategyUL is IStrategy, BaseUpgradeableStrategyUL { rewardTokens.push(_token); } - // We assume that all the tradings can be done on Sushiswap + /** + * liquidates rewards via defined dexes such as uni / sushi + * and converts accordingly for reinvesting underlying + */ function _liquidateReward() internal { if (!sell()) { - // Profits can be disabled for possible simplified and rapoolId exit + // Profits can be disabled for possible simplified and rapid exit emit ProfitsNotCollected(sell(), false); return; } + // TODO: check for sell floor is missing? + + // multiple reward tokens are supported -> liquidate all of them into common rewardToken (weth) for(uint256 i = 0; i < rewardTokens.length; i++){ address token = rewardTokens[i]; uint256 rewardBalance = IERC20(token).balanceOf(address(this)); @@ -362,10 +368,6 @@ contract ConvexStrategyUL is IStrategy, BaseUpgradeableStrategyUL { /* * Get the reward, sell it in exchange for underlying, invest what you got. * It's not much, but it's honest work. - * - * Note that although `onlyNotPausedInvesting` is not added here, - * calling `investAllUnderlying()` affectively blocks the usage of `doHardWork` - * when the investing is being paused by governance. */ function doHardWork() external onlyNotPausedInvesting restricted { IBaseRewardPool(rewardPool()).getReward(); From 5b62ae43aa8c9e8c64ce576f04d759ae99cb9c11 Mon Sep 17 00:00:00 2001 From: bergben Date: Mon, 4 Oct 2021 11:37:53 +0200 Subject: [PATCH 2/6] feat(oneInch-DAI_X): add emergency exit and make upgradeable with slot --- .../1inch/base/OneInchStrategy_DAI_X.sol | 52 ++++++++++++++----- .../convex/base/ConvexStrategyUL.sol | 2 - 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol b/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol index 772c57f..d5f00e5 100644 --- a/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol +++ b/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol @@ -27,16 +27,18 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { address public weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); uint256 maxUint = uint256(~0); + uint256 slippageNumerator = 9; + uint256 slippageDenominator = 10; // depositToken0 is DAI - address public depositToken1; + // additional storage slots (on top of BaseUpgradeableStrategy ones) are defined here + bytes32 internal constant _DEPOSIT_TOKEN_SLOT = 0x219270253dbc530471c88a9e7c321b36afda219583431e7b6c386d2d46e70c86; address[] public rewardTokens; - uint256 slippageNumerator = 9; - uint256 slippageDenominator = 10; - - constructor() public BaseUpgradeableStrategyUL() {} + constructor() public BaseUpgradeableStrategyUL() { + assert(_DEPOSIT_TOKEN_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.depositToken")) - 1)); + } function initializeBaseStrategy( address _storage, @@ -61,8 +63,8 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { ); require(IVault(_vault).underlying() == _underlying, "vault does not support the required LP token"); - depositToken1 = IMooniswap(_underlying).token1(); - require(depositToken1 != address(0), "token1 must be non-zero"); + _setDepositToken(IMooniswap(_underlying).token1()); + require(depositToken() != address(0), "token1 must be non-zero"); require(IMooniswap(_underlying).token0() == dai, "token0 must be dai"); rewardTokens = new address[](0); @@ -82,6 +84,17 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { IERC20(token).safeTransfer(recipient, amount); } + function emergencyExitRewardPool() internal { + withdrawUnderlyingFromPool(maxUint); + uint256 balance = IERC20(underlying()).balanceOf(address(this)); + IERC20(underlying()).safeTransfer(vault(), balance); + } + + function emergencyExit() public onlyGovernance { + emergencyExitRewardPool(); + _setPausedInvesting(true); + } + /* * Resumes the ability to invest into the underlying reward pools */ @@ -197,27 +210,27 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { remainingRewardBalance, 1, address(this), // target - storedLiquidationDexes[weth][depositToken1], - storedLiquidationPaths[weth][depositToken1] + storedLiquidationDexes[weth][depositToken()], + storedLiquidationPaths[weth][depositToken()] ); depositMooniSwap(); } function depositMooniSwap() internal { - int256 token1Amount = IERC20(depositToken1).balanceOf(address(this)); + int256 token1Amount = IERC20(depositToken()).balanceOf(address(this)); uint256 daiAmount = IERC20(dai).balanceOf(address(this)); if (!(daiAmount > 0 && token1Amount > 0)) { return; } - IERC20(depositToken1).safeApprove(underlying(), 0); - IERC20(depositToken1).safeApprove(underlying(), token1Amount); + IERC20(depositToken()).safeApprove(underlying(), 0); + IERC20(depositToken()).safeApprove(underlying(), token1Amount); IERC20(oneInch).safeApprove(underlying(), 0); IERC20(oneInch).safeApprove(underlying(), oneInchAmount); - // adding liquidity: DAI + depositToken1 + // adding liquidity: DAI + depositToken() IMooniswap(underlying()).deposit( [daiAmount, token1Amount], [ @@ -226,7 +239,6 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { ] ); } - /* * Get the reward, sell it in exchange for underlying, invest what you got. @@ -269,4 +281,16 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { function setSellFloor(uint256 floor) public onlyGovernance { _setSellFloor(floor); } + + function _setDepositToken(address _address) internal { + setAddress(_DEPOSIT_TOKEN_SLOT, _address); + } + + function depositToken() public view returns (address) { + return getAddress(_DEPOSIT_TOKEN_SLOT); + } + + function finalizeUpgrade() external onlyGovernance { + _finalizeUpgrade(); + } } \ No newline at end of file diff --git a/contracts/strategies/convex/base/ConvexStrategyUL.sol b/contracts/strategies/convex/base/ConvexStrategyUL.sol index d835617..a2a31ee 100644 --- a/contracts/strategies/convex/base/ConvexStrategyUL.sol +++ b/contracts/strategies/convex/base/ConvexStrategyUL.sol @@ -221,8 +221,6 @@ contract ConvexStrategyUL is IStrategy, BaseUpgradeableStrategyUL { return; } - // TODO: check for sell floor is missing? - // multiple reward tokens are supported -> liquidate all of them into common rewardToken (weth) for(uint256 i = 0; i < rewardTokens.length; i++){ address token = rewardTokens[i]; From 069714332524ec05124cedc5d85fc2d2454a62c0 Mon Sep 17 00:00:00 2001 From: bergben Date: Mon, 4 Oct 2021 13:56:00 +0200 Subject: [PATCH 3/6] feat(oneInch-dai): implement dai_X and dai-ldo --- .../interface/oneInch/IOneInchLiquidator.sol | 21 +++ .../1inch/OneInchStrategyMainnet_DAI_LDO.sol | 19 +- .../1inch/base/OneInchStrategy_DAI_X.sol | 164 ++++++++++++------ test/1inch/dai-ldo.js | 101 +++++++++++ 4 files changed, 249 insertions(+), 56 deletions(-) create mode 100644 contracts/base/interface/oneInch/IOneInchLiquidator.sol create mode 100644 test/1inch/dai-ldo.js diff --git a/contracts/base/interface/oneInch/IOneInchLiquidator.sol b/contracts/base/interface/oneInch/IOneInchLiquidator.sol new file mode 100644 index 0000000..f53963d --- /dev/null +++ b/contracts/base/interface/oneInch/IOneInchLiquidator.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.5.16; + +interface IOneInchLiquidator { + function changeReferral(address newReferral) external; + + function doSwap( + uint256 amountIn, + uint256 minAmountOut, + address spender, + address target, + address[] calldata path + ) external; + + function changePool( + address _token0, + address _token1, + address _pool + ) external; + +} diff --git a/contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol b/contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol index d31e3eb..78255ee 100644 --- a/contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol +++ b/contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol @@ -23,23 +23,28 @@ contract OneInchStrategy_DAI_LDO is OneInchStrategy_DAI_X { address stEth = address(0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32); bytes32 sushiDex = bytes32(0xcb2d20206d906069351c89a2cb7cdbd96c71998717cd5a82e724d955b654f67a); bytes32 uniV3Dex = bytes32(0x8f78a54cb77f4634a5bf3dd452ed6a2e33432c73821be59208661199511cd94f); + bytes32 oneInchDex = bytes32(0xd9bf0c0ec020d1a26ba6698a24db3a538215d8fbf30588bddde694887c4cb55e); + address daiStEthPoolOneInch = address(0xC1A900Ae76dB21dC5aa8E418Ac0F4E888A4C7431); + address oneInchEthPoolOneInch = address(0x0EF1B8a0E726Fc3948E15b23993015eB1627f210); + address eth = address(0x0000000000000000000000000000000000000000); OneInchStrategy_DAI_X.initializeBaseStrategy( _storage, underlying, _vault, rewardPool, //rewardPool - 300 // profit sharing numerator + 300, // profit sharing numerator + true // liquidateDepositTokenViaDai (deposit token = stEth, default deposit token 0 = DAI) ); rewardTokens = [oneInch, ldo]; - storedLiquidationPaths[oneInch][weth] = [oneInch, weth]; - storedLiquidationDexes[oneInch][weth] = [uniV3Dex]; + storedLiquidationPaths[oneInch][eth] = [oneInch, eth]; + storedLiquidationDexes[oneInch][eth] = [oneInchDex]; storedLiquidationPaths[ldo][weth] = [ldo, weth]; storedLiquidationDexes[ldo][weth] = [sushiDex]; storedLiquidationPaths[weth][dai] = [weth, dai]; storedLiquidationDexes[weth][dai] = [sushiDex]; - storedLiquidationPaths[weth][stEth] = [weth, dai]; - // TODO: stEth has liquidity almost exclusively on curve. - // Have to use curve here for buying the token - storedLiquidationDexes[weth][stEth] = [sushiDex]; + storedLiquidationPaths[dai][stEth] = [dai, stEth]; + storedLiquidationDexes[dai][stEth] = [oneInchDex]; + storedOneInchPools[dai][stEth] = daiStEthPoolOneInch; + storedOneInchPools[oneInch][eth] = oneInchEthPoolOneInch; } } diff --git a/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol b/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol index d5f00e5..63a13e2 100644 --- a/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol +++ b/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol @@ -7,10 +7,11 @@ import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../interface/IMooniswap.sol"; import "../interface/IFarmingRewardsV2.sol"; -import "../../../base/interface/uniswap/IUniswapV2Router02.sol"; import "../../../base/interface/IStrategy.sol"; +import "../../../base/upgradability/BaseUpgradeableStrategyUL.sol"; import "../../../base/interface/IVault.sol"; import "../../../base/interface/weth/Weth9.sol"; +import "../../../base/interface/oneInch/IOneInchLiquidator.sol"; import "../../../base/StrategyBase.sol"; @@ -25,6 +26,9 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { address public dai = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); address public weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + address public eth = address(0x0000000000000000000000000000000000000000); + bytes32 oneInchDex = bytes32(0xd9bf0c0ec020d1a26ba6698a24db3a538215d8fbf30588bddde694887c4cb55e); + address oneInchLiquidator = address(0xA6031a6D87b82B2d60df9B78E578537a2AeAe93a); uint256 maxUint = uint256(~0); uint256 slippageNumerator = 9; @@ -33,11 +37,14 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { // depositToken0 is DAI // additional storage slots (on top of BaseUpgradeableStrategy ones) are defined here bytes32 internal constant _DEPOSIT_TOKEN_SLOT = 0x219270253dbc530471c88a9e7c321b36afda219583431e7b6c386d2d46e70c86; + bytes32 internal constant _LIQUIDATE_DEPOSIT_TOKEN_VIA_DAI_SLOT = 0xe409979bbff331377f8c6e329d8221b165153f1b13926cd194bf26d1033513dd; address[] public rewardTokens; + mapping(address => mapping(address => address)) public storedOneInchPools; constructor() public BaseUpgradeableStrategyUL() { assert(_DEPOSIT_TOKEN_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.depositToken")) - 1)); + assert(_LIQUIDATE_DEPOSIT_TOKEN_VIA_DAI_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.liquidateDepositTokenViaDai")) - 1)); } function initializeBaseStrategy( @@ -45,7 +52,8 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { address _underlying, address _vault, address _rewardPool, - uint256 _profitSharingNumerator + uint256 _profitSharingNumerator, + bool _liquidateDepositTokenViaDai ) public initializer { BaseUpgradeableStrategyUL.initialize( @@ -67,6 +75,8 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { require(depositToken() != address(0), "token1 must be non-zero"); require(IMooniswap(_underlying).token0() == dai, "token0 must be dai"); + _setLiquidateDepositTokenViaDai(_liquidateDepositTokenViaDai); + rewardTokens = new address[](0); } @@ -121,14 +131,15 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { function withdrawToVault(uint256 amountUnderlying) external restricted { withdrawUnderlyingFromPool(amountUnderlying); require(IERC20(underlying()).balanceOf(address(this)) >= amountUnderlying, "insufficient balance for the withdrawal"); - IERC20(underlying()).safeTransfer(vault, amountUnderlying); + IERC20(underlying()).safeTransfer(vault(), amountUnderlying); } /** * Withdraws all the underlying tokens to the rewardPool(). */ function withdrawAllToVault() external restricted { - claimAndLiquidate(); + IFarmingRewardsV2(rewardPool()).getAllRewards(); + liquidateReward(); withdrawUnderlyingFromPool(maxUint); uint256 balance = IERC20(underlying()).balanceOf(address(this)); IERC20(underlying()).safeTransfer(vault(), balance); @@ -159,25 +170,7 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { return; } - // multiple reward tokens are supported -> liquidate all of them into common rewardToken (weth) - for(uint256 i = 0; i < rewardTokens.length; i++){ - address rewardToken = rewardTokens[i]; - uint256 rewardBalance = IERC20(rewardToken).balanceOf(address(this)); - if (rewardBalance == 0 || storedLiquidationDexes[rewardToken][weth].length < 1) { - continue; - } - - IERC20(rewardToken).safeApprove(universalLiquidator(), 0); - IERC20(rewardToken).safeApprove(universalLiquidator(), rewardBalance); - // we can accept 1 as the minimum because this will be called only by a trusted worker - ILiquidator(universalLiquidator()).swapTokenOnMultipleDEXes( - rewardBalance, - 1, - address(this), // target - storedLiquidationDexes[rewardToken][weth], - storedLiquidationPaths[rewardToken][weth] - ); - } + rewardTokensToCommon(); uint256 rewardBalance = IERC20(weth).balanceOf(address(this)); @@ -189,36 +182,100 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { return; } + commonToDepositTokens(remainingRewardBalance); + + depositMooniSwap(); + } + + /** + * swaps the common token (WETH) to the deposit tokens for reinvesting + */ + function commonToDepositTokens(uint256 amount) internal { // convert half of the remaining rewardBalance back to the main deposit token DAI - IERC20(rewardToken()).safeApprove(universalLiquidator(), 0); - IERC20(rewardToken()).safeApprove(universalLiquidator(), remainingRewardBalance.div(2)); - // we can accept 1 as minimum because this is called only by a trusted role - ILiquidator(universalLiquidator()).swapTokenOnMultipleDEXes( - remainingRewardBalance.div(2), - 1, - address(this), // target - storedLiquidationDexes[weth][dai], - storedLiquidationPaths[weth][dai] - ); + uint256 liquidateRewardToDaiBalance = amount.div(2); - // the remaining half is converted into the second token - remainingRewardBalance = IERC20(rewardToken()).balanceOf(address(this)); - IERC20(rewardToken()).safeApprove(universalLiquidator(), 0); - IERC20(rewardToken()).safeApprove(universalLiquidator(), remainingRewardBalance); - // we can accept 1 as minimum because this is called only by a trusted role - ILiquidator(universalLiquidator()).swapTokenOnMultipleDEXes( - remainingRewardBalance, - 1, - address(this), // target - storedLiquidationDexes[weth][depositToken()], - storedLiquidationPaths[weth][depositToken()] - ); + if(liquidateDepositTokenViaDai()) { + // if we liquidate the deposit token via DAI then we swap all of the rewardBalance to DAI + liquidateRewardToDaiBalance = amount; + } - depositMooniSwap(); + swapViaULorOneInchUL(weth, dai, liquidateRewardToDaiBalance); + + if(liquidateDepositTokenViaDai()) { + // convert half of the dai balance to the second token + uint256 remainingRewardBalance = IERC20(dai).balanceOf(address(this)); + swapViaULorOneInchUL(dai, depositToken(), remainingRewardBalance); + } else { + // the remaining half of the reward balance is converted into the second token + uint256 remainingRewardBalance = IERC20(rewardToken()).balanceOf(address(this)); + swapViaULorOneInchUL(weth, depositToken(), remainingRewardBalance); + } + } + + /** + * swaps all the reward tokens to WETH + */ + function rewardTokensToCommon() internal { + // multiple reward tokens are supported -> liquidate all of them into common rewardToken (weth) + for(uint256 i = 0; i < rewardTokens.length; i++){ + address rewardToken = rewardTokens[i]; + uint256 rewardBalance = IERC20(rewardToken).balanceOf(address(this)); + if (rewardBalance == 0 || (storedLiquidationDexes[rewardToken][weth].length < 1 && storedLiquidationDexes[rewardToken][eth].length < 1)) { + continue; + } + + swapViaULorOneInchUL(rewardToken, weth, rewardBalance); + } + } + + function swapViaULorOneInchUL(address fromToken, address toToken, uint256 amount) internal { + bool toWethHasToBeWrapped = false; + if(toToken == weth) { + // check if a path for weth exists or if we have to wrap + toWethHasToBeWrapped = storedLiquidationDexes[fromToken][toToken].length < 1; + } + bytes32[] memory dexes = storedLiquidationDexes[rewardToken()][weth]; + if(toWethHasToBeWrapped) { + dexes = storedLiquidationDexes[rewardToken()][eth]; + } + + if(dexes[0] == oneInchDex) { + // via 1inch + IERC20(fromToken).safeApprove(oneInchLiquidator, 0); + IERC20(fromToken).safeApprove(oneInchLiquidator, amount); + + IOneInchLiquidator(oneInchLiquidator).changePool(fromToken, toToken, storedOneInchPools[fromToken][toToken]); + // we can accept 1 as the minimum because this will be called only by a trusted worker + IOneInchLiquidator(oneInchLiquidator).doSwap( + amount, + 1, + address(this), // spender + address(this), // target + storedLiquidationPaths[fromToken][toToken] + ); + } else { + // via UL that handles uni and sushi + IERC20(fromToken).safeApprove(universalLiquidator(), 0); + IERC20(fromToken).safeApprove(universalLiquidator(), amount); + // we can accept 1 as the minimum because this will be called only by a trusted worker + ILiquidator(universalLiquidator()).swapTokenOnMultipleDEXes( + amount, + 1, + address(this), // target + storedLiquidationDexes[fromToken][toToken], + storedLiquidationPaths[fromToken][toToken] + ); + } + + // wrap if necessary + if(toWethHasToBeWrapped) { + // convert the received Ether into wrapped Ether + WETH9(weth).deposit.value(address(this).balance)(); + } } function depositMooniSwap() internal { - int256 token1Amount = IERC20(depositToken()).balanceOf(address(this)); + uint256 token1Amount = IERC20(depositToken()).balanceOf(address(this)); uint256 daiAmount = IERC20(dai).balanceOf(address(this)); if (!(daiAmount > 0 && token1Amount > 0)) { return; @@ -227,8 +284,8 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { IERC20(depositToken()).safeApprove(underlying(), 0); IERC20(depositToken()).safeApprove(underlying(), token1Amount); - IERC20(oneInch).safeApprove(underlying(), 0); - IERC20(oneInch).safeApprove(underlying(), oneInchAmount); + IERC20(dai).safeApprove(underlying(), 0); + IERC20(dai).safeApprove(underlying(), daiAmount); // adding liquidity: DAI + depositToken() IMooniswap(underlying()).deposit( @@ -290,6 +347,15 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { return getAddress(_DEPOSIT_TOKEN_SLOT); } + function _setLiquidateDepositTokenViaDai(bool _liquidateViaDai) internal { + setBoolean(_LIQUIDATE_DEPOSIT_TOKEN_VIA_DAI_SLOT, _liquidateViaDai); + } + + function liquidateDepositTokenViaDai() public view returns (bool) { + return getBoolean(_LIQUIDATE_DEPOSIT_TOKEN_VIA_DAI_SLOT); + } + + function finalizeUpgrade() external onlyGovernance { _finalizeUpgrade(); } diff --git a/test/1inch/dai-ldo.js b/test/1inch/dai-ldo.js new file mode 100644 index 0000000..651b1da --- /dev/null +++ b/test/1inch/dai-ldo.js @@ -0,0 +1,101 @@ +// Utilities +const Utils = require("../utilities/Utils.js"); +const { impersonates, setupCoreProtocol, depositVault } = require("../utilities/hh-utils.js"); + +const BigNumber = require("bignumber.js"); +const IERC20 = artifacts.require("@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20"); + +const OneInchStrategy_DAI_LDO = artifacts.require("OneInchStrategy_DAI_LDO"); + +// This test was developed at blockNumber 13352188 + +// Vanilla Mocha test. Increased compatibility with tools that integrate Mocha. +describe("Mainnet DAI/LDO", function() { + let accounts; + + // external contracts + let underlying; + + // external setup + let underlyingWhale = "0x37e46caEfEF61D30E680cf754998B4A39e5d8325"; + + // parties in the protocol + let governance; + let farmer1; + + // numbers used in tests + let farmerBalance; + + // Core protocol contracts + let controller; + let vault; + + async function setupExternalContracts() { + underlying = await IERC20.at("0xC1A900Ae76dB21dC5aa8E418Ac0F4E888A4C7431"); + console.log("Fetching Underlying at: ", underlying.address); + } + + async function setupBalance(){ + let etherGiver = accounts[9]; + // Give whale some ether to make sure the following actions are good + await web3.eth.sendTransaction({ from: etherGiver, to: underlyingWhale, value: 1e18}); + + farmerBalance = await underlying.balanceOf(underlyingWhale); + await underlying.transfer(farmer1, farmerBalance, { from: underlyingWhale }); + } + + before(async function() { + governance = "0xf00dD244228F51547f0563e60bCa65a30FBF5f7f"; + accounts = await web3.eth.getAccounts(); + + farmer1 = accounts[1]; + + // impersonate accounts + await impersonates([governance, underlyingWhale]); + + let oneInch = "0x111111111117dC0aa78b770fA6A738034120C302"; + let weth = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; + + await setupExternalContracts(); + [controller, vault, strategy] = await setupCoreProtocol({ + "existingVaultAddress": null, + "strategyArtifact": OneInchStrategy_DAI_LDO, + "underlying": underlying, + "governance": governance, + }); + + // whale send underlying to farmers + await setupBalance(); + }); + + describe("Happy path", function() { + it("Farmer should earn money", async function() { + let farmerOldBalance = new BigNumber(await underlying.balanceOf(farmer1)); + await depositVault(farmer1, underlying, vault, farmerBalance); + + // Using half days is to simulate how we doHardwork in the real world + let hours = 10; + let oldSharePrice; + let newSharePrice; + for (let i = 0; i < hours; i++) { + console.log("loop ", i); + let blocksPerHour = 2400; + oldSharePrice = new BigNumber(await vault.getPricePerFullShare()); + await controller.doHardWork(vault.address, { from: governance }); + newSharePrice = new BigNumber(await vault.getPricePerFullShare()); + + console.log("old shareprice: ", oldSharePrice.toFixed()); + console.log("new shareprice: ", newSharePrice.toFixed()); + console.log("growth: ", newSharePrice.toFixed() / oldSharePrice.toFixed()); + + await Utils.advanceNBlock(blocksPerHour); + } + await vault.withdraw(farmerBalance, { from: farmer1 }); + let farmerNewBalance = new BigNumber(await underlying.balanceOf(farmer1)); + Utils.assertBNGt(farmerNewBalance, farmerOldBalance); + + console.log("earned!"); + + }); + }); +}); From bbf9fab23cc41d4f0c660543a63214e7f27a1e4e Mon Sep 17 00:00:00 2001 From: bergben Date: Mon, 4 Oct 2021 21:00:41 +0200 Subject: [PATCH 4/6] WIP: feat(oneInch-dai-ldo): clean up strategy: 1inch swap via UL, implement test --- .../1inch/OneInchStrategyMainnet_DAI_LDO.sol | 22 ++- .../1inch/base/OneInchStrategy_DAI_X.sol | 134 +++++------------- test/1inch/dai-ldo.js | 40 +++++- 3 files changed, 82 insertions(+), 114 deletions(-) diff --git a/contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol b/contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol index 78255ee..3b839d3 100644 --- a/contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol +++ b/contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol @@ -2,7 +2,6 @@ pragma solidity 0.5.16; import "./base/OneInchStrategy_DAI_X.sol"; - /** * This strategy is for the DAI/LDO LP token on 1inch */ @@ -20,31 +19,26 @@ contract OneInchStrategy_DAI_LDO is OneInchStrategy_DAI_X { address rewardPool = address(0xd7012cDeBF10d5B352c601563aA3A8D1795A3F52); address oneInch = address(0x111111111117dC0aa78b770fA6A738034120C302); address ldo = address(0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32); - address stEth = address(0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32); + address stEth = address(0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84); bytes32 sushiDex = bytes32(0xcb2d20206d906069351c89a2cb7cdbd96c71998717cd5a82e724d955b654f67a); - bytes32 uniV3Dex = bytes32(0x8f78a54cb77f4634a5bf3dd452ed6a2e33432c73821be59208661199511cd94f); bytes32 oneInchDex = bytes32(0xd9bf0c0ec020d1a26ba6698a24db3a538215d8fbf30588bddde694887c4cb55e); - address daiStEthPoolOneInch = address(0xC1A900Ae76dB21dC5aa8E418Ac0F4E888A4C7431); - address oneInchEthPoolOneInch = address(0x0EF1B8a0E726Fc3948E15b23993015eB1627f210); - address eth = address(0x0000000000000000000000000000000000000000); + OneInchStrategy_DAI_X.initializeBaseStrategy( _storage, underlying, _vault, rewardPool, //rewardPool - 300, // profit sharing numerator - true // liquidateDepositTokenViaDai (deposit token = stEth, default deposit token 0 = DAI) + 300 // profit sharing numerator ); + rewardTokens = [oneInch, ldo]; - storedLiquidationPaths[oneInch][eth] = [oneInch, eth]; - storedLiquidationDexes[oneInch][eth] = [oneInchDex]; + storedLiquidationPaths[oneInch][weth] = [oneInch, weth]; + storedLiquidationDexes[oneInch][weth] = [oneInchDex]; storedLiquidationPaths[ldo][weth] = [ldo, weth]; storedLiquidationDexes[ldo][weth] = [sushiDex]; storedLiquidationPaths[weth][dai] = [weth, dai]; storedLiquidationDexes[weth][dai] = [sushiDex]; - storedLiquidationPaths[dai][stEth] = [dai, stEth]; - storedLiquidationDexes[dai][stEth] = [oneInchDex]; - storedOneInchPools[dai][stEth] = daiStEthPoolOneInch; - storedOneInchPools[oneInch][eth] = oneInchEthPoolOneInch; + storedLiquidationPaths[weth][stEth] = [weth, dai, stEth]; + storedLiquidationDexes[weth][stEth] = [sushiDex, oneInchDex]; } } diff --git a/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol b/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol index 63a13e2..d38b7d4 100644 --- a/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol +++ b/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol @@ -10,10 +10,10 @@ import "../interface/IFarmingRewardsV2.sol"; import "../../../base/interface/IStrategy.sol"; import "../../../base/upgradability/BaseUpgradeableStrategyUL.sol"; import "../../../base/interface/IVault.sol"; -import "../../../base/interface/weth/Weth9.sol"; -import "../../../base/interface/oneInch/IOneInchLiquidator.sol"; import "../../../base/StrategyBase.sol"; +import "hardhat/console.sol"; + /** * This strategy is for DAI / X 1inch LP tokens @@ -24,11 +24,8 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { using SafeERC20 for IERC20; using SafeMath for uint256; - address public dai = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); - address public weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); - address public eth = address(0x0000000000000000000000000000000000000000); - bytes32 oneInchDex = bytes32(0xd9bf0c0ec020d1a26ba6698a24db3a538215d8fbf30588bddde694887c4cb55e); - address oneInchLiquidator = address(0xA6031a6D87b82B2d60df9B78E578537a2AeAe93a); + address public constant dai = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); + address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); uint256 maxUint = uint256(~0); uint256 slippageNumerator = 9; @@ -37,14 +34,11 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { // depositToken0 is DAI // additional storage slots (on top of BaseUpgradeableStrategy ones) are defined here bytes32 internal constant _DEPOSIT_TOKEN_SLOT = 0x219270253dbc530471c88a9e7c321b36afda219583431e7b6c386d2d46e70c86; - bytes32 internal constant _LIQUIDATE_DEPOSIT_TOKEN_VIA_DAI_SLOT = 0xe409979bbff331377f8c6e329d8221b165153f1b13926cd194bf26d1033513dd; address[] public rewardTokens; - mapping(address => mapping(address => address)) public storedOneInchPools; constructor() public BaseUpgradeableStrategyUL() { assert(_DEPOSIT_TOKEN_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.depositToken")) - 1)); - assert(_LIQUIDATE_DEPOSIT_TOKEN_VIA_DAI_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.liquidateDepositTokenViaDai")) - 1)); } function initializeBaseStrategy( @@ -52,8 +46,7 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { address _underlying, address _vault, address _rewardPool, - uint256 _profitSharingNumerator, - bool _liquidateDepositTokenViaDai + uint256 _profitSharingNumerator ) public initializer { BaseUpgradeableStrategyUL.initialize( @@ -72,11 +65,9 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { require(IVault(_vault).underlying() == _underlying, "vault does not support the required LP token"); _setDepositToken(IMooniswap(_underlying).token1()); - require(depositToken() != address(0), "token1 must be non-zero"); + require(depositToken() != address(0), "depositToken (token1) must be non-zero"); require(IMooniswap(_underlying).token0() == dai, "token0 must be dai"); - _setLiquidateDepositTokenViaDai(_liquidateDepositTokenViaDai); - rewardTokens = new address[](0); } @@ -173,10 +164,12 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { rewardTokensToCommon(); uint256 rewardBalance = IERC20(weth).balanceOf(address(this)); + console.log("rewardBalance", rewardBalance); // share 30% of the wrapped Ether as a profit sharing reward notifyProfitInRewardToken(rewardBalance); uint256 remainingRewardBalance = IERC20(rewardToken()).balanceOf(address(this)); + console.log("remainingRewardBalance", remainingRewardBalance); if (remainingRewardBalance == 0) { return; @@ -187,96 +180,56 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { depositMooniSwap(); } - /** - * swaps the common token (WETH) to the deposit tokens for reinvesting - */ - function commonToDepositTokens(uint256 amount) internal { - // convert half of the remaining rewardBalance back to the main deposit token DAI - uint256 liquidateRewardToDaiBalance = amount.div(2); - - if(liquidateDepositTokenViaDai()) { - // if we liquidate the deposit token via DAI then we swap all of the rewardBalance to DAI - liquidateRewardToDaiBalance = amount; - } - - swapViaULorOneInchUL(weth, dai, liquidateRewardToDaiBalance); - - if(liquidateDepositTokenViaDai()) { - // convert half of the dai balance to the second token - uint256 remainingRewardBalance = IERC20(dai).balanceOf(address(this)); - swapViaULorOneInchUL(dai, depositToken(), remainingRewardBalance); - } else { - // the remaining half of the reward balance is converted into the second token - uint256 remainingRewardBalance = IERC20(rewardToken()).balanceOf(address(this)); - swapViaULorOneInchUL(weth, depositToken(), remainingRewardBalance); - } - } - /** * swaps all the reward tokens to WETH */ function rewardTokensToCommon() internal { // multiple reward tokens are supported -> liquidate all of them into common rewardToken (weth) for(uint256 i = 0; i < rewardTokens.length; i++){ - address rewardToken = rewardTokens[i]; - uint256 rewardBalance = IERC20(rewardToken).balanceOf(address(this)); - if (rewardBalance == 0 || (storedLiquidationDexes[rewardToken][weth].length < 1 && storedLiquidationDexes[rewardToken][eth].length < 1)) { + address token = rewardTokens[i]; + uint256 rewardBalance = IERC20(token).balanceOf(address(this)); + if (rewardBalance == 0 || storedLiquidationDexes[token][rewardToken()].length < 1) { continue; } - swapViaULorOneInchUL(rewardToken, weth, rewardBalance); + swapViaUL(token, rewardToken(), rewardBalance); } } - function swapViaULorOneInchUL(address fromToken, address toToken, uint256 amount) internal { - bool toWethHasToBeWrapped = false; - if(toToken == weth) { - // check if a path for weth exists or if we have to wrap - toWethHasToBeWrapped = storedLiquidationDexes[fromToken][toToken].length < 1; - } - bytes32[] memory dexes = storedLiquidationDexes[rewardToken()][weth]; - if(toWethHasToBeWrapped) { - dexes = storedLiquidationDexes[rewardToken()][eth]; - } + /** + * swaps the common token (WETH) to the deposit tokens for reinvesting + */ + function commonToDepositTokens(uint256 rewardBalance) internal { + // convert half of the rewardBalance back to the main deposit token DAI + swapViaUL(rewardToken(), dai, rewardBalance.div(2)); - if(dexes[0] == oneInchDex) { - // via 1inch - IERC20(fromToken).safeApprove(oneInchLiquidator, 0); - IERC20(fromToken).safeApprove(oneInchLiquidator, amount); - - IOneInchLiquidator(oneInchLiquidator).changePool(fromToken, toToken, storedOneInchPools[fromToken][toToken]); - // we can accept 1 as the minimum because this will be called only by a trusted worker - IOneInchLiquidator(oneInchLiquidator).doSwap( - amount, - 1, - address(this), // spender - address(this), // target - storedLiquidationPaths[fromToken][toToken] - ); - } else { - // via UL that handles uni and sushi - IERC20(fromToken).safeApprove(universalLiquidator(), 0); - IERC20(fromToken).safeApprove(universalLiquidator(), amount); - // we can accept 1 as the minimum because this will be called only by a trusted worker - ILiquidator(universalLiquidator()).swapTokenOnMultipleDEXes( - amount, - 1, - address(this), // target - storedLiquidationDexes[fromToken][toToken], - storedLiquidationPaths[fromToken][toToken] - ); - } + // the remaining half of the reward balance is converted into the second token + uint256 remainingRewardBalance = IERC20(rewardToken()).balanceOf(address(this)); + swapViaUL(rewardToken(), depositToken(), remainingRewardBalance); + } - // wrap if necessary - if(toWethHasToBeWrapped) { - // convert the received Ether into wrapped Ether - WETH9(weth).deposit.value(address(this).balance)(); - } + /** + * swaps given amount from token to other token via UL + */ + function swapViaUL(address fromToken, address toToken, uint256 amount) internal { + IERC20(fromToken).safeApprove(universalLiquidator(), 0); + IERC20(fromToken).safeApprove(universalLiquidator(), amount); + + // we can accept 1 as the minimum because this will be called only by a trusted worker + ILiquidator(universalLiquidator()).swapTokenOnMultipleDEXes( + amount, + 1, + address(this), // target + storedLiquidationDexes[fromToken][toToken], + storedLiquidationPaths[fromToken][toToken] + ); } function depositMooniSwap() internal { uint256 token1Amount = IERC20(depositToken()).balanceOf(address(this)); uint256 daiAmount = IERC20(dai).balanceOf(address(this)); + console.log("token1Amount", token1Amount); + console.log("daiAmount", daiAmount); if (!(daiAmount > 0 && token1Amount > 0)) { return; } @@ -347,15 +300,6 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { return getAddress(_DEPOSIT_TOKEN_SLOT); } - function _setLiquidateDepositTokenViaDai(bool _liquidateViaDai) internal { - setBoolean(_LIQUIDATE_DEPOSIT_TOKEN_VIA_DAI_SLOT, _liquidateViaDai); - } - - function liquidateDepositTokenViaDai() public view returns (bool) { - return getBoolean(_LIQUIDATE_DEPOSIT_TOKEN_VIA_DAI_SLOT); - } - - function finalizeUpgrade() external onlyGovernance { _finalizeUpgrade(); } diff --git a/test/1inch/dai-ldo.js b/test/1inch/dai-ldo.js index 651b1da..ab53d87 100644 --- a/test/1inch/dai-ldo.js +++ b/test/1inch/dai-ldo.js @@ -4,8 +4,9 @@ const { impersonates, setupCoreProtocol, depositVault } = require("../utilities/ const BigNumber = require("bignumber.js"); const IERC20 = artifacts.require("@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20"); +const IOneInchLiquidator = artifacts.require("IOneInchLiquidator"); -const OneInchStrategy_DAI_LDO = artifacts.require("OneInchStrategy_DAI_LDO"); +const Strategy = artifacts.require("OneInchStrategy_DAI_LDO"); // This test was developed at blockNumber 13352188 @@ -17,7 +18,10 @@ describe("Mainnet DAI/LDO", function() { let underlying; // external setup - let underlyingWhale = "0x37e46caEfEF61D30E680cf754998B4A39e5d8325"; + const underlyingWhale = "0x37e46caEfEF61D30E680cf754998B4A39e5d8325"; + const oneInchLiquidatorAddr = "0xA6031a6D87b82B2d60df9B78E578537a2AeAe93a"; + const daiStEthPoolOneInch = "0xC1A900Ae76dB21dC5aa8E418Ac0F4E888A4C7431"; + let oneInchLiquidator; // parties in the protocol let governance; @@ -40,7 +44,8 @@ describe("Mainnet DAI/LDO", function() { // Give whale some ether to make sure the following actions are good await web3.eth.sendTransaction({ from: etherGiver, to: underlyingWhale, value: 1e18}); - farmerBalance = await underlying.balanceOf(underlyingWhale); + farmerBalance = new BigNumber(await underlying.balanceOf(underlyingWhale)); + console.log('transfering farmerBalance', farmerBalance.toFixed()); await underlying.transfer(farmer1, farmerBalance, { from: underlyingWhale }); } @@ -55,15 +60,28 @@ describe("Mainnet DAI/LDO", function() { let oneInch = "0x111111111117dC0aa78b770fA6A738034120C302"; let weth = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; + let ldo = "0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32"; + let dai = "0x6B175474E89094C44Da98b954EedeAC495271d0F"; + let stEth = "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84"; await setupExternalContracts(); [controller, vault, strategy] = await setupCoreProtocol({ "existingVaultAddress": null, - "strategyArtifact": OneInchStrategy_DAI_LDO, + "strategyArtifact": Strategy, + "strategyArtifactIsUpgradable": true, "underlying": underlying, "governance": governance, + "liquidation": [{"1inch": [oneInch, weth]}, + {"sushi": [ldo, weth]}, + {"sushi": [weth, dai]}, + {"1inch": [dai, stEth]}, + ] }); + oneInchLiquidator = await IOneInchLiquidator.at(oneInchLiquidatorAddr); + // // change pool for 1inch Liquidator to dai-stETH Pool + await oneInchLiquidator.changePool(dai, stEth, daiStEthPoolOneInch, {from: governance}); + // whale send underlying to farmers await setupBalance(); }); @@ -75,11 +93,11 @@ describe("Mainnet DAI/LDO", function() { // Using half days is to simulate how we doHardwork in the real world let hours = 10; + let blocksPerHour = 2400; let oldSharePrice; let newSharePrice; for (let i = 0; i < hours; i++) { console.log("loop ", i); - let blocksPerHour = 2400; oldSharePrice = new BigNumber(await vault.getPricePerFullShare()); await controller.doHardWork(vault.address, { from: governance }); newSharePrice = new BigNumber(await vault.getPricePerFullShare()); @@ -88,13 +106,25 @@ describe("Mainnet DAI/LDO", function() { console.log("new shareprice: ", newSharePrice.toFixed()); console.log("growth: ", newSharePrice.toFixed() / oldSharePrice.toFixed()); + let apr = (newSharePrice.toFixed()/oldSharePrice.toFixed()-1)*(24/(blocksPerHour/272))*365; + let apy = ((newSharePrice.toFixed()/oldSharePrice.toFixed()-1)*(24/(blocksPerHour/272))+1)**365; + + console.log("instant APR:", apr*100, "%"); + console.log("instant APY:", (apy-1)*100, "%"); + await Utils.advanceNBlock(blocksPerHour); } await vault.withdraw(farmerBalance, { from: farmer1 }); let farmerNewBalance = new BigNumber(await underlying.balanceOf(farmer1)); Utils.assertBNGt(farmerNewBalance, farmerOldBalance); + apr = (farmerNewBalance.toFixed()/farmerOldBalance.toFixed()-1)*(24/(blocksPerHour*hours/272))*365; + apy = ((farmerNewBalance.toFixed()/farmerOldBalance.toFixed()-1)*(24/(blocksPerHour*hours/272))+1)**365; + console.log("earned!"); + console.log("Overall APR:", apr*100, "%"); + console.log("Overall APY:", (apy-1)*100, "%"); + }); }); From dfcd46d662c5ee3a66ae622f18f215405cafe555 Mon Sep 17 00:00:00 2001 From: bergben Date: Mon, 4 Oct 2021 23:41:41 +0200 Subject: [PATCH 5/6] fix(oneInch_dai-ldo): bugfix slippageNumerators not defined by marking as const --- .../strategies/1inch/base/OneInchStrategy_DAI_X.sol | 12 +++--------- test/1inch/dai-ldo.js | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol b/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol index d38b7d4..7e85da4 100644 --- a/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol +++ b/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol @@ -12,8 +12,6 @@ import "../../../base/upgradability/BaseUpgradeableStrategyUL.sol"; import "../../../base/interface/IVault.sol"; import "../../../base/StrategyBase.sol"; -import "hardhat/console.sol"; - /** * This strategy is for DAI / X 1inch LP tokens @@ -27,9 +25,9 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { address public constant dai = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); - uint256 maxUint = uint256(~0); - uint256 slippageNumerator = 9; - uint256 slippageDenominator = 10; + uint256 internal constant maxUint = uint256(~0); + uint256 public constant slippageNumerator = 9; + uint256 public constant slippageDenominator = 10; // depositToken0 is DAI // additional storage slots (on top of BaseUpgradeableStrategy ones) are defined here @@ -164,12 +162,10 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { rewardTokensToCommon(); uint256 rewardBalance = IERC20(weth).balanceOf(address(this)); - console.log("rewardBalance", rewardBalance); // share 30% of the wrapped Ether as a profit sharing reward notifyProfitInRewardToken(rewardBalance); uint256 remainingRewardBalance = IERC20(rewardToken()).balanceOf(address(this)); - console.log("remainingRewardBalance", remainingRewardBalance); if (remainingRewardBalance == 0) { return; @@ -228,8 +224,6 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { function depositMooniSwap() internal { uint256 token1Amount = IERC20(depositToken()).balanceOf(address(this)); uint256 daiAmount = IERC20(dai).balanceOf(address(this)); - console.log("token1Amount", token1Amount); - console.log("daiAmount", daiAmount); if (!(daiAmount > 0 && token1Amount > 0)) { return; } diff --git a/test/1inch/dai-ldo.js b/test/1inch/dai-ldo.js index ab53d87..a4ec7f2 100644 --- a/test/1inch/dai-ldo.js +++ b/test/1inch/dai-ldo.js @@ -33,6 +33,7 @@ describe("Mainnet DAI/LDO", function() { // Core protocol contracts let controller; let vault; + let strategy; async function setupExternalContracts() { underlying = await IERC20.at("0xC1A900Ae76dB21dC5aa8E418Ac0F4E888A4C7431"); @@ -66,7 +67,6 @@ describe("Mainnet DAI/LDO", function() { await setupExternalContracts(); [controller, vault, strategy] = await setupCoreProtocol({ - "existingVaultAddress": null, "strategyArtifact": Strategy, "strategyArtifactIsUpgradable": true, "underlying": underlying, From 1a4df835f7d442d9c57d2b69f227e53551e58c66 Mon Sep 17 00:00:00 2001 From: bergben Date: Tue, 5 Oct 2021 10:25:02 +0200 Subject: [PATCH 6/6] refactor(oneInch-dai-ldo): remove profitSharingNumerator param --- .../strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol | 3 +-- contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol b/contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol index 3b839d3..232e0db 100644 --- a/contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol +++ b/contracts/strategies/1inch/OneInchStrategyMainnet_DAI_LDO.sol @@ -27,8 +27,7 @@ contract OneInchStrategy_DAI_LDO is OneInchStrategy_DAI_X { _storage, underlying, _vault, - rewardPool, //rewardPool - 300 // profit sharing numerator + rewardPool //rewardPool ); rewardTokens = [oneInch, ldo]; diff --git a/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol b/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol index 7e85da4..56b3fc5 100644 --- a/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol +++ b/contracts/strategies/1inch/base/OneInchStrategy_DAI_X.sol @@ -43,8 +43,7 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { address _storage, address _underlying, address _vault, - address _rewardPool, - uint256 _profitSharingNumerator + address _rewardPool ) public initializer { BaseUpgradeableStrategyUL.initialize( @@ -53,7 +52,7 @@ contract OneInchStrategy_DAI_X is IStrategy, BaseUpgradeableStrategyUL { _vault, _rewardPool, weth, - _profitSharingNumerator, // profit sharing numerator + 300, // profit sharing numerator 1000, // profit sharing denominator true, // sell 0, // sell floor