├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ └── bug.md └── workflows │ ├── lint.yaml │ └── test.yaml ├── .gitignore ├── .prettierrc.json ├── .soliumrc.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── brownie-config.yaml ├── commitlint.config.js ├── contracts ├── controllers │ ├── Controller.sol │ └── DelegatedController.sol ├── exploits │ └── EvilGauge.sol ├── registries │ └── YRegistry.sol ├── strategies │ ├── CurveYCRVVoter.sol │ ├── StrategyCreamYFI.sol │ ├── StrategyCurve3CrvVoterProxy.sol │ ├── StrategyCurveBBTCVoterProxy.sol │ ├── StrategyCurveBTCVoterProxy.sol │ ├── StrategyCurveBUSDVoterProxy.sol │ ├── StrategyCurveCompoundVoterProxy.sol │ ├── StrategyCurveEURVoterProxy.sol │ ├── StrategyCurveGUSDVoterProxy.sol │ ├── StrategyCurveRENVoterProxy.sol │ ├── StrategyCurveUSDNVoterProxy.sol │ ├── StrategyCurveUSTVoterProxy.sol │ ├── StrategyCurveYVoterProxy.sol │ ├── StrategyCurvemUSDVoterProxy.sol │ ├── StrategyDAI3pool.sol │ ├── StrategyDAI3poolV2.sol │ ├── StrategyDAICurve.sol │ ├── StrategyDForceUSDC.sol │ ├── StrategyDForceUSDT.sol │ ├── StrategyGUSDRescue.sol │ ├── StrategyMKRVaultDAIDelegate.sol │ ├── StrategyProxy.sol │ ├── StrategyTUSDCurve.sol │ ├── StrategyTUSDypool.sol │ ├── StrategyUSDC3pool.sol │ ├── StrategyUSDT3pool.sol │ ├── StrategyVaultUSDC.sol │ ├── StrategyYFIGovernance.sol │ └── StrategymUSDCurve.sol ├── test │ └── Token.sol ├── utils │ ├── BTCOSMedianizer.sol │ └── ETHOSMedianizer.sol └── vaults │ ├── CurveVault.sol │ ├── yDelegatedVault.sol │ ├── yVault.sol │ └── yWETH.sol ├── interfaces ├── aave │ ├── Aave.sol │ ├── AaveToken.sol │ ├── LendingPoolAddressesProvider.sol │ └── Oracle.sol ├── compound │ └── Token.sol ├── cream │ └── Controller.sol ├── curve │ ├── Curve.sol │ ├── FeeDistribution.sol │ ├── Gauge.sol │ ├── Mintr.sol │ └── VoteEscrow.sol ├── dforce │ ├── Rewards.sol │ └── Token.sol ├── maker │ ├── Maker.sol │ └── OracleSecurityModule.sol ├── uniswap │ └── Uni.sol ├── weth │ └── WETH.sol └── yearn │ ├── IController.sol │ ├── IConverter.sol │ ├── IDelegatedVault.sol │ ├── IGovernance.sol │ ├── IOneSplitAudit.sol │ ├── IProxy.sol │ ├── IStrategy.sol │ ├── IToken.sol │ ├── IVault.sol │ ├── IVoterProxy.sol │ └── IWrappedVault.sol ├── package.json ├── requirements-dev.txt ├── tests └── functional │ ├── conftest.py │ ├── controllers │ ├── __init__.py │ └── test_config.py │ ├── strategies │ ├── __init__.py │ └── test_config.py │ ├── utils │ └── test_oracles.py │ └── vaults │ ├── __init__.py │ └── test_config.py └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | *.vy linguist-language=Python 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Any general feedback or bug reports about the yearn protocol. No new features proposals. 4 | --- 5 | 6 | ### What's your issue about? 7 | 8 | NOTE: For security issue see Security [section](#security) below. 9 | 10 | Please include following information: 11 | 12 | - A clear and concise summary of an issue. 13 | - Steps to reproduce (if applicable). 14 | - What is expected to happen. 15 | - What actually happens. 16 | - Notes. 17 | 18 | ### How can it be fixed? 19 | 20 | Fill this in if you know how to fix it. 21 | 22 | ### Security 23 | 24 | If this is a sensitive issue with potential security implications in the yearn protocol, please, follow the reporting guidelines in the [Security Doc](https://github.com/iearn-finance/yearn-protocol/blob/develop/SECURITY.md). 25 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - develop 8 | pull_request: 9 | branches: 10 | - master 11 | - develop 12 | 13 | jobs: 14 | 15 | solidity: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Check out github repository 20 | uses: actions/checkout@v2 21 | with: 22 | fetch-depth: 1 23 | 24 | - name: Setup node.js 25 | uses: actions/setup-node@v1 26 | with: 27 | node-version: '12.x' 28 | 29 | - name: Set yarn cache directory path 30 | id: yarn-cache-dir-path 31 | run: echo "::set-output name=dir::$(yarn cache dir)" 32 | 33 | - name: Restore yarn cache 34 | uses: actions/cache@v2 35 | id: yarn-cache 36 | with: 37 | path: | 38 | ${{ steps.yarn-cache-dir-path.outputs.dir }} 39 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 40 | restore-keys: | 41 | ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 42 | ${{ runner.os }}-yarn- 43 | 44 | - name: Install node.js dependencies 45 | run: yarn --frozen-lockfile 46 | 47 | - name: Run linter on *.sol and *.json 48 | run: yarn lint:check 49 | 50 | commits: 51 | runs-on: ubuntu-latest 52 | 53 | steps: 54 | - name: Check out github repository 55 | uses: actions/checkout@v2 56 | with: 57 | fetch-depth: 0 58 | 59 | - name: Run commitlint 60 | uses: wagoid/commitlint-github-action@v2 61 | 62 | brownie: 63 | runs-on: ubuntu-latest 64 | 65 | steps: 66 | - name: Check out github repository 67 | uses: actions/checkout@v2 68 | with: 69 | fetch-depth: 1 70 | 71 | - name: Set up python 3.8 72 | uses: actions/setup-python@v2 73 | with: 74 | python-version: 3.8 75 | 76 | - name: Set pip cache directory path 77 | id: pip-cache-dir-path 78 | run: | 79 | echo "::set-output name=dir::$(pip cache dir)" 80 | 81 | - name: Restore pip cache 82 | uses: actions/cache@v2 83 | id: pip-cache 84 | with: 85 | path: | 86 | ${{ steps.pip-cache-dir-path.outputs.dir }} 87 | key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }} 88 | restore-keys: | 89 | ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }} 90 | ${{ runner.os }}-pip- 91 | 92 | - name: Install python dependencies 93 | run: pip install -r requirements-dev.txt 94 | 95 | - name: Run black 96 | run: black --check --include "(tests|scripts)" . 97 | 98 | # TODO: Add Slither Static Analyzer 99 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - develop 8 | pull_request: 9 | branches: 10 | - master 11 | - develop 12 | 13 | env: 14 | ETHERSCAN_TOKEN: 6776WKCX4XHV7C6NQBTIME6XKM2YNBK29V 15 | WEB3_INFURA_PROJECT_ID: 164b1027c8254bf69a309a9ae0b81342 16 | 17 | jobs: 18 | 19 | brownie: 20 | runs-on: ubuntu-latest 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | type: [functional] 26 | 27 | steps: 28 | - name: Check out github repository 29 | uses: actions/checkout@v2 30 | with: 31 | fetch-depth: 1 32 | 33 | - name: Check ETHERSCAN_TOKEN 34 | run: | 35 | if [ -z "$ETHERSCAN_TOKEN" ] ; then 36 | echo "Missing ENV variable ETHERSCAN_TOKEN" 37 | exit 1 38 | fi 39 | 40 | - name: Check WEB3_INFURA_PROJECT_ID 41 | run: | 42 | if [ -z "$WEB3_INFURA_PROJECT_ID" ] ; then 43 | echo "Missing ENV variable WEB3_INFURA_PROJECT_ID" 44 | exit 1 45 | fi 46 | 47 | - name: Cache compiler installations 48 | uses: actions/cache@v2 49 | with: 50 | path: | 51 | ~/.solcx 52 | ~/.vvm 53 | key: ${{ runner.os }}-compiler-cache 54 | 55 | - name: Setup node.js 56 | uses: actions/setup-node@v1 57 | with: 58 | node-version: '12.x' 59 | 60 | - name: Install ganache 61 | run: npm install -g ganache-cli@6.11 62 | 63 | - name: Set up python 3.8 64 | uses: actions/setup-python@v2 65 | with: 66 | python-version: 3.8 67 | 68 | - name: Set pip cache directory path 69 | id: pip-cache-dir-path 70 | run: | 71 | echo "::set-output name=dir::$(pip cache dir)" 72 | 73 | - name: Restore pip cache 74 | uses: actions/cache@v2 75 | id: pip-cache 76 | with: 77 | path: | 78 | ${{ steps.pip-cache-dir-path.outputs.dir }} 79 | key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }} 80 | restore-keys: | 81 | ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }} 82 | ${{ runner.os }}-pip- 83 | 84 | - name: Install python dependencies 85 | run: pip install -r requirements-dev.txt 86 | 87 | - name: Run tests 88 | run: brownie test tests/${{ matrix.type }}/ -s --coverage 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .history 3 | .hypothesis/ 4 | build/ 5 | reports/ 6 | node_modules/ -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "printWidth": 88, 4 | "overrides": [ 5 | { 6 | "files": "*.sol", 7 | "options": { 8 | "printWidth": 145, 9 | "tabWidth": 4, 10 | "useTabs": false, 11 | "singleQuote": false, 12 | "bracketSpacing": false, 13 | "explicitTypes": "always" 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.soliumrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solium:recommended", 3 | "plugins": ["security"], 4 | "rules": { 5 | "imports-on-top": "error", 6 | "variable-declarations": "error", 7 | "array-declarations": "error", 8 | "no-unused-vars": "error", 9 | "quotes": ["error", "double"], 10 | "value-in-payable": "error", 11 | "linebreak-style": ["error", "unix"], 12 | "no-empty-blocks": "warning", 13 | "indentation": ["error", 4], 14 | "whitespace": "warning", 15 | "deprecated-suicide": "warning", 16 | "pragma-on-top": "error", 17 | "function-whitespace": "warning", 18 | "semicolon-whitespace": "warning", 19 | "comma-whitespace": "warning", 20 | "operator-whitespace": "warning", 21 | "emit": "warning", 22 | "no-constant": "warning", 23 | "max-len": "warning", 24 | "error-reason": "warning", 25 | "constructor": "warning", 26 | "visibility-first": "warning", 27 | "lbrace": "warning", 28 | "mixedcase": "off", 29 | "camelcase": "off", 30 | "uppercase": "off", 31 | "blank-lines": "off", 32 | "arg-overflow": "off", 33 | "function-order": "off", 34 | "conditionals-whitespace": "warning", 35 | "no-experimental": "off", 36 | "no-trailing-whitespace": "warning", 37 | "security/no-inline-assembly": "warning" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing to Yearn 2 | 3 | This is a living guide that defines a development process for yearn.finance protocol. 4 | 5 | We want to make contributing to this project as easy and transparent as possible, whether it's: 6 | 7 | - Bug report. 8 | - Current state of the code discussion. 9 | - Fix submission. 10 | - Feature proposal. 11 | - Maintainer application. 12 | 13 | ### We Develop with Github 14 | 15 | We use Github to host code, to track issues and feature requests, as well as accept pull requests. 16 | 17 | Pull requests are the best way to propose changes to the codebase (we use Github [flow](https://guides.github.com/introduction/flow/index.html)). We welcome your pull requests: 18 | 19 | 1. Fork the repo and create your branch from `master`. 20 | 2. If you've added code that should be tested, add tests or ensure it doesn't break current tests. 21 | 3. If you've changed something impacting current docs, please update the documentation. 22 | 4. Ensure the test suite passes (if applicable). 23 | 5. Ensure your code follows formatting rules. 24 | 6. Issue that pull request! 25 | 26 | ### Release Process 27 | 28 | The `master` branch has the up to date changes of the codebase with working code. Releases should be tracked via git tags to link a specific commit to a deployment for history and documentation purposes. 29 | 30 | ### Github Actions 31 | 32 | Repository uses GH actions to setup CI for [test](https://github.com/iearn-finance/yearn-protocol/blob/master/.github/workflows/test.yaml) harness. Be sure to setup any secret or env variable needed for your GH actions to work. 33 | 34 | ### Bug Reports 35 | 36 | We use GitHub issues to track public bugs. Report a bug by opening a new [issue](https://github.com/iearn-finance/yearn-protocol/issues/new); it's that easy! 37 | 38 | Before adding a new issue, please check that your issue is not already identified or hasn't been handled by searching the active/closed issues. 39 | 40 | **Great Bug Reports** tend to have: 41 | 42 | - A clear and concise summary of an issue. 43 | - Steps to reproduce a bug. 44 | - What is expected to happen. 45 | - What actually happens. 46 | - Notes. 47 | 48 | ### Consistent Coding Style 49 | 50 | - Setup [prettier](https://github.com/prettier/prettier) and [solium](https://github.com/duaraghav8/Ethlint) linters into your local coding environment. 51 | - Check that your changes adhere to the linting rules before pushing by running `yarn lint:check` and `yarn lint:fix`. 52 | - Merging may be blocked if a PR does not follow the coding style guidelines. 53 | 54 | ### License 55 | 56 | By contributing, you agree that your contributions will be licensed under its [APGL License](https://choosealicense.com/licenses/agpl-3.0/). 57 | 58 | ### References 59 | 60 | TBD 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yearn Protocol 2 | 3 | [![GitHub license](https://img.shields.io/badge/license-AGPL-blue.svg)](https://github.com/iearn-finance/yearn-protocol/blob/master/LICENSE) 4 | ![Lint](https://github.com/iearn-finance/yearn-protocol/workflows/Lint/badge.svg) 5 | ![Test](https://github.com/iearn-finance/yearn-protocol/workflows/Test/badge.svg) 6 | 7 | Yearn Protocol is a set of Ethereum Smart Contracts focused on creating a simple way to generate high risk-adjusted returns for depositors of various assets via best-in-class lending protocols, liquidity pools, and community-made yield farming strategies on Ethereum. 8 | 9 | Before getting started with this repo, please read: 10 | 11 | - [Andre's Overview Blog Post](https://medium.com/iearn/yearn-finance-v2-af2c6a6a3613), describing how yearn.finance works. 12 | - The [Delegated Vaults Blog Post](https://medium.com/iearn/delegated-vaults-explained-fa81f1c3fce2), explaining how the delegated vaults work. 13 | - [yETH Vault Explained](https://medium.com/iearn/yeth-vault-explained-c29d6b93a371), describing how the yETH vault works. 14 | 15 | ### Requirements 16 | 17 | To run the project you need: 18 | 19 | - Python 3.8 local development environment and Node.js 10.x development environment for Ganache. 20 | - Brownie local environment setup. See instructions: [ETH Brownie](https://github.com/eth-brownie/brownie). 21 | - Local env variables for [Etherscan API](https://etherscan.io/apis) and [Infura](https://infura.io/) (`ETHERSCAN_TOKEN`, `WEB3_INFURA_PROJECT_ID`). 22 | - Local Ganache environment installed with `npm install -g ganache-cli@6.11`. 23 | 24 | ### Installation 25 | 26 | To run the yearn protocol, pull the repository from GitHub and install its dependencies. You will need [yarn](https://yarnpkg.com/lang/en/docs/install/) installed. 27 | 28 | ```bash 29 | git clone https://github.com/iearn-finance/yearn-protocol 30 | cd yearn-protocol 31 | yarn install --lock-file 32 | ``` 33 | 34 | Compile the Smart Contracts: 35 | 36 | ```bash 37 | brownie compile 38 | ``` 39 | 40 | ### Tests 41 | 42 | Run tests: 43 | 44 | ```bash 45 | brownie test -s 46 | ``` 47 | 48 | Run tests with coverage: 49 | 50 | ```bash 51 | brownie test -s --coverage 52 | ``` 53 | 54 | ### Formatting 55 | 56 | Check linter rules for `*.json` and `*.sol` files: 57 | 58 | ```bash 59 | yarn lint:check 60 | ``` 61 | 62 | Fix linter errors for `*.json` and `*.sol` files: 63 | 64 | ```bash 65 | yarn lint:fix 66 | ``` 67 | 68 | Check linter rules for `*.py` files: 69 | 70 | ```bash 71 | black . --check --config black-config.toml 72 | ``` 73 | 74 | Fix linter errors for `*.py` files: 75 | 76 | ```bash 77 | black . --config black-config.toml 78 | ``` 79 | 80 | ### Security 81 | 82 | For security concerns, please visit [Bug Bounty](https://github.com/iearn-finance/yearn-protocol/blob/develop/SECURITY.md). 83 | 84 | ### Documentation 85 | 86 | You can read more about yearn finance on our documentation [webpage](https://docs.yearn.finance). 87 | 88 | ### Discussion 89 | 90 | For questions not covered in the docs, please visit [our Discord server](http://discord.yearn.finance). 91 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Security 2 | 3 | For full security process refer to yearn-security [repo](https://github.com/iearn-finance/yearn-security/blob/master/SECURITY.md). 4 | 5 | ### Scope 6 | 7 | The scope of the Bug Bounty program spans smart contracts utilized in the Yearn ecosystem – the Solidity and/or Vyper smart contracts in the `contracts` folder of the `master` branch of the yearn-protocol [repo](https://github.com/iearn-finance/yearn-protocol), including historical deployments that still see active use on Ethereum Mainnet associated with YFI, and excluding any contracts used in a test-only capacity (including test-only deployments). 8 | 9 | Note: Other contracts, outside of the ones mentioned above, might be considered on a case by case basis, please, reach out to the Yearn development team for clarification. 10 | -------------------------------------------------------------------------------- /brownie-config.yaml: -------------------------------------------------------------------------------- 1 | # use Ganache's forked mainnet mode as the default network 2 | networks: 3 | default: mainnet-fork 4 | 5 | # automatically fetch contract sources from Etherscan 6 | autofetch_sources: True 7 | 8 | # require OpenZepplin Contracts 9 | dependencies: 10 | - OpenZeppelin/openzeppelin-contracts@2.5.1 11 | - OpenZeppelin/openzeppelin-contracts@3.1.0 12 | 13 | # path remapping to support OpenZepplin imports with NPM-style path 14 | compiler: 15 | solc: 16 | version: 0.5.17 17 | remappings: 18 | - "@openzeppelinV2=OpenZeppelin/openzeppelin-contracts@2.5.1" 19 | - "@openzeppelinV3=OpenZeppelin/openzeppelin-contracts@3.1.0" 20 | 21 | reports: 22 | exclude_paths: 23 | - contracts/test/Token.sol 24 | exclude_contracts: 25 | - SafeMath 26 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {extends: ["@commitlint/config-conventional"]}; 2 | -------------------------------------------------------------------------------- /contracts/controllers/Controller.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/yearn/IConverter.sol"; 11 | import "../../interfaces/yearn/IOneSplitAudit.sol"; 12 | import "../../interfaces/yearn/IStrategy.sol"; 13 | 14 | contract Controller { 15 | using SafeERC20 for IERC20; 16 | using Address for address; 17 | using SafeMath for uint256; 18 | 19 | address public governance; 20 | address public strategist; 21 | 22 | address public onesplit; 23 | address public rewards; 24 | mapping(address => address) public vaults; 25 | mapping(address => address) public strategies; 26 | mapping(address => mapping(address => address)) public converters; 27 | 28 | mapping(address => mapping(address => bool)) public approvedStrategies; 29 | 30 | uint256 public split = 500; 31 | uint256 public constant max = 10000; 32 | 33 | constructor(address _rewards) public { 34 | governance = msg.sender; 35 | strategist = msg.sender; 36 | onesplit = address(0x50FDA034C0Ce7a8f7EFDAebDA7Aa7cA21CC1267e); 37 | rewards = _rewards; 38 | } 39 | 40 | function setRewards(address _rewards) public { 41 | require(msg.sender == governance, "!governance"); 42 | rewards = _rewards; 43 | } 44 | 45 | function setStrategist(address _strategist) public { 46 | require(msg.sender == governance, "!governance"); 47 | strategist = _strategist; 48 | } 49 | 50 | function setSplit(uint256 _split) public { 51 | require(msg.sender == governance, "!governance"); 52 | split = _split; 53 | } 54 | 55 | function setOneSplit(address _onesplit) public { 56 | require(msg.sender == governance, "!governance"); 57 | onesplit = _onesplit; 58 | } 59 | 60 | function setGovernance(address _governance) public { 61 | require(msg.sender == governance, "!governance"); 62 | governance = _governance; 63 | } 64 | 65 | function setVault(address _token, address _vault) public { 66 | require(msg.sender == strategist || msg.sender == governance, "!strategist"); 67 | require(vaults[_token] == address(0), "vault"); 68 | vaults[_token] = _vault; 69 | } 70 | 71 | function approveStrategy(address _token, address _strategy) public { 72 | require(msg.sender == governance, "!governance"); 73 | approvedStrategies[_token][_strategy] = true; 74 | } 75 | 76 | function revokeStrategy(address _token, address _strategy) public { 77 | require(msg.sender == governance, "!governance"); 78 | approvedStrategies[_token][_strategy] = false; 79 | } 80 | 81 | function setConverter( 82 | address _input, 83 | address _output, 84 | address _converter 85 | ) public { 86 | require(msg.sender == strategist || msg.sender == governance, "!strategist"); 87 | converters[_input][_output] = _converter; 88 | } 89 | 90 | function setStrategy(address _token, address _strategy) public { 91 | require(msg.sender == strategist || msg.sender == governance, "!strategist"); 92 | require(approvedStrategies[_token][_strategy] == true, "!approved"); 93 | 94 | address _current = strategies[_token]; 95 | if (_current != address(0)) { 96 | IStrategy(_current).withdrawAll(); 97 | } 98 | strategies[_token] = _strategy; 99 | } 100 | 101 | function earn(address _token, uint256 _amount) public { 102 | address _strategy = strategies[_token]; 103 | address _want = IStrategy(_strategy).want(); 104 | if (_want != _token) { 105 | address converter = converters[_token][_want]; 106 | IERC20(_token).safeTransfer(converter, _amount); 107 | _amount = IConverter(converter).convert(_strategy); 108 | IERC20(_want).safeTransfer(_strategy, _amount); 109 | } else { 110 | IERC20(_token).safeTransfer(_strategy, _amount); 111 | } 112 | IStrategy(_strategy).deposit(); 113 | } 114 | 115 | function balanceOf(address _token) external view returns (uint256) { 116 | return IStrategy(strategies[_token]).balanceOf(); 117 | } 118 | 119 | function withdrawAll(address _token) public { 120 | require(msg.sender == strategist || msg.sender == governance, "!strategist"); 121 | IStrategy(strategies[_token]).withdrawAll(); 122 | } 123 | 124 | function inCaseTokensGetStuck(address _token, uint256 _amount) public { 125 | require(msg.sender == strategist || msg.sender == governance, "!governance"); 126 | IERC20(_token).safeTransfer(msg.sender, _amount); 127 | } 128 | 129 | function inCaseStrategyTokenGetStuck(address _strategy, address _token) public { 130 | require(msg.sender == strategist || msg.sender == governance, "!governance"); 131 | IStrategy(_strategy).withdraw(_token); 132 | } 133 | 134 | function getExpectedReturn( 135 | address _strategy, 136 | address _token, 137 | uint256 parts 138 | ) public view returns (uint256 expected) { 139 | uint256 _balance = IERC20(_token).balanceOf(_strategy); 140 | address _want = IStrategy(_strategy).want(); 141 | (expected, ) = IOneSplitAudit(onesplit).getExpectedReturn(_token, _want, _balance, parts, 0); 142 | } 143 | 144 | // Only allows to withdraw non-core strategy tokens ~ this is over and above normal yield 145 | function yearn( 146 | address _strategy, 147 | address _token, 148 | uint256 parts 149 | ) public { 150 | require(msg.sender == strategist || msg.sender == governance, "!governance"); 151 | // This contract should never have value in it, but just incase since this is a public call 152 | uint256 _before = IERC20(_token).balanceOf(address(this)); 153 | IStrategy(_strategy).withdraw(_token); 154 | uint256 _after = IERC20(_token).balanceOf(address(this)); 155 | if (_after > _before) { 156 | uint256 _amount = _after.sub(_before); 157 | address _want = IStrategy(_strategy).want(); 158 | uint256[] memory _distribution; 159 | uint256 _expected; 160 | _before = IERC20(_want).balanceOf(address(this)); 161 | IERC20(_token).safeApprove(onesplit, 0); 162 | IERC20(_token).safeApprove(onesplit, _amount); 163 | (_expected, _distribution) = IOneSplitAudit(onesplit).getExpectedReturn(_token, _want, _amount, parts, 0); 164 | IOneSplitAudit(onesplit).swap(_token, _want, _amount, _expected, _distribution, 0); 165 | _after = IERC20(_want).balanceOf(address(this)); 166 | if (_after > _before) { 167 | _amount = _after.sub(_before); 168 | uint256 _reward = _amount.mul(split).div(max); 169 | earn(_want, _amount.sub(_reward)); 170 | IERC20(_want).safeTransfer(rewards, _reward); 171 | } 172 | } 173 | } 174 | 175 | function withdraw(address _token, uint256 _amount) public { 176 | require(msg.sender == vaults[_token], "!vault"); 177 | IStrategy(strategies[_token]).withdraw(_amount); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /contracts/controllers/DelegatedController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/yearn/IStrategy.sol"; 11 | import "../../interfaces/yearn/IConverter.sol"; 12 | import "../../interfaces/yearn/IOneSplitAudit.sol"; 13 | import "../../interfaces/yearn/IDelegatedVault.sol"; 14 | 15 | contract DelegatedController { 16 | using SafeERC20 for IERC20; 17 | using Address for address; 18 | using SafeMath for uint256; 19 | 20 | address public governance; 21 | address public onesplit; 22 | address public rewards; 23 | 24 | // Vault to strategy mapping 25 | mapping(address => address) public vaults; 26 | // Strategy to vault mapping 27 | mapping(address => address) public strategies; 28 | 29 | mapping(address => mapping(address => address)) public converters; 30 | 31 | mapping(address => bool) public isVault; 32 | mapping(address => bool) public isStrategy; 33 | 34 | uint256 public split = 500; 35 | uint256 public constant max = 10000; 36 | 37 | constructor(address _rewards) public { 38 | governance = msg.sender; 39 | onesplit = address(0x50FDA034C0Ce7a8f7EFDAebDA7Aa7cA21CC1267e); 40 | rewards = _rewards; 41 | } 42 | 43 | function setSplit(uint256 _split) external { 44 | require(msg.sender == governance, "!governance"); 45 | split = _split; 46 | } 47 | 48 | function setOneSplit(address _onesplit) external { 49 | require(msg.sender == governance, "!governance"); 50 | onesplit = _onesplit; 51 | } 52 | 53 | function setGovernance(address _governance) external { 54 | require(msg.sender == governance, "!governance"); 55 | governance = _governance; 56 | } 57 | 58 | function setConverter( 59 | address _input, 60 | address _output, 61 | address _converter 62 | ) external { 63 | require(msg.sender == governance, "!governance"); 64 | converters[_input][_output] = _converter; 65 | } 66 | 67 | function setStrategy(address _vault, address _strategy) external { 68 | require(msg.sender == governance, "!governance"); 69 | address _current = strategies[_vault]; 70 | if (_current != address(0)) { 71 | IStrategy(_current).withdrawAll(); 72 | } 73 | strategies[_vault] = _strategy; 74 | isStrategy[_strategy] = true; 75 | vaults[_strategy] = _vault; 76 | isVault[_vault] = true; 77 | } 78 | 79 | function want(address _vault) external view returns (address) { 80 | return IStrategy(strategies[_vault]).want(); 81 | } 82 | 83 | function earn(address _vault, uint256 _amount) public { 84 | address _strategy = strategies[_vault]; 85 | address _want = IStrategy(_strategy).want(); 86 | IERC20(_want).safeTransfer(_strategy, _amount); 87 | IStrategy(_strategy).deposit(); 88 | } 89 | 90 | function balanceOf(address _vault) external view returns (uint256) { 91 | return IStrategy(strategies[_vault]).balanceOf(); 92 | } 93 | 94 | function withdrawAll(address _strategy) external { 95 | require(msg.sender == governance, "!governance"); 96 | // WithdrawAll sends 'want' to 'vault' 97 | IStrategy(_strategy).withdrawAll(); 98 | } 99 | 100 | function inCaseTokensGetStuck(address _token, uint256 _amount) external { 101 | require(msg.sender == governance, "!governance"); 102 | IERC20(_token).safeTransfer(governance, _amount); 103 | } 104 | 105 | function inCaseStrategyGetStruck(address _strategy, address _token) external { 106 | require(msg.sender == governance, "!governance"); 107 | IStrategy(_strategy).withdraw(_token); 108 | IERC20(_token).safeTransfer(governance, IERC20(_token).balanceOf(address(this))); 109 | } 110 | 111 | function getExpectedReturn( 112 | address _strategy, 113 | address _token, 114 | uint256 parts 115 | ) external view returns (uint256 expected) { 116 | uint256 _balance = IERC20(_token).balanceOf(_strategy); 117 | address _want = IStrategy(_strategy).want(); 118 | (expected, ) = IOneSplitAudit(onesplit).getExpectedReturn(_token, _want, _balance, parts, 0); 119 | } 120 | 121 | function claimInsurance(address _vault) external { 122 | require(msg.sender == governance, "!governance"); 123 | IDelegatedVault(_vault).claimInsurance(); 124 | } 125 | 126 | // Only allows to withdraw non-core strategy tokens ~ this is over and above normal yield 127 | function delegatedHarvest(address _strategy, uint256 parts) external { 128 | // This contract should never have value in it, but just incase since this is a public call 129 | address _have = IStrategy(_strategy).want(); 130 | uint256 _before = IERC20(_have).balanceOf(address(this)); 131 | IStrategy(_strategy).skim(); 132 | uint256 _after = IERC20(_have).balanceOf(address(this)); 133 | if (_after > _before) { 134 | uint256 _amount = _after.sub(_before); 135 | address _want = IDelegatedVault(vaults[_strategy]).token(); 136 | uint256[] memory _distribution; 137 | uint256 _expected; 138 | _before = IERC20(_want).balanceOf(address(this)); 139 | IERC20(_have).safeApprove(onesplit, 0); 140 | IERC20(_have).safeApprove(onesplit, _amount); 141 | (_expected, _distribution) = IOneSplitAudit(onesplit).getExpectedReturn(_have, _want, _amount, parts, 0); 142 | IOneSplitAudit(onesplit).swap(_have, _want, _amount, _expected, _distribution, 0); 143 | _after = IERC20(_want).balanceOf(address(this)); 144 | if (_after > _before) { 145 | _amount = _after.sub(_before); 146 | uint256 _reward = _amount.mul(split).div(max); 147 | IERC20(_want).safeTransfer(vaults[_strategy], _amount.sub(_reward)); 148 | IERC20(_want).safeTransfer(rewards, _reward); 149 | } 150 | } 151 | } 152 | 153 | // Only allows to withdraw non-core strategy tokens ~ this is over and above normal yield 154 | function harvest( 155 | address _strategy, 156 | address _token, 157 | uint256 parts 158 | ) external { 159 | // This contract should never have value in it, but just incase since this is a public call 160 | uint256 _before = IERC20(_token).balanceOf(address(this)); 161 | IStrategy(_strategy).withdraw(_token); 162 | uint256 _after = IERC20(_token).balanceOf(address(this)); 163 | if (_after > _before) { 164 | uint256 _amount = _after.sub(_before); 165 | address _want = IStrategy(_strategy).want(); 166 | uint256[] memory _distribution; 167 | uint256 _expected; 168 | _before = IERC20(_want).balanceOf(address(this)); 169 | IERC20(_token).safeApprove(onesplit, 0); 170 | IERC20(_token).safeApprove(onesplit, _amount); 171 | (_expected, _distribution) = IOneSplitAudit(onesplit).getExpectedReturn(_token, _want, _amount, parts, 0); 172 | IOneSplitAudit(onesplit).swap(_token, _want, _amount, _expected, _distribution, 0); 173 | _after = IERC20(_want).balanceOf(address(this)); 174 | if (_after > _before) { 175 | _amount = _after.sub(_before); 176 | uint256 _reward = _amount.mul(split).div(max); 177 | earn(_want, _amount.sub(_reward)); 178 | IERC20(_want).safeTransfer(rewards, _reward); 179 | } 180 | } 181 | } 182 | 183 | function withdraw(address _vault, uint256 _amount) external { 184 | require(isVault[msg.sender] == true, "!vault"); 185 | IStrategy(strategies[_vault]).withdraw(_amount); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /contracts/exploits/EvilGauge.sol: -------------------------------------------------------------------------------- 1 | pragma solidity =0.5.17; 2 | 3 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 4 | 5 | contract EvilGauge { 6 | IERC20 token; 7 | address owner; 8 | 9 | constructor(address _token) public { 10 | owner = msg.sender; 11 | token = IERC20(_token); 12 | } 13 | 14 | function deposit(uint256 amount) public { 15 | token.transferFrom(msg.sender, owner, amount); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/registries/YRegistry.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 6 | import "@openzeppelinV2/contracts/utils/Address.sol"; 7 | import "@openzeppelinV2/contracts/utils/EnumerableSet.sol"; 8 | 9 | import "../../interfaces/yearn/IController.sol"; 10 | import "../../interfaces/yearn/IStrategy.sol"; 11 | import "../../interfaces/yearn/IVault.sol"; 12 | import "../../interfaces/yearn/IWrappedVault.sol"; 13 | 14 | contract YRegistry { 15 | using Address for address; 16 | using SafeMath for uint256; 17 | using EnumerableSet for EnumerableSet.AddressSet; 18 | 19 | address public governance; 20 | address public pendingGovernance; 21 | 22 | EnumerableSet.AddressSet private vaults; 23 | EnumerableSet.AddressSet private controllers; 24 | 25 | mapping(address => address) private wrappedVaults; 26 | 27 | mapping(address => bool) public isDelegatedVault; 28 | 29 | constructor(address _governance) public { 30 | require(_governance != address(0), "Missing Governance"); 31 | governance = _governance; 32 | } 33 | 34 | function getName() external pure returns (string memory) { 35 | return "YRegistry"; 36 | } 37 | 38 | function addVault(address _vault) public onlyGovernance { 39 | setVault(_vault); 40 | 41 | (address controller, , , , ) = getVaultData(_vault); 42 | 43 | setController(controller); 44 | } 45 | 46 | function addWrappedVault(address _vault) public onlyGovernance { 47 | setVault(_vault); 48 | address wrappedVault = IWrappedVault(_vault).vault(); 49 | setWrappedVault(_vault, wrappedVault); 50 | 51 | (address controller, , , , ) = getVaultData(_vault); 52 | 53 | // Adds to controllers array 54 | setController(controller); 55 | // TODO Add and track tokens and strategies? [historical] 56 | // (current ones can be obtained via getVaults + getVaultInfo) 57 | } 58 | 59 | function addDelegatedVault(address _vault) public onlyGovernance { 60 | setVault(_vault); 61 | setDelegatedVault(_vault); 62 | 63 | (address controller, , , , ) = getVaultData(_vault); 64 | 65 | // Adds to controllers array 66 | setController(controller); 67 | // TODO Add and track tokens and strategies? [historical] 68 | // (current ones can be obtained via getVaults + getVaultInfo) 69 | } 70 | 71 | function setVault(address _vault) internal { 72 | require(_vault.isContract(), "Vault is not a contract"); 73 | // Checks if vault is already on the array 74 | require(!vaults.contains(_vault), "Vault already exists"); 75 | // Adds unique _vault to vaults array 76 | vaults.add(_vault); 77 | } 78 | 79 | function setWrappedVault(address _vault, address _wrappedVault) internal { 80 | require(_wrappedVault.isContract(), "WrappedVault is not a contract"); 81 | wrappedVaults[_vault] = _wrappedVault; 82 | } 83 | 84 | function setDelegatedVault(address _vault) internal { 85 | // TODO Is there any way to check if a vault is delegated 86 | isDelegatedVault[_vault] = true; 87 | } 88 | 89 | function setController(address _controller) internal { 90 | // Adds Controller to controllers array 91 | if (!controllers.contains(_controller)) { 92 | controllers.add(_controller); 93 | } 94 | } 95 | 96 | function removeVault(address _vault) public onlyGovernance { 97 | vaults.remove(_vault); 98 | } 99 | 100 | function getVaultData(address _vault) 101 | internal 102 | view 103 | returns ( 104 | address controller, 105 | address token, 106 | address strategy, 107 | bool isWrapped, 108 | bool isDelegated 109 | ) 110 | { 111 | address vault = _vault; 112 | isWrapped = wrappedVaults[_vault] != address(0); 113 | if (isWrapped) { 114 | vault = wrappedVaults[_vault]; 115 | } 116 | isDelegated = isDelegatedVault[vault]; 117 | 118 | // Get values from controller 119 | controller = IVault(vault).controller(); 120 | if (isWrapped && IVault(vault).underlying() != address(0)) { 121 | token = IVault(_vault).token(); // Use non-wrapped vault 122 | } else { 123 | token = IVault(vault).token(); 124 | } 125 | 126 | if (isDelegated) { 127 | strategy = IController(controller).strategies(vault); 128 | } else { 129 | strategy = IController(controller).strategies(token); 130 | } 131 | 132 | // Check if vault is set on controller for token 133 | address controllerVault = address(0); 134 | if (isDelegated) { 135 | controllerVault = IController(controller).vaults(strategy); 136 | } else { 137 | controllerVault = IController(controller).vaults(token); 138 | } 139 | require(controllerVault == vault, "Controller vault address does not match"); // Might happen on Proxy Vaults 140 | 141 | // Check if strategy has the same token as vault 142 | if (isWrapped) { 143 | address underlying = IVault(vault).underlying(); 144 | require(underlying == token, "WrappedVault token address does not match"); // Might happen? 145 | } else if (!isDelegated) { 146 | address strategyToken = IStrategy(strategy).want(); 147 | require(token == strategyToken, "Strategy token address does not match"); // Might happen? 148 | } 149 | 150 | return (controller, token, strategy, isWrapped, isDelegated); 151 | } 152 | 153 | // Vaults getters 154 | function getVault(uint256 index) external view returns (address vault) { 155 | return vaults.get(index); 156 | } 157 | 158 | function getVaultsLength() external view returns (uint256) { 159 | return vaults.length(); 160 | } 161 | 162 | function getVaults() external view returns (address[] memory) { 163 | address[] memory vaultsArray = new address[](vaults.length()); 164 | for (uint256 i = 0; i < vaults.length(); i++) { 165 | vaultsArray[i] = vaults.get(i); 166 | } 167 | return vaultsArray; 168 | } 169 | 170 | function getVaultInfo(address _vault) 171 | external 172 | view 173 | returns ( 174 | address controller, 175 | address token, 176 | address strategy, 177 | bool isWrapped, 178 | bool isDelegated 179 | ) 180 | { 181 | (controller, token, strategy, isWrapped, isDelegated) = getVaultData(_vault); 182 | return (controller, token, strategy, isWrapped, isDelegated); 183 | } 184 | 185 | function getVaultsInfo() 186 | external 187 | view 188 | returns ( 189 | address[] memory vaultsAddresses, 190 | address[] memory controllerArray, 191 | address[] memory tokenArray, 192 | address[] memory strategyArray, 193 | bool[] memory isWrappedArray, 194 | bool[] memory isDelegatedArray 195 | ) 196 | { 197 | vaultsAddresses = new address[](vaults.length()); 198 | controllerArray = new address[](vaults.length()); 199 | tokenArray = new address[](vaults.length()); 200 | strategyArray = new address[](vaults.length()); 201 | isWrappedArray = new bool[](vaults.length()); 202 | isDelegatedArray = new bool[](vaults.length()); 203 | 204 | for (uint256 i = 0; i < vaults.length(); i++) { 205 | vaultsAddresses[i] = vaults.get(i); 206 | (address _controller, address _token, address _strategy, bool _isWrapped, bool _isDelegated) = getVaultData(vaults.get(i)); 207 | controllerArray[i] = _controller; 208 | tokenArray[i] = _token; 209 | strategyArray[i] = _strategy; 210 | isWrappedArray[i] = _isWrapped; 211 | isDelegatedArray[i] = _isDelegated; 212 | } 213 | } 214 | 215 | // Governance setters 216 | function setPendingGovernance(address _pendingGovernance) external onlyGovernance { 217 | pendingGovernance = _pendingGovernance; 218 | } 219 | 220 | function acceptGovernance() external onlyPendingGovernance { 221 | governance = msg.sender; 222 | } 223 | 224 | modifier onlyGovernance { 225 | require(msg.sender == governance, "Only governance can call this function."); 226 | _; 227 | } 228 | modifier onlyPendingGovernance { 229 | require(msg.sender == pendingGovernance, "Only pendingGovernance can call this function."); 230 | _; 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /contracts/strategies/CurveYCRVVoter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/curve/Curve.sol"; 11 | import "../../interfaces/curve/Gauge.sol"; 12 | import "../../interfaces/curve/Mintr.sol"; 13 | import "../../interfaces/curve/VoteEscrow.sol"; 14 | import "../../interfaces/uniswap/Uni.sol"; 15 | import "../../interfaces/yearn/IToken.sol"; 16 | 17 | contract CurveYCRVVoter { 18 | using SafeERC20 for IERC20; 19 | using Address for address; 20 | using SafeMath for uint256; 21 | 22 | address public constant want = address(0xdF5e0e81Dff6FAF3A7e52BA697820c5e32D806A8); 23 | address public constant pool = address(0xFA712EE4788C042e2B7BB55E6cb8ec569C4530c1); 24 | address public constant mintr = address(0xd061D61a4d941c39E5453435B6345Dc261C2fcE0); 25 | address public constant crv = address(0xD533a949740bb3306d119CC777fa900bA034cd52); 26 | 27 | address public constant escrow = address(0x5f3b5DfEb7B28CDbD7FAba78963EE202a494e2A2); 28 | 29 | address public governance; 30 | address public strategy; 31 | 32 | constructor() public { 33 | governance = msg.sender; 34 | } 35 | 36 | function getName() external pure returns (string memory) { 37 | return "CurveYCRVVoter"; 38 | } 39 | 40 | function setStrategy(address _strategy) external { 41 | require(msg.sender == governance, "!governance"); 42 | strategy = _strategy; 43 | } 44 | 45 | function deposit() public { 46 | uint256 _want = IERC20(want).balanceOf(address(this)); 47 | if (_want > 0) { 48 | IERC20(want).safeApprove(pool, 0); 49 | IERC20(want).safeApprove(pool, _want); 50 | Gauge(pool).deposit(_want); 51 | } 52 | } 53 | 54 | // Controller only function for creating additional rewards from dust 55 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 56 | require(msg.sender == strategy, "!controller"); 57 | balance = _asset.balanceOf(address(this)); 58 | _asset.safeTransfer(strategy, balance); 59 | } 60 | 61 | // Withdraw partial funds, normally used with a vault withdrawal 62 | function withdraw(uint256 _amount) external { 63 | require(msg.sender == strategy, "!controller"); 64 | uint256 _balance = IERC20(want).balanceOf(address(this)); 65 | if (_balance < _amount) { 66 | _amount = _withdrawSome(_amount.sub(_balance)); 67 | _amount = _amount.add(_balance); 68 | } 69 | IERC20(want).safeTransfer(strategy, _amount); 70 | } 71 | 72 | // Withdraw all funds, normally used when migrating strategies 73 | function withdrawAll() external returns (uint256 balance) { 74 | require(msg.sender == strategy, "!controller"); 75 | _withdrawAll(); 76 | 77 | balance = IERC20(want).balanceOf(address(this)); 78 | IERC20(want).safeTransfer(strategy, balance); 79 | } 80 | 81 | function _withdrawAll() internal { 82 | Gauge(pool).withdraw(Gauge(pool).balanceOf(address(this))); 83 | } 84 | 85 | function createLock(uint256 _value, uint256 _unlockTime) external { 86 | require(msg.sender == strategy || msg.sender == governance, "!authorized"); 87 | IERC20(crv).safeApprove(escrow, 0); 88 | IERC20(crv).safeApprove(escrow, _value); 89 | VoteEscrow(escrow).create_lock(_value, _unlockTime); 90 | } 91 | 92 | function increaseAmount(uint256 _value) external { 93 | require(msg.sender == strategy || msg.sender == governance, "!authorized"); 94 | IERC20(crv).safeApprove(escrow, 0); 95 | IERC20(crv).safeApprove(escrow, _value); 96 | VoteEscrow(escrow).increase_amount(_value); 97 | } 98 | 99 | function release() external { 100 | require(msg.sender == strategy || msg.sender == governance, "!authorized"); 101 | VoteEscrow(escrow).withdraw(); 102 | } 103 | 104 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 105 | Gauge(pool).withdraw(_amount); 106 | return _amount; 107 | } 108 | 109 | function balanceOfWant() public view returns (uint256) { 110 | return IERC20(want).balanceOf(address(this)); 111 | } 112 | 113 | function balanceOfPool() public view returns (uint256) { 114 | return Gauge(pool).balanceOf(address(this)); 115 | } 116 | 117 | function balanceOf() public view returns (uint256) { 118 | return balanceOfWant().add(balanceOfPool()); 119 | } 120 | 121 | function setGovernance(address _governance) external { 122 | require(msg.sender == governance, "!governance"); 123 | governance = _governance; 124 | } 125 | 126 | function execute( 127 | address to, 128 | uint256 value, 129 | bytes calldata data 130 | ) external returns (bool, bytes memory) { 131 | require(msg.sender == strategy || msg.sender == governance, "!governance"); 132 | (bool success, bytes memory result) = to.call.value(value)(data); 133 | 134 | return (success, result); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyCreamYFI.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/cream/Controller.sol"; 11 | import "../../interfaces/compound/Token.sol"; 12 | import "../../interfaces/uniswap/Uni.sol"; 13 | 14 | import "../../interfaces/yearn/IController.sol"; 15 | 16 | contract StrategyCreamYFI { 17 | using SafeERC20 for IERC20; 18 | using Address for address; 19 | using SafeMath for uint256; 20 | 21 | address public constant want = address(0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e); 22 | 23 | Creamtroller public constant creamtroller = Creamtroller(0x3d5BC3c8d13dcB8bF317092d84783c2697AE9258); 24 | 25 | address public constant crYFI = address(0xCbaE0A83f4f9926997c8339545fb8eE32eDc6b76); 26 | address public constant cream = address(0x2ba592F78dB6436527729929AAf6c908497cB200); 27 | 28 | address public constant uni = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 29 | address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // used for cream <> weth <> yfi route 30 | 31 | uint256 public performanceFee = 500; 32 | uint256 public constant performanceMax = 10000; 33 | 34 | uint256 public withdrawalFee = 50; 35 | uint256 public constant withdrawalMax = 10000; 36 | 37 | address public governance; 38 | address public controller; 39 | address public strategist; 40 | 41 | constructor(address _controller) public { 42 | governance = msg.sender; 43 | strategist = msg.sender; 44 | controller = _controller; 45 | } 46 | 47 | function getName() external pure returns (string memory) { 48 | return "StrategyCreamYFI"; 49 | } 50 | 51 | function setStrategist(address _strategist) external { 52 | require(msg.sender == governance, "!governance"); 53 | strategist = _strategist; 54 | } 55 | 56 | function setWithdrawalFee(uint256 _withdrawalFee) external { 57 | require(msg.sender == governance, "!governance"); 58 | withdrawalFee = _withdrawalFee; 59 | } 60 | 61 | function setPerformanceFee(uint256 _performanceFee) external { 62 | require(msg.sender == governance, "!governance"); 63 | performanceFee = _performanceFee; 64 | } 65 | 66 | function deposit() public { 67 | uint256 _want = IERC20(want).balanceOf(address(this)); 68 | if (_want > 0) { 69 | IERC20(want).safeApprove(crYFI, 0); 70 | IERC20(want).safeApprove(crYFI, _want); 71 | cToken(crYFI).mint(_want); 72 | } 73 | } 74 | 75 | // Controller only function for creating additional rewards from dust 76 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 77 | require(msg.sender == controller, "!controller"); 78 | require(want != address(_asset), "want"); 79 | require(crYFI != address(_asset), "crYFI"); 80 | require(cream != address(_asset), "cream"); 81 | balance = _asset.balanceOf(address(this)); 82 | _asset.safeTransfer(controller, balance); 83 | } 84 | 85 | // Withdraw partial funds, normally used with a vault withdrawal 86 | function withdraw(uint256 _amount) external { 87 | require(msg.sender == controller, "!controller"); 88 | uint256 _balance = IERC20(want).balanceOf(address(this)); 89 | if (_balance < _amount) { 90 | _amount = _withdrawSome(_amount.sub(_balance)); 91 | _amount = _amount.add(_balance); 92 | } 93 | 94 | uint256 _fee = _amount.mul(withdrawalFee).div(withdrawalMax); 95 | 96 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 97 | address _vault = IController(controller).vaults(address(want)); 98 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 99 | 100 | IERC20(want).safeTransfer(_vault, _amount.sub(_fee)); 101 | } 102 | 103 | // Withdraw all funds, normally used when migrating strategies 104 | function withdrawAll() external returns (uint256 balance) { 105 | require(msg.sender == controller, "!controller"); 106 | _withdrawAll(); 107 | 108 | balance = IERC20(want).balanceOf(address(this)); 109 | 110 | address _vault = IController(controller).vaults(address(want)); 111 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 112 | IERC20(want).safeTransfer(_vault, balance); 113 | } 114 | 115 | function _withdrawAll() internal { 116 | uint256 amount = balanceC(); 117 | if (amount > 0) { 118 | _withdrawSome(balanceCInToken().sub(1)); 119 | } 120 | } 121 | 122 | function harvest() public { 123 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 124 | Creamtroller(creamtroller).claimComp(address(this)); 125 | uint256 _cream = IERC20(cream).balanceOf(address(this)); 126 | if (_cream > 0) { 127 | IERC20(cream).safeApprove(uni, 0); 128 | IERC20(cream).safeApprove(uni, _cream); 129 | 130 | address[] memory path = new address[](3); 131 | path[0] = cream; 132 | path[1] = weth; 133 | path[2] = want; 134 | 135 | Uni(uni).swapExactTokensForTokens(_cream, uint256(0), path, address(this), now.add(1800)); 136 | } 137 | uint256 _want = IERC20(want).balanceOf(address(this)); 138 | if (_want > 0) { 139 | uint256 _fee = _want.mul(performanceFee).div(performanceMax); 140 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 141 | deposit(); 142 | } 143 | } 144 | 145 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 146 | uint256 b = balanceC(); 147 | uint256 bT = balanceCInToken(); 148 | // can have unintentional rounding errors 149 | uint256 amount = (b.mul(_amount)).div(bT).add(1); 150 | uint256 _before = IERC20(want).balanceOf(address(this)); 151 | _withdrawC(amount); 152 | uint256 _after = IERC20(want).balanceOf(address(this)); 153 | uint256 _withdrew = _after.sub(_before); 154 | return _withdrew; 155 | } 156 | 157 | function balanceOfWant() public view returns (uint256) { 158 | return IERC20(want).balanceOf(address(this)); 159 | } 160 | 161 | function _withdrawC(uint256 amount) internal { 162 | cToken(crYFI).redeem(amount); 163 | } 164 | 165 | function balanceCInToken() public view returns (uint256) { 166 | // Mantisa 1e18 to decimals 167 | uint256 b = balanceC(); 168 | if (b > 0) { 169 | b = b.mul(cToken(crYFI).exchangeRateStored()).div(1e18); 170 | } 171 | return b; 172 | } 173 | 174 | function balanceC() public view returns (uint256) { 175 | return IERC20(crYFI).balanceOf(address(this)); 176 | } 177 | 178 | function balanceOf() public view returns (uint256) { 179 | return balanceOfWant().add(balanceCInToken()); 180 | } 181 | 182 | function setGovernance(address _governance) external { 183 | require(msg.sender == governance, "!governance"); 184 | governance = _governance; 185 | } 186 | 187 | function setController(address _controller) external { 188 | require(msg.sender == governance, "!governance"); 189 | controller = _controller; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyCurve3CrvVoterProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 4 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 5 | import "@openzeppelinV2/contracts/utils/Address.sol"; 6 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 7 | 8 | import "../../interfaces/yearn/IController.sol"; 9 | import "../../interfaces/curve/Gauge.sol"; 10 | import "../../interfaces/curve/Mintr.sol"; 11 | import "../../interfaces/uniswap/Uni.sol"; 12 | import "../../interfaces/curve/Curve.sol"; 13 | import "../../interfaces/yearn/IToken.sol"; 14 | import "../../interfaces/yearn/IVoterProxy.sol"; 15 | 16 | contract StrategyCurve3CrvVoterProxy { 17 | using SafeERC20 for IERC20; 18 | using Address for address; 19 | using SafeMath for uint256; 20 | 21 | address public constant want = address(0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490); 22 | address public constant crv = address(0xD533a949740bb3306d119CC777fa900bA034cd52); 23 | address public constant uni = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 24 | address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // used for crv <> weth <> dai route 25 | 26 | address public constant dai = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); 27 | address public constant curve = address(0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7); 28 | 29 | address public constant gauge = address(0xbFcF63294aD7105dEa65aA58F8AE5BE2D9d0952A); 30 | address public constant voter = address(0xF147b8125d2ef93FB6965Db97D6746952a133934); 31 | 32 | uint256 public keepCRV = 1000; 33 | uint256 public performanceFee = 450; 34 | uint256 public strategistReward = 50; 35 | uint256 public withdrawalFee = 50; 36 | uint256 public constant FEE_DENOMINATOR = 10000; 37 | 38 | address public proxy; 39 | 40 | address public governance; 41 | address public controller; 42 | address public strategist; 43 | 44 | uint256 public earned; // lifetime strategy earnings denominated in `want` token 45 | 46 | event Harvested(uint256 wantEarned, uint256 lifetimeEarned); 47 | 48 | constructor(address _controller) public { 49 | governance = msg.sender; 50 | strategist = msg.sender; 51 | controller = _controller; 52 | } 53 | 54 | function getName() external pure returns (string memory) { 55 | return "StrategyCurve3CrvVoterProxy"; 56 | } 57 | 58 | function setStrategist(address _strategist) external { 59 | require(msg.sender == governance || msg.sender == strategist, "!authorized"); 60 | strategist = _strategist; 61 | } 62 | 63 | function setKeepCRV(uint256 _keepCRV) external { 64 | require(msg.sender == governance, "!governance"); 65 | keepCRV = _keepCRV; 66 | } 67 | 68 | function setWithdrawalFee(uint256 _withdrawalFee) external { 69 | require(msg.sender == governance, "!governance"); 70 | withdrawalFee = _withdrawalFee; 71 | } 72 | 73 | function setPerformanceFee(uint256 _performanceFee) external { 74 | require(msg.sender == governance, "!governance"); 75 | performanceFee = _performanceFee; 76 | } 77 | 78 | function setStrategistReward(uint256 _strategistReward) external { 79 | require(msg.sender == governance, "!governance"); 80 | strategistReward = _strategistReward; 81 | } 82 | 83 | function setProxy(address _proxy) external { 84 | require(msg.sender == governance, "!governance"); 85 | proxy = _proxy; 86 | } 87 | 88 | function deposit() public { 89 | uint256 _want = IERC20(want).balanceOf(address(this)); 90 | if (_want > 0) { 91 | IERC20(want).safeTransfer(proxy, _want); 92 | IVoterProxy(proxy).deposit(gauge, want); 93 | } 94 | } 95 | 96 | // Controller only function for creating additional rewards from dust 97 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 98 | require(msg.sender == controller, "!controller"); 99 | require(want != address(_asset), "want"); 100 | require(crv != address(_asset), "crv"); 101 | require(dai != address(_asset), "dai"); 102 | balance = _asset.balanceOf(address(this)); 103 | _asset.safeTransfer(controller, balance); 104 | } 105 | 106 | // Withdraw partial funds, normally used with a vault withdrawal 107 | function withdraw(uint256 _amount) external { 108 | require(msg.sender == controller, "!controller"); 109 | uint256 _balance = IERC20(want).balanceOf(address(this)); 110 | if (_balance < _amount) { 111 | _amount = _withdrawSome(_amount.sub(_balance)); 112 | _amount = _amount.add(_balance); 113 | } 114 | 115 | uint256 _fee = _amount.mul(withdrawalFee).div(FEE_DENOMINATOR); 116 | 117 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 118 | address _vault = IController(controller).vaults(address(want)); 119 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 120 | IERC20(want).safeTransfer(_vault, _amount.sub(_fee)); 121 | } 122 | 123 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 124 | return IVoterProxy(proxy).withdraw(gauge, want, _amount); 125 | } 126 | 127 | // Withdraw all funds, normally used when migrating strategies 128 | function withdrawAll() external returns (uint256 balance) { 129 | require(msg.sender == controller, "!controller"); 130 | _withdrawAll(); 131 | 132 | balance = IERC20(want).balanceOf(address(this)); 133 | 134 | address _vault = IController(controller).vaults(address(want)); 135 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 136 | IERC20(want).safeTransfer(_vault, balance); 137 | } 138 | 139 | function _withdrawAll() internal { 140 | IVoterProxy(proxy).withdrawAll(gauge, want); 141 | } 142 | 143 | function harvest() public { 144 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 145 | IVoterProxy(proxy).harvest(gauge); 146 | uint256 _crv = IERC20(crv).balanceOf(address(this)); 147 | if (_crv > 0) { 148 | uint256 _keepCRV = _crv.mul(keepCRV).div(FEE_DENOMINATOR); 149 | IERC20(crv).safeTransfer(voter, _keepCRV); 150 | _crv = _crv.sub(_keepCRV); 151 | 152 | IERC20(crv).safeApprove(uni, 0); 153 | IERC20(crv).safeApprove(uni, _crv); 154 | 155 | address[] memory path = new address[](3); 156 | path[0] = crv; 157 | path[1] = weth; 158 | path[2] = dai; 159 | 160 | Uni(uni).swapExactTokensForTokens(_crv, uint256(0), path, address(this), now.add(1800)); 161 | } 162 | uint256 _dai = IERC20(dai).balanceOf(address(this)); 163 | if (_dai > 0) { 164 | IERC20(dai).safeApprove(curve, 0); 165 | IERC20(dai).safeApprove(curve, _dai); 166 | ICurveFi(curve).add_liquidity([_dai, 0, 0], 0); 167 | } 168 | uint256 _want = IERC20(want).balanceOf(address(this)); 169 | if (_want > 0) { 170 | uint256 _fee = _want.mul(performanceFee).div(FEE_DENOMINATOR); 171 | uint256 _reward = _want.mul(strategistReward).div(FEE_DENOMINATOR); 172 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 173 | IERC20(want).safeTransfer(strategist, _reward); 174 | deposit(); 175 | } 176 | IVoterProxy(proxy).lock(); 177 | earned = earned.add(_want); 178 | emit Harvested(_want, earned); 179 | } 180 | 181 | function balanceOfWant() public view returns (uint256) { 182 | return IERC20(want).balanceOf(address(this)); 183 | } 184 | 185 | function balanceOfPool() public view returns (uint256) { 186 | return IVoterProxy(proxy).balanceOf(gauge); 187 | } 188 | 189 | function balanceOf() public view returns (uint256) { 190 | return balanceOfWant().add(balanceOfPool()); 191 | } 192 | 193 | function setGovernance(address _governance) external { 194 | require(msg.sender == governance, "!governance"); 195 | governance = _governance; 196 | } 197 | 198 | function setController(address _controller) external { 199 | require(msg.sender == governance, "!governance"); 200 | controller = _controller; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyCurveBTCVoterProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/yearn/IController.sol"; 11 | import "../../interfaces/curve/Gauge.sol"; 12 | import "../../interfaces/curve/Mintr.sol"; 13 | import "../../interfaces/uniswap/Uni.sol"; 14 | import "../../interfaces/curve/Curve.sol"; 15 | import "../../interfaces/yearn/IToken.sol"; 16 | import "../../interfaces/yearn/IVoterProxy.sol"; 17 | 18 | contract StrategyCurveBTCVoterProxy { 19 | using SafeERC20 for IERC20; 20 | using Address for address; 21 | using SafeMath for uint256; 22 | 23 | address public constant want = address(0x075b1bb99792c9E1041bA13afEf80C91a1e70fB3); 24 | address public constant crv = address(0xD533a949740bb3306d119CC777fa900bA034cd52); 25 | address public constant uni = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 26 | address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // used for crv <> weth <> wbtc route 27 | 28 | address public constant wbtc = address(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599); 29 | address public constant curve = address(0x7fC77b5c7614E1533320Ea6DDc2Eb61fa00A9714); 30 | 31 | address public constant gauge = address(0x705350c4BcD35c9441419DdD5d2f097d7a55410F); 32 | address public constant voter = address(0xF147b8125d2ef93FB6965Db97D6746952a133934); 33 | 34 | uint256 public keepCRV = 1000; 35 | uint256 public constant keepCRVMax = 10000; 36 | 37 | uint256 public performanceFee = 500; 38 | uint256 public constant performanceMax = 10000; 39 | 40 | uint256 public withdrawalFee = 50; 41 | uint256 public constant withdrawalMax = 10000; 42 | 43 | address public proxy; 44 | 45 | address public governance; 46 | address public controller; 47 | address public strategist; 48 | 49 | uint256 public earned; // lifetime strategy earnings denominated in `want` token 50 | 51 | event Harvested(uint256 wantEarned, uint256 lifetimeEarned); 52 | 53 | constructor(address _controller) public { 54 | governance = msg.sender; 55 | strategist = msg.sender; 56 | controller = _controller; 57 | } 58 | 59 | function getName() external pure returns (string memory) { 60 | return "StrategyCurveBTCVoterProxy"; 61 | } 62 | 63 | function setStrategist(address _strategist) external { 64 | require(msg.sender == governance, "!governance"); 65 | strategist = _strategist; 66 | } 67 | 68 | function setKeepCRV(uint256 _keepCRV) external { 69 | require(msg.sender == governance, "!governance"); 70 | keepCRV = _keepCRV; 71 | } 72 | 73 | function setWithdrawalFee(uint256 _withdrawalFee) external { 74 | require(msg.sender == governance, "!governance"); 75 | withdrawalFee = _withdrawalFee; 76 | } 77 | 78 | function setPerformanceFee(uint256 _performanceFee) external { 79 | require(msg.sender == governance, "!governance"); 80 | performanceFee = _performanceFee; 81 | } 82 | 83 | function setProxy(address _proxy) external { 84 | require(msg.sender == governance, "!governance"); 85 | proxy = _proxy; 86 | } 87 | 88 | function deposit() public { 89 | uint256 _want = IERC20(want).balanceOf(address(this)); 90 | if (_want > 0) { 91 | IERC20(want).safeTransfer(proxy, _want); 92 | IVoterProxy(proxy).deposit(gauge, want); 93 | } 94 | } 95 | 96 | // Controller only function for creating additional rewards from dust 97 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 98 | require(msg.sender == controller, "!controller"); 99 | require(want != address(_asset), "want"); 100 | require(crv != address(_asset), "crv"); 101 | require(wbtc != address(_asset), "wbtc"); 102 | balance = _asset.balanceOf(address(this)); 103 | _asset.safeTransfer(controller, balance); 104 | } 105 | 106 | // Withdraw partial funds, normally used with a vault withdrawal 107 | function withdraw(uint256 _amount) external { 108 | require(msg.sender == controller, "!controller"); 109 | uint256 _balance = IERC20(want).balanceOf(address(this)); 110 | if (_balance < _amount) { 111 | _amount = _withdrawSome(_amount.sub(_balance)); 112 | _amount = _amount.add(_balance); 113 | } 114 | 115 | uint256 _fee = _amount.mul(withdrawalFee).div(withdrawalMax); 116 | 117 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 118 | address _vault = IController(controller).vaults(address(want)); 119 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 120 | 121 | IERC20(want).safeTransfer(_vault, _amount.sub(_fee)); 122 | } 123 | 124 | // Withdraw all funds, normally used when migrating strategies 125 | function withdrawAll() external returns (uint256 balance) { 126 | require(msg.sender == controller, "!controller"); 127 | _withdrawAll(); 128 | 129 | balance = IERC20(want).balanceOf(address(this)); 130 | 131 | address _vault = IController(controller).vaults(address(want)); 132 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 133 | IERC20(want).safeTransfer(_vault, balance); 134 | } 135 | 136 | function _withdrawAll() internal { 137 | uint256 _before = balanceOf(); 138 | IVoterProxy(proxy).withdrawAll(gauge, want); 139 | require(_before == balanceOf(), "!slippage"); 140 | } 141 | 142 | function harvest() public { 143 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 144 | IVoterProxy(proxy).harvest(gauge); 145 | uint256 _crv = IERC20(crv).balanceOf(address(this)); 146 | if (_crv > 0) { 147 | uint256 _keepCRV = _crv.mul(keepCRV).div(keepCRVMax); 148 | IERC20(crv).safeTransfer(voter, _keepCRV); 149 | _crv = _crv.sub(_keepCRV); 150 | 151 | IERC20(crv).safeApprove(uni, 0); 152 | IERC20(crv).safeApprove(uni, _crv); 153 | 154 | address[] memory path = new address[](3); 155 | path[0] = crv; 156 | path[1] = weth; 157 | path[2] = wbtc; 158 | 159 | Uni(uni).swapExactTokensForTokens(_crv, uint256(0), path, address(this), now.add(1800)); 160 | } 161 | uint256 _wbtc = IERC20(wbtc).balanceOf(address(this)); 162 | if (_wbtc > 0) { 163 | IERC20(wbtc).safeApprove(curve, 0); 164 | IERC20(wbtc).safeApprove(curve, _wbtc); 165 | ICurveFi(curve).add_liquidity([0, _wbtc, 0], 0); 166 | } 167 | uint256 _want = IERC20(want).balanceOf(address(this)); 168 | if (_want > 0) { 169 | uint256 _fee = _want.mul(performanceFee).div(performanceMax); 170 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 171 | deposit(); 172 | } 173 | IVoterProxy(proxy).lock(); 174 | earned = earned.add(_want); 175 | emit Harvested(_want, earned); 176 | } 177 | 178 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 179 | return IVoterProxy(proxy).withdraw(gauge, want, _amount); 180 | } 181 | 182 | function balanceOfWant() public view returns (uint256) { 183 | return IERC20(want).balanceOf(address(this)); 184 | } 185 | 186 | function balanceOfPool() public view returns (uint256) { 187 | return IVoterProxy(proxy).balanceOf(gauge); 188 | } 189 | 190 | function balanceOf() public view returns (uint256) { 191 | return balanceOfWant().add(balanceOfPool()); 192 | } 193 | 194 | function setGovernance(address _governance) external { 195 | require(msg.sender == governance, "!governance"); 196 | governance = _governance; 197 | } 198 | 199 | function setController(address _controller) external { 200 | require(msg.sender == governance, "!governance"); 201 | controller = _controller; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyCurveBUSDVoterProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/yearn/IController.sol"; 11 | import "../../interfaces/curve/Gauge.sol"; 12 | import "../../interfaces/curve/Mintr.sol"; 13 | import "../../interfaces/uniswap/Uni.sol"; 14 | import "../../interfaces/curve/Curve.sol"; 15 | import "../../interfaces/yearn/IToken.sol"; 16 | import "../../interfaces/yearn/IVoterProxy.sol"; 17 | 18 | contract StrategyCurveBUSDVoterProxy { 19 | using SafeERC20 for IERC20; 20 | using Address for address; 21 | using SafeMath for uint256; 22 | 23 | address public constant want = address(0x3B3Ac5386837Dc563660FB6a0937DFAa5924333B); 24 | address public constant crv = address(0xD533a949740bb3306d119CC777fa900bA034cd52); 25 | address public constant uni = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 26 | address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // used for crv <> weth <> dai route 27 | 28 | address public constant dai = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); 29 | address public constant ydai = address(0xC2cB1040220768554cf699b0d863A3cd4324ce32); 30 | address public constant curve = address(0x79a8C46DeA5aDa233ABaFFD40F3A0A2B1e5A4F27); 31 | 32 | address public constant gauge = address(0x69Fb7c45726cfE2baDeE8317005d3F94bE838840); 33 | address public constant voter = address(0xF147b8125d2ef93FB6965Db97D6746952a133934); 34 | 35 | uint256 public keepCRV = 1000; 36 | uint256 public constant keepCRVMax = 10000; 37 | 38 | uint256 public performanceFee = 500; 39 | uint256 public constant performanceMax = 10000; 40 | 41 | uint256 public withdrawalFee = 50; 42 | uint256 public constant withdrawalMax = 10000; 43 | 44 | address public proxy; 45 | 46 | address public governance; 47 | address public controller; 48 | address public strategist; 49 | 50 | constructor(address _controller) public { 51 | governance = msg.sender; 52 | strategist = msg.sender; 53 | controller = _controller; 54 | } 55 | 56 | function getName() external pure returns (string memory) { 57 | return "StrategyCurveBUSDVoterProxy"; 58 | } 59 | 60 | function setStrategist(address _strategist) external { 61 | require(msg.sender == governance, "!governance"); 62 | strategist = _strategist; 63 | } 64 | 65 | function setKeepCRV(uint256 _keepCRV) external { 66 | require(msg.sender == governance, "!governance"); 67 | keepCRV = _keepCRV; 68 | } 69 | 70 | function setWithdrawalFee(uint256 _withdrawalFee) external { 71 | require(msg.sender == governance, "!governance"); 72 | withdrawalFee = _withdrawalFee; 73 | } 74 | 75 | function setPerformanceFee(uint256 _performanceFee) external { 76 | require(msg.sender == governance, "!governance"); 77 | performanceFee = _performanceFee; 78 | } 79 | 80 | function setProxy(address _proxy) external { 81 | require(msg.sender == governance, "!governance"); 82 | proxy = _proxy; 83 | } 84 | 85 | function deposit() public { 86 | uint256 _want = IERC20(want).balanceOf(address(this)); 87 | if (_want > 0) { 88 | IERC20(want).safeTransfer(proxy, _want); 89 | IVoterProxy(proxy).deposit(gauge, want); 90 | } 91 | } 92 | 93 | // Controller only function for creating additional rewards from dust 94 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 95 | require(msg.sender == controller, "!controller"); 96 | require(want != address(_asset), "want"); 97 | require(crv != address(_asset), "crv"); 98 | require(ydai != address(_asset), "ydai"); 99 | require(dai != address(_asset), "dai"); 100 | balance = _asset.balanceOf(address(this)); 101 | _asset.safeTransfer(controller, balance); 102 | } 103 | 104 | // Withdraw partial funds, normally used with a vault withdrawal 105 | function withdraw(uint256 _amount) external { 106 | require(msg.sender == controller, "!controller"); 107 | uint256 _balance = IERC20(want).balanceOf(address(this)); 108 | if (_balance < _amount) { 109 | _amount = _withdrawSome(_amount.sub(_balance)); 110 | _amount = _amount.add(_balance); 111 | } 112 | 113 | uint256 _fee = _amount.mul(withdrawalFee).div(withdrawalMax); 114 | 115 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 116 | address _vault = IController(controller).vaults(address(want)); 117 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 118 | 119 | IERC20(want).safeTransfer(_vault, _amount.sub(_fee)); 120 | } 121 | 122 | // Withdraw all funds, normally used when migrating strategies 123 | function withdrawAll() external returns (uint256 balance) { 124 | require(msg.sender == controller, "!controller"); 125 | _withdrawAll(); 126 | 127 | balance = IERC20(want).balanceOf(address(this)); 128 | 129 | address _vault = IController(controller).vaults(address(want)); 130 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 131 | IERC20(want).safeTransfer(_vault, balance); 132 | } 133 | 134 | function _withdrawAll() internal { 135 | IVoterProxy(proxy).withdrawAll(gauge, want); 136 | } 137 | 138 | function harvest() public { 139 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 140 | IVoterProxy(proxy).harvest(gauge); 141 | uint256 _crv = IERC20(crv).balanceOf(address(this)); 142 | if (_crv > 0) { 143 | uint256 _keepCRV = _crv.mul(keepCRV).div(keepCRVMax); 144 | IERC20(crv).safeTransfer(voter, _keepCRV); 145 | _crv = _crv.sub(_keepCRV); 146 | 147 | IERC20(crv).safeApprove(uni, 0); 148 | IERC20(crv).safeApprove(uni, _crv); 149 | 150 | address[] memory path = new address[](3); 151 | path[0] = crv; 152 | path[1] = weth; 153 | path[2] = dai; 154 | 155 | Uni(uni).swapExactTokensForTokens(_crv, uint256(0), path, address(this), now.add(1800)); 156 | } 157 | uint256 _dai = IERC20(dai).balanceOf(address(this)); 158 | if (_dai > 0) { 159 | IERC20(dai).safeApprove(ydai, 0); 160 | IERC20(dai).safeApprove(ydai, _dai); 161 | yERC20(ydai).deposit(_dai); 162 | } 163 | uint256 _ydai = IERC20(ydai).balanceOf(address(this)); 164 | if (_ydai > 0) { 165 | IERC20(ydai).safeApprove(curve, 0); 166 | IERC20(ydai).safeApprove(curve, _ydai); 167 | ICurveFi(curve).add_liquidity([_ydai, 0, 0, 0], 0); 168 | } 169 | uint256 _want = IERC20(want).balanceOf(address(this)); 170 | if (_want > 0) { 171 | uint256 _fee = _want.mul(performanceFee).div(performanceMax); 172 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 173 | deposit(); 174 | } 175 | IVoterProxy(proxy).lock(); 176 | } 177 | 178 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 179 | return IVoterProxy(proxy).withdraw(gauge, want, _amount); 180 | } 181 | 182 | function balanceOfWant() public view returns (uint256) { 183 | return IERC20(want).balanceOf(address(this)); 184 | } 185 | 186 | function balanceOfPool() public view returns (uint256) { 187 | return IVoterProxy(proxy).balanceOf(gauge); 188 | } 189 | 190 | function balanceOf() public view returns (uint256) { 191 | return balanceOfWant().add(balanceOfPool()); 192 | } 193 | 194 | function setGovernance(address _governance) external { 195 | require(msg.sender == governance, "!governance"); 196 | governance = _governance; 197 | } 198 | 199 | function setController(address _controller) external { 200 | require(msg.sender == governance, "!governance"); 201 | controller = _controller; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyCurveCompoundVoterProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 4 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 5 | import "@openzeppelinV2/contracts/utils/Address.sol"; 6 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 7 | 8 | import "../../interfaces/yearn/IController.sol"; 9 | import "../../interfaces/curve/Gauge.sol"; 10 | import "../../interfaces/curve/Mintr.sol"; 11 | import "../../interfaces/uniswap/Uni.sol"; 12 | import "../../interfaces/curve/Curve.sol"; 13 | import "../../interfaces/yearn/IToken.sol"; 14 | import "../../interfaces/yearn/IVoterProxy.sol"; 15 | 16 | contract StrategyCurveCompoundVoterProxy { 17 | using SafeERC20 for IERC20; 18 | using Address for address; 19 | using SafeMath for uint256; 20 | 21 | address public constant want = address(0x845838DF265Dcd2c412A1Dc9e959c7d08537f8a2); 22 | address public constant crv = address(0xD533a949740bb3306d119CC777fa900bA034cd52); 23 | address public constant uni = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 24 | address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // used for crv <> weth <> dai route 25 | 26 | address public constant dai = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); 27 | address public constant curve = address(0xA2B47E3D5c44877cca798226B7B8118F9BFb7A56); 28 | 29 | address public constant gauge = address(0x7ca5b0a2910B33e9759DC7dDB0413949071D7575); 30 | address public constant voter = address(0xF147b8125d2ef93FB6965Db97D6746952a133934); 31 | 32 | uint256 public keepCRV = 1500; 33 | uint256 public performanceFee = 450; 34 | uint256 public strategistReward = 50; 35 | uint256 public withdrawalFee = 0; 36 | uint256 public constant FEE_DENOMINATOR = 10000; 37 | 38 | address public proxy; 39 | 40 | address public governance; 41 | address public controller; 42 | address public strategist; 43 | 44 | uint256 public earned; // lifetime strategy earnings denominated in `want` token 45 | 46 | event Harvested(uint256 wantEarned, uint256 lifetimeEarned); 47 | 48 | constructor(address _controller) public { 49 | governance = msg.sender; 50 | strategist = msg.sender; 51 | controller = _controller; 52 | } 53 | 54 | function getName() external pure returns (string memory) { 55 | return "StrategyCurveCompoundVoterProxy"; 56 | } 57 | 58 | function setStrategist(address _strategist) external { 59 | require(msg.sender == governance || msg.sender == strategist, "!authorized"); 60 | strategist = _strategist; 61 | } 62 | 63 | function setKeepCRV(uint256 _keepCRV) external { 64 | require(msg.sender == governance, "!governance"); 65 | keepCRV = _keepCRV; 66 | } 67 | 68 | function setWithdrawalFee(uint256 _withdrawalFee) external { 69 | require(msg.sender == governance, "!governance"); 70 | withdrawalFee = _withdrawalFee; 71 | } 72 | 73 | function setPerformanceFee(uint256 _performanceFee) external { 74 | require(msg.sender == governance, "!governance"); 75 | performanceFee = _performanceFee; 76 | } 77 | 78 | function setStrategistReward(uint256 _strategistReward) external { 79 | require(msg.sender == governance, "!governance"); 80 | strategistReward = _strategistReward; 81 | } 82 | 83 | function setProxy(address _proxy) external { 84 | require(msg.sender == governance, "!governance"); 85 | proxy = _proxy; 86 | } 87 | 88 | function deposit() public { 89 | uint256 _want = IERC20(want).balanceOf(address(this)); 90 | if (_want > 0) { 91 | IERC20(want).safeTransfer(proxy, _want); 92 | IVoterProxy(proxy).deposit(gauge, want); 93 | } 94 | } 95 | 96 | // Controller only function for creating additional rewards from dust 97 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 98 | require(msg.sender == controller, "!controller"); 99 | require(want != address(_asset), "want"); 100 | require(crv != address(_asset), "crv"); 101 | require(dai != address(_asset), "dai"); 102 | balance = _asset.balanceOf(address(this)); 103 | _asset.safeTransfer(controller, balance); 104 | } 105 | 106 | // Withdraw partial funds, normally used with a vault withdrawal 107 | function withdraw(uint256 _amount) external { 108 | require(msg.sender == controller, "!controller"); 109 | uint256 _balance = IERC20(want).balanceOf(address(this)); 110 | if (_balance < _amount) { 111 | _amount = _withdrawSome(_amount.sub(_balance)); 112 | _amount = _amount.add(_balance); 113 | } 114 | 115 | uint256 _fee = _amount.mul(withdrawalFee).div(FEE_DENOMINATOR); 116 | 117 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 118 | address _vault = IController(controller).vaults(address(want)); 119 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 120 | IERC20(want).safeTransfer(_vault, _amount.sub(_fee)); 121 | } 122 | 123 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 124 | return IVoterProxy(proxy).withdraw(gauge, want, _amount); 125 | } 126 | 127 | // Withdraw all funds, normally used when migrating strategies 128 | function withdrawAll() external returns (uint256 balance) { 129 | require(msg.sender == controller, "!controller"); 130 | _withdrawAll(); 131 | 132 | balance = IERC20(want).balanceOf(address(this)); 133 | 134 | address _vault = IController(controller).vaults(address(want)); 135 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 136 | IERC20(want).safeTransfer(_vault, balance); 137 | } 138 | 139 | function _withdrawAll() internal { 140 | IVoterProxy(proxy).withdrawAll(gauge, want); 141 | } 142 | 143 | function harvest() public { 144 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 145 | IVoterProxy(proxy).harvest(gauge); 146 | uint256 _crv = IERC20(crv).balanceOf(address(this)); 147 | if (_crv > 0) { 148 | uint256 _keepCRV = _crv.mul(keepCRV).div(FEE_DENOMINATOR); 149 | IERC20(crv).safeTransfer(voter, _keepCRV); 150 | _crv = _crv.sub(_keepCRV); 151 | 152 | IERC20(crv).safeApprove(uni, 0); 153 | IERC20(crv).safeApprove(uni, _crv); 154 | 155 | address[] memory path = new address[](3); 156 | path[0] = crv; 157 | path[1] = weth; 158 | path[2] = dai; 159 | 160 | Uni(uni).swapExactTokensForTokens(_crv, uint256(0), path, address(this), now.add(1800)); 161 | } 162 | uint256 _dai = IERC20(dai).balanceOf(address(this)); 163 | if (_dai > 0) { 164 | IERC20(dai).safeApprove(curve, 0); 165 | IERC20(dai).safeApprove(curve, _dai); 166 | ICurveFi(curve).add_liquidity([_dai, 0, 0], 0); 167 | } 168 | uint256 _want = IERC20(want).balanceOf(address(this)); 169 | if (_want > 0) { 170 | uint256 _fee = _want.mul(performanceFee).div(FEE_DENOMINATOR); 171 | uint256 _reward = _want.mul(strategistReward).div(FEE_DENOMINATOR); 172 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 173 | IERC20(want).safeTransfer(strategist, _reward); 174 | deposit(); 175 | } 176 | IVoterProxy(proxy).lock(); 177 | earned = earned.add(_want); 178 | emit Harvested(_want, earned); 179 | } 180 | 181 | function balanceOfWant() public view returns (uint256) { 182 | return IERC20(want).balanceOf(address(this)); 183 | } 184 | 185 | function balanceOfPool() public view returns (uint256) { 186 | return IVoterProxy(proxy).balanceOf(gauge); 187 | } 188 | 189 | function balanceOf() public view returns (uint256) { 190 | return balanceOfWant().add(balanceOfPool()); 191 | } 192 | 193 | function setGovernance(address _governance) external { 194 | require(msg.sender == governance, "!governance"); 195 | governance = _governance; 196 | } 197 | 198 | function setController(address _controller) external { 199 | require(msg.sender == governance, "!governance"); 200 | controller = _controller; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyCurveGUSDVoterProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/yearn/IController.sol"; 11 | import "../../interfaces/curve/Gauge.sol"; 12 | import "../../interfaces/curve/Mintr.sol"; 13 | import "../../interfaces/uniswap/Uni.sol"; 14 | import "../../interfaces/curve/Curve.sol"; 15 | import "../../interfaces/yearn/IVoterProxy.sol"; 16 | 17 | contract StrategyCurveGUSDVoterProxy { 18 | using SafeERC20 for IERC20; 19 | using Address for address; 20 | using SafeMath for uint256; 21 | 22 | address public constant want = address(0xD2967f45c4f384DEEa880F807Be904762a3DeA07); 23 | address public constant crv = address(0xD533a949740bb3306d119CC777fa900bA034cd52); 24 | 25 | address public constant curve = address(0x0aE274c98c0415C0651AF8cF52b010136E4a0082); 26 | address public constant gauge = address(0xC5cfaDA84E902aD92DD40194f0883ad49639b023); 27 | address public constant voter = address(0xF147b8125d2ef93FB6965Db97D6746952a133934); 28 | 29 | address public constant uniswap = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 30 | address public constant sushiswap = address(0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F); 31 | address public constant dai = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); 32 | address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // used for crv <> weth <> dai route 33 | 34 | uint256 public keepCRV = 1000; 35 | uint256 public treasuryFee = 1000; 36 | uint256 public strategistReward = 1000; 37 | uint256 public withdrawalFee = 0; 38 | uint256 public constant FEE_DENOMINATOR = 10000; 39 | 40 | address public proxy; 41 | address public dex; 42 | 43 | address public governance; 44 | address public controller; 45 | address public strategist; 46 | address public keeper; 47 | 48 | uint256 public earned; // lifetime strategy earnings denominated in `want` token 49 | 50 | event Harvested(uint256 wantEarned, uint256 lifetimeEarned); 51 | 52 | constructor(address _controller) public { 53 | governance = msg.sender; 54 | strategist = msg.sender; 55 | keeper = msg.sender; 56 | controller = _controller; 57 | // standardize constructor 58 | proxy = address(0xC17ADf949f524213a540609c386035D7D685B16F); 59 | dex = sushiswap; 60 | } 61 | 62 | function getName() external pure returns (string memory) { 63 | return "StrategyCurveGUSDVoterProxy"; 64 | } 65 | 66 | function setStrategist(address _strategist) external { 67 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 68 | strategist = _strategist; 69 | } 70 | 71 | function setKeeper(address _keeper) external { 72 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 73 | keeper = _keeper; 74 | } 75 | 76 | function setKeepCRV(uint256 _keepCRV) external { 77 | require(msg.sender == governance, "!governance"); 78 | keepCRV = _keepCRV; 79 | } 80 | 81 | function setWithdrawalFee(uint256 _withdrawalFee) external { 82 | require(msg.sender == governance, "!governance"); 83 | withdrawalFee = _withdrawalFee; 84 | } 85 | 86 | function setTreasuryFee(uint256 _treasuryFee) external { 87 | require(msg.sender == governance, "!governance"); 88 | treasuryFee = _treasuryFee; 89 | } 90 | 91 | function setStrategistReward(uint256 _strategistReward) external { 92 | require(msg.sender == governance, "!governance"); 93 | strategistReward = _strategistReward; 94 | } 95 | 96 | function setProxy(address _proxy) external { 97 | require(msg.sender == governance, "!governance"); 98 | proxy = _proxy; 99 | } 100 | 101 | function switchDex(bool isUniswap) external { 102 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 103 | if (isUniswap) { 104 | dex = uniswap; 105 | } else { 106 | dex = sushiswap; 107 | } 108 | } 109 | 110 | function deposit() public { 111 | uint256 _want = IERC20(want).balanceOf(address(this)); 112 | if (_want > 0) { 113 | IERC20(want).safeTransfer(proxy, _want); 114 | IVoterProxy(proxy).deposit(gauge, want); 115 | } 116 | } 117 | 118 | // Controller only function for creating additional rewards from dust 119 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 120 | require(msg.sender == controller, "!controller"); 121 | require(want != address(_asset), "want"); 122 | require(crv != address(_asset), "crv"); 123 | require(dai != address(_asset), "dai"); 124 | balance = _asset.balanceOf(address(this)); 125 | _asset.safeTransfer(controller, balance); 126 | } 127 | 128 | // Withdraw partial funds, normally used with a vault withdrawal 129 | function withdraw(uint256 _amount) external { 130 | require(msg.sender == controller, "!controller"); 131 | uint256 _balance = IERC20(want).balanceOf(address(this)); 132 | if (_balance < _amount) { 133 | _amount = _withdrawSome(_amount.sub(_balance)); 134 | _amount = _amount.add(_balance); 135 | } 136 | 137 | uint256 _fee = _amount.mul(withdrawalFee).div(FEE_DENOMINATOR); 138 | 139 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 140 | address _vault = IController(controller).vaults(address(want)); 141 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 142 | IERC20(want).safeTransfer(_vault, _amount.sub(_fee)); 143 | } 144 | 145 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 146 | return IVoterProxy(proxy).withdraw(gauge, want, _amount); 147 | } 148 | 149 | // Withdraw all funds, normally used when migrating strategies 150 | function withdrawAll() external returns (uint256 balance) { 151 | require(msg.sender == controller, "!controller"); 152 | _withdrawAll(); 153 | 154 | balance = IERC20(want).balanceOf(address(this)); 155 | 156 | address _vault = IController(controller).vaults(address(want)); 157 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 158 | IERC20(want).safeTransfer(_vault, balance); 159 | } 160 | 161 | function _withdrawAll() internal { 162 | IVoterProxy(proxy).withdrawAll(gauge, want); 163 | } 164 | 165 | function harvest() public { 166 | require(msg.sender == keeper || msg.sender == strategist || msg.sender == governance, "!keepers"); 167 | IVoterProxy(proxy).harvest(gauge); 168 | uint256 _crv = IERC20(crv).balanceOf(address(this)); 169 | if (_crv > 0) { 170 | uint256 _keepCRV = _crv.mul(keepCRV).div(FEE_DENOMINATOR); 171 | IERC20(crv).safeTransfer(voter, _keepCRV); 172 | _crv = _crv.sub(_keepCRV); 173 | 174 | IERC20(crv).safeApprove(dex, 0); 175 | IERC20(crv).safeApprove(dex, _crv); 176 | 177 | address[] memory path = new address[](3); 178 | path[0] = crv; 179 | path[1] = weth; 180 | path[2] = dai; 181 | 182 | Uni(dex).swapExactTokensForTokens(_crv, uint256(0), path, address(this), now.add(1800)); 183 | } 184 | uint256 _dai = IERC20(dai).balanceOf(address(this)); 185 | if (_dai > 0) { 186 | IERC20(dai).safeApprove(curve, 0); 187 | IERC20(dai).safeApprove(curve, _dai); 188 | ICurveFi(curve).add_liquidity([0, _dai, 0, 0], 0); 189 | } 190 | uint256 _want = IERC20(want).balanceOf(address(this)); 191 | if (_want > 0) { 192 | uint256 _fee = _want.mul(treasuryFee).div(FEE_DENOMINATOR); 193 | uint256 _reward = _want.mul(strategistReward).div(FEE_DENOMINATOR); 194 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 195 | IERC20(want).safeTransfer(strategist, _reward); 196 | deposit(); 197 | } 198 | IVoterProxy(proxy).lock(); 199 | earned = earned.add(_want); 200 | emit Harvested(_want, earned); 201 | } 202 | 203 | function balanceOfWant() public view returns (uint256) { 204 | return IERC20(want).balanceOf(address(this)); 205 | } 206 | 207 | function balanceOfPool() public view returns (uint256) { 208 | return IVoterProxy(proxy).balanceOf(gauge); 209 | } 210 | 211 | function balanceOf() public view returns (uint256) { 212 | return balanceOfWant().add(balanceOfPool()); 213 | } 214 | 215 | function setGovernance(address _governance) external { 216 | require(msg.sender == governance, "!governance"); 217 | governance = _governance; 218 | } 219 | 220 | function setController(address _controller) external { 221 | require(msg.sender == governance, "!governance"); 222 | controller = _controller; 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyCurveUSTVoterProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/yearn/IController.sol"; 11 | import "../../interfaces/curve/Gauge.sol"; 12 | import "../../interfaces/curve/Mintr.sol"; 13 | import "../../interfaces/uniswap/Uni.sol"; 14 | import "../../interfaces/curve/Curve.sol"; 15 | import "../../interfaces/yearn/IVoterProxy.sol"; 16 | 17 | contract StrategyCurveUSTVoterProxy { 18 | using SafeERC20 for IERC20; 19 | using Address for address; 20 | using SafeMath for uint256; 21 | 22 | address public constant want = address(0x94e131324b6054c0D789b190b2dAC504e4361b53); 23 | address public constant crv = address(0xD533a949740bb3306d119CC777fa900bA034cd52); 24 | 25 | address public constant curve = address(0xB0a0716841F2Fc03fbA72A891B8Bb13584F52F2d); 26 | address public constant gauge = address(0x3B7020743Bc2A4ca9EaF9D0722d42E20d6935855); 27 | address public constant voter = address(0xF147b8125d2ef93FB6965Db97D6746952a133934); 28 | 29 | address public constant uniswap = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 30 | address public constant sushiswap = address(0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F); 31 | address public constant dai = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); 32 | address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // used for crv <> weth <> dai route 33 | 34 | uint256 public keepCRV = 1000; 35 | uint256 public treasuryFee = 500; 36 | uint256 public strategistReward = 50; 37 | uint256 public withdrawalFee = 0; 38 | uint256 public constant FEE_DENOMINATOR = 10000; 39 | 40 | address public proxy; 41 | address public dex; 42 | 43 | address public governance; 44 | address public controller; 45 | address public strategist; 46 | address public keeper; 47 | 48 | uint256 public earned; // lifetime strategy earnings denominated in `want` token 49 | 50 | event Harvested(uint256 wantEarned, uint256 lifetimeEarned); 51 | 52 | constructor(address _controller) public { 53 | governance = msg.sender; 54 | strategist = msg.sender; 55 | keeper = msg.sender; 56 | controller = _controller; 57 | // standardize constructor 58 | proxy = address(0xC17ADf949f524213a540609c386035D7D685B16F); 59 | dex = sushiswap; 60 | } 61 | 62 | function getName() external pure returns (string memory) { 63 | return "StrategyCurveUSTVoterProxy"; 64 | } 65 | 66 | function setStrategist(address _strategist) external { 67 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 68 | strategist = _strategist; 69 | } 70 | 71 | function setKeeper(address _keeper) external { 72 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 73 | keeper = _keeper; 74 | } 75 | 76 | function setKeepCRV(uint256 _keepCRV) external { 77 | require(msg.sender == governance, "!governance"); 78 | keepCRV = _keepCRV; 79 | } 80 | 81 | function setWithdrawalFee(uint256 _withdrawalFee) external { 82 | require(msg.sender == governance, "!governance"); 83 | withdrawalFee = _withdrawalFee; 84 | } 85 | 86 | function setTreasuryFee(uint256 _treasuryFee) external { 87 | require(msg.sender == governance, "!governance"); 88 | treasuryFee = _treasuryFee; 89 | } 90 | 91 | function setStrategistReward(uint256 _strategistReward) external { 92 | require(msg.sender == governance, "!governance"); 93 | strategistReward = _strategistReward; 94 | } 95 | 96 | function setProxy(address _proxy) external { 97 | require(msg.sender == governance, "!governance"); 98 | proxy = _proxy; 99 | } 100 | 101 | function switchDex(bool isUniswap) external { 102 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 103 | if (isUniswap) { 104 | dex = uniswap; 105 | } else { 106 | dex = sushiswap; 107 | } 108 | } 109 | 110 | function deposit() public { 111 | uint256 _want = IERC20(want).balanceOf(address(this)); 112 | if (_want > 0) { 113 | IERC20(want).safeTransfer(proxy, _want); 114 | IVoterProxy(proxy).deposit(gauge, want); 115 | } 116 | } 117 | 118 | // Controller only function for creating additional rewards from dust 119 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 120 | require(msg.sender == controller, "!controller"); 121 | require(want != address(_asset), "want"); 122 | require(crv != address(_asset), "crv"); 123 | require(dai != address(_asset), "dai"); 124 | balance = _asset.balanceOf(address(this)); 125 | _asset.safeTransfer(controller, balance); 126 | } 127 | 128 | // Withdraw partial funds, normally used with a vault withdrawal 129 | function withdraw(uint256 _amount) external { 130 | require(msg.sender == controller, "!controller"); 131 | uint256 _balance = IERC20(want).balanceOf(address(this)); 132 | if (_balance < _amount) { 133 | _amount = _withdrawSome(_amount.sub(_balance)); 134 | _amount = _amount.add(_balance); 135 | } 136 | 137 | uint256 _fee = _amount.mul(withdrawalFee).div(FEE_DENOMINATOR); 138 | 139 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 140 | address _vault = IController(controller).vaults(address(want)); 141 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 142 | IERC20(want).safeTransfer(_vault, _amount.sub(_fee)); 143 | } 144 | 145 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 146 | return IVoterProxy(proxy).withdraw(gauge, want, _amount); 147 | } 148 | 149 | // Withdraw all funds, normally used when migrating strategies 150 | function withdrawAll() external returns (uint256 balance) { 151 | require(msg.sender == controller, "!controller"); 152 | _withdrawAll(); 153 | 154 | balance = IERC20(want).balanceOf(address(this)); 155 | 156 | address _vault = IController(controller).vaults(address(want)); 157 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 158 | IERC20(want).safeTransfer(_vault, balance); 159 | } 160 | 161 | function _withdrawAll() internal { 162 | IVoterProxy(proxy).withdrawAll(gauge, want); 163 | } 164 | 165 | function harvest() public { 166 | require(msg.sender == keeper || msg.sender == strategist || msg.sender == governance, "!keepers"); 167 | IVoterProxy(proxy).harvest(gauge); 168 | uint256 _crv = IERC20(crv).balanceOf(address(this)); 169 | if (_crv > 0) { 170 | uint256 _keepCRV = _crv.mul(keepCRV).div(FEE_DENOMINATOR); 171 | IERC20(crv).safeTransfer(voter, _keepCRV); 172 | _crv = _crv.sub(_keepCRV); 173 | 174 | IERC20(crv).safeApprove(dex, 0); 175 | IERC20(crv).safeApprove(dex, _crv); 176 | 177 | address[] memory path = new address[](3); 178 | path[0] = crv; 179 | path[1] = weth; 180 | path[2] = dai; 181 | 182 | Uni(dex).swapExactTokensForTokens(_crv, uint256(0), path, address(this), now.add(1800)); 183 | } 184 | uint256 _dai = IERC20(dai).balanceOf(address(this)); 185 | if (_dai > 0) { 186 | IERC20(dai).safeApprove(curve, 0); 187 | IERC20(dai).safeApprove(curve, _dai); 188 | ICurveFi(curve).add_liquidity([0, _dai, 0, 0], 0); 189 | } 190 | uint256 _want = IERC20(want).balanceOf(address(this)); 191 | if (_want > 0) { 192 | uint256 _fee = _want.mul(treasuryFee).div(FEE_DENOMINATOR); 193 | uint256 _reward = _want.mul(strategistReward).div(FEE_DENOMINATOR); 194 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 195 | IERC20(want).safeTransfer(strategist, _reward); 196 | deposit(); 197 | } 198 | IVoterProxy(proxy).lock(); 199 | earned = earned.add(_want); 200 | emit Harvested(_want, earned); 201 | } 202 | 203 | function balanceOfWant() public view returns (uint256) { 204 | return IERC20(want).balanceOf(address(this)); 205 | } 206 | 207 | function balanceOfPool() public view returns (uint256) { 208 | return IVoterProxy(proxy).balanceOf(gauge); 209 | } 210 | 211 | function balanceOf() public view returns (uint256) { 212 | return balanceOfWant().add(balanceOfPool()); 213 | } 214 | 215 | function setGovernance(address _governance) external { 216 | require(msg.sender == governance, "!governance"); 217 | governance = _governance; 218 | } 219 | 220 | function setController(address _controller) external { 221 | require(msg.sender == governance, "!governance"); 222 | controller = _controller; 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyCurveYVoterProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/yearn/IController.sol"; 11 | import "../../interfaces/curve/Gauge.sol"; 12 | import "../../interfaces/curve/Mintr.sol"; 13 | import "../../interfaces/uniswap/Uni.sol"; 14 | import "../../interfaces/curve/Curve.sol"; 15 | import "../../interfaces/yearn/IToken.sol"; 16 | import "../../interfaces/yearn/IVoterProxy.sol"; 17 | 18 | contract StrategyCurveYVoterProxy { 19 | using SafeERC20 for IERC20; 20 | using Address for address; 21 | using SafeMath for uint256; 22 | 23 | address public constant want = address(0xdF5e0e81Dff6FAF3A7e52BA697820c5e32D806A8); 24 | address public constant crv = address(0xD533a949740bb3306d119CC777fa900bA034cd52); 25 | address public constant uni = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 26 | address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // used for crv <> weth <> dai route 27 | 28 | address public constant dai = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); 29 | address public constant ydai = address(0x16de59092dAE5CcF4A1E6439D611fd0653f0Bd01); 30 | address public constant curve = address(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51); 31 | 32 | address public constant gauge = address(0xFA712EE4788C042e2B7BB55E6cb8ec569C4530c1); 33 | address public constant voter = address(0xF147b8125d2ef93FB6965Db97D6746952a133934); 34 | 35 | uint256 public keepCRV = 1000; 36 | uint256 public constant keepCRVMax = 10000; 37 | 38 | uint256 public performanceFee = 500; 39 | uint256 public constant performanceMax = 10000; 40 | 41 | uint256 public withdrawalFee = 50; 42 | uint256 public constant withdrawalMax = 10000; 43 | 44 | address public proxy; 45 | 46 | address public governance; 47 | address public controller; 48 | address public strategist; 49 | 50 | uint256 public earned; // lifetime strategy earnings denominated in `want` token 51 | 52 | event Harvested(uint256 wantEarned, uint256 lifetimeEarned); 53 | 54 | constructor(address _controller) public { 55 | governance = msg.sender; 56 | strategist = msg.sender; 57 | controller = _controller; 58 | } 59 | 60 | function getName() external pure returns (string memory) { 61 | return "StrategyCurveYVoterProxy"; 62 | } 63 | 64 | function setStrategist(address _strategist) external { 65 | require(msg.sender == governance, "!governance"); 66 | strategist = _strategist; 67 | } 68 | 69 | function setKeepCRV(uint256 _keepCRV) external { 70 | require(msg.sender == governance, "!governance"); 71 | keepCRV = _keepCRV; 72 | } 73 | 74 | function setWithdrawalFee(uint256 _withdrawalFee) external { 75 | require(msg.sender == governance, "!governance"); 76 | withdrawalFee = _withdrawalFee; 77 | } 78 | 79 | function setPerformanceFee(uint256 _performanceFee) external { 80 | require(msg.sender == governance, "!governance"); 81 | performanceFee = _performanceFee; 82 | } 83 | 84 | function setProxy(address _proxy) external { 85 | require(msg.sender == governance, "!governance"); 86 | proxy = _proxy; 87 | } 88 | 89 | function deposit() public { 90 | uint256 _want = IERC20(want).balanceOf(address(this)); 91 | if (_want > 0) { 92 | IERC20(want).safeTransfer(proxy, _want); 93 | IVoterProxy(proxy).deposit(gauge, want); 94 | } 95 | } 96 | 97 | // Controller only function for creating additional rewards from dust 98 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 99 | require(msg.sender == controller, "!controller"); 100 | require(want != address(_asset), "want"); 101 | require(crv != address(_asset), "crv"); 102 | require(ydai != address(_asset), "ydai"); 103 | require(dai != address(_asset), "dai"); 104 | balance = _asset.balanceOf(address(this)); 105 | _asset.safeTransfer(controller, balance); 106 | } 107 | 108 | // Withdraw partial funds, normally used with a vault withdrawal 109 | function withdraw(uint256 _amount) external { 110 | require(msg.sender == controller, "!controller"); 111 | uint256 _balance = IERC20(want).balanceOf(address(this)); 112 | if (_balance < _amount) { 113 | _amount = _withdrawSome(_amount.sub(_balance)); 114 | _amount = _amount.add(_balance); 115 | } 116 | 117 | uint256 _fee = _amount.mul(withdrawalFee).div(withdrawalMax); 118 | 119 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 120 | address _vault = IController(controller).vaults(address(want)); 121 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 122 | 123 | IERC20(want).safeTransfer(_vault, _amount.sub(_fee)); 124 | } 125 | 126 | // Withdraw all funds, normally used when migrating strategies 127 | function withdrawAll() external returns (uint256 balance) { 128 | require(msg.sender == controller, "!controller"); 129 | _withdrawAll(); 130 | 131 | balance = IERC20(want).balanceOf(address(this)); 132 | 133 | address _vault = IController(controller).vaults(address(want)); 134 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 135 | IERC20(want).safeTransfer(_vault, balance); 136 | } 137 | 138 | function _withdrawAll() internal { 139 | IVoterProxy(proxy).withdrawAll(gauge, want); 140 | } 141 | 142 | function harvest() public { 143 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 144 | IVoterProxy(proxy).harvest(gauge); 145 | uint256 _crv = IERC20(crv).balanceOf(address(this)); 146 | if (_crv > 0) { 147 | uint256 _keepCRV = _crv.mul(keepCRV).div(keepCRVMax); 148 | IERC20(crv).safeTransfer(voter, _keepCRV); 149 | _crv = _crv.sub(_keepCRV); 150 | 151 | IERC20(crv).safeApprove(uni, 0); 152 | IERC20(crv).safeApprove(uni, _crv); 153 | 154 | address[] memory path = new address[](3); 155 | path[0] = crv; 156 | path[1] = weth; 157 | path[2] = dai; 158 | 159 | Uni(uni).swapExactTokensForTokens(_crv, uint256(0), path, address(this), now.add(1800)); 160 | } 161 | uint256 _dai = IERC20(dai).balanceOf(address(this)); 162 | if (_dai > 0) { 163 | IERC20(dai).safeApprove(ydai, 0); 164 | IERC20(dai).safeApprove(ydai, _dai); 165 | yERC20(ydai).deposit(_dai); 166 | } 167 | uint256 _ydai = IERC20(ydai).balanceOf(address(this)); 168 | if (_ydai > 0) { 169 | IERC20(ydai).safeApprove(curve, 0); 170 | IERC20(ydai).safeApprove(curve, _ydai); 171 | ICurveFi(curve).add_liquidity([_ydai, 0, 0, 0], 0); 172 | } 173 | uint256 _want = IERC20(want).balanceOf(address(this)); 174 | if (_want > 0) { 175 | uint256 _fee = _want.mul(performanceFee).div(performanceMax); 176 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 177 | deposit(); 178 | } 179 | IVoterProxy(proxy).lock(); 180 | earned = earned.add(_want); 181 | emit Harvested(_want, earned); 182 | } 183 | 184 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 185 | return IVoterProxy(proxy).withdraw(gauge, want, _amount); 186 | } 187 | 188 | function balanceOfWant() public view returns (uint256) { 189 | return IERC20(want).balanceOf(address(this)); 190 | } 191 | 192 | function balanceOfPool() public view returns (uint256) { 193 | return IVoterProxy(proxy).balanceOf(gauge); 194 | } 195 | 196 | function balanceOf() public view returns (uint256) { 197 | return balanceOfWant().add(balanceOfPool()); 198 | } 199 | 200 | function setGovernance(address _governance) external { 201 | require(msg.sender == governance, "!governance"); 202 | governance = _governance; 203 | } 204 | 205 | function setController(address _controller) external { 206 | require(msg.sender == governance, "!governance"); 207 | controller = _controller; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyCurvemUSDVoterProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/yearn/IController.sol"; 11 | import "../../interfaces/curve/Gauge.sol"; 12 | import "../../interfaces/curve/Mintr.sol"; 13 | import "../../interfaces/uniswap/Uni.sol"; 14 | import "../../interfaces/curve/Curve.sol"; 15 | import "../../interfaces/yearn/IVoterProxy.sol"; 16 | 17 | contract StrategyCurvemUSDVoterProxy { 18 | using SafeERC20 for IERC20; 19 | using Address for address; 20 | using SafeMath for uint256; 21 | 22 | address public constant want = address(0x1AEf73d49Dedc4b1778d0706583995958Dc862e6); 23 | address public constant crv = address(0xD533a949740bb3306d119CC777fa900bA034cd52); 24 | 25 | address public constant curve = address(0x78CF256256C8089d68Cde634Cf7cDEFb39286470); 26 | address public constant gauge = address(0x5f626c30EC1215f4EdCc9982265E8b1F411D1352); 27 | address public constant voter = address(0xF147b8125d2ef93FB6965Db97D6746952a133934); 28 | 29 | address public constant uniswap = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 30 | address public constant sushiswap = address(0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F); 31 | address public constant dai = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); 32 | address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // used for crv <> weth <> dai route 33 | 34 | uint256 public keepCRV = 1000; 35 | uint256 public treasuryFee = 1000; 36 | uint256 public strategistReward = 1000; 37 | uint256 public withdrawalFee = 0; 38 | uint256 public constant FEE_DENOMINATOR = 10000; 39 | 40 | address public proxy; 41 | address public dex; 42 | 43 | address public governance; 44 | address public controller; 45 | address public strategist; 46 | address public keeper; 47 | 48 | uint256 public earned; // lifetime strategy earnings denominated in `want` token 49 | 50 | event Harvested(uint256 wantEarned, uint256 lifetimeEarned); 51 | 52 | constructor(address _controller) public { 53 | governance = msg.sender; 54 | strategist = msg.sender; 55 | keeper = msg.sender; 56 | controller = _controller; 57 | // standardize constructor 58 | proxy = address(0xC17ADf949f524213a540609c386035D7D685B16F); 59 | dex = sushiswap; 60 | } 61 | 62 | function getName() external pure returns (string memory) { 63 | return "StrategyCurvemUSDVoterProxy"; 64 | } 65 | 66 | function setStrategist(address _strategist) external { 67 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 68 | strategist = _strategist; 69 | } 70 | 71 | function setKeeper(address _keeper) external { 72 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 73 | keeper = _keeper; 74 | } 75 | 76 | function setKeepCRV(uint256 _keepCRV) external { 77 | require(msg.sender == governance, "!governance"); 78 | keepCRV = _keepCRV; 79 | } 80 | 81 | function setWithdrawalFee(uint256 _withdrawalFee) external { 82 | require(msg.sender == governance, "!governance"); 83 | withdrawalFee = _withdrawalFee; 84 | } 85 | 86 | function setTreasuryFee(uint256 _treasuryFee) external { 87 | require(msg.sender == governance, "!governance"); 88 | treasuryFee = _treasuryFee; 89 | } 90 | 91 | function setStrategistReward(uint256 _strategistReward) external { 92 | require(msg.sender == governance, "!governance"); 93 | strategistReward = _strategistReward; 94 | } 95 | 96 | function setProxy(address _proxy) external { 97 | require(msg.sender == governance, "!governance"); 98 | proxy = _proxy; 99 | } 100 | 101 | function switchDex(bool isUniswap) external { 102 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 103 | if (isUniswap) { 104 | dex = uniswap; 105 | } else { 106 | dex = sushiswap; 107 | } 108 | } 109 | 110 | function deposit() public { 111 | uint256 _want = IERC20(want).balanceOf(address(this)); 112 | if (_want > 0) { 113 | IERC20(want).safeTransfer(proxy, _want); 114 | IVoterProxy(proxy).deposit(gauge, want); 115 | } 116 | } 117 | 118 | // Controller only function for creating additional rewards from dust 119 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 120 | require(msg.sender == controller, "!controller"); 121 | require(want != address(_asset), "want"); 122 | require(crv != address(_asset), "crv"); 123 | require(dai != address(_asset), "dai"); 124 | balance = _asset.balanceOf(address(this)); 125 | _asset.safeTransfer(controller, balance); 126 | } 127 | 128 | // Withdraw partial funds, normally used with a vault withdrawal 129 | function withdraw(uint256 _amount) external { 130 | require(msg.sender == controller, "!controller"); 131 | uint256 _balance = IERC20(want).balanceOf(address(this)); 132 | if (_balance < _amount) { 133 | _amount = _withdrawSome(_amount.sub(_balance)); 134 | _amount = _amount.add(_balance); 135 | } 136 | 137 | uint256 _fee = _amount.mul(withdrawalFee).div(FEE_DENOMINATOR); 138 | 139 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 140 | address _vault = IController(controller).vaults(address(want)); 141 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 142 | IERC20(want).safeTransfer(_vault, _amount.sub(_fee)); 143 | } 144 | 145 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 146 | return IVoterProxy(proxy).withdraw(gauge, want, _amount); 147 | } 148 | 149 | // Withdraw all funds, normally used when migrating strategies 150 | function withdrawAll() external returns (uint256 balance) { 151 | require(msg.sender == controller, "!controller"); 152 | _withdrawAll(); 153 | 154 | balance = IERC20(want).balanceOf(address(this)); 155 | 156 | address _vault = IController(controller).vaults(address(want)); 157 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 158 | IERC20(want).safeTransfer(_vault, balance); 159 | } 160 | 161 | function _withdrawAll() internal { 162 | IVoterProxy(proxy).withdrawAll(gauge, want); 163 | } 164 | 165 | function harvest() public { 166 | require(msg.sender == keeper || msg.sender == strategist || msg.sender == governance, "!keepers"); 167 | IVoterProxy(proxy).harvest(gauge); 168 | uint256 _crv = IERC20(crv).balanceOf(address(this)); 169 | if (_crv > 0) { 170 | uint256 _keepCRV = _crv.mul(keepCRV).div(FEE_DENOMINATOR); 171 | IERC20(crv).safeTransfer(voter, _keepCRV); 172 | _crv = _crv.sub(_keepCRV); 173 | 174 | IERC20(crv).safeApprove(dex, 0); 175 | IERC20(crv).safeApprove(dex, _crv); 176 | 177 | address[] memory path = new address[](3); 178 | path[0] = crv; 179 | path[1] = weth; 180 | path[2] = dai; 181 | 182 | Uni(dex).swapExactTokensForTokens(_crv, uint256(0), path, address(this), now.add(1800)); 183 | } 184 | uint256 _dai = IERC20(dai).balanceOf(address(this)); 185 | if (_dai > 0) { 186 | IERC20(dai).safeApprove(curve, 0); 187 | IERC20(dai).safeApprove(curve, _dai); 188 | ICurveFi(curve).add_liquidity([0, _dai, 0, 0], 0); 189 | } 190 | uint256 _want = IERC20(want).balanceOf(address(this)); 191 | if (_want > 0) { 192 | uint256 _fee = _want.mul(treasuryFee).div(FEE_DENOMINATOR); 193 | uint256 _reward = _want.mul(strategistReward).div(FEE_DENOMINATOR); 194 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 195 | IERC20(want).safeTransfer(strategist, _reward); 196 | deposit(); 197 | } 198 | IVoterProxy(proxy).lock(); 199 | earned = earned.add(_want); 200 | emit Harvested(_want, earned); 201 | } 202 | 203 | function balanceOfWant() public view returns (uint256) { 204 | return IERC20(want).balanceOf(address(this)); 205 | } 206 | 207 | function balanceOfPool() public view returns (uint256) { 208 | return IVoterProxy(proxy).balanceOf(gauge); 209 | } 210 | 211 | function balanceOf() public view returns (uint256) { 212 | return balanceOfWant().add(balanceOfPool()); 213 | } 214 | 215 | function setGovernance(address _governance) external { 216 | require(msg.sender == governance, "!governance"); 217 | governance = _governance; 218 | } 219 | 220 | function setController(address _controller) external { 221 | require(msg.sender == governance, "!governance"); 222 | controller = _controller; 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyDAICurve.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/curve/Curve.sol"; 11 | import "../../interfaces/curve/Mintr.sol"; 12 | 13 | import "../../interfaces/yearn/IController.sol"; 14 | import "../../interfaces/yearn/IToken.sol"; 15 | 16 | contract StrategyDAICurve { 17 | using SafeERC20 for IERC20; 18 | using Address for address; 19 | using SafeMath for uint256; 20 | 21 | address public constant want = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); 22 | address public constant y = address(0x16de59092dAE5CcF4A1E6439D611fd0653f0Bd01); 23 | address public constant ycrv = address(0xdF5e0e81Dff6FAF3A7e52BA697820c5e32D806A8); 24 | address public constant yycrv = address(0x5dbcF33D8c2E976c6b560249878e6F1491Bca25c); 25 | address public constant curve = address(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51); 26 | 27 | address public constant dai = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); 28 | address public constant ydai = address(0x16de59092dAE5CcF4A1E6439D611fd0653f0Bd01); 29 | 30 | address public constant usdc = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); 31 | address public constant yusdc = address(0xd6aD7a6750A7593E092a9B218d66C0A814a3436e); 32 | 33 | address public constant usdt = address(0xdAC17F958D2ee523a2206206994597C13D831ec7); 34 | address public constant yusdt = address(0x83f798e925BcD4017Eb265844FDDAbb448f1707D); 35 | 36 | address public constant tusd = address(0x0000000000085d4780B73119b644AE5ecd22b376); 37 | address public constant ytusd = address(0x73a052500105205d34Daf004eAb301916DA8190f); 38 | 39 | address public governance; 40 | address public controller; 41 | 42 | constructor(address _controller) public { 43 | governance = msg.sender; 44 | controller = _controller; 45 | } 46 | 47 | function getName() external pure returns (string memory) { 48 | return "StrategyDAICurve"; 49 | } 50 | 51 | function deposit() public { 52 | uint256 _want = IERC20(want).balanceOf(address(this)); 53 | if (_want > 0) { 54 | IERC20(want).safeApprove(y, 0); 55 | IERC20(want).safeApprove(y, _want); 56 | yERC20(y).deposit(_want); 57 | } 58 | uint256 _y = IERC20(y).balanceOf(address(this)); 59 | if (_y > 0) { 60 | IERC20(y).safeApprove(curve, 0); 61 | IERC20(y).safeApprove(curve, _y); 62 | ICurveFi(curve).add_liquidity([_y, 0, 0, 0], 0); 63 | } 64 | uint256 _ycrv = IERC20(ycrv).balanceOf(address(this)); 65 | if (_ycrv > 0) { 66 | IERC20(ycrv).safeApprove(yycrv, 0); 67 | IERC20(ycrv).safeApprove(yycrv, _ycrv); 68 | yERC20(yycrv).deposit(_ycrv); 69 | } 70 | } 71 | 72 | // Controller only function for creating additional rewards from dust 73 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 74 | require(msg.sender == controller, "!controller"); 75 | require(want != address(_asset), "want"); 76 | require(y != address(_asset), "y"); 77 | require(ycrv != address(_asset), "ycrv"); 78 | require(yycrv != address(_asset), "yycrv"); 79 | balance = _asset.balanceOf(address(this)); 80 | _asset.safeTransfer(controller, balance); 81 | } 82 | 83 | // Withdraw partial funds, normally used with a vault withdrawal 84 | function withdraw(uint256 _amount) external { 85 | require(msg.sender == controller, "!controller"); 86 | uint256 _balance = IERC20(want).balanceOf(address(this)); 87 | if (_balance < _amount) { 88 | _amount = _withdrawSome(_amount.sub(_balance)); 89 | _amount = _amount.add(_balance); 90 | } 91 | 92 | address _vault = IController(controller).vaults(address(want)); 93 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 94 | IERC20(want).safeTransfer(_vault, _amount); 95 | } 96 | 97 | // Withdraw all funds, normally used when migrating strategies 98 | function withdrawAll() external returns (uint256 balance) { 99 | require(msg.sender == controller, "!controller"); 100 | _withdrawAll(); 101 | 102 | balance = IERC20(want).balanceOf(address(this)); 103 | 104 | address _vault = IController(controller).vaults(address(want)); 105 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 106 | IERC20(want).safeTransfer(_vault, balance); 107 | } 108 | 109 | function withdrawUnderlying(uint256 _amount) internal returns (uint256) { 110 | IERC20(ycrv).safeApprove(curve, 0); 111 | IERC20(ycrv).safeApprove(curve, _amount); 112 | ICurveFi(curve).remove_liquidity(_amount, [uint256(0), 0, 0, 0]); 113 | 114 | uint256 _yusdc = IERC20(yusdc).balanceOf(address(this)); 115 | uint256 _yusdt = IERC20(yusdt).balanceOf(address(this)); 116 | uint256 _ytusd = IERC20(ytusd).balanceOf(address(this)); 117 | 118 | if (_yusdc > 0) { 119 | IERC20(yusdc).safeApprove(curve, 0); 120 | IERC20(yusdc).safeApprove(curve, _yusdc); 121 | ICurveFi(curve).exchange(1, 0, _yusdc, 0); 122 | } 123 | if (_yusdt > 0) { 124 | IERC20(yusdt).safeApprove(curve, 0); 125 | IERC20(yusdt).safeApprove(curve, _yusdt); 126 | ICurveFi(curve).exchange(2, 0, _yusdt, 0); 127 | } 128 | if (_ytusd > 0) { 129 | IERC20(ytusd).safeApprove(curve, 0); 130 | IERC20(ytusd).safeApprove(curve, _ytusd); 131 | ICurveFi(curve).exchange(3, 0, _ytusd, 0); 132 | } 133 | 134 | uint256 _before = IERC20(want).balanceOf(address(this)); 135 | yERC20(ydai).withdraw(IERC20(ydai).balanceOf(address(this))); 136 | uint256 _after = IERC20(want).balanceOf(address(this)); 137 | 138 | return _after.sub(_before); 139 | } 140 | 141 | function _withdrawAll() internal { 142 | uint256 _yycrv = IERC20(yycrv).balanceOf(address(this)); 143 | if (_yycrv > 0) { 144 | yERC20(yycrv).withdraw(_yycrv); 145 | withdrawUnderlying(IERC20(ycrv).balanceOf(address(this))); 146 | } 147 | } 148 | 149 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 150 | // calculate amount of ycrv to withdraw for amount of _want_ 151 | uint256 _ycrv = _amount.mul(1e18).div(ICurveFi(curve).get_virtual_price()); 152 | // calculate amount of yycrv to withdraw for amount of _ycrv_ 153 | uint256 _yycrv = _ycrv.mul(1e18).div(yERC20(yycrv).getPricePerFullShare()); 154 | uint256 _before = IERC20(ycrv).balanceOf(address(this)); 155 | yERC20(yycrv).withdraw(_yycrv); 156 | uint256 _after = IERC20(ycrv).balanceOf(address(this)); 157 | return withdrawUnderlying(_after.sub(_before)); 158 | } 159 | 160 | function balanceOfWant() public view returns (uint256) { 161 | return IERC20(want).balanceOf(address(this)); 162 | } 163 | 164 | function balanceOfYYCRV() public view returns (uint256) { 165 | return IERC20(yycrv).balanceOf(address(this)); 166 | } 167 | 168 | function balanceOfYYCRVinYCRV() public view returns (uint256) { 169 | return balanceOfYYCRV().mul(yERC20(yycrv).getPricePerFullShare()).div(1e18); 170 | } 171 | 172 | function balanceOfYYCRVinyTUSD() public view returns (uint256) { 173 | return balanceOfYYCRVinYCRV().mul(ICurveFi(curve).get_virtual_price()).div(1e18); 174 | } 175 | 176 | function balanceOfYCRV() public view returns (uint256) { 177 | return IERC20(ycrv).balanceOf(address(this)); 178 | } 179 | 180 | function balanceOfYCRVyTUSD() public view returns (uint256) { 181 | return balanceOfYCRV().mul(ICurveFi(curve).get_virtual_price()).div(1e18); 182 | } 183 | 184 | function balanceOf() public view returns (uint256) { 185 | return balanceOfWant().add(balanceOfYYCRVinyTUSD()); 186 | } 187 | 188 | function setGovernance(address _governance) external { 189 | require(msg.sender == governance, "!governance"); 190 | governance = _governance; 191 | } 192 | 193 | function setController(address _controller) external { 194 | require(msg.sender == governance, "!governance"); 195 | controller = _controller; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyDForceUSDC.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/dforce/Rewards.sol"; 11 | import "../../interfaces/dforce/Token.sol"; 12 | import "../../interfaces/uniswap/Uni.sol"; 13 | 14 | import "../../interfaces/yearn/IController.sol"; 15 | 16 | contract StrategyDForceUSDC { 17 | using SafeERC20 for IERC20; 18 | using Address for address; 19 | using SafeMath for uint256; 20 | 21 | address public constant want = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); // USDC 22 | address public constant dusdc = address(0x16c9cF62d8daC4a38FB50Ae5fa5d51E9170F3179); 23 | address public constant pool = address(0xB71dEFDd6240c45746EC58314a01dd6D833fD3b5); 24 | address public constant df = address(0x431ad2ff6a9C365805eBaD47Ee021148d6f7DBe0); 25 | address public constant uni = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 26 | address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // used for df <> weth <> usdc route 27 | 28 | uint256 public performanceFee = 5000; 29 | uint256 public constant performanceMax = 10000; 30 | 31 | uint256 public withdrawalFee = 500; 32 | uint256 public constant withdrawalMax = 10000; 33 | 34 | address public governance; 35 | address public controller; 36 | address public strategist; 37 | 38 | constructor(address _controller) public { 39 | governance = msg.sender; 40 | strategist = msg.sender; 41 | controller = _controller; 42 | } 43 | 44 | function getName() external pure returns (string memory) { 45 | return "StrategyDForceUSDC"; 46 | } 47 | 48 | function setStrategist(address _strategist) external { 49 | require(msg.sender == governance, "!governance"); 50 | strategist = _strategist; 51 | } 52 | 53 | function setWithdrawalFee(uint256 _withdrawalFee) external { 54 | require(msg.sender == governance, "!governance"); 55 | withdrawalFee = _withdrawalFee; 56 | } 57 | 58 | function setPerformanceFee(uint256 _performanceFee) external { 59 | require(msg.sender == governance, "!governance"); 60 | performanceFee = _performanceFee; 61 | } 62 | 63 | function deposit() public { 64 | uint256 _want = IERC20(want).balanceOf(address(this)); 65 | if (_want > 0) { 66 | IERC20(want).safeApprove(dusdc, 0); 67 | IERC20(want).safeApprove(dusdc, _want); 68 | dERC20(dusdc).mint(address(this), _want); 69 | } 70 | 71 | uint256 _dusdc = IERC20(dusdc).balanceOf(address(this)); 72 | if (_dusdc > 0) { 73 | IERC20(dusdc).safeApprove(pool, 0); 74 | IERC20(dusdc).safeApprove(pool, _dusdc); 75 | dRewards(pool).stake(_dusdc); 76 | } 77 | } 78 | 79 | // Controller only function for creating additional rewards from dust 80 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 81 | require(msg.sender == controller, "!controller"); 82 | require(want != address(_asset), "want"); 83 | require(dusdc != address(_asset), "dusdc"); 84 | balance = _asset.balanceOf(address(this)); 85 | _asset.safeTransfer(controller, balance); 86 | } 87 | 88 | // Withdraw partial funds, normally used with a vault withdrawal 89 | function withdraw(uint256 _amount) external { 90 | require(msg.sender == controller, "!controller"); 91 | uint256 _balance = IERC20(want).balanceOf(address(this)); 92 | if (_balance < _amount) { 93 | _amount = _withdrawSome(_amount.sub(_balance)); 94 | _amount = _amount.add(_balance); 95 | } 96 | 97 | uint256 _fee = _amount.mul(withdrawalFee).div(withdrawalMax); 98 | 99 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 100 | address _vault = IController(controller).vaults(address(want)); 101 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 102 | 103 | IERC20(want).safeTransfer(_vault, _amount.sub(_fee)); 104 | } 105 | 106 | // Withdraw all funds, normally used when migrating strategies 107 | function withdrawAll() external returns (uint256 balance) { 108 | require(msg.sender == controller, "!controller"); 109 | _withdrawAll(); 110 | 111 | balance = IERC20(want).balanceOf(address(this)); 112 | 113 | address _vault = IController(controller).vaults(address(want)); 114 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 115 | IERC20(want).safeTransfer(_vault, balance); 116 | } 117 | 118 | function _withdrawAll() internal { 119 | dRewards(pool).exit(); 120 | uint256 _dusdc = IERC20(dusdc).balanceOf(address(this)); 121 | if (_dusdc > 0) { 122 | dERC20(dusdc).redeem(address(this), _dusdc); 123 | } 124 | } 125 | 126 | function harvest() public { 127 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 128 | dRewards(pool).getReward(); 129 | uint256 _df = IERC20(df).balanceOf(address(this)); 130 | if (_df > 0) { 131 | IERC20(df).safeApprove(uni, 0); 132 | IERC20(df).safeApprove(uni, _df); 133 | 134 | address[] memory path = new address[](3); 135 | path[0] = df; 136 | path[1] = weth; 137 | path[2] = want; 138 | 139 | Uni(uni).swapExactTokensForTokens(_df, uint256(0), path, address(this), now.add(1800)); 140 | } 141 | uint256 _want = IERC20(want).balanceOf(address(this)); 142 | if (_want > 0) { 143 | uint256 _fee = _want.mul(performanceFee).div(performanceMax); 144 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 145 | deposit(); 146 | } 147 | } 148 | 149 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 150 | uint256 _dusdc = _amount.mul(1e18).div(dERC20(dusdc).getExchangeRate()); 151 | uint256 _before = IERC20(dusdc).balanceOf(address(this)); 152 | dRewards(pool).withdraw(_dusdc); 153 | uint256 _after = IERC20(dusdc).balanceOf(address(this)); 154 | uint256 _withdrew = _after.sub(_before); 155 | _before = IERC20(want).balanceOf(address(this)); 156 | dERC20(dusdc).redeem(address(this), _withdrew); 157 | _after = IERC20(want).balanceOf(address(this)); 158 | _withdrew = _after.sub(_before); 159 | return _withdrew; 160 | } 161 | 162 | function balanceOfWant() public view returns (uint256) { 163 | return IERC20(want).balanceOf(address(this)); 164 | } 165 | 166 | function balanceOfPool() public view returns (uint256) { 167 | return (dRewards(pool).balanceOf(address(this))).mul(dERC20(dusdc).getExchangeRate()).div(1e18); 168 | } 169 | 170 | function getExchangeRate() public view returns (uint256) { 171 | return dERC20(dusdc).getExchangeRate(); 172 | } 173 | 174 | function balanceOfDUSDC() public view returns (uint256) { 175 | return dERC20(dusdc).getTokenBalance(address(this)); 176 | } 177 | 178 | function balanceOf() public view returns (uint256) { 179 | return balanceOfWant().add(balanceOfDUSDC()).add(balanceOfPool()); 180 | } 181 | 182 | function setGovernance(address _governance) external { 183 | require(msg.sender == governance, "!governance"); 184 | governance = _governance; 185 | } 186 | 187 | function setController(address _controller) external { 188 | require(msg.sender == governance, "!governance"); 189 | controller = _controller; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyDForceUSDT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/dforce/Rewards.sol"; 11 | import "../../interfaces/dforce/Token.sol"; 12 | import "../../interfaces/uniswap/Uni.sol"; 13 | 14 | import "../../interfaces/yearn/IController.sol"; 15 | 16 | contract StrategyDForceUSDT { 17 | using SafeERC20 for IERC20; 18 | using Address for address; 19 | using SafeMath for uint256; 20 | 21 | address public constant want = address(0xdAC17F958D2ee523a2206206994597C13D831ec7); 22 | address public constant d = address(0x868277d475E0e475E38EC5CdA2d9C83B5E1D9fc8); 23 | address public constant pool = address(0x324EebDAa45829c6A8eE903aFBc7B61AF48538df); 24 | address public constant df = address(0x431ad2ff6a9C365805eBaD47Ee021148d6f7DBe0); 25 | address public constant uni = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 26 | address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // used for df <> weth <> usdc route 27 | 28 | uint256 public performanceFee = 5000; 29 | uint256 public constant performanceMax = 10000; 30 | 31 | uint256 public withdrawalFee = 50; 32 | uint256 public constant withdrawalMax = 10000; 33 | 34 | address public governance; 35 | address public controller; 36 | address public strategist; 37 | 38 | constructor(address _controller) public { 39 | governance = msg.sender; 40 | strategist = msg.sender; 41 | controller = _controller; 42 | } 43 | 44 | function getName() external pure returns (string memory) { 45 | return "StrategyDForceUSDT"; 46 | } 47 | 48 | function setStrategist(address _strategist) external { 49 | require(msg.sender == governance, "!governance"); 50 | strategist = _strategist; 51 | } 52 | 53 | function setWithdrawalFee(uint256 _withdrawalFee) external { 54 | require(msg.sender == governance, "!governance"); 55 | withdrawalFee = _withdrawalFee; 56 | } 57 | 58 | function setPerformanceFee(uint256 _performanceFee) external { 59 | require(msg.sender == governance, "!governance"); 60 | performanceFee = _performanceFee; 61 | } 62 | 63 | function deposit() public { 64 | uint256 _want = IERC20(want).balanceOf(address(this)); 65 | if (_want > 0) { 66 | IERC20(want).safeApprove(d, 0); 67 | IERC20(want).safeApprove(d, _want); 68 | dERC20(d).mint(address(this), _want); 69 | } 70 | 71 | uint256 _d = IERC20(d).balanceOf(address(this)); 72 | if (_d > 0) { 73 | IERC20(d).safeApprove(pool, 0); 74 | IERC20(d).safeApprove(pool, _d); 75 | dRewards(pool).stake(_d); 76 | } 77 | } 78 | 79 | // Controller only function for creating additional rewards from dust 80 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 81 | require(msg.sender == controller, "!controller"); 82 | require(want != address(_asset), "want"); 83 | require(d != address(_asset), "d"); 84 | balance = _asset.balanceOf(address(this)); 85 | _asset.safeTransfer(controller, balance); 86 | } 87 | 88 | // Withdraw partial funds, normally used with a vault withdrawal 89 | function withdraw(uint256 _amount) external { 90 | require(msg.sender == controller, "!controller"); 91 | uint256 _balance = IERC20(want).balanceOf(address(this)); 92 | if (_balance < _amount) { 93 | _amount = _withdrawSome(_amount.sub(_balance)); 94 | _amount = _amount.add(_balance); 95 | } 96 | 97 | uint256 _fee = _amount.mul(withdrawalFee).div(withdrawalMax); 98 | 99 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 100 | address _vault = IController(controller).vaults(address(want)); 101 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 102 | 103 | IERC20(want).safeTransfer(_vault, _amount.sub(_fee)); 104 | } 105 | 106 | // Withdraw all funds, normally used when migrating strategies 107 | function withdrawAll() external returns (uint256 balance) { 108 | require(msg.sender == controller, "!controller"); 109 | _withdrawAll(); 110 | 111 | balance = IERC20(want).balanceOf(address(this)); 112 | 113 | address _vault = IController(controller).vaults(address(want)); 114 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 115 | IERC20(want).safeTransfer(_vault, balance); 116 | } 117 | 118 | function _withdrawAll() internal { 119 | dRewards(pool).exit(); 120 | uint256 _d = IERC20(d).balanceOf(address(this)); 121 | if (_d > 0) { 122 | dERC20(d).redeem(address(this), _d); 123 | } 124 | } 125 | 126 | function harvest() public { 127 | require(msg.sender == strategist || msg.sender == governance, "!authorized"); 128 | dRewards(pool).getReward(); 129 | uint256 _df = IERC20(df).balanceOf(address(this)); 130 | if (_df > 0) { 131 | IERC20(df).safeApprove(uni, 0); 132 | IERC20(df).safeApprove(uni, _df); 133 | 134 | address[] memory path = new address[](3); 135 | path[0] = df; 136 | path[1] = weth; 137 | path[2] = want; 138 | 139 | Uni(uni).swapExactTokensForTokens(_df, uint256(0), path, address(this), now.add(1800)); 140 | } 141 | uint256 _want = IERC20(want).balanceOf(address(this)); 142 | if (_want > 0) { 143 | uint256 _fee = _want.mul(performanceFee).div(performanceMax); 144 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 145 | deposit(); 146 | } 147 | } 148 | 149 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 150 | uint256 _d = _amount.mul(1e18).div(dERC20(d).getExchangeRate()); 151 | uint256 _before = IERC20(d).balanceOf(address(this)); 152 | dRewards(pool).withdraw(_d); 153 | uint256 _after = IERC20(d).balanceOf(address(this)); 154 | uint256 _withdrew = _after.sub(_before); 155 | _before = IERC20(want).balanceOf(address(this)); 156 | dERC20(d).redeem(address(this), _withdrew); 157 | _after = IERC20(want).balanceOf(address(this)); 158 | _withdrew = _after.sub(_before); 159 | return _withdrew; 160 | } 161 | 162 | function balanceOfWant() public view returns (uint256) { 163 | return IERC20(want).balanceOf(address(this)); 164 | } 165 | 166 | function balanceOfPool() public view returns (uint256) { 167 | return (dRewards(pool).balanceOf(address(this))).mul(dERC20(d).getExchangeRate()).div(1e18); 168 | } 169 | 170 | function getExchangeRate() public view returns (uint256) { 171 | return dERC20(d).getExchangeRate(); 172 | } 173 | 174 | function balanceOfD() public view returns (uint256) { 175 | return dERC20(d).getTokenBalance(address(this)); 176 | } 177 | 178 | function balanceOf() public view returns (uint256) { 179 | return balanceOfWant().add(balanceOfD()).add(balanceOfPool()); 180 | } 181 | 182 | function setGovernance(address _governance) external { 183 | require(msg.sender == governance, "!governance"); 184 | governance = _governance; 185 | } 186 | 187 | function setController(address _controller) external { 188 | require(msg.sender == governance, "!governance"); 189 | controller = _controller; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyGUSDRescue.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | interface IERC20 { 4 | function totalSupply() external view returns (uint256); 5 | 6 | function balanceOf(address account) external view returns (uint256); 7 | 8 | function transfer(address recipient, uint256 amount) external returns (bool); 9 | 10 | function allowance(address owner, address spender) external view returns (uint256); 11 | 12 | function decimals() external view returns (uint256); 13 | 14 | function approve(address spender, uint256 amount) external returns (bool); 15 | 16 | function transferFrom( 17 | address sender, 18 | address recipient, 19 | uint256 amount 20 | ) external returns (bool); 21 | 22 | event Transfer(address indexed from, address indexed to, uint256 value); 23 | event Approval(address indexed owner, address indexed spender, uint256 value); 24 | } 25 | 26 | contract StrategyGUSDRescue { 27 | address public constant want = address(0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd); 28 | address public governance = address(0xFEB4acf3df3cDEA7399794D0869ef76A6EfAff52); 29 | 30 | constructor() public {} 31 | 32 | function getName() external pure returns (string memory) { 33 | return "StrategyGUSDRescue"; 34 | } 35 | 36 | function deposit() public { 37 | uint256 _want = IERC20(want).balanceOf(address(this)); 38 | IERC20(want).transfer(governance, _want); 39 | } 40 | 41 | function withdrawAll() public returns (uint256) { 42 | return 0; 43 | } 44 | 45 | function balanceOf() public pure returns (uint256) { 46 | return 0; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/yearn/IProxy.sol"; 11 | import "../../interfaces/curve/Mintr.sol"; 12 | import "../../interfaces/curve/FeeDistribution.sol"; 13 | import "../../interfaces/curve/Gauge.sol"; 14 | 15 | library SafeProxy { 16 | function safeExecute( 17 | IProxy proxy, 18 | address to, 19 | uint256 value, 20 | bytes memory data 21 | ) internal { 22 | (bool success, ) = proxy.execute(to, value, data); 23 | if (!success) assert(false); 24 | } 25 | } 26 | 27 | contract StrategyProxy { 28 | using SafeERC20 for IERC20; 29 | using Address for address; 30 | using SafeMath for uint256; 31 | using SafeProxy for IProxy; 32 | 33 | IProxy public constant proxy = IProxy(0xF147b8125d2ef93FB6965Db97D6746952a133934); 34 | address public constant mintr = address(0xd061D61a4d941c39E5453435B6345Dc261C2fcE0); 35 | address public constant crv = address(0xD533a949740bb3306d119CC777fa900bA034cd52); 36 | address public constant gauge = address(0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB); 37 | address public constant yveCRV = address(0xc5bDdf9843308380375a611c18B50Fb9341f502A); 38 | address public constant CRV3 = address(0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490); 39 | FeeDistribution public constant feeDistribution = FeeDistribution(0xA464e6DCda8AC41e03616F95f4BC98a13b8922Dc); 40 | 41 | // gauge => strategies 42 | mapping(address => address) public strategies; 43 | mapping(address => bool) public voters; 44 | address public governance; 45 | 46 | uint256 lastTimeCursor; 47 | 48 | constructor() public { 49 | governance = msg.sender; 50 | } 51 | 52 | function setGovernance(address _governance) external { 53 | require(msg.sender == governance, "!governance"); 54 | governance = _governance; 55 | } 56 | 57 | function approveStrategy(address _gauge, address _strategy) external { 58 | require(msg.sender == governance, "!governance"); 59 | strategies[_gauge] = _strategy; 60 | } 61 | 62 | function revokeStrategy(address _gauge) external { 63 | require(msg.sender == governance, "!governance"); 64 | strategies[_gauge] = address(0); 65 | } 66 | 67 | function approveVoter(address _voter) external { 68 | require(msg.sender == governance, "!governance"); 69 | voters[_voter] = true; 70 | } 71 | 72 | function revokeVoter(address _voter) external { 73 | require(msg.sender == governance, "!governance"); 74 | voters[_voter] = false; 75 | } 76 | 77 | function lock() external { 78 | uint256 amount = IERC20(crv).balanceOf(address(proxy)); 79 | if (amount > 0) proxy.increaseAmount(amount); 80 | } 81 | 82 | function vote(address _gauge, uint256 _amount) public { 83 | require(voters[msg.sender], "!voter"); 84 | proxy.safeExecute(gauge, 0, abi.encodeWithSignature("vote_for_gauge_weights(address,uint256)", _gauge, _amount)); 85 | } 86 | 87 | function withdraw( 88 | address _gauge, 89 | address _token, 90 | uint256 _amount 91 | ) public returns (uint256) { 92 | require(strategies[_gauge] == msg.sender, "!strategy"); 93 | uint256 _balance = IERC20(_token).balanceOf(address(proxy)); 94 | proxy.safeExecute(_gauge, 0, abi.encodeWithSignature("withdraw(uint256)", _amount)); 95 | _balance = IERC20(_token).balanceOf(address(proxy)).sub(_balance); 96 | proxy.safeExecute(_token, 0, abi.encodeWithSignature("transfer(address,uint256)", msg.sender, _balance)); 97 | return _balance; 98 | } 99 | 100 | function balanceOf(address _gauge) public view returns (uint256) { 101 | return IERC20(_gauge).balanceOf(address(proxy)); 102 | } 103 | 104 | function withdrawAll(address _gauge, address _token) external returns (uint256) { 105 | require(strategies[_gauge] == msg.sender, "!strategy"); 106 | return withdraw(_gauge, _token, balanceOf(_gauge)); 107 | } 108 | 109 | function deposit(address _gauge, address _token) external { 110 | require(strategies[_gauge] == msg.sender, "!strategy"); 111 | uint256 _balance = IERC20(_token).balanceOf(address(this)); 112 | IERC20(_token).safeTransfer(address(proxy), _balance); 113 | _balance = IERC20(_token).balanceOf(address(proxy)); 114 | 115 | proxy.safeExecute(_token, 0, abi.encodeWithSignature("approve(address,uint256)", _gauge, 0)); 116 | proxy.safeExecute(_token, 0, abi.encodeWithSignature("approve(address,uint256)", _gauge, _balance)); 117 | proxy.safeExecute(_gauge, 0, abi.encodeWithSignature("deposit(uint256)", _balance)); 118 | } 119 | 120 | function harvest(address _gauge) external { 121 | require(strategies[_gauge] == msg.sender, "!strategy"); 122 | uint256 _balance = IERC20(crv).balanceOf(address(proxy)); 123 | proxy.safeExecute(mintr, 0, abi.encodeWithSignature("mint(address)", _gauge)); 124 | _balance = (IERC20(crv).balanceOf(address(proxy))).sub(_balance); 125 | proxy.safeExecute(crv, 0, abi.encodeWithSignature("transfer(address,uint256)", msg.sender, _balance)); 126 | } 127 | 128 | function claim(address recipient) external { 129 | require(msg.sender == yveCRV, "!strategy"); 130 | if (now < lastTimeCursor.add(604800)) return; 131 | 132 | address p = address(proxy); 133 | feeDistribution.claim_many([p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p, p]); 134 | lastTimeCursor = feeDistribution.time_cursor_of(address(proxy)); 135 | 136 | uint256 amount = IERC20(CRV3).balanceOf(address(proxy)); 137 | if (amount > 0) { 138 | proxy.safeExecute(CRV3, 0, abi.encodeWithSignature("transfer(address,uint256)", recipient, amount)); 139 | } 140 | } 141 | 142 | function claimRewards(address _gauge, address _token) external { 143 | require(strategies[_gauge] == msg.sender, "!strategy"); 144 | Gauge(_gauge).claim_rewards(address(proxy)); 145 | proxy.safeExecute(_token, 0, abi.encodeWithSignature("transfer(address,uint256)", msg.sender, IERC20(_token).balanceOf(address(proxy)))); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyTUSDCurve.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/curve/Curve.sol"; 11 | import "../../interfaces/uniswap/Uni.sol"; 12 | import "../../interfaces/curve/Mintr.sol"; 13 | 14 | import "../../interfaces/yearn/IController.sol"; 15 | import "../../interfaces/yearn/IToken.sol"; 16 | 17 | contract StrategyTUSDCurve { 18 | using SafeERC20 for IERC20; 19 | using Address for address; 20 | using SafeMath for uint256; 21 | 22 | address public constant want = address(0x0000000000085d4780B73119b644AE5ecd22b376); 23 | address public constant y = address(0x73a052500105205d34Daf004eAb301916DA8190f); 24 | address public constant ycrv = address(0xdF5e0e81Dff6FAF3A7e52BA697820c5e32D806A8); 25 | address public constant yycrv = address(0x5dbcF33D8c2E976c6b560249878e6F1491Bca25c); 26 | address public constant curve = address(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51); 27 | 28 | address public constant dai = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); 29 | address public constant ydai = address(0x16de59092dAE5CcF4A1E6439D611fd0653f0Bd01); 30 | 31 | address public constant usdc = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); 32 | address public constant yusdc = address(0xd6aD7a6750A7593E092a9B218d66C0A814a3436e); 33 | 34 | address public constant usdt = address(0xdAC17F958D2ee523a2206206994597C13D831ec7); 35 | address public constant yusdt = address(0x83f798e925BcD4017Eb265844FDDAbb448f1707D); 36 | 37 | address public constant tusd = address(0x0000000000085d4780B73119b644AE5ecd22b376); 38 | address public constant ytusd = address(0x73a052500105205d34Daf004eAb301916DA8190f); 39 | 40 | address public governance; 41 | address public controller; 42 | 43 | constructor(address _controller) public { 44 | governance = msg.sender; 45 | controller = _controller; 46 | } 47 | 48 | function getName() external pure returns (string memory) { 49 | return "StrategyTUSDCurve"; 50 | } 51 | 52 | function deposit() public { 53 | uint256 _want = IERC20(want).balanceOf(address(this)); 54 | if (_want > 0) { 55 | IERC20(want).safeApprove(y, 0); 56 | IERC20(want).safeApprove(y, _want); 57 | yERC20(y).deposit(_want); 58 | } 59 | uint256 _y = IERC20(y).balanceOf(address(this)); 60 | if (_y > 0) { 61 | IERC20(y).safeApprove(curve, 0); 62 | IERC20(y).safeApprove(curve, _y); 63 | ICurveFi(curve).add_liquidity([0, 0, 0, _y], 0); 64 | } 65 | uint256 _ycrv = IERC20(ycrv).balanceOf(address(this)); 66 | if (_ycrv > 0) { 67 | IERC20(ycrv).safeApprove(yycrv, 0); 68 | IERC20(ycrv).safeApprove(yycrv, _ycrv); 69 | yERC20(yycrv).deposit(_ycrv); 70 | } 71 | } 72 | 73 | // Controller only function for creating additional rewards from dust 74 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 75 | require(msg.sender == controller, "!controller"); 76 | require(want != address(_asset), "want"); 77 | require(y != address(_asset), "y"); 78 | require(ycrv != address(_asset), "ycrv"); 79 | require(yycrv != address(_asset), "yycrv"); 80 | balance = _asset.balanceOf(address(this)); 81 | _asset.safeTransfer(controller, balance); 82 | } 83 | 84 | // Withdraw partial funds, normally used with a vault withdrawal 85 | function withdraw(uint256 _amount) external { 86 | require(msg.sender == controller, "!controller"); 87 | uint256 _balance = IERC20(want).balanceOf(address(this)); 88 | if (_balance < _amount) { 89 | _amount = _withdrawSome(_amount.sub(_balance)); 90 | _amount = _amount.add(_balance); 91 | } 92 | 93 | address _vault = IController(controller).vaults(address(want)); 94 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 95 | IERC20(want).safeTransfer(_vault, _amount); 96 | } 97 | 98 | // Withdraw all funds, normally used when migrating strategies 99 | function withdrawAll() external returns (uint256 balance) { 100 | require(msg.sender == controller, "!controller"); 101 | _withdrawAll(); 102 | 103 | balance = IERC20(want).balanceOf(address(this)); 104 | 105 | address _vault = IController(controller).vaults(address(want)); 106 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 107 | IERC20(want).safeTransfer(_vault, balance); 108 | } 109 | 110 | function withdrawTUSD(uint256 _amount) internal returns (uint256) { 111 | IERC20(ycrv).safeApprove(curve, 0); 112 | IERC20(ycrv).safeApprove(curve, _amount); 113 | ICurveFi(curve).remove_liquidity(_amount, [uint256(0), 0, 0, 0]); 114 | 115 | uint256 _ydai = IERC20(ydai).balanceOf(address(this)); 116 | uint256 _yusdc = IERC20(yusdc).balanceOf(address(this)); 117 | uint256 _yusdt = IERC20(yusdt).balanceOf(address(this)); 118 | 119 | if (_ydai > 0) { 120 | IERC20(ydai).safeApprove(curve, 0); 121 | IERC20(ydai).safeApprove(curve, _ydai); 122 | ICurveFi(curve).exchange(0, 3, _ydai, 0); 123 | } 124 | if (_yusdc > 0) { 125 | IERC20(yusdc).safeApprove(curve, 0); 126 | IERC20(yusdc).safeApprove(curve, _yusdc); 127 | ICurveFi(curve).exchange(1, 3, _yusdc, 0); 128 | } 129 | if (_yusdt > 0) { 130 | IERC20(yusdt).safeApprove(curve, 0); 131 | IERC20(yusdt).safeApprove(curve, _yusdt); 132 | ICurveFi(curve).exchange(2, 3, _yusdt, 0); 133 | } 134 | 135 | uint256 _before = IERC20(want).balanceOf(address(this)); 136 | yERC20(ytusd).withdraw(IERC20(ytusd).balanceOf(address(this))); 137 | uint256 _after = IERC20(want).balanceOf(address(this)); 138 | 139 | return _after.sub(_before); 140 | } 141 | 142 | function _withdrawAll() internal { 143 | uint256 _yycrv = IERC20(yycrv).balanceOf(address(this)); 144 | if (_yycrv > 0) { 145 | yERC20(yycrv).withdraw(_yycrv); 146 | withdrawTUSD(IERC20(ycrv).balanceOf(address(this))); 147 | } 148 | } 149 | 150 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 151 | // calculate amount of ycrv to withdraw for amount of _want_ 152 | uint256 _ycrv = _amount.mul(1e18).div(ICurveFi(curve).get_virtual_price()); 153 | // calculate amount of yycrv to withdraw for amount of _ycrv_ 154 | uint256 _yycrv = _ycrv.mul(1e18).div(yERC20(yycrv).getPricePerFullShare()); 155 | uint256 _before = IERC20(ycrv).balanceOf(address(this)); 156 | yERC20(yycrv).withdraw(_yycrv); 157 | uint256 _after = IERC20(ycrv).balanceOf(address(this)); 158 | return withdrawTUSD(_after.sub(_before)); 159 | } 160 | 161 | function balanceOfWant() public view returns (uint256) { 162 | return IERC20(want).balanceOf(address(this)); 163 | } 164 | 165 | function balanceOfYYCRV() public view returns (uint256) { 166 | return IERC20(yycrv).balanceOf(address(this)); 167 | } 168 | 169 | function balanceOfYYCRVinYCRV() public view returns (uint256) { 170 | return balanceOfYYCRV().mul(yERC20(yycrv).getPricePerFullShare()).div(1e18); 171 | } 172 | 173 | function balanceOfYYCRVinyTUSD() public view returns (uint256) { 174 | return balanceOfYYCRVinYCRV().mul(ICurveFi(curve).get_virtual_price()).div(1e18); 175 | } 176 | 177 | function balanceOfYCRV() public view returns (uint256) { 178 | return IERC20(ycrv).balanceOf(address(this)); 179 | } 180 | 181 | function balanceOfYCRVyTUSD() public view returns (uint256) { 182 | return balanceOfYCRV().mul(ICurveFi(curve).get_virtual_price()).div(1e18); 183 | } 184 | 185 | function balanceOf() public view returns (uint256) { 186 | return balanceOfWant().add(balanceOfYYCRVinyTUSD()); 187 | } 188 | 189 | function setGovernance(address _governance) external { 190 | require(msg.sender == governance, "!governance"); 191 | governance = _governance; 192 | } 193 | 194 | function setController(address _controller) external { 195 | require(msg.sender == governance, "!governance"); 196 | controller = _controller; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyVaultUSDC.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/aave/Aave.sol"; 11 | import "../../interfaces/aave/LendingPoolAddressesProvider.sol"; 12 | 13 | import "../../interfaces/yearn/IController.sol"; 14 | import "../../interfaces/yearn/IVault.sol"; 15 | 16 | contract StrategyVaultUSDC { 17 | using SafeERC20 for IERC20; 18 | using Address for address; 19 | using SafeMath for uint256; 20 | 21 | address public constant want = address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); 22 | address public constant vault = address(0x597aD1e0c13Bfe8025993D9e79C69E1c0233522e); 23 | 24 | address public constant aave = address(0x24a42fD28C976A61Df5D00D0599C34c4f90748c8); 25 | 26 | address public governance; 27 | address public controller; 28 | 29 | constructor(address _controller) public { 30 | governance = msg.sender; 31 | controller = _controller; 32 | } 33 | 34 | function deposit() external { 35 | uint256 _balance = IERC20(want).balanceOf(address(this)); 36 | if (_balance > 0) { 37 | IERC20(want).safeApprove(address(vault), 0); 38 | IERC20(want).safeApprove(address(vault), _balance); 39 | IVault(vault).deposit(_balance); 40 | } 41 | } 42 | 43 | function getAave() public view returns (address) { 44 | return LendingPoolAddressesProvider(aave).getLendingPool(); 45 | } 46 | 47 | function getName() external pure returns (string memory) { 48 | return "StrategyVaultUSDC"; 49 | } 50 | 51 | function debt() external view returns (uint256) { 52 | (, uint256 currentBorrowBalance, , , , , , , , ) = Aave(getAave()).getUserReserveData( 53 | want, 54 | IController(controller).vaults(address(this)) 55 | ); 56 | return currentBorrowBalance; 57 | } 58 | 59 | function have() public view returns (uint256) { 60 | uint256 _have = balanceOf(); 61 | return _have; 62 | } 63 | 64 | function skimmable() public view returns (uint256) { 65 | (, uint256 currentBorrowBalance, , , , , , , , ) = Aave(getAave()).getUserReserveData( 66 | want, 67 | IController(controller).vaults(address(this)) 68 | ); 69 | uint256 _have = have(); 70 | if (_have > currentBorrowBalance) { 71 | return _have.sub(currentBorrowBalance); 72 | } else { 73 | return 0; 74 | } 75 | } 76 | 77 | function skim() external { 78 | uint256 _balance = IERC20(want).balanceOf(address(this)); 79 | uint256 _amount = skimmable(); 80 | if (_balance < _amount) { 81 | _amount = _withdrawSome(_amount.sub(_balance)); 82 | _amount = _amount.add(_balance); 83 | } 84 | IERC20(want).safeTransfer(controller, _amount); 85 | } 86 | 87 | // Controller only function for creating additional rewards from dust 88 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 89 | require(msg.sender == controller, "!controller"); 90 | require(address(_asset) != address(want), "!want"); 91 | require(address(_asset) != address(vault), "!vault"); 92 | balance = _asset.balanceOf(address(this)); 93 | _asset.safeTransfer(controller, balance); 94 | } 95 | 96 | // Withdraw partial funds, normally used with a vault withdrawal 97 | function withdraw(uint256 _amount) external { 98 | require(msg.sender == controller, "!controller"); 99 | uint256 _balance = IERC20(want).balanceOf(address(this)); 100 | if (_balance < _amount) { 101 | _amount = _withdrawSome(_amount.sub(_balance)); 102 | _amount = _amount.add(_balance); 103 | } 104 | address _vault = IController(controller).vaults(address(this)); 105 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 106 | IERC20(want).safeTransfer(_vault, _amount); 107 | } 108 | 109 | // Withdraw all funds, normally used when migrating strategies 110 | function withdrawAll() external returns (uint256 balance) { 111 | require(msg.sender == controller, "!controller"); 112 | _withdrawAll(); 113 | balance = IERC20(want).balanceOf(address(this)); 114 | address _vault = IController(controller).vaults(address(this)); 115 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 116 | IERC20(want).safeTransfer(_vault, balance); 117 | } 118 | 119 | function _withdrawAll() internal { 120 | IVault(vault).withdraw(IERC20(vault).balanceOf(address(this))); 121 | } 122 | 123 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 124 | uint256 _redeem = IERC20(vault).balanceOf(address(this)).mul(_amount).div(balanceSavingsInToken()); 125 | uint256 _before = IERC20(want).balanceOf(address(this)); 126 | IVault(vault).withdraw(_redeem); 127 | uint256 _after = IERC20(want).balanceOf(address(this)); 128 | return _after.sub(_before); 129 | } 130 | 131 | function balanceOf() public view returns (uint256) { 132 | return IERC20(want).balanceOf(address(this)).add(balanceSavingsInToken()); 133 | } 134 | 135 | function balanceSavingsInToken() public view returns (uint256) { 136 | return IERC20(vault).balanceOf(address(this)).mul(IVault(vault).getPricePerFullShare()).div(1e18); 137 | } 138 | 139 | function setGovernance(address _governance) external { 140 | require(msg.sender == governance, "!governance"); 141 | governance = _governance; 142 | } 143 | 144 | function setController(address _controller) external { 145 | require(msg.sender == governance, "!governance"); 146 | controller = _controller; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /contracts/strategies/StrategyYFIGovernance.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 7 | import "@openzeppelinV2/contracts/utils/Address.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 9 | 10 | import "../../interfaces/yearn/IController.sol"; 11 | import "../../interfaces/yearn/IGovernance.sol"; 12 | import "../../interfaces/yearn/IToken.sol"; 13 | import "../../interfaces/uniswap/Uni.sol"; 14 | import "../../interfaces/curve/Curve.sol"; 15 | 16 | /* 17 | 18 | A strategy must implement the following calls; 19 | 20 | - deposit() 21 | - withdraw(address) must exclude any tokens used in the yield - Controller role - withdraw should return to Controller 22 | - withdraw(uint) - Controller | Vault role - withdraw should always return to vault 23 | - withdrawAll() - Controller | Vault role - withdraw should always return to vault 24 | - balanceOf() 25 | 26 | Where possible, strategies must remain as immutable as possible, instead of updating variables, we update the contract by linking it in the controller 27 | 28 | */ 29 | 30 | contract StrategyYFIGovernance { 31 | using SafeERC20 for IERC20; 32 | using Address for address; 33 | using SafeMath for uint256; 34 | 35 | address public constant want = address(0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e); 36 | address public constant gov = address(0xBa37B002AbaFDd8E89a1995dA52740bbC013D992); 37 | address public constant curve = address(0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51); 38 | address public constant zap = address(0xbBC81d23Ea2c3ec7e56D39296F0cbB648873a5d3); 39 | 40 | address public constant reward = address(0xdF5e0e81Dff6FAF3A7e52BA697820c5e32D806A8); 41 | address public constant usdt = address(0xdAC17F958D2ee523a2206206994597C13D831ec7); 42 | 43 | address public constant uni = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); 44 | address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // used for crv <> weth <> dai route 45 | 46 | uint256 public fee = 500; 47 | uint256 public constant max = 10000; 48 | 49 | address public governance; 50 | address public controller; 51 | address public strategist; 52 | 53 | constructor(address _controller) public { 54 | governance = msg.sender; 55 | strategist = msg.sender; 56 | controller = _controller; 57 | } 58 | 59 | function setFee(uint256 _fee) external { 60 | require(msg.sender == governance, "!governance"); 61 | fee = _fee; 62 | } 63 | 64 | function setStrategist(address _strategist) external { 65 | require(msg.sender == governance, "!governance"); 66 | strategist = _strategist; 67 | } 68 | 69 | function deposit() public { 70 | IERC20(want).safeApprove(gov, 0); 71 | IERC20(want).safeApprove(gov, IERC20(want).balanceOf(address(this))); 72 | IGovernance(gov).stake(IERC20(want).balanceOf(address(this))); 73 | } 74 | 75 | // Controller only function for creating additional rewards from dust 76 | function withdraw(IERC20 _asset) external returns (uint256 balance) { 77 | require(msg.sender == controller, "!controller"); 78 | require(want != address(_asset), "want"); 79 | balance = _asset.balanceOf(address(this)); 80 | _asset.safeTransfer(controller, balance); 81 | } 82 | 83 | // Withdraw partial funds, normally used with a vault withdrawal 84 | function withdraw(uint256 _amount) external { 85 | require(msg.sender == controller, "!controller"); 86 | uint256 _balance = IERC20(want).balanceOf(address(this)); 87 | if (_balance < _amount) { 88 | _amount = _withdrawSome(_amount.sub(_balance)); 89 | _amount = _amount.add(_balance); 90 | } 91 | 92 | uint256 _fee = _amount.mul(fee).div(max); 93 | IERC20(want).safeTransfer(IController(controller).rewards(), _fee); 94 | address _vault = IController(controller).vaults(address(want)); 95 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 96 | 97 | IERC20(want).safeTransfer(_vault, _amount.sub(_fee)); 98 | } 99 | 100 | // Withdraw all funds, normally used when migrating strategies 101 | function withdrawAll() external returns (uint256 balance) { 102 | require(msg.sender == controller, "!controller"); 103 | _withdrawAll(); 104 | balance = IERC20(want).balanceOf(address(this)); 105 | 106 | address _vault = IController(controller).vaults(address(want)); 107 | require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds 108 | IERC20(want).safeTransfer(_vault, balance); 109 | } 110 | 111 | function _withdrawAll() internal { 112 | IGovernance(gov).exit(); 113 | } 114 | 115 | function harvest() public { 116 | require(msg.sender == strategist || msg.sender == governance || msg.sender == tx.origin, "!authorized"); 117 | IGovernance(gov).getReward(); 118 | uint256 _balance = IERC20(reward).balanceOf(address(this)); 119 | if (_balance > 0) { 120 | IERC20(reward).safeApprove(zap, 0); 121 | IERC20(reward).safeApprove(zap, _balance); 122 | Zap(zap).remove_liquidity_one_coin(_balance, 2, 0); 123 | } 124 | _balance = IERC20(usdt).balanceOf(address(this)); 125 | if (_balance > 0) { 126 | IERC20(usdt).safeApprove(uni, 0); 127 | IERC20(usdt).safeApprove(uni, _balance); 128 | 129 | address[] memory path = new address[](3); 130 | path[0] = usdt; 131 | path[1] = weth; 132 | path[2] = want; 133 | 134 | Uni(uni).swapExactTokensForTokens(_balance, uint256(0), path, address(this), now.add(1800)); 135 | } 136 | if (IERC20(want).balanceOf(address(this)) > 0) { 137 | deposit(); 138 | } 139 | } 140 | 141 | function _withdrawSome(uint256 _amount) internal returns (uint256) { 142 | IGovernance(gov).withdraw(_amount); 143 | return _amount; 144 | } 145 | 146 | function balanceOfWant() public view returns (uint256) { 147 | return IERC20(want).balanceOf(address(this)); 148 | } 149 | 150 | function balanceOfYGov() public view returns (uint256) { 151 | return IGovernance(gov).balanceOf(address(this)); 152 | } 153 | 154 | function balanceOf() public view returns (uint256) { 155 | return balanceOfWant().add(balanceOfYGov()); 156 | } 157 | 158 | function voteFor(uint256 _proposal) external { 159 | require(msg.sender == governance, "!governance"); 160 | IGovernance(gov).voteFor(_proposal); 161 | } 162 | 163 | function voteAgainst(uint256 _proposal) external { 164 | require(msg.sender == governance, "!governance"); 165 | IGovernance(gov).voteAgainst(_proposal); 166 | } 167 | 168 | function setGovernance(address _governance) external { 169 | require(msg.sender == governance, "!governance"); 170 | governance = _governance; 171 | } 172 | 173 | function setController(address _controller) external { 174 | require(msg.sender == governance, "!governance"); 175 | controller = _controller; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /contracts/test/Token.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.5.17; 3 | 4 | import "@openzeppelinV2/contracts/token/ERC20/ERC20.sol"; 5 | import "@openzeppelinV2/contracts/token/ERC20/ERC20Detailed.sol"; 6 | 7 | contract Token is ERC20, ERC20Detailed { 8 | constructor() public ERC20Detailed("yearn.finance test token", "TEST", 18) { 9 | _mint(msg.sender, 30000 * 10**18); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/utils/BTCOSMedianizer.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 4 | 5 | import "../../interfaces/maker/OracleSecurityModule.sol"; 6 | 7 | interface UniswapAnchoredView { 8 | function price(string calldata) external view returns (uint256); 9 | } 10 | 11 | contract BTCOSMedianizer { 12 | using SafeMath for uint256; 13 | 14 | mapping(address => bool) public authorized; 15 | address public governance; 16 | 17 | OracleSecurityModule public constant OSM = OracleSecurityModule(0xf185d0682d50819263941e5f4EacC763CC5C6C42); 18 | UniswapAnchoredView public constant MEDIANIZER = UniswapAnchoredView(0x9B8Eb8b3d6e2e0Db36F41455185FEF7049a35CaE); 19 | string public symbol = "BTC"; 20 | 21 | constructor() public { 22 | governance = msg.sender; 23 | } 24 | 25 | function setGovernance(address _governance) external { 26 | require(msg.sender == governance, "!governance"); 27 | governance = _governance; 28 | } 29 | 30 | function setAuthorized(address _authorized) external { 31 | require(msg.sender == governance, "!governance"); 32 | authorized[_authorized] = true; 33 | } 34 | 35 | function revokeAuthorized(address _authorized) external { 36 | require(msg.sender == governance, "!governance"); 37 | authorized[_authorized] = false; 38 | } 39 | 40 | function read() external view returns (uint256 price, bool osm) { 41 | if (authorized[msg.sender] && OSM.bud(address(this)) == 1) { 42 | (bytes32 _val, bool _has) = OSM.peek(); 43 | if (_has) return (uint256(_val), true); 44 | } 45 | return ((MEDIANIZER.price(symbol)).mul(1e12), false); 46 | } 47 | 48 | function foresight() external view returns (uint256 price, bool osm) { 49 | if (authorized[msg.sender] && OSM.bud(address(this)) == 1) { 50 | (bytes32 _val, bool _has) = OSM.peep(); 51 | if (_has) return (uint256(_val), true); 52 | } 53 | return ((MEDIANIZER.price(symbol)).mul(1e12), false); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /contracts/utils/ETHOSMedianizer.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | import "../../interfaces/maker/OracleSecurityModule.sol"; 4 | 5 | interface Medianizer { 6 | function read() external view returns (bytes32); 7 | } 8 | 9 | contract ETHOSMedianizer { 10 | mapping(address => bool) public authorized; 11 | address public governance; 12 | 13 | OracleSecurityModule public constant OSM = OracleSecurityModule(0x81FE72B5A8d1A857d176C3E7d5Bd2679A9B85763); 14 | Medianizer public constant MEDIANIZER = Medianizer(0x729D19f657BD0614b4985Cf1D82531c67569197B); 15 | 16 | constructor() public { 17 | governance = msg.sender; 18 | } 19 | 20 | function setGovernance(address _governance) external { 21 | require(msg.sender == governance, "!governance"); 22 | governance = _governance; 23 | } 24 | 25 | function setAuthorized(address _authorized) external { 26 | require(msg.sender == governance, "!governance"); 27 | authorized[_authorized] = true; 28 | } 29 | 30 | function revokeAuthorized(address _authorized) external { 31 | require(msg.sender == governance, "!governance"); 32 | authorized[_authorized] = false; 33 | } 34 | 35 | function read() external view returns (uint256 price, bool osm) { 36 | if (authorized[msg.sender]) { 37 | if (OSM.bud(address(this)) == 1) { 38 | (bytes32 _val, ) = OSM.peek(); 39 | return (uint256(_val), true); 40 | } 41 | } 42 | return (uint256(MEDIANIZER.read()), false); 43 | } 44 | 45 | function foresight() external view returns (uint256 price, bool osm) { 46 | if (authorized[msg.sender]) { 47 | if (OSM.bud(address(this)) == 1) { 48 | (bytes32 _val, ) = OSM.peep(); 49 | return (uint256(_val), true); 50 | } 51 | } 52 | return (uint256(MEDIANIZER.read()), false); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /contracts/vaults/yVault.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 4 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 5 | import "@openzeppelinV2/contracts/utils/Address.sol"; 6 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 7 | import "@openzeppelinV2/contracts/token/ERC20/ERC20.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/ERC20Detailed.sol"; 9 | import "@openzeppelinV2/contracts/ownership/Ownable.sol"; 10 | 11 | import "../../interfaces/yearn/IController.sol"; 12 | 13 | contract yVault is ERC20, ERC20Detailed { 14 | using SafeERC20 for IERC20; 15 | using Address for address; 16 | using SafeMath for uint256; 17 | 18 | IERC20 public token; 19 | 20 | uint256 public min = 9500; 21 | uint256 public constant max = 10000; 22 | 23 | address public governance; 24 | address public controller; 25 | 26 | constructor(address _token, address _controller) 27 | public 28 | ERC20Detailed( 29 | string(abi.encodePacked("yearn ", ERC20Detailed(_token).name())), 30 | string(abi.encodePacked("y", ERC20Detailed(_token).symbol())), 31 | ERC20Detailed(_token).decimals() 32 | ) 33 | { 34 | token = IERC20(_token); 35 | governance = msg.sender; 36 | controller = _controller; 37 | } 38 | 39 | function balance() public view returns (uint256) { 40 | return token.balanceOf(address(this)).add(IController(controller).balanceOf(address(token))); 41 | } 42 | 43 | function setMin(uint256 _min) external { 44 | require(msg.sender == governance, "!governance"); 45 | min = _min; 46 | } 47 | 48 | function setGovernance(address _governance) public { 49 | require(msg.sender == governance, "!governance"); 50 | governance = _governance; 51 | } 52 | 53 | function setController(address _controller) public { 54 | require(msg.sender == governance, "!governance"); 55 | controller = _controller; 56 | } 57 | 58 | // Custom logic in here for how much the vault allows to be borrowed 59 | // Sets minimum required on-hand to keep small withdrawals cheap 60 | function available() public view returns (uint256) { 61 | return token.balanceOf(address(this)).mul(min).div(max); 62 | } 63 | 64 | function earn() public { 65 | uint256 _bal = available(); 66 | token.safeTransfer(controller, _bal); 67 | IController(controller).earn(address(token), _bal); 68 | } 69 | 70 | function depositAll() external { 71 | deposit(token.balanceOf(msg.sender)); 72 | } 73 | 74 | function deposit(uint256 _amount) public { 75 | uint256 _pool = balance(); 76 | uint256 _before = token.balanceOf(address(this)); 77 | token.safeTransferFrom(msg.sender, address(this), _amount); 78 | uint256 _after = token.balanceOf(address(this)); 79 | _amount = _after.sub(_before); // Additional check for deflationary tokens 80 | uint256 shares = 0; 81 | if (totalSupply() == 0) { 82 | shares = _amount; 83 | } else { 84 | shares = (_amount.mul(totalSupply())).div(_pool); 85 | } 86 | _mint(msg.sender, shares); 87 | } 88 | 89 | function withdrawAll() external { 90 | withdraw(balanceOf(msg.sender)); 91 | } 92 | 93 | // Used to swap any borrowed reserve over the debt limit to liquidate to 'token' 94 | function harvest(address reserve, uint256 amount) external { 95 | require(msg.sender == controller, "!controller"); 96 | require(reserve != address(token), "token"); 97 | IERC20(reserve).safeTransfer(controller, amount); 98 | } 99 | 100 | // No rebalance implementation for lower fees and faster swaps 101 | function withdraw(uint256 _shares) public { 102 | uint256 r = (balance().mul(_shares)).div(totalSupply()); 103 | _burn(msg.sender, _shares); 104 | 105 | // Check balance 106 | uint256 b = token.balanceOf(address(this)); 107 | if (b < r) { 108 | uint256 _withdraw = r.sub(b); 109 | IController(controller).withdraw(address(token), _withdraw); 110 | uint256 _after = token.balanceOf(address(this)); 111 | uint256 _diff = _after.sub(b); 112 | if (_diff < _withdraw) { 113 | r = b.add(_diff); 114 | } 115 | } 116 | 117 | token.safeTransfer(msg.sender, r); 118 | } 119 | 120 | function getPricePerFullShare() public view returns (uint256) { 121 | return balance().mul(1e18).div(totalSupply()); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /contracts/vaults/yWETH.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | import "@openzeppelinV2/contracts/token/ERC20/IERC20.sol"; 4 | import "@openzeppelinV2/contracts/math/SafeMath.sol"; 5 | import "@openzeppelinV2/contracts/utils/Address.sol"; 6 | import "@openzeppelinV2/contracts/token/ERC20/SafeERC20.sol"; 7 | import "@openzeppelinV2/contracts/token/ERC20/ERC20.sol"; 8 | import "@openzeppelinV2/contracts/token/ERC20/ERC20Detailed.sol"; 9 | import "@openzeppelinV2/contracts/ownership/Ownable.sol"; 10 | 11 | import "../../interfaces/weth/WETH.sol"; 12 | import "../../interfaces/yearn/IController.sol"; 13 | 14 | // NOTE: The name of this contract was modified from yVault so as not to conflict with yVault.sol 15 | contract yWETH is ERC20, ERC20Detailed { 16 | using SafeERC20 for IERC20; 17 | using Address for address; 18 | using SafeMath for uint256; 19 | 20 | IERC20 public token; 21 | 22 | uint256 public min = 9990; 23 | uint256 public constant max = 10000; 24 | 25 | address public governance; 26 | address public controller; 27 | 28 | constructor(address _token, address _controller) 29 | public 30 | ERC20Detailed( 31 | string(abi.encodePacked("yearn ", ERC20Detailed(_token).name())), 32 | string(abi.encodePacked("y", ERC20Detailed(_token).symbol())), 33 | ERC20Detailed(_token).decimals() 34 | ) 35 | { 36 | token = IERC20(_token); 37 | governance = msg.sender; 38 | controller = _controller; 39 | } 40 | 41 | function balance() public view returns (uint256) { 42 | return token.balanceOf(address(this)).add(IController(controller).balanceOf(address(token))); 43 | } 44 | 45 | function setMin(uint256 _min) external { 46 | require(msg.sender == governance, "!governance"); 47 | min = _min; 48 | } 49 | 50 | function setGovernance(address _governance) public { 51 | require(msg.sender == governance, "!governance"); 52 | governance = _governance; 53 | } 54 | 55 | function setController(address _controller) public { 56 | require(msg.sender == governance, "!governance"); 57 | controller = _controller; 58 | } 59 | 60 | // Custom logic in here for how much the vault allows to be borrowed 61 | // Sets minimum required on-hand to keep small withdrawals cheap 62 | function available() public view returns (uint256) { 63 | return token.balanceOf(address(this)).mul(min).div(max); 64 | } 65 | 66 | function earn() public { 67 | uint256 _bal = available(); 68 | token.safeTransfer(controller, _bal); 69 | IController(controller).earn(address(token), _bal); 70 | } 71 | 72 | function depositAll() external { 73 | deposit(token.balanceOf(msg.sender)); 74 | } 75 | 76 | function deposit(uint256 _amount) public { 77 | uint256 _pool = balance(); 78 | uint256 _before = token.balanceOf(address(this)); 79 | token.safeTransferFrom(msg.sender, address(this), _amount); 80 | uint256 _after = token.balanceOf(address(this)); 81 | _amount = _after.sub(_before); // Additional check for deflationary tokens 82 | uint256 shares = 0; 83 | if (totalSupply() == 0) { 84 | shares = _amount; 85 | } else { 86 | shares = (_amount.mul(totalSupply())).div(_pool); 87 | } 88 | _mint(msg.sender, shares); 89 | } 90 | 91 | function depositETH() public payable { 92 | uint256 _pool = balance(); 93 | uint256 _before = token.balanceOf(address(this)); 94 | uint256 _amount = msg.value; 95 | WETH(address(token)).deposit.value(_amount)(); 96 | uint256 _after = token.balanceOf(address(this)); 97 | _amount = _after.sub(_before); // Additional check for deflationary tokens 98 | uint256 shares = 0; 99 | if (totalSupply() == 0) { 100 | shares = _amount; 101 | } else { 102 | shares = (_amount.mul(totalSupply())).div(_pool); 103 | } 104 | _mint(msg.sender, shares); 105 | } 106 | 107 | function withdrawAll() external { 108 | withdraw(balanceOf(msg.sender)); 109 | } 110 | 111 | function withdrawAllETH() external { 112 | withdrawETH(balanceOf(msg.sender)); 113 | } 114 | 115 | // Used to swap any borrowed reserve over the debt limit to liquidate to 'token' 116 | function harvest(address reserve, uint256 amount) external { 117 | require(msg.sender == controller, "!controller"); 118 | require(reserve != address(token), "token"); 119 | IERC20(reserve).safeTransfer(controller, amount); 120 | } 121 | 122 | // No rebalance implementation for lower fees and faster swaps 123 | function withdraw(uint256 _shares) public { 124 | uint256 r = (balance().mul(_shares)).div(totalSupply()); 125 | _burn(msg.sender, _shares); 126 | 127 | // Check balance 128 | uint256 b = token.balanceOf(address(this)); 129 | if (b < r) { 130 | uint256 _withdraw = r.sub(b); 131 | IController(controller).withdraw(address(token), _withdraw); 132 | uint256 _after = token.balanceOf(address(this)); 133 | uint256 _diff = _after.sub(b); 134 | if (_diff < _withdraw) { 135 | r = b.add(_diff); 136 | } 137 | } 138 | 139 | token.safeTransfer(msg.sender, r); 140 | } 141 | 142 | // No rebalance implementation for lower fees and faster swaps 143 | function withdrawETH(uint256 _shares) public { 144 | uint256 r = (balance().mul(_shares)).div(totalSupply()); 145 | _burn(msg.sender, _shares); 146 | 147 | // Check balance 148 | uint256 b = token.balanceOf(address(this)); 149 | if (b < r) { 150 | uint256 _withdraw = r.sub(b); 151 | IController(controller).withdraw(address(token), _withdraw); 152 | uint256 _after = token.balanceOf(address(this)); 153 | uint256 _diff = _after.sub(b); 154 | if (_diff < _withdraw) { 155 | r = b.add(_diff); 156 | } 157 | } 158 | 159 | WETH(address(token)).withdraw(r); 160 | address(msg.sender).transfer(r); 161 | } 162 | 163 | function getPricePerFullShare() public view returns (uint256) { 164 | return balance().mul(1e18).div(totalSupply()); 165 | } 166 | 167 | function() external payable { 168 | if (msg.sender != address(token)) { 169 | depositETH(); 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /interfaces/aave/Aave.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | interface Aave { 4 | function borrow( 5 | address _reserve, 6 | uint256 _amount, 7 | uint256 _interestRateModel, 8 | uint16 _referralCode 9 | ) external; 10 | 11 | function setUserUseReserveAsCollateral(address _reserve, bool _useAsCollateral) external; 12 | 13 | function repay( 14 | address _reserve, 15 | uint256 _amount, 16 | address payable _onBehalfOf 17 | ) external payable; 18 | 19 | function getUserAccountData(address _user) 20 | external 21 | view 22 | returns ( 23 | uint256 totalLiquidityETH, 24 | uint256 totalCollateralETH, 25 | uint256 totalBorrowsETH, 26 | uint256 totalFeesETH, 27 | uint256 availableBorrowsETH, 28 | uint256 currentLiquidationThreshold, 29 | uint256 ltv, 30 | uint256 healthFactor 31 | ); 32 | 33 | function getUserReserveData(address _reserve, address _user) 34 | external 35 | view 36 | returns ( 37 | uint256 currentATokenBalance, 38 | uint256 currentBorrowBalance, 39 | uint256 principalBorrowBalance, 40 | uint256 borrowRateMode, 41 | uint256 borrowRate, 42 | uint256 liquidityRate, 43 | uint256 originationFee, 44 | uint256 variableBorrowIndex, 45 | uint256 lastUpdateTimestamp, 46 | bool usageAsCollateralEnabled 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /interfaces/aave/AaveToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | interface AaveToken { 4 | function underlyingAssetAddress() external view returns (address); 5 | } 6 | -------------------------------------------------------------------------------- /interfaces/aave/LendingPoolAddressesProvider.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | interface LendingPoolAddressesProvider { 4 | function getLendingPool() external view returns (address); 5 | 6 | function getLendingPoolCore() external view returns (address); 7 | 8 | function getPriceOracle() external view returns (address); 9 | } 10 | -------------------------------------------------------------------------------- /interfaces/aave/Oracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | interface Oracle { 4 | function getAssetPrice(address reserve) external view returns (uint256); 5 | 6 | function latestAnswer() external view returns (uint256); 7 | } 8 | -------------------------------------------------------------------------------- /interfaces/compound/Token.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface cToken { 6 | function mint(uint256 mintAmount) external returns (uint256); 7 | 8 | function redeem(uint256 redeemTokens) external returns (uint256); 9 | 10 | function redeemUnderlying(uint256 redeemAmount) external returns (uint256); 11 | 12 | function borrow(uint256 borrowAmount) external returns (uint256); 13 | 14 | function repayBorrow(uint256 repayAmount) external returns (uint256); 15 | 16 | function exchangeRateStored() external view returns (uint256); 17 | 18 | function balanceOf(address _owner) external view returns (uint256); 19 | 20 | function underlying() external view returns (address); 21 | } 22 | -------------------------------------------------------------------------------- /interfaces/cream/Controller.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface Creamtroller { 6 | function claimComp(address holder) external; 7 | } 8 | -------------------------------------------------------------------------------- /interfaces/curve/Curve.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface ICurveFi { 6 | function get_virtual_price() external view returns (uint256); 7 | 8 | function add_liquidity( 9 | // EURs 10 | uint256[2] calldata amounts, 11 | uint256 min_mint_amount 12 | ) external; 13 | 14 | function add_liquidity( 15 | // sBTC pool 16 | uint256[3] calldata amounts, 17 | uint256 min_mint_amount 18 | ) external; 19 | 20 | function add_liquidity( 21 | // bUSD pool 22 | uint256[4] calldata amounts, 23 | uint256 min_mint_amount 24 | ) external; 25 | 26 | function remove_liquidity_imbalance(uint256[4] calldata amounts, uint256 max_burn_amount) external; 27 | 28 | function remove_liquidity(uint256 _amount, uint256[4] calldata amounts) external; 29 | 30 | function remove_liquidity_one_coin( 31 | uint256 _token_amount, 32 | int128 i, 33 | uint256 min_amount 34 | ) external; 35 | 36 | function exchange( 37 | int128 from, 38 | int128 to, 39 | uint256 _from_amount, 40 | uint256 _min_to_amount 41 | ) external; 42 | 43 | function exchange_underlying( 44 | int128 from, 45 | int128 to, 46 | uint256 _from_amount, 47 | uint256 _min_to_amount 48 | ) external; 49 | 50 | function balances(int128) external view returns (uint256); 51 | 52 | function get_dy( 53 | int128 from, 54 | int128 to, 55 | uint256 _from_amount 56 | ) external view returns (uint256); 57 | } 58 | 59 | interface Zap { 60 | function remove_liquidity_one_coin( 61 | uint256, 62 | int128, 63 | uint256 64 | ) external; 65 | } 66 | -------------------------------------------------------------------------------- /interfaces/curve/FeeDistribution.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | interface FeeDistribution { 4 | function claim_many(address[20] calldata) external returns (bool); 5 | 6 | function last_token_time() external view returns (uint256); 7 | 8 | function time_cursor() external view returns (uint256); 9 | 10 | function time_cursor_of(address) external view returns (uint256); 11 | } 12 | -------------------------------------------------------------------------------- /interfaces/curve/Gauge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface Gauge { 6 | function deposit(uint256) external; 7 | 8 | function balanceOf(address) external view returns (uint256); 9 | 10 | function withdraw(uint256) external; 11 | 12 | function claim_rewards(address) external; 13 | 14 | function rewarded_token() external returns (address); 15 | 16 | function reward_tokens(uint256) external returns (address); 17 | } 18 | -------------------------------------------------------------------------------- /interfaces/curve/Mintr.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface Mintr { 6 | function mint(address) external; 7 | } 8 | -------------------------------------------------------------------------------- /interfaces/curve/VoteEscrow.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface VoteEscrow { 6 | function create_lock(uint256, uint256) external; 7 | 8 | function increase_amount(uint256) external; 9 | 10 | function withdraw() external; 11 | } 12 | -------------------------------------------------------------------------------- /interfaces/dforce/Rewards.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface dRewards { 6 | function withdraw(uint256) external; 7 | 8 | function getReward() external; 9 | 10 | function stake(uint256) external; 11 | 12 | function balanceOf(address) external view returns (uint256); 13 | 14 | function exit() external; 15 | } 16 | -------------------------------------------------------------------------------- /interfaces/dforce/Token.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface dERC20 { 6 | function mint(address, uint256) external; 7 | 8 | function redeem(address, uint256) external; 9 | 10 | function getTokenBalance(address) external view returns (uint256); 11 | 12 | function getExchangeRate() external view returns (uint256); 13 | } 14 | -------------------------------------------------------------------------------- /interfaces/maker/Maker.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | interface GemLike { 4 | function approve(address, uint256) external; 5 | 6 | function transfer(address, uint256) external; 7 | 8 | function transferFrom( 9 | address, 10 | address, 11 | uint256 12 | ) external; 13 | 14 | function deposit() external payable; 15 | 16 | function withdraw(uint256) external; 17 | } 18 | 19 | interface ManagerLike { 20 | function cdpCan( 21 | address, 22 | uint256, 23 | address 24 | ) external view returns (uint256); 25 | 26 | function ilks(uint256) external view returns (bytes32); 27 | 28 | function owns(uint256) external view returns (address); 29 | 30 | function urns(uint256) external view returns (address); 31 | 32 | function vat() external view returns (address); 33 | 34 | function open(bytes32, address) external returns (uint256); 35 | 36 | function give(uint256, address) external; 37 | 38 | function cdpAllow( 39 | uint256, 40 | address, 41 | uint256 42 | ) external; 43 | 44 | function urnAllow(address, uint256) external; 45 | 46 | function frob( 47 | uint256, 48 | int256, 49 | int256 50 | ) external; 51 | 52 | function flux( 53 | uint256, 54 | address, 55 | uint256 56 | ) external; 57 | 58 | function move( 59 | uint256, 60 | address, 61 | uint256 62 | ) external; 63 | 64 | function exit( 65 | address, 66 | uint256, 67 | address, 68 | uint256 69 | ) external; 70 | 71 | function quit(uint256, address) external; 72 | 73 | function enter(address, uint256) external; 74 | 75 | function shift(uint256, uint256) external; 76 | } 77 | 78 | interface VatLike { 79 | function can(address, address) external view returns (uint256); 80 | 81 | function ilks(bytes32) 82 | external 83 | view 84 | returns ( 85 | uint256, 86 | uint256, 87 | uint256, 88 | uint256, 89 | uint256 90 | ); 91 | 92 | function dai(address) external view returns (uint256); 93 | 94 | function urns(bytes32, address) external view returns (uint256, uint256); 95 | 96 | function frob( 97 | bytes32, 98 | address, 99 | address, 100 | address, 101 | int256, 102 | int256 103 | ) external; 104 | 105 | function hope(address) external; 106 | 107 | function move( 108 | address, 109 | address, 110 | uint256 111 | ) external; 112 | } 113 | 114 | interface GemJoinLike { 115 | function dec() external returns (uint256); 116 | 117 | function gem() external returns (GemLike); 118 | 119 | function join(address, uint256) external payable; 120 | 121 | function exit(address, uint256) external; 122 | } 123 | 124 | interface GNTJoinLike { 125 | function bags(address) external view returns (address); 126 | 127 | function make(address) external returns (address); 128 | } 129 | 130 | interface DaiJoinLike { 131 | function vat() external returns (VatLike); 132 | 133 | function dai() external returns (GemLike); 134 | 135 | function join(address, uint256) external payable; 136 | 137 | function exit(address, uint256) external; 138 | } 139 | 140 | interface HopeLike { 141 | function hope(address) external; 142 | 143 | function nope(address) external; 144 | } 145 | 146 | interface EndLike { 147 | function fix(bytes32) external view returns (uint256); 148 | 149 | function cash(bytes32, uint256) external; 150 | 151 | function free(bytes32) external; 152 | 153 | function pack(uint256) external; 154 | 155 | function skim(bytes32, address) external; 156 | } 157 | 158 | interface JugLike { 159 | function drip(bytes32) external returns (uint256); 160 | } 161 | 162 | interface PotLike { 163 | function pie(address) external view returns (uint256); 164 | 165 | function drip() external returns (uint256); 166 | 167 | function join(uint256) external; 168 | 169 | function exit(uint256) external; 170 | } 171 | 172 | interface SpotLike { 173 | function ilks(bytes32) external view returns (address, uint256); 174 | } 175 | 176 | interface OSMedianizer { 177 | function read() external view returns (uint256, bool); 178 | 179 | function foresight() external view returns (uint256, bool); 180 | } 181 | -------------------------------------------------------------------------------- /interfaces/maker/OracleSecurityModule.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | interface OracleSecurityModule { 4 | function peek() external view returns (bytes32, bool); 5 | 6 | function peep() external view returns (bytes32, bool); 7 | 8 | function bud(address) external view returns (uint256); 9 | } 10 | -------------------------------------------------------------------------------- /interfaces/uniswap/Uni.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface Uni { 6 | function swapExactTokensForTokens( 7 | uint256, 8 | uint256, 9 | address[] calldata, 10 | address, 11 | uint256 12 | ) external; 13 | } 14 | -------------------------------------------------------------------------------- /interfaces/weth/WETH.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | interface WETH { 4 | function deposit() external payable; 5 | 6 | function withdraw(uint256 wad) external; 7 | 8 | event Deposit(address indexed dst, uint256 wad); 9 | event Withdrawal(address indexed src, uint256 wad); 10 | } 11 | -------------------------------------------------------------------------------- /interfaces/yearn/IController.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface IController { 6 | function withdraw(address, uint256) external; 7 | 8 | function balanceOf(address) external view returns (uint256); 9 | 10 | function earn(address, uint256) external; 11 | 12 | function want(address) external view returns (address); 13 | 14 | function rewards() external view returns (address); 15 | 16 | function vaults(address) external view returns (address); 17 | 18 | function strategies(address) external view returns (address); 19 | 20 | function approvedStrategies(address, address) external view returns (bool); 21 | } 22 | -------------------------------------------------------------------------------- /interfaces/yearn/IConverter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface IConverter { 6 | function convert(address) external returns (uint256); 7 | } 8 | -------------------------------------------------------------------------------- /interfaces/yearn/IDelegatedVault.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | interface IDelegatedVault { 4 | function token() external view returns (address); 5 | 6 | function deposit(uint256) external; 7 | 8 | function depositAll() external; 9 | 10 | function withdraw(uint256) external; 11 | 12 | function withdrawAll() external; 13 | 14 | function getPricePerFullShare() external view returns (uint256); 15 | 16 | function claimInsurance() external; 17 | } 18 | -------------------------------------------------------------------------------- /interfaces/yearn/IGovernance.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface IGovernance { 6 | function withdraw(uint256) external; 7 | 8 | function getReward() external; 9 | 10 | function stake(uint256) external; 11 | 12 | function balanceOf(address) external view returns (uint256); 13 | 14 | function exit() external; 15 | 16 | function voteFor(uint256) external; 17 | 18 | function voteAgainst(uint256) external; 19 | } 20 | -------------------------------------------------------------------------------- /interfaces/yearn/IOneSplitAudit.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface IOneSplitAudit { 6 | function swap( 7 | address fromToken, 8 | address destToken, 9 | uint256 amount, 10 | uint256 minReturn, 11 | uint256[] calldata distribution, 12 | uint256 flags 13 | ) external payable returns (uint256 returnAmount); 14 | 15 | function getExpectedReturn( 16 | address fromToken, 17 | address destToken, 18 | uint256 amount, 19 | uint256 parts, 20 | uint256 flags // See constants in IOneSplit.sol 21 | ) external view returns (uint256 returnAmount, uint256[] memory distribution); 22 | } 23 | -------------------------------------------------------------------------------- /interfaces/yearn/IProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface IProxy { 6 | function execute( 7 | address to, 8 | uint256 value, 9 | bytes calldata data 10 | ) external returns (bool, bytes memory); 11 | 12 | function increaseAmount(uint256) external; 13 | } 14 | -------------------------------------------------------------------------------- /interfaces/yearn/IStrategy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface IStrategy { 6 | function want() external view returns (address); 7 | 8 | function deposit() external; 9 | 10 | // NOTE: must exclude any tokens used in the yield 11 | // Controller role - withdraw should return to Controller 12 | function withdraw(address) external; 13 | 14 | // Controller | Vault role - withdraw should always return to Vault 15 | function withdraw(uint256) external; 16 | 17 | function skim() external; 18 | 19 | // Controller | Vault role - withdraw should always return to Vault 20 | function withdrawAll() external returns (uint256); 21 | 22 | function balanceOf() external view returns (uint256); 23 | 24 | function withdrawalFee() external view returns (uint256); 25 | } 26 | -------------------------------------------------------------------------------- /interfaces/yearn/IToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | // NOTE: Basically an alias for Vaults 6 | interface yERC20 { 7 | function deposit(uint256 _amount) external; 8 | 9 | function withdraw(uint256 _amount) external; 10 | 11 | function getPricePerFullShare() external view returns (uint256); 12 | } 13 | -------------------------------------------------------------------------------- /interfaces/yearn/IVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface IVault { 6 | function token() external view returns (address); 7 | 8 | function underlying() external view returns (address); 9 | 10 | function name() external view returns (string memory); 11 | 12 | function symbol() external view returns (string memory); 13 | 14 | function decimals() external view returns (uint8); 15 | 16 | function controller() external view returns (address); 17 | 18 | function governance() external view returns (address); 19 | 20 | function getPricePerFullShare() external view returns (uint256); 21 | 22 | function deposit(uint256) external; 23 | 24 | function depositAll() external; 25 | 26 | function withdraw(uint256) external; 27 | 28 | function withdrawAll() external; 29 | } 30 | -------------------------------------------------------------------------------- /interfaces/yearn/IVoterProxy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface IVoterProxy { 6 | function withdraw( 7 | address _gauge, 8 | address _token, 9 | uint256 _amount 10 | ) external returns (uint256); 11 | 12 | function balanceOf(address _gauge) external view returns (uint256); 13 | 14 | function withdrawAll(address _gauge, address _token) external returns (uint256); 15 | 16 | function deposit(address _gauge, address _token) external; 17 | 18 | function harvest(address _gauge) external; 19 | 20 | function lock() external; 21 | 22 | function claimRewards(address _gauge, address _token) external; 23 | } 24 | -------------------------------------------------------------------------------- /interfaces/yearn/IWrappedVault.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.5.17; 4 | 5 | interface IWrappedVault { 6 | function token() external view returns (address); 7 | 8 | function name() external view returns (string memory); 9 | 10 | function symbol() external view returns (string memory); 11 | 12 | function decimals() external view returns (uint8); 13 | 14 | function governance() external view returns (address); 15 | 16 | function vault() external view returns (address); 17 | 18 | function getPricePerFullShare() external view returns (uint256); 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yearn-protocol", 3 | "devDependencies": { 4 | "@commitlint/cli": "^11.0.0", 5 | "@commitlint/config-conventional": "^11.0.0", 6 | "ethlint": "^1.2.5", 7 | "husky": "^4.3.0", 8 | "prettier": "^2.1.2", 9 | "prettier-plugin-solidity": "^1.0.0-alpha.57", 10 | "pretty-quick": "^3.0.2" 11 | }, 12 | "scripts": { 13 | "lint": "pretty-quick --pattern '**/*.*(sol|json|md)' --verbose", 14 | "lint:check": "prettier --check **/*.sol **/*.json **/*.md", 15 | "lint:fix": "pretty-quick --pattern '**/*.*(sol|json|md)' --verbose", 16 | "lint:fix-staged": "pretty-quick --pattern '**/*.*(sol|json|md)' --staged --verbose" 17 | }, 18 | "husky": { 19 | "hooks": { 20 | "pre-commit": "yarn lint:fix-staged", 21 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | black==19.10b0 2 | eth-brownie>=1.11.7,<2.0.0 3 | -------------------------------------------------------------------------------- /tests/functional/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.fixture 5 | def rewards(a): 6 | yield a[2] 7 | 8 | 9 | @pytest.fixture 10 | def gov(a): 11 | yield a[3] 12 | 13 | 14 | @pytest.fixture 15 | def token(a, Token): 16 | # Must be ERC20 17 | yield a[0].deploy(Token) 18 | 19 | 20 | @pytest.fixture 21 | def controller(a): 22 | yield a[4] 23 | 24 | 25 | @pytest.fixture 26 | def andre(accounts): 27 | return accounts.at("0x2D407dDb06311396fE14D4b49da5F0471447d45C", force=True) 28 | 29 | 30 | @pytest.fixture 31 | def ychad(accounts): 32 | return accounts.at("0xFEB4acf3df3cDEA7399794D0869ef76A6EfAff52", force=True) 33 | 34 | 35 | @pytest.fixture 36 | def binance(accounts): 37 | return accounts.at("0x3f5CE5FBFe3E9af3971dD833D26bA9b5C936f0bE", force=True) 38 | -------------------------------------------------------------------------------- /tests/functional/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | # Just here to disambiguate the test files (can't use same name without a module) 2 | -------------------------------------------------------------------------------- /tests/functional/controllers/test_config.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import brownie 3 | 4 | 5 | def test_controller_deployment(gov, rewards, Controller): 6 | controller = gov.deploy(Controller, rewards) 7 | # Double check all the deployment variable values 8 | assert controller.governance() == gov 9 | assert controller.rewards() == rewards 10 | assert controller.onesplit() == "0x50FDA034C0Ce7a8f7EFDAebDA7Aa7cA21CC1267e" 11 | assert controller.split() == 500 12 | 13 | 14 | @pytest.mark.parametrize( 15 | "getter,setter,val", 16 | [ 17 | ("split", "setSplit", 1000), 18 | ("onesplit", "setOneSplit", None), 19 | ("governance", "setGovernance", None), 20 | ], 21 | ) 22 | def test_controller_setParams(accounts, gov, rewards, getter, setter, val, Controller): 23 | if not val: 24 | # Can't access fixtures, so use None to mean an address literal 25 | val = accounts[1] 26 | 27 | controller = gov.deploy(Controller, rewards) 28 | 29 | # Only governance can set this param 30 | with brownie.reverts("!governance"): 31 | getattr(controller, setter)(val, {"from": accounts[1]}) 32 | getattr(controller, setter)(val, {"from": gov}) 33 | assert getattr(controller, getter)() == val 34 | 35 | # When changing governance contract, make sure previous no longer has access 36 | if getter == "governance": 37 | with brownie.reverts("!governance"): 38 | getattr(controller, setter)(val, {"from": gov}) 39 | -------------------------------------------------------------------------------- /tests/functional/strategies/__init__.py: -------------------------------------------------------------------------------- 1 | # Just here to disambiguate the test files (can't use same name without a module) 2 | -------------------------------------------------------------------------------- /tests/functional/strategies/test_config.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import brownie 3 | 4 | 5 | from brownie import ( 6 | StrategyCreamYFI, 7 | StrategyCurveBTCVoterProxy, 8 | StrategyCurveBUSDVoterProxy, 9 | StrategyCurveEURVoterProxy, 10 | StrategyCurveYVoterProxy, 11 | StrategyCurve3CrvVoterProxy, 12 | StrategyDAICurve, 13 | StrategyDForceUSDC, 14 | StrategyDForceUSDT, 15 | StrategyMKRVaultDAIDelegate, 16 | StrategyTUSDCurve, 17 | StrategyVaultUSDC, 18 | ) 19 | 20 | STRATEGIES = [ 21 | StrategyCreamYFI, 22 | StrategyCurveBTCVoterProxy, 23 | StrategyCurveBUSDVoterProxy, 24 | StrategyCurveEURVoterProxy, 25 | StrategyCurveYVoterProxy, 26 | StrategyCurve3CrvVoterProxy, 27 | StrategyDAICurve, 28 | StrategyDForceUSDC, 29 | StrategyDForceUSDT, 30 | StrategyMKRVaultDAIDelegate, 31 | StrategyTUSDCurve, 32 | StrategyVaultUSDC, 33 | ] 34 | 35 | 36 | @pytest.mark.parametrize("Strategy", STRATEGIES) 37 | def test_strategy_deployment(gov, controller, Strategy): 38 | strategy = gov.deploy(Strategy, controller) 39 | # Double check all the deployment variable values 40 | assert strategy.governance() == gov 41 | assert strategy.controller() == controller 42 | assert strategy.getName() == Strategy._name 43 | 44 | 45 | @pytest.mark.parametrize( 46 | "getter,setter,val", 47 | [ 48 | ("governance", "setGovernance", None), 49 | ("controller", "setController", None), 50 | ("strategist", "setStrategist", None), 51 | ("fee", "setFee", 100), 52 | ("withdrawalFee", "setWithdrawalFee", 100), 53 | ("performanceFee", "setPerformanceFee", 1000), 54 | ], 55 | ) 56 | @pytest.mark.parametrize("Strategy", STRATEGIES) 57 | def test_strategy_setParams(accounts, gov, controller, getter, setter, val, Strategy): 58 | if not val: 59 | # Can't access fixtures, so use None to mean an address literal 60 | val = accounts[1] 61 | 62 | strategy = gov.deploy(Strategy, controller) 63 | 64 | if not hasattr(strategy, getter): 65 | return # Some combinations aren't valid 66 | 67 | # Only governance can set this param 68 | with brownie.reverts(): 69 | getattr(strategy, setter)(val, {"from": accounts[1]}) 70 | getattr(strategy, setter)(val, {"from": gov}) 71 | assert getattr(strategy, getter)() == val 72 | 73 | # When changing governance contract, make sure previous no longer has access 74 | if getter == "governance": 75 | with brownie.reverts("!governance"): 76 | getattr(strategy, setter)(val, {"from": gov}) 77 | -------------------------------------------------------------------------------- /tests/functional/utils/test_oracles.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from brownie import ( 4 | BTCOSMedianizer, 5 | ETHOSMedianizer, 6 | ) 7 | 8 | ORACLES = [BTCOSMedianizer, ETHOSMedianizer] 9 | 10 | 11 | @pytest.mark.parametrize( 12 | "Oracle,osm,medianizer", 13 | [ 14 | ( 15 | BTCOSMedianizer, 16 | "0xf185d0682d50819263941e5f4EacC763CC5C6C42", 17 | "0x9B8Eb8b3d6e2e0Db36F41455185FEF7049a35CaE", 18 | ), 19 | ( 20 | ETHOSMedianizer, 21 | "0x81FE72B5A8d1A857d176C3E7d5Bd2679A9B85763", 22 | "0x729D19f657BD0614b4985Cf1D82531c67569197B", 23 | ), 24 | ], 25 | ) 26 | def test_hardcoded_config(a, Oracle, osm, medianizer): 27 | oracle = a[0].deploy(Oracle) 28 | assert oracle.OSM() == osm 29 | assert oracle.MEDIANIZER() == medianizer 30 | 31 | 32 | @pytest.mark.parametrize("Oracle", ORACLES) 33 | def test_governance(a, gov, Oracle): 34 | oracle = a[0].deploy(Oracle) 35 | assert oracle.governance() == a[0] 36 | oracle.setGovernance(gov) 37 | assert oracle.governance() == gov 38 | 39 | 40 | @pytest.mark.parametrize("Oracle", ORACLES) 41 | def test_whitelist(a, gov, Oracle): 42 | oracle = a[0].deploy(Oracle) 43 | assert not oracle.authorized(gov) 44 | oracle.setAuthorized(gov) 45 | assert oracle.authorized(gov) 46 | oracle.revokeAuthorized(gov) 47 | assert not oracle.authorized(gov) 48 | 49 | 50 | @pytest.mark.parametrize("Oracle", ORACLES) 51 | @pytest.mark.parametrize("func", ["read", "foresight"]) 52 | def test_read(a, Oracle, func): 53 | oracle = a[0].deploy(Oracle) 54 | price, osm = getattr(oracle, func)() 55 | assert price > 0 56 | assert not osm 57 | 58 | 59 | @pytest.mark.xfail 60 | @pytest.mark.parametrize("func", ["read", "foresight"]) 61 | def test_read_bud(a, interface, OSMedianizer, func): 62 | oracle = OSMedianizer.at("0x82c93333e4E295AA17a05B15092159597e823e8a") 63 | osm = interface.OracleSecurityModule(oracle.OSM()) 64 | assert osm.bud(oracle), "kiss first" 65 | reader = a[0] # TODO: someone authorized 66 | assert oracle.authorized(reader) 67 | price, osm = getattr(oracle, func)({"from": reader}) 68 | assert price > 0 69 | assert osm 70 | -------------------------------------------------------------------------------- /tests/functional/vaults/__init__.py: -------------------------------------------------------------------------------- 1 | # Just here to disambiguate the test files (can't use same name without a module) 2 | -------------------------------------------------------------------------------- /tests/functional/vaults/test_config.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import brownie 3 | 4 | from brownie import ( 5 | yVault, 6 | yWETH, 7 | yDelegatedVault, 8 | ) 9 | 10 | VAULTS = [yVault, yWETH, yDelegatedVault] 11 | 12 | 13 | @pytest.mark.parametrize("Vault", VAULTS) 14 | def test_vault_deployment(gov, token, controller, Vault): 15 | vault = gov.deploy(Vault, token, controller) 16 | # Addresses 17 | assert vault.governance() == gov 18 | assert vault.controller() == controller 19 | assert vault.token() == token 20 | # UI Stuff 21 | assert vault.name() == "yearn " + token.name() 22 | assert vault.symbol() == "y" + token.symbol() 23 | assert vault.decimals() == token.decimals() 24 | 25 | 26 | @pytest.mark.parametrize( 27 | "getter,setter,val", 28 | [ 29 | ("min", "setMin", 9000), 30 | ("healthFactor", "setHealthFactor", 100), 31 | ("controller", "setController", None), 32 | ("governance", "setGovernance", None), 33 | ], 34 | ) 35 | @pytest.mark.parametrize("Vault", VAULTS) 36 | def test_vault_setParams(accounts, gov, token, controller, getter, setter, val, Vault): 37 | if not val: 38 | # Can't access fixtures, so use None to mean an address literal 39 | val = accounts[1] 40 | 41 | vault = gov.deploy(Vault, token, controller) 42 | 43 | if not hasattr(vault, getter): 44 | return # Some combinations aren't valid 45 | 46 | # Only governance can set this param 47 | with brownie.reverts("!governance"): 48 | getattr(vault, setter)(val, {"from": accounts[1]}) 49 | getattr(vault, setter)(val, {"from": gov}) 50 | assert getattr(vault, getter)() == val 51 | 52 | # When changing governance contract, make sure previous no longer has access 53 | if getter == "governance": 54 | with brownie.reverts("!governance"): 55 | getattr(vault, setter)(val, {"from": gov}) 56 | --------------------------------------------------------------------------------