-
Notifications
You must be signed in to change notification settings - Fork 99
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
DAO vote 2: Incentives, deprecate PSMs, holding PCV deposits, agEUR redeem #922
Changes from all commits
fbad5df
e0befcf
6f56d32
b680a68
83ec87b
426df3a
17f9e5f
b29336f
61e3b82
89c7af1
8914779
1048638
a463428
1172404
576ad46
2a7e69f
99e8fc9
8b9c21f
d103dbe
00fbf62
d4a8350
8a1f13e
eb91028
5418dcd
8eba3dc
1f247cf
3e8f219
5cb431d
92d2a4d
2ca3fdb
7af96a3
1423b6c
e86046d
24f912a
c412101
e4e6a79
d1b8aa3
7cfbedf
42bd13f
573a79c
6cc1cf9
a87c58f
c09390b
8ff2362
3257c55
5d1c81a
ce7f3ba
bcd53a2
c8752ea
b36d84d
16cd275
7b4b7fb
018673a
c06dd03
efe1aa0
e4f57e7
a53546a
2c246fd
7ca0882
d2865bb
8c6b092
2a7d274
69c2559
554772d
d774d7b
641de87
8f0784f
eff85cb
0207ae4
e658a45
6602988
772fcff
6342473
af42cb2
37779fc
cc39769
168f1d8
1120e74
b61b54b
3a2d500
4026cfd
a57d189
a0345e6
b2623f3
eeeae8a
40c8c98
1fd1d8b
6839611
dd3fa37
ee43d55
b89ba4a
f87074b
557d829
ada90b6
b9356f4
0fbcfc4
6e25ba6
3e85fcf
2cc1777
e2fce1f
88d1f50
908e672
e6087a9
d05bb88
fa1814e
932eac9
8060165
86dade8
9da309f
0379984
98eb881
1a83990
651c7d4
10aebf1
5ae93c9
c42f02e
2033b63
fd61897
8a76ba8
0ace7d5
de8bb6b
353f812
5ecdb51
5a5b4b6
1f5713a
d78f1ed
9b27fb1
4bd52bf
9b576bd
b72a2fc
581d9f1
8111c05
32d7e54
029ef38
5f91b60
0bdc2f7
32e805d
a3313c4
cfffff7
6100eae
5f29a06
39f76d1
f66b0f0
7e7e113
0e1fca5
d96810b
8a1e55a
ba920bf
847de09
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 |
---|---|---|
@@ -1 +1 @@ | ||
15039959 | ||
15089978 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
pragma solidity ^0.8.0; | ||
|
||
import "../core/TribeRoles.sol"; | ||
import "../pcv/PCVDeposit.sol"; | ||
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; | ||
|
||
/// @title Delegator PCV Deposit | ||
/// This contract simply holds an ERC20 token, and delegate its voting power | ||
/// to an address. The ERC20 token needs to implement a delegate(address) method. | ||
/// @author eswak | ||
contract DelegatorPCVDeposit is PCVDeposit { | ||
using SafeERC20 for IERC20; | ||
|
||
event DelegateUpdate(address indexed oldDelegate, address indexed newDelegate); | ||
|
||
/// @notice the token that is being used for voting | ||
ERC20Votes public token; | ||
|
||
/// @notice the snapshot delegate for the deposit | ||
address public delegate; | ||
|
||
/// @notice Delegator PCV Deposit constructor | ||
/// @param _core Fei Core for reference | ||
/// @param _token token to custody and delegate with | ||
/// @param _initialDelegate the initial delegate | ||
constructor( | ||
address _core, | ||
address _token, | ||
address _initialDelegate | ||
) CoreRef(_core) { | ||
token = ERC20Votes(_token); | ||
if (_initialDelegate != address(0)) _delegate(_initialDelegate); | ||
} | ||
|
||
/// @notice withdraw tokens from the PCV allocation | ||
/// @param amount of tokens withdrawn | ||
/// @param to the address to send PCV to | ||
function withdraw(address to, uint256 amount) external virtual override onlyPCVController { | ||
IERC20(token).safeTransfer(to, amount); | ||
emit Withdrawal(msg.sender, to, amount); | ||
} | ||
|
||
/// @notice no-op | ||
function deposit() external override {} | ||
|
||
/// @notice returns total balance of PCV in the Deposit | ||
function balance() public view virtual override returns (uint256) { | ||
return token.balanceOf(address(this)); | ||
} | ||
|
||
/// @notice display the related token of the balance reported | ||
function balanceReportedIn() public view override returns (address) { | ||
return address(token); | ||
} | ||
|
||
/// @notice sets the snapshot delegate | ||
/// @dev callable by governor or admin | ||
function setDelegate(address newDelegate) external onlyTribeRole(TribeRoles.METAGOVERNANCE_VOTE_ADMIN) { | ||
_delegate(newDelegate); | ||
} | ||
|
||
function _delegate(address newDelegate) internal { | ||
address oldDelegate = delegate; | ||
delegate = newDelegate; | ||
|
||
token.delegate(delegate); | ||
|
||
emit DelegateUpdate(oldDelegate, newDelegate); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
pragma solidity ^0.8.0; | ||
|
||
import "../core/TribeRoles.sol"; | ||
import "./DelegatorPCVDeposit.sol"; | ||
|
||
interface IAuraLocker { | ||
struct LockedBalance { | ||
uint112 amount; | ||
uint32 unlockTime; | ||
} | ||
struct EarnedData { | ||
address token; | ||
uint256 amount; | ||
} | ||
|
||
function balanceOf(address _user) external view returns (uint256); | ||
|
||
function lock(address _account, uint256 _amount) external; | ||
|
||
function getReward(address _account, bool _stake) external; | ||
|
||
function processExpiredLocks(bool _relock) external; | ||
|
||
function emergencyWithdraw() external; | ||
|
||
function delegates(address account) external view returns (address); | ||
|
||
function getVotes(address account) external view returns (uint256); | ||
|
||
function lockedBalances(address _user) | ||
external | ||
view | ||
returns ( | ||
uint256 total, | ||
uint256 unlockable, | ||
uint256 locked, | ||
LockedBalance[] memory lockData | ||
); | ||
|
||
function claimableRewards(address _account) external view returns (EarnedData[] memory userRewards); | ||
|
||
function notifyRewardAmount(address _rewardsToken, uint256 _reward) external; | ||
} | ||
|
||
interface IAuraMerkleDrop { | ||
function claim( | ||
bytes32[] calldata _proof, | ||
uint256 _amount, | ||
bool _lock | ||
) external returns (bool); | ||
} | ||
|
||
/// @title Vote-locked AURA PCVDeposit | ||
/// This contract is a derivative of the DelegatorPCVDeposit contract, that performs an | ||
/// on-chain delegation. This contract is meant to hold AURA and vlAURA tokens, and allow | ||
/// locking of AURA to vlAURA and renew vlAURA locks, or exit vlAURA locks to get back | ||
/// liquid AURA. This contract can also claim vlAURA rewards. | ||
/// The first version of this contract also allows claiming of the AURA airdrop. | ||
/// @author eswak | ||
contract VlAuraDelegatorPCVDeposit is DelegatorPCVDeposit { | ||
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. Can you update the description of this contract to provide more context? 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. sure |
||
using SafeERC20 for IERC20; | ||
|
||
address public aura; | ||
address public auraLocker; | ||
address public auraMerkleDrop; | ||
|
||
/// @notice constructor | ||
/// @param _core Fei Core for reference | ||
constructor(address _core) | ||
DelegatorPCVDeposit( | ||
_core, | ||
address(0), // token | ||
address(0) // initialDelegate | ||
) | ||
{} | ||
|
||
// At deploy time, Aura Protocol wasn't live yet, so we need to set the | ||
// contract addresses manually, not in the constructor. | ||
function initialize( | ||
address _aura, | ||
address _auraLocker, | ||
address _auraMerkleDrop | ||
) external { | ||
require( | ||
aura == address(0) || | ||
auraLocker == address(0) || | ||
auraMerkleDrop == address(0) || | ||
address(token) == address(0), | ||
"initialized" | ||
); | ||
|
||
aura = _aura; | ||
auraLocker = _auraLocker; | ||
auraMerkleDrop = _auraMerkleDrop; | ||
token = ERC20Votes(_auraLocker); | ||
} | ||
|
||
/// @notice noop, vlAURA can't be transferred. | ||
/// wait for lock expiry, and call withdrawERC20 on AURA. | ||
function withdraw(address, uint256) external override {} | ||
|
||
/// @notice returns the balance of locked + unlocked | ||
function balance() public view virtual override returns (uint256) { | ||
return IERC20(aura).balanceOf(address(this)) + IERC20(auraLocker).balanceOf(address(this)); | ||
} | ||
|
||
/// @notice claim AURA airdrop and vote-lock it for 16 weeks | ||
/// this function is not access controlled & can be called by anyone. | ||
function claimAirdropAndLock(bytes32[] calldata _proof, uint256 _amount) external returns (bool) { | ||
return IAuraMerkleDrop(auraMerkleDrop).claim(_proof, _amount, true); | ||
} | ||
|
||
/// @notice lock AURA held on this contract to vlAURA | ||
function lock() external whenNotPaused onlyTribeRole(TribeRoles.METAGOVERNANCE_TOKEN_STAKING) { | ||
uint256 amount = IERC20(aura).balanceOf(address(this)); | ||
IERC20(aura).safeApprove(auraLocker, amount); | ||
IAuraLocker(auraLocker).lock(address(this), amount); | ||
} | ||
|
||
/// @notice refresh lock after it has expired | ||
function relock() external whenNotPaused onlyTribeRole(TribeRoles.METAGOVERNANCE_TOKEN_STAKING) { | ||
IAuraLocker(auraLocker).processExpiredLocks(true); | ||
} | ||
|
||
/// @notice exit lock after it has expired | ||
function unlock() external whenNotPaused onlyTribeRole(TribeRoles.METAGOVERNANCE_TOKEN_STAKING) { | ||
IAuraLocker(auraLocker).processExpiredLocks(false); | ||
} | ||
|
||
/// @notice emergency withdraw if system is shut down | ||
function emergencyWithdraw() external whenNotPaused onlyTribeRole(TribeRoles.METAGOVERNANCE_TOKEN_STAKING) { | ||
IAuraLocker(auraLocker).emergencyWithdraw(); | ||
} | ||
|
||
/// @notice get rewards & stake them (rewards claiming is permissionless) | ||
function getReward() external { | ||
IAuraLocker(auraLocker).getReward(address(this), true); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
pragma solidity ^0.8.0; | ||
|
||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
import {PCVDeposit} from "./PCVDeposit.sol"; | ||
import {CoreRef} from "../refs/CoreRef.sol"; | ||
import {Constants} from "../Constants.sol"; | ||
|
||
/// @title ERC20HoldingPCVDeposit | ||
/// @notice PCVDeposit that is used to hold ERC20 tokens as a safe harbour. Deposit is a no-op | ||
contract ERC20HoldingPCVDeposit is PCVDeposit { | ||
using SafeERC20 for IERC20; | ||
|
||
/// @notice Token which the balance is reported in | ||
IERC20 immutable token; | ||
|
||
/// @notice Fei ERC20 token address | ||
address private constant FEI = 0x956F47F50A910163D8BF957Cf5846D573E7f87CA; | ||
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. Would prefer that we not inline this and use CoreRef instead 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. This was approved and deployed in this PR: #930 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. Would be nice to use CoreRef, but don't think it warrants making a change now and redeploying |
||
|
||
constructor(address _core, IERC20 _token) CoreRef(_core) { | ||
require(address(_token) != FEI, "FEI not supported"); | ||
token = _token; | ||
} | ||
|
||
/// @notice Empty receive function to receive ETH | ||
receive() external payable {} | ||
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. Should add fallback as well to catch all use cases of sending eth to this contract 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. What use case of sending eth to this contract would a 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. Fallback handles contract calls that don't match any function, receive() only handles transfers |
||
|
||
/////// READ-ONLY Methods ///////////// | ||
|
||
/// @notice returns total balance of PCV in the deposit | ||
function balance() public view override returns (uint256) { | ||
return token.balanceOf(address(this)); | ||
} | ||
|
||
/// @notice returns the resistant balance and FEI in the deposit | ||
function resistantBalanceAndFei() public view override returns (uint256, uint256) { | ||
return (balance(), 0); | ||
} | ||
|
||
/// @notice display the related token of the balance reported | ||
function balanceReportedIn() public view override returns (address) { | ||
return address(token); | ||
} | ||
|
||
/// @notice No-op deposit | ||
function deposit() external override whenNotPaused { | ||
emit Deposit(msg.sender, balance()); | ||
} | ||
|
||
/// @notice Withdraw underlying | ||
/// @param amountUnderlying of tokens withdrawn | ||
/// @param to the address to send PCV to | ||
function withdraw(address to, uint256 amountUnderlying) external override onlyPCVController whenNotPaused { | ||
token.safeTransfer(to, amountUnderlying); | ||
emit Withdrawal(msg.sender, to, amountUnderlying); | ||
} | ||
|
||
/// @notice Wraps all ETH held by the contract to WETH. Permissionless, anyone can call it | ||
function wrapETH() public { | ||
uint256 ethBalance = address(this).balance; | ||
if (ethBalance != 0) { | ||
Constants.WETH.deposit{value: ethBalance}(); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Until I got to the part where the metagovernance role was used, I didn't realize this contract was for that purpose and not just a standard erc20 delegation contract. Should we change the name to reflect that this contract's purpose is to be used for metagovernance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what should change here, a comment or the artifact name ? We use "Delegator" in the name for all contracts that call erc20vote.delegate() and other than that it's a very simple pcv deposit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Artifact name (delegating normal votes vs using for metagovernance is the confusing part)