From 4c93eb3beea8987be536ead41e2a9801c8bd2014 Mon Sep 17 00:00:00 2001 From: amatureApe Date: Sat, 21 May 2022 13:28:58 -0500 Subject: [PATCH 1/2] stella jars WIP --- scripts/degenApe.js | 8 +- src/interfaces/stella-rewarder.sol | 7 + .../stella/strategy-stella-ETHmad-GLMR-lp.sol | 40 ++++++ .../stella/strategy-stella-farm-base.sol | 45 ++---- src/strategies/moonbeam/strategy-base.sol | 108 +++++---------- .../moonbeam/strategy-stella-farm-base-v2.sol | 131 ++++++++++++++++++ src/strategies/strategy-base.sol | 120 ++++------------ 7 files changed, 255 insertions(+), 204 deletions(-) create mode 100644 src/interfaces/stella-rewarder.sol create mode 100644 src/strategies/moonbeam/stella/strategy-stella-ETHmad-GLMR-lp.sol create mode 100644 src/strategies/moonbeam/strategy-stella-farm-base-v2.sol diff --git a/scripts/degenApe.js b/scripts/degenApe.js index bd9f9f298..65c13f8b8 100644 --- a/scripts/degenApe.js +++ b/scripts/degenApe.js @@ -16,10 +16,10 @@ const { sleep, fastVerifyContracts, slowVerifyContracts } = require("./degenUtil // Script configs const sleepConfig = { sleepToggle: true, sleepTime: 10000 }; const callAttempts = 3; -const generatePfcore = false; +const generatePfcore = true; // Pf-core generation configs -const outputFolder = "scripts/degenApe/degenApeOutputs"; +const outputFolder = "scripts/degenApeOutputs"; // These arguments need to be set manually before the script can make pf-core // @param - chain: The chain on which the script is running @@ -48,12 +48,12 @@ const pfcoreArgs = { // Addresses & Contracts const governance = "0x4204FDD868FFe0e62F57e6A626F8C9530F7d5AD1"; const strategist = "0x4204FDD868FFe0e62F57e6A626F8C9530F7d5AD1"; -const controller = "0xc335740c951F45200b38C5Ca84F0A9663b51AEC6"; +const controller = "0x95ca4584eA2007D578fa2693CCC76D930a96d165"; const timelock = "0x4204FDD868FFe0e62F57e6A626F8C9530F7d5AD1"; const harvester = ["0x0f571D2625b503BB7C1d2b5655b483a2Fa696fEf"]; const contracts = [ - "src/strategies/optimism/zipswap/strategy-zip-$gohm-$weth-lp.sol:StrategyZipEthGohmLp" + "src/strategies/moonbeam/stella/strategy-stella-ETHmad-GLMR-lp.sol:StrategyStellaETHmadGLMRLp" ]; const testedStrategies = []; diff --git a/src/interfaces/stella-rewarder.sol b/src/interfaces/stella-rewarder.sol new file mode 100644 index 000000000..c6e0b9c6b --- /dev/null +++ b/src/interfaces/stella-rewarder.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.7; + +// interface for Solarchef contract +interface IStellaRewarder { + function pendingTokens(uint256 _pid, address _user) external view returns (uint256); +} diff --git a/src/strategies/moonbeam/stella/strategy-stella-ETHmad-GLMR-lp.sol b/src/strategies/moonbeam/stella/strategy-stella-ETHmad-GLMR-lp.sol new file mode 100644 index 000000000..efe01525b --- /dev/null +++ b/src/strategies/moonbeam/stella/strategy-stella-ETHmad-GLMR-lp.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.7; + +import "../strategy-stella-farm-base-v2.sol"; + +contract StrategyStellaETHmadGLMRLp is StrategyStellaFarmBaseV2 { + uint256 public ETHmad_glmr_poolId = 11; + + // Token addresses + address public ETHmad_glmr_lp = 0x9d19EDBFd29D2e01537624B25806493dA0d73bBE; + address public ETHmad = 0x30D2a9F5FDf90ACe8c17952cbb4eE48a55D916A7; + address public rewarderContract = 0x9Fe074a56FFa7f4079C6190be6E8452911b7E349; + + constructor( + address _governance, + address _strategist, + address _controller, + address _timelock + ) + public + StrategyStellaFarmBaseV2( + ETHmad_glmr_lp, + ETHmad_glmr_poolId, + rewarderContract, + _governance, + _strategist, + _controller, + _timelock + ) + { + swapRoutes[glmr] = [stella, glmr]; + swapRoutes[ETHmad] = [glmr, ETHmad]; + } + + // **** Views **** + + function getName() external pure override returns (string memory) { + return "StrategyStellaEthGlmrLp"; + } +} diff --git a/src/strategies/moonbeam/stella/strategy-stella-farm-base.sol b/src/strategies/moonbeam/stella/strategy-stella-farm-base.sol index 92c3c0f7c..1a902e219 100644 --- a/src/strategies/moonbeam/stella/strategy-stella-farm-base.sol +++ b/src/strategies/moonbeam/stella/strategy-stella-farm-base.sol @@ -7,8 +7,7 @@ import "../../../interfaces/stella-chef.sol"; abstract contract StrategyStellaFarmBase is StrategyBase { // Token addresses address public constant stella = 0x0E358838ce72d5e61E0018a2ffaC4bEC5F4c88d2; - address public constant stellaChef = - 0xEDFB330F5FA216C9D2039B99C8cE9dA85Ea91c1E; + address public constant stellaChef = 0xEDFB330F5FA216C9D2039B99C8cE9dA85Ea91c1E; address public token0; address public token1; @@ -27,10 +26,7 @@ abstract contract StrategyStellaFarmBase is StrategyBase { address _strategist, address _controller, address _timelock - ) - public - StrategyBase(_lp, _governance, _strategist, _controller, _timelock) - { + ) public StrategyBase(_lp, _governance, _strategist, _controller, _timelock) { // Stellaswap router sushiRouter = 0xd0A01ec574D1fC6652eDF79cb2F880fd47D34Ab1; poolId = _poolId; @@ -39,10 +35,7 @@ abstract contract StrategyStellaFarmBase is StrategyBase { } function balanceOfPool() public view override returns (uint256) { - (uint256 amount, , , ) = IStellaChef(stellaChef).userInfo( - poolId, - address(this) - ); + (uint256 amount, , , ) = IStellaChef(stellaChef).userInfo(poolId, address(this)); return amount; } @@ -61,11 +54,7 @@ abstract contract StrategyStellaFarmBase is StrategyBase { } } - function _withdrawSome(uint256 _amount) - internal - override - returns (uint256) - { + function _withdrawSome(uint256 _amount) internal override returns (uint256) { IStellaChef(stellaChef).withdraw(poolId, _amount); return _amount; } @@ -84,10 +73,7 @@ abstract contract StrategyStellaFarmBase is StrategyBase { if (_stella > 0) { uint256 _keepSTELLA = _stella.mul(keepSTELLA).div(keepSTELLAMax); - IERC20(stella).safeTransfer( - IController(controller).treasury(), - _keepSTELLA - ); + IERC20(stella).safeTransfer(IController(controller).treasury(), _keepSTELLA); _stella = _stella.sub(_keepSTELLA); uint256 toToken0 = _stella.div(2); uint256 toToken1 = _stella.sub(toToken0); @@ -110,26 +96,11 @@ abstract contract StrategyStellaFarmBase is StrategyBase { IERC20(token1).safeApprove(sushiRouter, 0); IERC20(token1).safeApprove(sushiRouter, _token1); - UniswapRouterV2(sushiRouter).addLiquidity( - token0, - token1, - _token0, - _token1, - 0, - 0, - address(this), - now + 60 - ); + UniswapRouterV2(sushiRouter).addLiquidity(token0, token1, _token0, _token1, 0, 0, address(this), now + 60); // Donates DUST - IERC20(token0).transfer( - IController(controller).treasury(), - IERC20(token0).balanceOf(address(this)) - ); - IERC20(token1).safeTransfer( - IController(controller).treasury(), - IERC20(token1).balanceOf(address(this)) - ); + IERC20(token0).transfer(IController(controller).treasury(), IERC20(token0).balanceOf(address(this))); + IERC20(token1).safeTransfer(IController(controller).treasury(), IERC20(token1).balanceOf(address(this))); } _distributePerformanceFeesAndDeposit(); diff --git a/src/strategies/moonbeam/strategy-base.sol b/src/strategies/moonbeam/strategy-base.sol index 33cba191d..648153d52 100644 --- a/src/strategies/moonbeam/strategy-base.sol +++ b/src/strategies/moonbeam/strategy-base.sol @@ -34,7 +34,7 @@ abstract contract StrategyBase { // Tokens address public want; - address public constant glmr = 0xAcc15dC74880C9944775448304B263D191c6077F; + address public constant glmr = 0xAcc15dC74880C9944775448304B263D191c6077F; // User accounts address public governance; @@ -69,12 +69,8 @@ abstract contract StrategyBase { // **** Modifiers **** // - modifier onlyBenevolent { - require( - harvesters[msg.sender] || - msg.sender == governance || - msg.sender == strategist - ); + modifier onlyBenevolent() { + require(harvesters[msg.sender] || msg.sender == governance || msg.sender == strategist); _; } @@ -84,30 +80,28 @@ abstract contract StrategyBase { return IERC20(want).balanceOf(address(this)); } - function balanceOfPool() public virtual view returns (uint256); + function balanceOfPool() public view virtual returns (uint256); function balanceOf() public view returns (uint256) { return balanceOfWant().add(balanceOfPool()); } - function getName() external virtual pure returns (string memory); + function getName() external pure virtual returns (string memory); // **** Setters **** // function whitelistHarvesters(address[] calldata _harvesters) external { - require(msg.sender == governance || - msg.sender == strategist || harvesters[msg.sender], "not authorized"); - - for (uint i = 0; i < _harvesters.length; i ++) { + require(msg.sender == governance || msg.sender == strategist || harvesters[msg.sender], "not authorized"); + + for (uint256 i = 0; i < _harvesters.length; i++) { harvesters[_harvesters[i]] = true; } } function revokeHarvesters(address[] calldata _harvesters) external { - require(msg.sender == governance || - msg.sender == strategist, "not authorized"); + require(msg.sender == governance || msg.sender == strategist, "not authorized"); - for (uint i = 0; i < _harvesters.length; i ++) { + for (uint256 i = 0; i < _harvesters.length; i++) { harvesters[_harvesters[i]] = false; } } @@ -127,9 +121,7 @@ abstract contract StrategyBase { performanceDevFee = _performanceDevFee; } - function setPerformanceTreasuryFee(uint256 _performanceTreasuryFee) - external - { + function setPerformanceTreasuryFee(uint256 _performanceTreasuryFee) external { require(msg.sender == timelock, "!timelock"); performanceTreasuryFee = _performanceTreasuryFee; } @@ -174,18 +166,11 @@ abstract contract StrategyBase { _amount = _amount.add(_balance); } - uint256 _feeDev = _amount.mul(withdrawalDevFundFee).div( - withdrawalDevFundMax - ); + uint256 _feeDev = _amount.mul(withdrawalDevFundFee).div(withdrawalDevFundMax); IERC20(want).safeTransfer(IController(controller).devfund(), _feeDev); - uint256 _feeTreasury = _amount.mul(withdrawalTreasuryFee).div( - withdrawalTreasuryMax - ); - IERC20(want).safeTransfer( - IController(controller).treasury(), - _feeTreasury - ); + uint256 _feeTreasury = _amount.mul(withdrawalTreasuryFee).div(withdrawalTreasuryMax); + IERC20(want).safeTransfer(IController(controller).treasury(), _feeTreasury); address _jar = IController(controller).jars(address(want)); require(_jar != address(0), "!jar"); // additional protection so we don't burn the funds @@ -194,10 +179,7 @@ abstract contract StrategyBase { } // Withdraw funds, used to swap between strategies - function withdrawForSwap(uint256 _amount) - external - returns (uint256 balance) - { + function withdrawForSwap(uint256 _amount) external returns (uint256 balance) { require(msg.sender == controller, "!controller"); _withdrawSome(_amount); @@ -230,39 +212,25 @@ abstract contract StrategyBase { // **** Emergency functions **** - function execute(address _target, bytes memory _data) - public - payable - returns (bytes memory response) - { + function execute(address _target, bytes memory _data) public payable returns (bytes memory response) { require(msg.sender == timelock, "!timelock"); require(_target != address(0), "!target"); // call contract in current context assembly { - let succeeded := delegatecall( - sub(gas(), 5000), - _target, - add(_data, 0x20), - mload(_data), - 0, - 0 - ) + let succeeded := delegatecall(sub(gas(), 5000), _target, add(_data, 0x20), mload(_data), 0, 0) let size := returndatasize() response := mload(0x40) - mstore( - 0x40, - add(response, and(add(add(size, 0x20), 0x1f), not(0x1f))) - ) + mstore(0x40, add(response, and(add(add(size, 0x20), 0x1f), not(0x1f)))) mstore(response, size) returndatacopy(add(response, 0x20), 0, size) switch iszero(succeeded) - case 1 { - // throw if delegatecall failed - revert(add(response, 0x20), size) - } + case 1 { + // throw if delegatecall failed + revert(add(response, 0x20), size) + } } } @@ -286,33 +254,27 @@ abstract contract StrategyBase { path[1] = glmr; path[2] = _to; } - + IERC20(_from).safeApprove(sushiRouter, 0); IERC20(_from).safeApprove(sushiRouter, _amount); - UniswapRouterV2(sushiRouter).swapExactTokensForTokens( - _amount, - 0, - path, - address(this), - now.add(60) - ); + UniswapRouterV2(sushiRouter).swapExactTokensForTokens(_amount, 0, path, address(this), now.add(60)); } - function _swapSushiswapWithPath( - address[] memory path, - uint256 _amount - ) internal { + function _swapSushiswapWithPath(address[] memory path, uint256 _amount) internal { require(path[1] != address(0)); IERC20(path[0]).safeApprove(sushiRouter, 0); IERC20(path[0]).safeApprove(sushiRouter, _amount); - UniswapRouterV2(sushiRouter).swapExactTokensForTokens( - _amount, - 1, - path, - address(this), - block.timestamp - ); + UniswapRouterV2(sushiRouter).swapExactTokensForTokens(_amount, 1, path, address(this), block.timestamp); + } + + function _swap( + address router, + address[] memory path, + uint256 _amount + ) internal { + require(path[1] != address(0)); + UniswapRouterV2(router).swapExactTokensForTokens(_amount, 0, path, address(this), now.add(60)); } function _distributePerformanceFeesAndDeposit() internal { diff --git a/src/strategies/moonbeam/strategy-stella-farm-base-v2.sol b/src/strategies/moonbeam/strategy-stella-farm-base-v2.sol new file mode 100644 index 000000000..13a02bd9a --- /dev/null +++ b/src/strategies/moonbeam/strategy-stella-farm-base-v2.sol @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.7; + +import "./strategy-base.sol"; +import "../../interfaces/stella-chef.sol"; +import "../../interfaces/stella-rewarder.sol"; +import "../../interfaces/weth.sol"; + +abstract contract StrategyStellaFarmBaseV2 is StrategyBase { + // Token addresses + address public constant stella = 0x0E358838ce72d5e61E0018a2ffaC4bEC5F4c88d2; + address public constant stellaChef = 0xF3a5454496E26ac57da879bf3285Fa85DEBF0388; + address public constant stellaRouter = 0xd0A01ec574D1fC6652eDF79cb2F880fd47D34Ab1; + address public rewarder; + + address public token0; + address public token1; + + // How much STELLA tokens to keep? + uint256 public keepREWARD = 1000; + uint256 public constant keepREWARDMax = 10000; + + uint256 public poolId; + mapping(address => address[]) public swapRoutes; + + constructor( + address _lp, + uint256 _poolId, + address _rewarder, + address _governance, + address _strategist, + address _controller, + address _timelock + ) public StrategyBase(_lp, _governance, _strategist, _controller, _timelock) { + poolId = _poolId; + rewarder = _rewarder; + token0 = IUniswapV2Pair(_lp).token0(); + token1 = IUniswapV2Pair(_lp).token1(); + + IERC20(token0).approve(stellaRouter, uint256(-1)); + IERC20(token1).approve(stellaRouter, uint256(-1)); + IERC20(stella).approve(stellaRouter, uint256(-1)); + IERC20(glmr).approve(stellaRouter, uint256(-1)); + IERC20(want).approve(stellaChef, uint256(-1)); + } + + function balanceOfPool() public view override returns (uint256) { + (uint256 amount, , , ) = IStellaChef(stellaChef).userInfo(poolId, address(this)); + return amount; + } + + function getHarvestable() external view returns (uint256, uint256) { + uint256 PendingStella = IStellaChef(stellaChef).pendingStella(poolId, address(this)); + uint256 PendingGlmr = IStellaRewarder(rewarder).pendingTokens(poolId, address(this)); + + return (PendingStella, PendingGlmr); + } + + // **** Setters **** + + function deposit() public override { + uint256 _want = IERC20(want).balanceOf(address(this)); + if (_want > 0) { + IStellaChef(stellaChef).deposit(poolId, _want); + } + } + + function _withdrawSome(uint256 _amount) internal override returns (uint256) { + IStellaChef(stellaChef).withdraw(poolId, _amount); + return _amount; + } + + function setKeepSTELLA(uint256 _keepREWARD) external { + require(msg.sender == timelock, "!timelock"); + keepREWARD = _keepREWARD; + } + + // **** State Mutations **** + + function harvest() public override { + // Collects STELLA tokens + IStellaChef(stellaChef).deposit(poolId, 0); + + uint256 _nakedGlmr = address(this).balance; + WETH(glmr).deposit{value: _nakedGlmr}(); + + uint256 _stella = IERC20(stella).balanceOf(address(this)); + + if (_stella > 0) { + if (swapRoutes[glmr].length > 1) { + _swap(stellaRouter, swapRoutes[glmr], _stella); + } + uint256 _glmr = IERC20(glmr).balanceOf(address(this)); + uint256 _keepReward = _glmr.mul(keepREWARD).div(keepREWARDMax); + IERC20(glmr).safeTransfer(IController(controller).treasury(), _keepReward); + + _glmr = IERC20(glmr).balanceOf(address(this)); + if (glmr == token0 || glmr == token1) { + address toToken = glmr == token0 ? token1 : token0; + + if (swapRoutes[toToken].length > 1 && _glmr > 0) { + _swap(stellaRouter, swapRoutes[toToken], _glmr.div(2)); + } + } else { + uint256 toToken0 = _glmr.div(2); + uint256 toToken1 = _glmr.sub(toToken0); + + if (swapRoutes[token0].length > 1) { + _swap(stellaRouter, swapRoutes[token0], toToken0); + } + if (swapRoutes[token1].length > 1) { + _swap(stellaRouter, swapRoutes[token1], toToken1); + } + } + } + + // Adds in liquidity for token0/token1 + uint256 _token0 = IERC20(token0).balanceOf(address(this)); + uint256 _token1 = IERC20(token1).balanceOf(address(this)); + + if (_token0 > 0 && _token1 > 0) { + UniswapRouterV2(stellaRouter).addLiquidity(token0, token1, _token0, _token1, 0, 0, address(this), now + 60); + + // Donates DUST + IERC20(token0).transfer(IController(controller).treasury(), IERC20(token0).balanceOf(address(this))); + IERC20(token1).safeTransfer(IController(controller).treasury(), IERC20(token1).balanceOf(address(this))); + } + + _distributePerformanceFeesAndDeposit(); + } +} diff --git a/src/strategies/strategy-base.sol b/src/strategies/strategy-base.sol index 9a60f1078..153782689 100644 --- a/src/strategies/strategy-base.sol +++ b/src/strategies/strategy-base.sol @@ -45,12 +45,9 @@ abstract contract StrategyBase { address public univ2Router2 = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; address public sushiRouter = 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F; - address public constant nonFungiblePositionManager = - 0xC36442b4a4522E871399CD717aBDD847Ab11FE88; - address public constant univ3Factory = - 0x1F98431c8aD98523631AE4a59f267346ea31F984; - address public constant univ3Router = - 0xE592427A0AEce92De3Edee1F18E0157C05861564; + address public constant nonFungiblePositionManager = 0xC36442b4a4522E871399CD717aBDD847Ab11FE88; + address public constant univ3Factory = 0x1F98431c8aD98523631AE4a59f267346ea31F984; + address public constant univ3Router = 0xE592427A0AEce92De3Edee1F18E0157C05861564; mapping(address => bool) public harvesters; @@ -76,12 +73,8 @@ abstract contract StrategyBase { // **** Modifiers **** // - modifier onlyBenevolent { - require( - harvesters[msg.sender] || - msg.sender == governance || - msg.sender == strategist - ); + modifier onlyBenevolent() { + require(harvesters[msg.sender] || msg.sender == governance || msg.sender == strategist); _; } @@ -102,12 +95,7 @@ abstract contract StrategyBase { // **** Setters **** // function whitelistHarvesters(address[] calldata _harvesters) external { - require( - msg.sender == governance || - msg.sender == strategist || - harvesters[msg.sender], - "not authorized" - ); + require(msg.sender == governance || msg.sender == strategist || harvesters[msg.sender], "not authorized"); for (uint256 i = 0; i < _harvesters.length; i++) { harvesters[_harvesters[i]] = true; @@ -115,10 +103,7 @@ abstract contract StrategyBase { } function revokeHarvesters(address[] calldata _harvesters) external { - require( - msg.sender == governance || msg.sender == strategist, - "not authorized" - ); + require(msg.sender == governance || msg.sender == strategist, "not authorized"); for (uint256 i = 0; i < _harvesters.length; i++) { harvesters[_harvesters[i]] = false; @@ -140,9 +125,7 @@ abstract contract StrategyBase { performanceDevFee = _performanceDevFee; } - function setPerformanceTreasuryFee(uint256 _performanceTreasuryFee) - external - { + function setPerformanceTreasuryFee(uint256 _performanceTreasuryFee) external { require(msg.sender == timelock, "!timelock"); performanceTreasuryFee = _performanceTreasuryFee; } @@ -187,21 +170,14 @@ abstract contract StrategyBase { _amount = _amount.add(_balance); } - uint256 _feeDev = _amount.mul(withdrawalDevFundFee).div( - withdrawalDevFundMax - ); + uint256 _feeDev = _amount.mul(withdrawalDevFundFee).div(withdrawalDevFundMax); if (_feeDev > 0) { IERC20(want).safeTransfer(IController(controller).devfund(), _feeDev); } - uint256 _feeTreasury = _amount.mul(withdrawalTreasuryFee).div( - withdrawalTreasuryMax - ); + uint256 _feeTreasury = _amount.mul(withdrawalTreasuryFee).div(withdrawalTreasuryMax); if (_feeTreasury > 0) { - IERC20(want).safeTransfer( - IController(controller).treasury(), - _feeTreasury - ); + IERC20(want).safeTransfer(IController(controller).treasury(), _feeTreasury); } address _jar = IController(controller).jars(address(want)); @@ -211,10 +187,7 @@ abstract contract StrategyBase { } // Withdraw funds, used to swap between strategies - function withdrawForSwap(uint256 _amount) - external - returns (uint256 balance) - { + function withdrawForSwap(uint256 _amount) external returns (uint256 balance) { require(msg.sender == controller, "!controller"); _withdrawSome(_amount); @@ -247,31 +220,17 @@ abstract contract StrategyBase { // **** Emergency functions **** - function execute(address _target, bytes memory _data) - public - payable - returns (bytes memory response) - { + function execute(address _target, bytes memory _data) public payable returns (bytes memory response) { require(msg.sender == timelock, "!timelock"); require(_target != address(0), "!target"); // call contract in current context assembly { - let succeeded := delegatecall( - sub(gas(), 5000), - _target, - add(_data, 0x20), - mload(_data), - 0, - 0 - ) + let succeeded := delegatecall(sub(gas(), 5000), _target, add(_data, 0x20), mload(_data), 0, 0) let size := returndatasize() response := mload(0x40) - mstore( - 0x40, - add(response, and(add(add(size, 0x20), 0x1f), not(0x1f))) - ) + mstore(0x40, add(response, and(add(add(size, 0x20), 0x1f), not(0x1f)))) mstore(response, size) returndatacopy(add(response, 0x20), 0, size) @@ -304,27 +263,13 @@ abstract contract StrategyBase { path[2] = _to; } - UniswapRouterV2(univ2Router2).swapExactTokensForTokens( - _amount, - 0, - path, - address(this), - now.add(60) - ); + UniswapRouterV2(univ2Router2).swapExactTokensForTokens(_amount, 0, path, address(this), now.add(60)); } - function _swapUniswapWithPath(address[] memory path, uint256 _amount) - internal - { + function _swapUniswapWithPath(address[] memory path, uint256 _amount) internal { require(path[1] != address(0)); - UniswapRouterV2(univ2Router2).swapExactTokensForTokens( - _amount, - 0, - path, - address(this), - now.add(60) - ); + UniswapRouterV2(univ2Router2).swapExactTokensForTokens(_amount, 0, path, address(this), now.add(60)); } function _swapSushiswap( @@ -347,27 +292,22 @@ abstract contract StrategyBase { path[2] = _to; } - UniswapRouterV2(sushiRouter).swapExactTokensForTokens( - _amount, - 0, - path, - address(this), - now.add(60) - ); + UniswapRouterV2(sushiRouter).swapExactTokensForTokens(_amount, 0, path, address(this), now.add(60)); } - function _swapSushiswapWithPath(address[] memory path, uint256 _amount) - internal - { + function _swapSushiswapWithPath(address[] memory path, uint256 _amount) internal { require(path[1] != address(0)); - UniswapRouterV2(sushiRouter).swapExactTokensForTokens( - _amount, - 0, - path, - address(this), - now.add(60) - ); + UniswapRouterV2(sushiRouter).swapExactTokensForTokens(_amount, 0, path, address(this), now.add(60)); + } + + function _swap( + address router, + address[] memory path, + uint256 _amount + ) internal { + require(path[1] != address(0)); + UniswapRouterV2(router).swapExactTokensForTokens(_amount, 0, path, address(this), now.add(60)); } function _distributePerformanceFeesAndDeposit() internal { From 0f7fb77bce54588278bbc618d362f52a0cd9a77e Mon Sep 17 00:00:00 2001 From: amatureApe Date: Thu, 26 May 2022 13:41:57 -0500 Subject: [PATCH 2/2] more stella WIP --- hardhat.config.js | 3 +- package-lock.json | 167 +- package.json | 2 +- scripts/degenApe.js | 7 +- scripts/degenUtils.js | 2 +- .../strategy-stella-USDCmad-GLMR-lp.sol | 40 + .../moonbeam/strategy-stella-farm-base-v2.sol | 6 +- .../strategy-stella-ETHmad-glmr-lp.test.js | 15 + strategyOut.sol | 1795 +++++++++++++++++ yarn.lock | 75 +- 10 files changed, 1975 insertions(+), 137 deletions(-) create mode 100644 src/strategies/moonbeam/stella/strategy-stella-USDCmad-GLMR-lp.sol create mode 100644 src/tests/strategies/moonbeam/stellaswap/strategy-stella-ETHmad-glmr-lp.test.js create mode 100644 strategyOut.sol diff --git a/hardhat.config.js b/hardhat.config.js index 62ad6b11f..2877cf7e7 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -38,7 +38,7 @@ module.exports = { networks: { hardhat: { forking: { - url: `https://ftmrpc.ultimatenodes.io/`, + url: `https://rpc.api.moonbeam.network`, }, accounts: { mnemonic: process.env.MNEMONIC, @@ -95,6 +95,7 @@ module.exports = { mainnet: `${process.env.ETHERSCAN_APIKEY}`, aurora: `${process.env.AURORASCAN_APIKEY}`, xdai: `${process.env.GNOSIS_APIKEY}`, + moonbeam: `${process.env.MOONBEAM_APIKEY}` }, }, paths: { diff --git a/package-lock.json b/package-lock.json index 78662a5a6..b7865e5c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,7 @@ "hardhat-deploy": "^0.7.2", "hardhat-gas-reporter": "^1.0.4", "hardhat-preprocessor": "^0.1.4", - "mocha": "^9.0.2", + "mocha": "^9.2.2", "prettier-plugin-solidity": "^1.0.0-beta.9", "solidity-coverage": "^0.7.16" } @@ -5446,9 +5446,15 @@ "dev": true }, "node_modules/chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -6042,9 +6048,9 @@ "dev": true }, "node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dependencies": { "ms": "2.1.2" }, @@ -21267,9 +21273,9 @@ "peer": true }, "node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -24359,33 +24365,32 @@ } }, "node_modules/mocha": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.0.2.tgz", - "integrity": "sha512-FpspiWU+UT9Sixx/wKimvnpkeW0mh6ROAKkIaPokj3xZgxeRhcna/k5X57jJghEr8X+Cgu/Vegf8zCX5ugSuTA==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.1", + "chokidar": "3.5.3", + "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.1.23", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.1.5", + "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -24408,29 +24413,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/mocha/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -24474,6 +24456,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mocha/node_modules/minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -24631,9 +24625,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -31019,9 +31013,9 @@ "dev": true }, "node_modules/workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "node_modules/wrap-ansi": { @@ -35695,9 +35689,9 @@ } }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "requires": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -36190,9 +36184,9 @@ "dev": true }, "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "requires": { "ms": "2.1.2" } @@ -48441,9 +48435,9 @@ "peer": true }, "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -50859,33 +50853,32 @@ } }, "mocha": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.0.2.tgz", - "integrity": "sha512-FpspiWU+UT9Sixx/wKimvnpkeW0mh6ROAKkIaPokj3xZgxeRhcna/k5X57jJghEr8X+Cgu/Vegf8zCX5ugSuTA==", + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.2", - "debug": "4.3.1", + "chokidar": "3.5.3", + "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.7", + "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", - "minimatch": "3.0.4", + "minimatch": "4.2.1", "ms": "2.1.3", - "nanoid": "3.1.23", + "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.1.5", + "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -50897,23 +50890,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -50942,6 +50918,15 @@ "p-locate": "^5.0.0" } }, + "minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -51081,9 +51066,9 @@ "dev": true }, "nanoid": { - "version": "3.1.23", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", - "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true }, "nanomatch": { @@ -56291,9 +56276,9 @@ "dev": true }, "workerpool": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", - "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true }, "wrap-ansi": { diff --git a/package.json b/package.json index 57b608e57..26d4a685e 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "hardhat-deploy": "^0.7.2", "hardhat-gas-reporter": "^1.0.4", "hardhat-preprocessor": "^0.1.4", - "mocha": "^9.0.2", + "mocha": "^9.2.2", "prettier-plugin-solidity": "^1.0.0-beta.9", "solidity-coverage": "^0.7.16" }, diff --git a/scripts/degenApe.js b/scripts/degenApe.js index 65c13f8b8..ab8bff2d6 100644 --- a/scripts/degenApe.js +++ b/scripts/degenApe.js @@ -53,10 +53,11 @@ const timelock = "0x4204FDD868FFe0e62F57e6A626F8C9530F7d5AD1"; const harvester = ["0x0f571D2625b503BB7C1d2b5655b483a2Fa696fEf"]; const contracts = [ - "src/strategies/moonbeam/stella/strategy-stella-ETHmad-GLMR-lp.sol:StrategyStellaETHmadGLMRLp" + "src/strategies/moonbeam/stella/strategy-stella-USDCmad-GLMR-lp.sol:StrategyStellaUSDCmadGLMRLp" ]; -const testedStrategies = []; +const testedStrategies = [ +]; const executeTx = async (calls, fn, ...args) => { let transaction; @@ -238,7 +239,7 @@ ratio: ${ratio.toString()} const main = async () => { await deployContractsAndGeneratePfcore(); // await fastVerifyContracts(testedStrategies); - await slowVerifyContracts(testedStrategies); + await slowVerifyContracts(testedStrategies, governance, strategist, controller, timelock); }; main() diff --git a/scripts/degenUtils.js b/scripts/degenUtils.js index 1bd382198..ea1bee7cd 100644 --- a/scripts/degenUtils.js +++ b/scripts/degenUtils.js @@ -22,7 +22,7 @@ const fastVerifyContracts = async (strategies) => { } // Use this for verificationof 5 contracts or more -const slowVerifyContracts = async (strategies) => { +const slowVerifyContracts = async (strategies, governance, strategist, controller, timelock) => { for (strategy of strategies) { try { await hre.run("verify:verify", { diff --git a/src/strategies/moonbeam/stella/strategy-stella-USDCmad-GLMR-lp.sol b/src/strategies/moonbeam/stella/strategy-stella-USDCmad-GLMR-lp.sol new file mode 100644 index 000000000..23cf3c8bf --- /dev/null +++ b/src/strategies/moonbeam/stella/strategy-stella-USDCmad-GLMR-lp.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.7; + +import "../strategy-stella-farm-base-v2.sol"; + +contract StrategyStellaUSDCmadGLMRLp is StrategyStellaFarmBaseV2 { + uint256 public USDCmad_glmr_poolId = 12; + + // Token addresses + address public USDCmad_glmr_lp = 0x9bFcf685e641206115dadc0C9ab17181e1d4975c; + address public USDCmad = 0x8f552a71EFE5eeFc207Bf75485b356A0b3f01eC9; + address public rewarderContract = 0x9200Cb047A9c4b34A17cCF86334E3f434f948301; + + constructor( + address _governance, + address _strategist, + address _controller, + address _timelock + ) + public + StrategyStellaFarmBaseV2( + USDCmad_glmr_lp, + USDCmad_glmr_poolId, + rewarderContract, + _governance, + _strategist, + _controller, + _timelock + ) + { + swapRoutes[glmr] = [stella, glmr]; + swapRoutes[USDCmad] = [glmr, USDCmad]; + } + + // **** Views **** + + function getName() external pure override returns (string memory) { + return "StrategyStellaUSDCmadGlmrLp"; + } +} diff --git a/src/strategies/moonbeam/strategy-stella-farm-base-v2.sol b/src/strategies/moonbeam/strategy-stella-farm-base-v2.sol index 13a02bd9a..a10c136d1 100644 --- a/src/strategies/moonbeam/strategy-stella-farm-base-v2.sol +++ b/src/strategies/moonbeam/strategy-stella-farm-base-v2.sol @@ -10,7 +10,7 @@ abstract contract StrategyStellaFarmBaseV2 is StrategyBase { // Token addresses address public constant stella = 0x0E358838ce72d5e61E0018a2ffaC4bEC5F4c88d2; address public constant stellaChef = 0xF3a5454496E26ac57da879bf3285Fa85DEBF0388; - address public constant stellaRouter = 0xd0A01ec574D1fC6652eDF79cb2F880fd47D34Ab1; + address public constant stellaRouter = 0x70085a09D30D6f8C4ecF6eE10120d1847383BB57; address public rewarder; address public token0; @@ -82,7 +82,9 @@ abstract contract StrategyStellaFarmBaseV2 is StrategyBase { IStellaChef(stellaChef).deposit(poolId, 0); uint256 _nakedGlmr = address(this).balance; - WETH(glmr).deposit{value: _nakedGlmr}(); + if (_nakedGlmr > 0) { + WETH(glmr).deposit{value: _nakedGlmr}(); + } uint256 _stella = IERC20(stella).balanceOf(address(this)); diff --git a/src/tests/strategies/moonbeam/stellaswap/strategy-stella-ETHmad-glmr-lp.test.js b/src/tests/strategies/moonbeam/stellaswap/strategy-stella-ETHmad-glmr-lp.test.js new file mode 100644 index 000000000..ad0c9b583 --- /dev/null +++ b/src/tests/strategies/moonbeam/stellaswap/strategy-stella-ETHmad-glmr-lp.test.js @@ -0,0 +1,15 @@ +const { toWei } = require("../../../utils/testHelper"); +const { getWantFromWhale } = require("../../../utils/setupHelper"); +const { doTestBehaviorBase } = require("../../testBehaviorBase"); + +describe("StrategyStellaETHmadGLMRLp", () => { + const want_addr = "0x9d19EDBFd29D2e01537624B25806493dA0d73bBE"; + const whale_addr = "0x3466acc8d2d7064367b837cf6eefac9659d3ad2a"; + + before("Get want token", async () => { + [alice] = await hre.ethers.getSigners(); + await getWantFromWhale(want_addr, toWei(100, 18), alice, whale_addr); + }); + + doTestBehaviorBase("StrategyStellaETHmadGLMRLp", want_addr, true); +}); \ No newline at end of file diff --git a/strategyOut.sol b/strategyOut.sol new file mode 100644 index 000000000..ff0bab77d --- /dev/null +++ b/strategyOut.sol @@ -0,0 +1,1795 @@ +// Sources flattened with hardhat v2.6.8 https://hardhat.org + +// File src/lib/safe-math.sol + +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + +/** + * @dev Wrappers over Solidity's arithmetic operations with added overflow + * checks. + * + * Arithmetic operations in Solidity wrap on overflow. This can easily result + * in bugs, because programmers usually assume that an overflow raises an + * error, which is the standard behavior in high level programming languages. + * `SafeMath` restores this intuition by reverting the transaction when an + * operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + */ +library SafeMath { + /** + * @dev Returns the addition of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `+` operator. + * + * Requirements: + * + * - Addition cannot overflow. + */ + function add(uint256 a, uint256 b) internal pure returns (uint256) { + uint256 c = a + b; + require(c >= a, "SafeMath: addition overflow"); + + return c; + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + return sub(a, b, "SafeMath: subtraction overflow"); + } + + /** + * @dev Returns the subtraction of two unsigned integers, reverting with custom message on + * overflow (when the result is negative). + * + * Counterpart to Solidity's `-` operator. + * + * Requirements: + * + * - Subtraction cannot overflow. + */ + function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + require(b <= a, errorMessage); + uint256 c = a - b; + + return c; + } + + /** + * @dev Returns the multiplication of two unsigned integers, reverting on + * overflow. + * + * Counterpart to Solidity's `*` operator. + * + * Requirements: + * + * - Multiplication cannot overflow. + */ + function mul(uint256 a, uint256 b) internal pure returns (uint256) { + // Gas optimization: this is cheaper than requiring 'a' not being zero, but the + // benefit is lost if 'b' is also tested. + // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 + if (a == 0) { + return 0; + } + + uint256 c = a * b; + require(c / a == b, "SafeMath: multiplication overflow"); + + return c; + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b) internal pure returns (uint256) { + return div(a, b, "SafeMath: division by zero"); + } + + /** + * @dev Returns the integer division of two unsigned integers. Reverts with custom message on + * division by zero. The result is rounded towards zero. + * + * Counterpart to Solidity's `/` operator. Note: this function uses a + * `revert` opcode (which leaves remaining gas untouched) while Solidity + * uses an invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + require(b > 0, errorMessage); + uint256 c = a / b; + // assert(a == b * c + a % b); // There is no case in which this doesn't hold + + return c; + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b) internal pure returns (uint256) { + return mod(a, b, "SafeMath: modulo by zero"); + } + + /** + * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), + * Reverts with custom message when dividing by zero. + * + * Counterpart to Solidity's `%` operator. This function uses a `revert` + * opcode (which leaves remaining gas untouched) while Solidity uses an + * invalid opcode to revert (consuming all remaining gas). + * + * Requirements: + * + * - The divisor cannot be zero. + */ + function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { + require(b != 0, errorMessage); + return a % b; + } +} + + +// File src/lib/context.sol + +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + +/* + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with GSN meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address payable) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes memory) { + this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 + return msg.data; + } +} + + +// File src/lib/erc20.sol + +// File: contracts/GSN/Context.sol + +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + + +// File: contracts/token/ERC20/IERC20.sol + + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `recipient`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address recipient, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `sender` to `recipient` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); +} + +// File: contracts/utils/Address.sol + + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize, which returns 0 for contracts in + // construction, since the code is only stored at the end of the + // constructor execution. + + uint256 size; + // solhint-disable-next-line no-inline-assembly + assembly { size := extcodesize(account) } + return size > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require(address(this).balance >= amount, "Address: insufficient balance"); + + // solhint-disable-next-line avoid-low-level-calls, avoid-call-value + (bool success, ) = recipient.call{ value: amount }(""); + require(success, "Address: unable to send value, recipient may have reverted"); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain`call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) internal returns (bytes memory) { + return functionCall(target, data, "Address: low-level call failed"); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { + return _functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { + return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { + require(address(this).balance >= value, "Address: insufficient balance for call"); + return _functionCallWithValue(target, data, value, errorMessage); + } + + function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) { + require(isContract(target), "Address: call to non-contract"); + + // solhint-disable-next-line avoid-low-level-calls + (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); + if (success) { + return returndata; + } else { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + + // solhint-disable-next-line no-inline-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } + } +} + +// File: contracts/token/ERC20/ERC20.sol + +/** + * @dev Implementation of the {IERC20} interface. + * + * This implementation is agnostic to the way tokens are created. This means + * that a supply mechanism has to be added in a derived contract using {_mint}. + * For a generic mechanism see {ERC20PresetMinterPauser}. + * + * TIP: For a detailed writeup see our guide + * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How + * to implement supply mechanisms]. + * + * We have followed general OpenZeppelin guidelines: functions revert instead + * of returning `false` on failure. This behavior is nonetheless conventional + * and does not conflict with the expectations of ERC20 applications. + * + * Additionally, an {Approval} event is emitted on calls to {transferFrom}. + * This allows applications to reconstruct the allowance for all accounts just + * by listening to said events. Other implementations of the EIP may not emit + * these events, as it isn't required by the specification. + * + * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} + * functions have been added to mitigate the well-known issues around setting + * allowances. See {IERC20-approve}. + */ +contract ERC20 is Context, IERC20 { + using SafeMath for uint256; + using Address for address; + + mapping (address => uint256) private _balances; + + mapping (address => mapping (address => uint256)) private _allowances; + + uint256 private _totalSupply; + + string private _name; + string private _symbol; + uint8 private _decimals; + + /** + * @dev Sets the values for {name} and {symbol}, initializes {decimals} with + * a default value of 18. + * + * To select a different value for {decimals}, use {_setupDecimals}. + * + * All three of these values are immutable: they can only be set once during + * construction. + */ + constructor (string memory name, string memory symbol) public { + _name = name; + _symbol = symbol; + _decimals = 18; + } + + /** + * @dev Returns the name of the token. + */ + function name() public view returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5,05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is + * called. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view returns (uint8) { + return _decimals; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view override returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view override returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - `recipient` cannot be the zero address. + * - the caller must have a balance of at least `amount`. + */ + function transfer(address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(_msgSender(), recipient, amount); + return true; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance(address owner, address spender) public view virtual override returns (uint256) { + return _allowances[owner][spender]; + } + + /** + * @dev See {IERC20-approve}. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function approve(address spender, uint256 amount) public virtual override returns (bool) { + _approve(_msgSender(), spender, amount); + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Emits an {Approval} event indicating the updated allowance. This is not + * required by the EIP. See the note at the beginning of {ERC20}; + * + * Requirements: + * - `sender` and `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + * - the caller must have allowance for ``sender``'s tokens of at least + * `amount`. + */ + function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { + _transfer(sender, recipient, amount); + _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); + return true; + } + + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + */ + function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); + return true; + } + + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `spender` must have allowance for the caller of at least + * `subtractedValue`. + */ + function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { + _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); + return true; + } + + /** + * @dev Moves tokens `amount` from `sender` to `recipient`. + * + * This is internal function is equivalent to {transfer}, and can be used to + * e.g. implement automatic token fees, slashing mechanisms, etc. + * + * Emits a {Transfer} event. + * + * Requirements: + * + * - `sender` cannot be the zero address. + * - `recipient` cannot be the zero address. + * - `sender` must have a balance of at least `amount`. + */ + function _transfer(address sender, address recipient, uint256 amount) internal virtual { + require(sender != address(0), "ERC20: transfer from the zero address"); + require(recipient != address(0), "ERC20: transfer to the zero address"); + + _beforeTokenTransfer(sender, recipient, amount); + + _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); + _balances[recipient] = _balances[recipient].add(amount); + emit Transfer(sender, recipient, amount); + } + + /** @dev Creates `amount` tokens and assigns them to `account`, increasing + * the total supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + * + * Requirements + * + * - `to` cannot be the zero address. + */ + function _mint(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: mint to the zero address"); + + _beforeTokenTransfer(address(0), account, amount); + + _totalSupply = _totalSupply.add(amount); + _balances[account] = _balances[account].add(amount); + emit Transfer(address(0), account, amount); + } + + /** + * @dev Destroys `amount` tokens from `account`, reducing the + * total supply. + * + * Emits a {Transfer} event with `to` set to the zero address. + * + * Requirements + * + * - `account` cannot be the zero address. + * - `account` must have at least `amount` tokens. + */ + function _burn(address account, uint256 amount) internal virtual { + require(account != address(0), "ERC20: burn from the zero address"); + + _beforeTokenTransfer(account, address(0), amount); + + _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); + _totalSupply = _totalSupply.sub(amount); + emit Transfer(account, address(0), amount); + } + + /** + * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. + * + * This internal function is equivalent to `approve`, and can be used to + * e.g. set automatic allowances for certain subsystems, etc. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `owner` cannot be the zero address. + * - `spender` cannot be the zero address. + */ + function _approve(address owner, address spender, uint256 amount) internal virtual { + require(owner != address(0), "ERC20: approve from the zero address"); + require(spender != address(0), "ERC20: approve to the zero address"); + + _allowances[owner][spender] = amount; + emit Approval(owner, spender, amount); + } + + /** + * @dev Sets {decimals} to a value other than the default one of 18. + * + * WARNING: This function should only be called from the constructor. Most + * applications that interact with token contracts will not expect + * {decimals} to ever change, and may work incorrectly if it does. + */ + function _setupDecimals(uint8 decimals_) internal { + _decimals = decimals_; + } + + /** + * @dev Hook that is called before any transfer of tokens. This includes + * minting and burning. + * + * Calling conditions: + * + * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens + * will be to transferred to `to`. + * - when `from` is zero, `amount` tokens will be minted for `to`. + * - when `to` is zero, `amount` of ``from``'s tokens will be burned. + * - `from` and `to` are never both zero. + * + * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. + */ + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } +} + +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using SafeMath for uint256; + using Address for address; + + function safeTransfer(IERC20 token, address to, uint256 value) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); + } + + function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { + _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); + } + + /** + * @dev Deprecated. This function has issues similar to the ones found in + * {IERC20-approve}, and its usage is discouraged. + * + * Whenever possible, use {safeIncreaseAllowance} and + * {safeDecreaseAllowance} instead. + */ + function safeApprove(IERC20 token, address spender, uint256 value) internal { + // safeApprove should only be called when setting an initial allowance, + // or when resetting it to zero. To increase and decrease it, use + // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' + // solhint-disable-next-line max-line-length + require((value == 0) || (token.allowance(address(this), spender) == 0), + "SafeERC20: approve from non-zero to non-zero allowance" + ); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); + } + + function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { + uint256 newAllowance = token.allowance(address(this), spender).add(value); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + + function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { + uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); + _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); + } + + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). + * @param token The token targeted by the call. + * @param data The call data (encoded using abi.encode or one of its variants). + */ + function _callOptionalReturn(IERC20 token, bytes memory data) private { + // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since + // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that + // the target address contains contract code and also asserts for success in the low-level call. + + bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); + if (returndata.length > 0) { // Return data is optional + // solhint-disable-next-line max-line-length + require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); + } + } +} + + +// File src/interfaces/jar.sol + +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.2; + +interface IJar is IERC20 { + function token() external view returns (address); + + function reward() external view returns (address); + + function claimInsurance() external; // NOTE: Only yDelegatedVault implements this + + function getRatio() external view returns (uint256); + + function depositAll() external; + + function balance() external view returns (uint256); + + function deposit(uint256) external; + + function withdrawAll() external; + + function withdraw(uint256) external; + + function earn() external; + + function decimals() external view returns (uint8); +} + + +// File src/interfaces/staking-rewards.sol + +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.2; + +interface IStakingRewards { + function balanceOf(address account) external view returns (uint256); + + function balances(address account) external view returns (uint256); + + function earned(address account) external view returns (uint256); + + function exit() external; + + function getReward() external; + + function getRewardForDuration() external view returns (uint256); + + function lastTimeRewardApplicable() external view returns (uint256); + + function lastUpdateTime() external view returns (uint256); + + function notifyRewardAmount(uint256 reward) external; + + function periodFinish() external view returns (uint256); + + function rewardPerToken() external view returns (uint256); + + function rewardPerTokenStored() external view returns (uint256); + + function rewardRate() external view returns (uint256); + + function rewards(address) external view returns (uint256); + + function rewardsDistribution() external view returns (address); + + function rewardsDuration() external view returns (uint256); + + function rewardsToken() external view returns (address); + + function stake(uint256 amount) external; + + function deposit(uint256 amount) external; + + function stakeWithPermit( + uint256 amount, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + function stakingToken() external view returns (address); + + function totalSupply() external view returns (uint256); + + function userRewardPerTokenPaid(address) external view returns (uint256); + + function withdraw(uint256 amount) external; +} + +interface IStakingRewardsFactory { + function deploy(address stakingToken, uint256 rewardAmount) external; + + function isOwner() external view returns (bool); + + function notifyRewardAmount(address stakingToken) external; + + function notifyRewardAmounts() external; + + function owner() external view returns (address); + + function renounceOwnership() external; + + function rewardsToken() external view returns (address); + + function stakingRewardsGenesis() external view returns (uint256); + + function stakingRewardsInfoByStakingToken(address) + external + view + returns (address stakingRewards, uint256 rewardAmount); + + function stakingTokens(uint256) external view returns (address); + + function transferOwnership(address newOwner) external; +} + + +// File src/interfaces/masterchef.sol + +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.7; + +interface IMasterchef { + function BONUS_MULTIPLIER() external view returns (uint256); + + function add( + uint256 _allocPoint, + address _lpToken, + bool _withUpdate + ) external; + + function bonusEndBlock() external view returns (uint256); + + function deposit(uint256 _pid, uint256 _amount) external; + + function dev(address _devaddr) external; + + function devFundDivRate() external view returns (uint256); + + function devaddr() external view returns (address); + + function emergencyWithdraw(uint256 _pid) external; + + function getMultiplier(uint256 _from, uint256 _to) + external + view + returns (uint256); + + function massUpdatePools() external; + + function owner() external view returns (address); + + function pendingPickle(uint256 _pid, address _user) + external + view + returns (uint256); + + function pendingReward(uint256 _pid, address _user) + external + view + returns (uint256); + + function pending(uint256 _pid, address _user) + external + view + returns (uint256); + + function pickle() external view returns (address); + + function picklePerBlock() external view returns (uint256); + + function poolInfo(uint256) + external + view + returns ( + address lpToken, + uint256 allocPoint, + uint256 lastRewardBlock, + uint256 accPicklePerShare + ); + + function poolLength() external view returns (uint256); + + function renounceOwnership() external; + + function set( + uint256 _pid, + uint256 _allocPoint, + bool _withUpdate + ) external; + + function setBonusEndBlock(uint256 _bonusEndBlock) external; + + function setDevFundDivRate(uint256 _devFundDivRate) external; + + function setPicklePerBlock(uint256 _picklePerBlock) external; + + function startBlock() external view returns (uint256); + + function totalAllocPoint() external view returns (uint256); + + function transferOwnership(address newOwner) external; + + function updatePool(uint256 _pid) external; + + function userInfo(uint256, address) + external + view + returns (uint256 amount, uint256 rewardDebt); + + function withdraw(uint256 _pid, uint256 _amount) external; +} + + +// File src/interfaces/uniswapv2.sol + +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.2; + +interface UniswapRouterV2 { + function swapExactTokensForTokens( + uint256 amountIn, + uint256 amountOutMin, + address[] calldata path, + address to, + uint256 deadline + ) external returns (uint256[] memory amounts); + + function addLiquidity( + address tokenA, + address tokenB, + uint256 amountADesired, + uint256 amountBDesired, + uint256 amountAMin, + uint256 amountBMin, + address to, + uint256 deadline + ) + external + returns ( + uint256 amountA, + uint256 amountB, + uint256 liquidity + ); + + function addLiquidityETH( + address token, + uint256 amountTokenDesired, + uint256 amountTokenMin, + uint256 amountETHMin, + address to, + uint256 deadline + ) + external + payable + returns ( + uint256 amountToken, + uint256 amountETH, + uint256 liquidity + ); + + function removeLiquidity( + address tokenA, + address tokenB, + uint256 liquidity, + uint256 amountAMin, + uint256 amountBMin, + address to, + uint256 deadline + ) external returns (uint256 amountA, uint256 amountB); + + function getAmountsOut(uint256 amountIn, address[] calldata path) + external + view + returns (uint256[] memory amounts); + + function getAmountsIn(uint256 amountOut, address[] calldata path) + external + view + returns (uint256[] memory amounts); + + function swapETHForExactTokens( + uint256 amountOut, + address[] calldata path, + address to, + uint256 deadline + ) external payable returns (uint256[] memory amounts); + + function swapExactETHForTokens( + uint256 amountOutMin, + address[] calldata path, + address to, + uint256 deadline + ) external payable returns (uint256[] memory amounts); +} + +interface IUniswapV2Pair { + event Approval( + address indexed owner, + address indexed spender, + uint256 value + ); + event Transfer(address indexed from, address indexed to, uint256 value); + + function name() external pure returns (string memory); + + function symbol() external pure returns (string memory); + + function decimals() external pure returns (uint8); + + function totalSupply() external view returns (uint256); + + function balanceOf(address owner) external view returns (uint256); + + function allowance(address owner, address spender) + external + view + returns (uint256); + + function approve(address spender, uint256 value) external returns (bool); + + function transfer(address to, uint256 value) external returns (bool); + + function transferFrom( + address from, + address to, + uint256 value + ) external returns (bool); + + function DOMAIN_SEPARATOR() external view returns (bytes32); + + function PERMIT_TYPEHASH() external pure returns (bytes32); + + function nonces(address owner) external view returns (uint256); + + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + event Mint(address indexed sender, uint256 amount0, uint256 amount1); + event Burn( + address indexed sender, + uint256 amount0, + uint256 amount1, + address indexed to + ); + event Swap( + address indexed sender, + uint256 amount0In, + uint256 amount1In, + uint256 amount0Out, + uint256 amount1Out, + address indexed to + ); + event Sync(uint112 reserve0, uint112 reserve1); + + function MINIMUM_LIQUIDITY() external pure returns (uint256); + + function factory() external view returns (address); + + function token0() external view returns (address); + + function token1() external view returns (address); + + function getReserves() + external + view + returns ( + uint112 reserve0, + uint112 reserve1, + uint32 blockTimestampLast + ); + + function price0CumulativeLast() external view returns (uint256); + + function price1CumulativeLast() external view returns (uint256); + + function kLast() external view returns (uint256); + + function mint(address to) external returns (uint256 liquidity); + + function burn(address to) + external + returns (uint256 amount0, uint256 amount1); + + function swap( + uint256 amount0Out, + uint256 amount1Out, + address to, + bytes calldata data + ) external; + + function skim(address to) external; + + function sync() external; +} + +interface IUniswapV2Factory { + event PairCreated( + address indexed token0, + address indexed token1, + address pair, + uint256 + ); + + function getPair(address tokenA, address tokenB) + external + view + returns (address pair); + + function allPairs(uint256) external view returns (address pair); + + function allPairsLength() external view returns (uint256); + + function feeTo() external view returns (address); + + function feeToSetter() external view returns (address); + + function createPair(address tokenA, address tokenB) + external + returns (address pair); +} + + +// File src/interfaces/controller.sol + +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + +interface IController { + function jars(address) external view returns (address); + + function rewards() external view returns (address); + + function devfund() external view returns (address); + + function treasury() external view returns (address); + + function balanceOf(address) external view returns (uint256); + + function withdraw(address, uint256) external; + + function withdrawReward(address, uint256) external; + + function earn(address, uint256) external; + + function strategies(address) external view returns (address); +} + + +// File src/strategies/moonbeam/strategy-base.sol + +pragma solidity ^0.6.7; + + + + + + +// Strategy Contract Basics + +abstract contract StrategyBase { + using SafeERC20 for IERC20; + using Address for address; + using SafeMath for uint256; + + // Perfomance fees + uint256 public performanceTreasuryFee = 0; + uint256 public constant performanceTreasuryMax = 10000; + + uint256 public performanceDevFee = 0; + uint256 public constant performanceDevMax = 10000; + + // Withdrawal fee 0% + // - 0% to treasury + // - 0% to dev fund + uint256 public withdrawalTreasuryFee = 0; + uint256 public constant withdrawalTreasuryMax = 100000; + + uint256 public withdrawalDevFundFee = 0; + uint256 public constant withdrawalDevFundMax = 100000; + + // Tokens + address public want; + address public constant glmr = 0xAcc15dC74880C9944775448304B263D191c6077F; + + // User accounts + address public governance; + address public controller; + address public strategist; + address public timelock; + + // Dex + address public sushiRouter; + + mapping(address => bool) public harvesters; + + constructor( + address _want, + address _governance, + address _strategist, + address _controller, + address _timelock + ) public { + require(_want != address(0)); + require(_governance != address(0)); + require(_strategist != address(0)); + require(_controller != address(0)); + require(_timelock != address(0)); + + want = _want; + governance = _governance; + strategist = _strategist; + controller = _controller; + timelock = _timelock; + } + + // **** Modifiers **** // + + modifier onlyBenevolent() { + require(harvesters[msg.sender] || msg.sender == governance || msg.sender == strategist); + _; + } + + // **** Views **** // + + function balanceOfWant() public view returns (uint256) { + return IERC20(want).balanceOf(address(this)); + } + + function balanceOfPool() public view virtual returns (uint256); + + function balanceOf() public view returns (uint256) { + return balanceOfWant().add(balanceOfPool()); + } + + function getName() external pure virtual returns (string memory); + + // **** Setters **** // + + function whitelistHarvesters(address[] calldata _harvesters) external { + require(msg.sender == governance || msg.sender == strategist || harvesters[msg.sender], "not authorized"); + + for (uint256 i = 0; i < _harvesters.length; i++) { + harvesters[_harvesters[i]] = true; + } + } + + function revokeHarvesters(address[] calldata _harvesters) external { + require(msg.sender == governance || msg.sender == strategist, "not authorized"); + + for (uint256 i = 0; i < _harvesters.length; i++) { + harvesters[_harvesters[i]] = false; + } + } + + function setWithdrawalDevFundFee(uint256 _withdrawalDevFundFee) external { + require(msg.sender == timelock, "!timelock"); + withdrawalDevFundFee = _withdrawalDevFundFee; + } + + function setWithdrawalTreasuryFee(uint256 _withdrawalTreasuryFee) external { + require(msg.sender == timelock, "!timelock"); + withdrawalTreasuryFee = _withdrawalTreasuryFee; + } + + function setPerformanceDevFee(uint256 _performanceDevFee) external { + require(msg.sender == timelock, "!timelock"); + performanceDevFee = _performanceDevFee; + } + + function setPerformanceTreasuryFee(uint256 _performanceTreasuryFee) external { + require(msg.sender == timelock, "!timelock"); + performanceTreasuryFee = _performanceTreasuryFee; + } + + function setStrategist(address _strategist) external { + require(msg.sender == governance, "!governance"); + strategist = _strategist; + } + + function setGovernance(address _governance) external { + require(msg.sender == governance, "!governance"); + governance = _governance; + } + + function setTimelock(address _timelock) external { + require(msg.sender == timelock, "!timelock"); + timelock = _timelock; + } + + function setController(address _controller) external { + require(msg.sender == timelock, "!timelock"); + controller = _controller; + } + + // **** State mutations **** // + function deposit() public virtual; + + // Controller only function for creating additional rewards from dust + function withdraw(IERC20 _asset) external returns (uint256 balance) { + require(msg.sender == controller, "!controller"); + require(want != address(_asset), "want"); + balance = _asset.balanceOf(address(this)); + _asset.safeTransfer(controller, balance); + } + + // Withdraw partial funds, normally used with a jar withdrawal + function withdraw(uint256 _amount) external { + require(msg.sender == controller, "!controller"); + uint256 _balance = IERC20(want).balanceOf(address(this)); + if (_balance < _amount) { + _amount = _withdrawSome(_amount.sub(_balance)); + _amount = _amount.add(_balance); + } + + uint256 _feeDev = _amount.mul(withdrawalDevFundFee).div(withdrawalDevFundMax); + IERC20(want).safeTransfer(IController(controller).devfund(), _feeDev); + + uint256 _feeTreasury = _amount.mul(withdrawalTreasuryFee).div(withdrawalTreasuryMax); + IERC20(want).safeTransfer(IController(controller).treasury(), _feeTreasury); + + address _jar = IController(controller).jars(address(want)); + require(_jar != address(0), "!jar"); // additional protection so we don't burn the funds + + IERC20(want).safeTransfer(_jar, _amount.sub(_feeDev).sub(_feeTreasury)); + } + + // Withdraw funds, used to swap between strategies + function withdrawForSwap(uint256 _amount) external returns (uint256 balance) { + require(msg.sender == controller, "!controller"); + _withdrawSome(_amount); + + balance = IERC20(want).balanceOf(address(this)); + + address _jar = IController(controller).jars(address(want)); + require(_jar != address(0), "!jar"); + IERC20(want).safeTransfer(_jar, balance); + } + + // Withdraw all funds, normally used when migrating strategies + function withdrawAll() external returns (uint256 balance) { + require(msg.sender == controller, "!controller"); + _withdrawAll(); + + balance = IERC20(want).balanceOf(address(this)); + + address _jar = IController(controller).jars(address(want)); + require(_jar != address(0), "!jar"); // additional protection so we don't burn the funds + IERC20(want).safeTransfer(_jar, balance); + } + + function _withdrawAll() internal { + _withdrawSome(balanceOfPool()); + } + + function _withdrawSome(uint256 _amount) internal virtual returns (uint256); + + function harvest() public virtual; + + // **** Emergency functions **** + + function execute(address _target, bytes memory _data) public payable returns (bytes memory response) { + require(msg.sender == timelock, "!timelock"); + require(_target != address(0), "!target"); + + // call contract in current context + assembly { + let succeeded := delegatecall(sub(gas(), 5000), _target, add(_data, 0x20), mload(_data), 0, 0) + let size := returndatasize() + + response := mload(0x40) + mstore(0x40, add(response, and(add(add(size, 0x20), 0x1f), not(0x1f)))) + mstore(response, size) + returndatacopy(add(response, 0x20), 0, size) + + switch iszero(succeeded) + case 1 { + // throw if delegatecall failed + revert(add(response, 0x20), size) + } + } + } + + // **** Internal functions **** + function _swapSushiswap( + address _from, + address _to, + uint256 _amount + ) internal { + require(_to != address(0)); + + address[] memory path; + + if (_from == glmr || _to == glmr) { + path = new address[](2); + path[0] = _from; + path[1] = _to; + } else { + path = new address[](3); + path[0] = _from; + path[1] = glmr; + path[2] = _to; + } + + IERC20(_from).safeApprove(sushiRouter, 0); + IERC20(_from).safeApprove(sushiRouter, _amount); + UniswapRouterV2(sushiRouter).swapExactTokensForTokens(_amount, 0, path, address(this), now.add(60)); + } + + function _swapSushiswapWithPath(address[] memory path, uint256 _amount) internal { + require(path[1] != address(0)); + + IERC20(path[0]).safeApprove(sushiRouter, 0); + IERC20(path[0]).safeApprove(sushiRouter, _amount); + UniswapRouterV2(sushiRouter).swapExactTokensForTokens(_amount, 1, path, address(this), block.timestamp); + } + + function _swap( + address router, + address[] memory path, + uint256 _amount + ) internal { + require(path[1] != address(0)); + UniswapRouterV2(router).swapExactTokensForTokens(_amount, 0, path, address(this), now.add(60)); + } + + function _distributePerformanceFeesAndDeposit() internal { + uint256 _want = IERC20(want).balanceOf(address(this)); + + if (_want > 0) { + // Treasury fees + IERC20(want).safeTransfer( + IController(controller).treasury(), + _want.mul(performanceTreasuryFee).div(performanceTreasuryMax) + ); + + // Performance fee + IERC20(want).safeTransfer( + IController(controller).devfund(), + _want.mul(performanceDevFee).div(performanceDevMax) + ); + + deposit(); + } + } +} + + +// File src/interfaces/stella-chef.sol + +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.7; + +// interface for Solarchef contract +interface IStellaChef { + function deposit(uint256 _pid, uint256 _amount) external; + + function pendingStella(uint256 _pid, address _user) + external + view + returns (uint256); + + function userInfo(uint256, address) + external + view + returns ( + uint256 amount, + uint256 rewardDebt, + uint256 rewardLockedUp, + uint256 nextHarvestUntil + ); + + function withdraw(uint256 _pid, uint256 _amount) external; +} + + +// File src/interfaces/stella-rewarder.sol + +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.7; + +// interface for Solarchef contract +interface IStellaRewarder { + function pendingTokens(uint256 _pid, address _user) external view returns (uint256); +} + + +// File src/interfaces/weth.sol + +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.0; + +interface WETH { + function name() external view returns (string memory); + + function approve(address guy, uint256 wad) external returns (bool); + + function totalSupply() external view returns (uint256); + + function transferFrom( + address src, + address dst, + uint256 wad + ) external returns (bool); + + function withdraw(uint256 wad) external; + + function decimals() external view returns (uint8); + + function balanceOf(address) external view returns (uint256); + + function symbol() external view returns (string memory); + + function transfer(address dst, uint256 wad) external returns (bool); + + function deposit() external payable; + + function allowance(address, address) external view returns (uint256); +} + + +// File src/strategies/moonbeam/strategy-stella-farm-base-v2.sol + +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.7; + + + + +abstract contract StrategyStellaFarmBaseV2 is StrategyBase { + // Token addresses + address public constant stella = 0x0E358838ce72d5e61E0018a2ffaC4bEC5F4c88d2; + address public constant stellaChef = 0xF3a5454496E26ac57da879bf3285Fa85DEBF0388; + address public constant stellaRouter = 0xd0A01ec574D1fC6652eDF79cb2F880fd47D34Ab1; + address public rewarder; + + address public token0; + address public token1; + + // How much STELLA tokens to keep? + uint256 public keepREWARD = 1000; + uint256 public constant keepREWARDMax = 10000; + + uint256 public poolId; + mapping(address => address[]) public swapRoutes; + + constructor( + address _lp, + uint256 _poolId, + address _rewarder, + address _governance, + address _strategist, + address _controller, + address _timelock + ) public StrategyBase(_lp, _governance, _strategist, _controller, _timelock) { + poolId = _poolId; + rewarder = _rewarder; + token0 = IUniswapV2Pair(_lp).token0(); + token1 = IUniswapV2Pair(_lp).token1(); + + IERC20(token0).approve(stellaRouter, uint256(-1)); + IERC20(token1).approve(stellaRouter, uint256(-1)); + IERC20(stella).approve(stellaRouter, uint256(-1)); + IERC20(glmr).approve(stellaRouter, uint256(-1)); + IERC20(want).approve(stellaChef, uint256(-1)); + } + + function balanceOfPool() public view override returns (uint256) { + (uint256 amount, , , ) = IStellaChef(stellaChef).userInfo(poolId, address(this)); + return amount; + } + + function getHarvestable() external view returns (uint256, uint256) { + uint256 PendingStella = IStellaChef(stellaChef).pendingStella(poolId, address(this)); + uint256 PendingGlmr = IStellaRewarder(rewarder).pendingTokens(poolId, address(this)); + + return (PendingStella, PendingGlmr); + } + + // **** Setters **** + + function deposit() public override { + uint256 _want = IERC20(want).balanceOf(address(this)); + if (_want > 0) { + IStellaChef(stellaChef).deposit(poolId, _want); + } + } + + function _withdrawSome(uint256 _amount) internal override returns (uint256) { + IStellaChef(stellaChef).withdraw(poolId, _amount); + return _amount; + } + + function setKeepSTELLA(uint256 _keepREWARD) external { + require(msg.sender == timelock, "!timelock"); + keepREWARD = _keepREWARD; + } + + // **** State Mutations **** + + function harvest() public override { + // Collects STELLA tokens + IStellaChef(stellaChef).deposit(poolId, 0); + + uint256 _nakedGlmr = address(this).balance; + WETH(glmr).deposit{value: _nakedGlmr}(); + + uint256 _stella = IERC20(stella).balanceOf(address(this)); + + if (_stella > 0) { + if (swapRoutes[glmr].length > 1) { + _swap(stellaRouter, swapRoutes[glmr], _stella); + } + uint256 _glmr = IERC20(glmr).balanceOf(address(this)); + uint256 _keepReward = _glmr.mul(keepREWARD).div(keepREWARDMax); + IERC20(glmr).safeTransfer(IController(controller).treasury(), _keepReward); + + _glmr = IERC20(glmr).balanceOf(address(this)); + if (glmr == token0 || glmr == token1) { + address toToken = glmr == token0 ? token1 : token0; + + if (swapRoutes[toToken].length > 1 && _glmr > 0) { + _swap(stellaRouter, swapRoutes[toToken], _glmr.div(2)); + } + } else { + uint256 toToken0 = _glmr.div(2); + uint256 toToken1 = _glmr.sub(toToken0); + + if (swapRoutes[token0].length > 1) { + _swap(stellaRouter, swapRoutes[token0], toToken0); + } + if (swapRoutes[token1].length > 1) { + _swap(stellaRouter, swapRoutes[token1], toToken1); + } + } + } + + // Adds in liquidity for token0/token1 + uint256 _token0 = IERC20(token0).balanceOf(address(this)); + uint256 _token1 = IERC20(token1).balanceOf(address(this)); + + if (_token0 > 0 && _token1 > 0) { + UniswapRouterV2(stellaRouter).addLiquidity(token0, token1, _token0, _token1, 0, 0, address(this), now + 60); + + // Donates DUST + IERC20(token0).transfer(IController(controller).treasury(), IERC20(token0).balanceOf(address(this))); + IERC20(token1).safeTransfer(IController(controller).treasury(), IERC20(token1).balanceOf(address(this))); + } + + _distributePerformanceFeesAndDeposit(); + } +} + + +// File src/strategies/moonbeam/stella/strategy-stella-ETHmad-GLMR-lp.sol + +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.7; + +contract StrategyStellaETHmadGLMRLp is StrategyStellaFarmBaseV2 { + uint256 public ETHmad_glmr_poolId = 11; + + // Token addresses + address public ETHmad_glmr_lp = 0x9d19EDBFd29D2e01537624B25806493dA0d73bBE; + address public ETHmad = 0x30D2a9F5FDf90ACe8c17952cbb4eE48a55D916A7; + address public rewarderContract = 0x9Fe074a56FFa7f4079C6190be6E8452911b7E349; + + constructor( + address _governance, + address _strategist, + address _controller, + address _timelock + ) + public + StrategyStellaFarmBaseV2( + ETHmad_glmr_lp, + ETHmad_glmr_poolId, + rewarderContract, + _governance, + _strategist, + _controller, + _timelock + ) + { + swapRoutes[glmr] = [stella, glmr]; + swapRoutes[ETHmad] = [glmr, ETHmad]; + } + + // **** Views **** + + function getName() external pure override returns (string memory) { + return "StrategyStellaEthGlmrLp"; + } +} diff --git a/yarn.lock b/yarn.lock index b1bbfae72..1ac57bbf0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3169,10 +3169,10 @@ optionalDependencies: "fsevents" "^1.2.7" -"chokidar@^3.4.0", "chokidar@^3.4.1", "chokidar@3.5.2": - "integrity" "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==" - "resolved" "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz" - "version" "3.5.2" +"chokidar@^3.4.0", "chokidar@^3.4.1", "chokidar@3.5.3": + "integrity" "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==" + "resolved" "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + "version" "3.5.3" dependencies: "anymatch" "~3.1.2" "braces" "~3.0.2" @@ -3750,10 +3750,10 @@ dependencies: "ms" "^2.1.1" -"debug@^4.0.1", "debug@^4.1.1", "debug@^4.3.1", "debug@4": - "integrity" "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==" - "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz" - "version" "4.3.2" +"debug@^4.0.1", "debug@^4.1.1", "debug@^4.3.1", "debug@4", "debug@4.3.3": + "integrity" "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==" + "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz" + "version" "4.3.3" dependencies: "ms" "2.1.2" @@ -3778,13 +3778,6 @@ dependencies: "ms" "^2.1.1" -"debug@4.3.1": - "integrity" "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==" - "resolved" "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz" - "version" "4.3.1" - dependencies: - "ms" "2.1.2" - "decamelize@^1.0.0": "integrity" "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" "resolved" "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" @@ -5878,10 +5871,10 @@ "once" "^1.3.0" "path-is-absolute" "^1.0.0" -"glob@^7.0.0", "glob@^7.1.2", "glob@^7.1.3", "glob@7.1.7": - "integrity" "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==" - "resolved" "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" - "version" "7.1.7" +"glob@^7.0.0", "glob@^7.1.2", "glob@^7.1.3", "glob@7.2.0": + "integrity" "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==" + "resolved" "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" + "version" "7.2.0" dependencies: "fs.realpath" "^1.0.0" "inflight" "^1.0.4" @@ -7960,6 +7953,13 @@ dependencies: "brace-expansion" "^1.1.7" +"minimatch@4.2.1": + "integrity" "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==" + "resolved" "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz" + "version" "4.2.1" + dependencies: + "brace-expansion" "^1.1.7" + "minimist@^1.2.0", "minimist@^1.2.3", "minimist@^1.2.5", "minimist@~1.2.5": "integrity" "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" "resolved" "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz" @@ -8074,33 +8074,32 @@ "yargs-parser" "13.1.2" "yargs-unparser" "1.6.0" -"mocha@^9.0.2": - "integrity" "sha512-FpspiWU+UT9Sixx/wKimvnpkeW0mh6ROAKkIaPokj3xZgxeRhcna/k5X57jJghEr8X+Cgu/Vegf8zCX5ugSuTA==" - "resolved" "https://registry.npmjs.org/mocha/-/mocha-9.0.2.tgz" - "version" "9.0.2" +"mocha@^9.2.2": + "integrity" "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==" + "resolved" "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz" + "version" "9.2.2" dependencies: "@ungap/promise-all-settled" "1.1.2" "ansi-colors" "4.1.1" "browser-stdout" "1.3.1" - "chokidar" "3.5.2" - "debug" "4.3.1" + "chokidar" "3.5.3" + "debug" "4.3.3" "diff" "5.0.0" "escape-string-regexp" "4.0.0" "find-up" "5.0.0" - "glob" "7.1.7" + "glob" "7.2.0" "growl" "1.10.5" "he" "1.2.0" "js-yaml" "4.1.0" "log-symbols" "4.1.0" - "minimatch" "3.0.4" + "minimatch" "4.2.1" "ms" "2.1.3" - "nanoid" "3.1.23" + "nanoid" "3.3.1" "serialize-javascript" "6.0.0" "strip-json-comments" "3.1.1" "supports-color" "8.1.1" "which" "2.0.2" - "wide-align" "1.1.3" - "workerpool" "6.1.5" + "workerpool" "6.2.0" "yargs" "16.2.0" "yargs-parser" "20.2.4" "yargs-unparser" "2.0.0" @@ -8208,10 +8207,10 @@ "resolved" "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz" "version" "0.1.2" -"nanoid@3.1.23": - "integrity" "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==" - "resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz" - "version" "3.1.23" +"nanoid@3.3.1": + "integrity" "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==" + "resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz" + "version" "3.3.1" "nanomatch@^1.2.9": "integrity" "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==" @@ -12512,10 +12511,10 @@ "resolved" "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" "version" "0.0.2" -"workerpool@6.1.5": - "integrity" "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==" - "resolved" "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz" - "version" "6.1.5" +"workerpool@6.2.0": + "integrity" "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==" + "resolved" "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz" + "version" "6.2.0" "wrap-ansi@^2.0.0": "integrity" "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU="