-
Notifications
You must be signed in to change notification settings - Fork 98
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into feat/pcvguardian-v3
- Loading branch information
Showing
49 changed files
with
2,003 additions
and
1,131 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
15039959 | ||
15089978 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 { | ||
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
|
||
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 {} | ||
|
||
/////// 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}(); | ||
} | ||
} | ||
} |
Oops, something went wrong.