-
Notifications
You must be signed in to change notification settings - Fork 3
16slim/ve angle #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 35 commits
fe6844d
7a07c62
0be3adf
e35f803
c863f46
3676450
64c75d7
649c309
f189c10
f389c7e
46571e0
d0140f0
a9ff880
4096176
31ff372
9db840c
88f18fd
618637b
15ce260
de4174d
5072669
06b0dae
aa72c39
2435759
a6aa5d6
cdbe6c4
915b8a4
dfc900d
16f7e42
432873a
8c607ed
ada3a34
8fb4d14
d7571d9
f4e0c73
8c63ff1
528a4c4
8ba167e
c848384
542f347
748466f
5c3038e
ea48325
cf81296
6937fad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,181 @@ | ||
| // SPDX-License-Identifier: AGPL-3.0 | ||
| pragma solidity ^0.8.12; | ||
| pragma experimental ABIEncoderV2; | ||
|
|
||
| import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
| import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
| import {Address} from "@openzeppelin/contracts/utils/Address.sol"; | ||
|
|
||
| import {YearnAngleVoter} from "./YearnAngleVoter.sol"; | ||
|
|
||
| import "./interfaces/curve/ICurve.sol"; | ||
| import "./interfaces/Angle/IStableMaster.sol"; | ||
| import "./interfaces/Angle/IAngleGauge.sol"; | ||
| import "./interfaces/Uniswap/IUniV2.sol"; | ||
|
|
||
| library SafeVoter { | ||
| function safeExecute( | ||
| YearnAngleVoter voter, | ||
| address to, | ||
| uint256 value, | ||
| bytes memory data | ||
| ) internal { | ||
| (bool success, ) = voter.execute(to, value, data); | ||
| require(success); | ||
| } | ||
| } | ||
|
|
||
| contract AngleStrategyVoterProxy { | ||
| using SafeVoter for YearnAngleVoter; | ||
| using SafeERC20 for IERC20; | ||
| using Address for address; | ||
|
|
||
| YearnAngleVoter public yearnAngleVoter; | ||
| address public constant angleToken = address(0x31429d1856aD1377A8A0079410B297e1a9e214c2); | ||
|
|
||
| // gauge => strategies | ||
| mapping(address => address) public strategies; | ||
| mapping(address => bool) public voters; | ||
| address public governance; | ||
|
|
||
| constructor(address _voter) public { | ||
| governance = address(0xFEB4acf3df3cDEA7399794D0869ef76A6EfAff52); | ||
| yearnAngleVoter = YearnAngleVoter(_voter); | ||
| } | ||
|
|
||
| function setGovernance(address _governance) external { | ||
| require(msg.sender == governance, "!governance"); | ||
| governance = _governance; | ||
| } | ||
|
|
||
| function approveStrategy(address _gauge, address _strategy) external { | ||
| require(msg.sender == governance, "!governance"); | ||
| strategies[_gauge] = _strategy; | ||
| } | ||
|
|
||
| function revokeStrategy(address _gauge) external { | ||
| require(msg.sender == governance, "!governance"); | ||
| strategies[_gauge] = address(0); | ||
| } | ||
|
|
||
| function approveVoter(address _voter) external { | ||
| require(msg.sender == governance, "!governance"); | ||
| voters[_voter] = true; | ||
| } | ||
|
|
||
| function revokeVoter(address _voter) external { | ||
| require(msg.sender == governance, "!governance"); | ||
| voters[_voter] = false; | ||
| } | ||
|
|
||
| function lock(uint256 amount, uint256 unlockTime) external { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you see any harm adjusting this function to have zero arguments? My thought is that we always max lock the full balance. There is no access control on this function, what if someone locks for less than max? Do we care?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I included a msg.sender check to it too to check for governance, I normally prefer to leave things as flexible as possible just in case. Wdyt? |
||
| if (amount > 0) { | ||
| IERC20(angleToken).transfer(address(yearnAngleVoter), amount); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to define the expected behavior of this function a bit better. Ideally we have an open Seems this design is expecting the proxy to carry an ANGLE token balance. Usually all tokens kept will be in the voter, not proxy. Which means this logic will probably get skipped.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had the flow to leave the tokens in voter proxy rather than voter as I wanted to the voter to be a mere interface for veAngle. But as I think it's standard design, I changed it to Angle rewards be kept in the voter instead |
||
| yearnAngleVoter.createLock(amount, unlockTime); | ||
| } | ||
| } | ||
|
|
||
| function increaseAmount(uint256 amount) external { | ||
| if (amount > 0) { | ||
| IERC20(angleToken).transfer(address(yearnAngleVoter), amount); | ||
| yearnAngleVoter.increaseAmount(amount); | ||
| } | ||
| } | ||
|
|
||
| function vote(address _gauge, uint256 _amount) public { | ||
| require(voters[msg.sender], "!voter"); | ||
| yearnAngleVoter.safeExecute(_gauge, 0, abi.encodeWithSignature("vote_for_gauge_weights(address,uint256)", _gauge, _amount)); | ||
| } | ||
|
|
||
| function withdraw( | ||
| address _gauge, | ||
| address _token, | ||
| uint256 _amount | ||
| ) public returns (uint256) { | ||
| require(strategies[_gauge] == msg.sender, "!strategy"); | ||
| uint256 _balance = IERC20(_token).balanceOf(address(yearnAngleVoter)); | ||
| yearnAngleVoter.safeExecute(_gauge, 0, abi.encodeWithSignature("withdraw(uint256)", _amount)); | ||
| _balance = IERC20(_token).balanceOf(address(yearnAngleVoter)) - _balance; | ||
| yearnAngleVoter.safeExecute(_token, 0, abi.encodeWithSignature("transfer(address,uint256)", msg.sender, _balance)); | ||
|
charlesndalton marked this conversation as resolved.
|
||
| return _balance; | ||
| } | ||
|
|
||
| function withdrawFromStableMaster(address stableMaster, uint256 amount, | ||
| address poolManager, address token, address gauge) external { | ||
|
16slim marked this conversation as resolved.
|
||
| require(strategies[gauge] == msg.sender, "!strategy"); | ||
|
|
||
| IERC20(token).safeTransfer(address(yearnAngleVoter), amount); | ||
|
|
||
| yearnAngleVoter.safeExecute(stableMaster, 0, abi.encodeWithSignature( | ||
| "withdraw(uint256,address,address,address)", | ||
| amount, | ||
| address(yearnAngleVoter), | ||
| msg.sender, | ||
| poolManager | ||
| )); | ||
| } | ||
|
|
||
| function balanceOfStakedSanToken(address _gauge) public view returns (uint256) { | ||
| return IERC20(_gauge).balanceOf(address(yearnAngleVoter)); | ||
| } | ||
|
|
||
| function withdrawAll(address _gauge, address _token) external returns (uint256) { | ||
|
charlesndalton marked this conversation as resolved.
|
||
| require(strategies[_gauge] == msg.sender, "!strategy"); | ||
| return withdraw(_gauge, _token, balanceOfStakedSanToken(_gauge)); | ||
| } | ||
|
|
||
| function stake(address gauge, uint256 amount, address token) external { | ||
| require(strategies[gauge] == msg.sender, "!strategy"); | ||
|
|
||
| _checkAllowance(token, gauge, amount); | ||
|
|
||
| yearnAngleVoter.safeExecute(gauge, 0, abi.encodeWithSignature( | ||
| "deposit(uint256)", | ||
| amount | ||
| )); | ||
| } | ||
|
|
||
| function depositToStableMaster(address stableMaster, uint256 amount, | ||
| address poolManager, address token, address gauge) external { | ||
| require(strategies[gauge] == msg.sender, "!strategy"); | ||
|
|
||
| IERC20(token).safeTransfer(address(yearnAngleVoter), amount); | ||
|
|
||
| _checkAllowance(token, stableMaster, amount); | ||
|
|
||
| yearnAngleVoter.safeExecute(stableMaster, 0, abi.encodeWithSignature( | ||
| "deposit(uint256,address,address)", | ||
| amount, | ||
| address(yearnAngleVoter), | ||
| poolManager | ||
| )); | ||
| } | ||
|
|
||
| function claimRewards(address _gauge) external { | ||
| require(strategies[_gauge] == msg.sender, "!strategy"); | ||
| yearnAngleVoter.safeExecute( | ||
| _gauge, | ||
| 0, | ||
| abi.encodeWithSelector( | ||
| IAngleGauge.claim_rewards.selector | ||
| ) | ||
| ); | ||
| address _token = address(angleToken); | ||
| yearnAngleVoter.safeExecute(_token, 0, abi.encodeWithSignature("transfer(address,uint256)", msg.sender, IERC20(_token).balanceOf(address(yearnAngleVoter)))); | ||
| } | ||
|
|
||
| function balanceOfSanToken(address sanToken) public view returns (uint256) { | ||
| return IERC20(sanToken).balanceOf(address(yearnAngleVoter)); | ||
| } | ||
|
|
||
| function _checkAllowance( | ||
| address _token, | ||
| address _contract, | ||
| uint256 _amount | ||
| ) internal { | ||
| if (IERC20(_token).allowance(address(yearnAngleVoter), _contract) < _amount) { | ||
| yearnAngleVoter.safeExecute(_token, 0, abi.encodeWithSignature("approve(address,uint256)", _contract, 0)); | ||
| yearnAngleVoter.safeExecute(_token, 0, abi.encodeWithSignature("approve(address,uint256)", _contract, _amount)); | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.