Skip to content

Commit

Permalink
Merge pull request #634 from fei-protocol/feat/vebal
Browse files Browse the repository at this point in the history
FIP-92: Vote-locking BAL
  • Loading branch information
Joeysantoro authored Apr 20, 2022
2 parents 70e3856 + 336c6c0 commit 36916ee
Show file tree
Hide file tree
Showing 20 changed files with 863 additions and 45 deletions.
19 changes: 14 additions & 5 deletions contracts/metagov/AngleDelegatorPCVDeposit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,23 @@ pragma solidity ^0.8.0;
import "./SnapshotDelegatorPCVDeposit.sol";
import "./utils/VoteEscrowTokenManager.sol";
import "./utils/LiquidityGaugeManager.sol";
import "./utils/OZGovernorVoter.sol";
import "./utils/GovernorVoter.sol";

/// @title ANGLE Token PCV Deposit
/// @author Fei Protocol
contract AngleDelegatorPCVDeposit is
SnapshotDelegatorPCVDeposit,
VoteEscrowTokenManager,
LiquidityGaugeManager,
OZGovernorVoter
GovernorVoter
{
address private constant ANGLE_TOKEN =
0x31429d1856aD1377A8A0079410B297e1a9e214c2;
address private constant ANGLE_VE_TOKEN =
0x0C462Dbb9EC8cD1630f1728B2CFD2769d09f0dd5;
address private constant ANGLE_GAUGE_MANAGER =
0x9aD7e7b0877582E14c17702EecF49018DD6f2367;
bytes32 private constant ANGLE_SNAPSHOT_SPACE =
keccak256("anglegovernance.eth");
bytes32 private constant ANGLE_SNAPSHOT_SPACE = "anglegovernance.eth";

/// @notice ANGLE token manager
/// @param _core Fei Core for reference
Expand All @@ -39,11 +38,21 @@ contract AngleDelegatorPCVDeposit is
4 * 365 * 86400 // vote-escrow time = 4 years
)
LiquidityGaugeManager(ANGLE_GAUGE_MANAGER)
OZGovernorVoter()
GovernorVoter()
{}

/// @notice returns total balance of PCV in the Deposit
function balance() public view override returns (uint256) {
return _totalTokensManaged(); // liquid and vote-escrowed tokens
}

/// @notice returns the token address to be staked in the given gauge
function _tokenStakedInGauge(address gaugeAddress)
internal
view
override
returns (address)
{
return ILiquidityGauge(gaugeAddress).staking_token();
}
}
2 changes: 2 additions & 0 deletions contracts/metagov/SnapshotDelegatorPCVDeposit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ contract SnapshotDelegatorPCVDeposit is PCVDeposit {
external
onlyTribeRole(TribeRoles.METAGOVERNANCE_VOTE_ADMIN)
{
DELEGATE_REGISTRY.clearDelegate(spaceId);
spaceId = _spaceId;
_delegate(delegate);
}

/// @notice sets the snapshot delegate
Expand Down
46 changes: 46 additions & 0 deletions contracts/metagov/VeBalDelegatorPCVDeposit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import "./SnapshotDelegatorPCVDeposit.sol";
import "./utils/VoteEscrowTokenManager.sol";
import "./utils/LiquidityGaugeManager.sol";
import "./utils/GovernorVoter.sol";

/// @title 80-BAL-20-WETH BPT PCV Deposit
/// @author Fei Protocol
contract VeBalDelegatorPCVDeposit is
SnapshotDelegatorPCVDeposit,
VoteEscrowTokenManager,
LiquidityGaugeManager,
GovernorVoter
{
address public constant B_80BAL_20WETH =
0x5c6Ee304399DBdB9C8Ef030aB642B10820DB8F56;
address public constant VE_BAL = 0xC128a9954e6c874eA3d62ce62B468bA073093F25;
address public constant BALANCER_GAUGE_CONTROLLER =
0xC128468b7Ce63eA702C1f104D55A2566b13D3ABD;

/// @notice veBAL token manager
/// @param _core Fei Core for reference
/// @param _initialDelegate initial delegate for snapshot votes
constructor(address _core, address _initialDelegate)
SnapshotDelegatorPCVDeposit(
_core,
IERC20(B_80BAL_20WETH), // token used in reporting
"balancer.eth", // initial snapshot spaceId
_initialDelegate
)
VoteEscrowTokenManager(
IERC20(B_80BAL_20WETH), // liquid token
IVeToken(VE_BAL), // vote-escrowed token
365 * 86400 // vote-escrow time = 1 year
)
LiquidityGaugeManager(BALANCER_GAUGE_CONTROLLER)
GovernorVoter()
{}

/// @notice returns total balance of PCV in the Deposit
function balance() public view override returns (uint256) {
return _totalTokensManaged(); // liquid and vote-escrowed tokens
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,24 @@ pragma solidity ^0.8.0;
import "../../refs/CoreRef.sol";
import "../../core/TribeRoles.sol";

interface IOZGovernor {
interface IMetagovGovernor {
// OpenZeppelin Governor propose signature
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) external returns (uint256 proposalId);

// Governor Bravo propose signature
function propose(
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas,
string memory description
) external returns (uint256 proposalId);

function castVote(uint256 proposalId, uint8 support)
external
returns (uint256 weight);
Expand All @@ -21,21 +31,44 @@ interface IOZGovernor {

/// @title Abstract class to interact with an OZ governor.
/// @author Fei Protocol
abstract contract OZGovernorVoter is CoreRef {
abstract contract GovernorVoter is CoreRef {
// Events
event Proposed(IOZGovernor indexed governor, uint256 proposalId);
event Proposed(IMetagovGovernor indexed governor, uint256 proposalId);
event Voted(
IOZGovernor indexed governor,
IMetagovGovernor indexed governor,
uint256 proposalId,
uint256 weight,
uint8 support
);

/// @notice propose a new proposal on the target governor.
function propose(
IOZGovernor governor,
/// @notice propose a new proposal on the target OZ governor.
function proposeOZ(
IMetagovGovernor governor,
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
)
external
onlyTribeRole(TribeRoles.METAGOVERNANCE_VOTE_ADMIN)
returns (uint256)
{
uint256 proposalId = governor.propose(
targets,
values,
calldatas,
description
);
emit Proposed(governor, proposalId);
return proposalId;
}

/// @notice propose a new proposal on the target Bravo governor.
function proposeBravo(
IMetagovGovernor governor,
address[] memory targets,
uint256[] memory values,
string[] memory signatures,
bytes[] memory calldatas,
string memory description
)
Expand All @@ -46,6 +79,7 @@ abstract contract OZGovernorVoter is CoreRef {
uint256 proposalId = governor.propose(
targets,
values,
signatures,
calldatas,
description
);
Expand All @@ -55,7 +89,7 @@ abstract contract OZGovernorVoter is CoreRef {

/// @notice cast a vote on a given proposal on the target governor.
function castVote(
IOZGovernor governor,
IMetagovGovernor governor,
uint256 proposalId,
uint8 support
)
Expand Down
24 changes: 22 additions & 2 deletions contracts/metagov/utils/LiquidityGaugeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ interface ILiquidityGauge {

function balanceOf(address) external view returns (uint256);

// curve & balancer use lp_token()
function lp_token() external view returns (address);

// angle use staking_token()
function staking_token() external view returns (address);

function reward_tokens(uint256 i) external view returns (address token);
Expand Down Expand Up @@ -80,6 +84,16 @@ abstract contract LiquidityGaugeManager is CoreRef {
emit GaugeControllerChanged(oldController, gaugeController);
}

/// @notice returns the token address to be staked in the given gauge
function _tokenStakedInGauge(address gaugeAddress)
internal
view
virtual
returns (address)
{
return ILiquidityGauge(gaugeAddress).lp_token();
}

/// @notice Set gauge for a given token.
/// @param token the token address to stake in gauge
/// @param gaugeAddress the address of the gauge where to stake token
Expand All @@ -88,7 +102,7 @@ abstract contract LiquidityGaugeManager is CoreRef {
onlyTribeRole(TribeRoles.METAGOVERNANCE_GAUGE_ADMIN)
{
require(
ILiquidityGauge(gaugeAddress).staking_token() == token,
_tokenStakedInGauge(gaugeAddress) == token,
"LiquidityGaugeManager: wrong gauge for token"
);
require(
Expand Down Expand Up @@ -181,7 +195,13 @@ abstract contract LiquidityGaugeManager is CoreRef {

/// @notice Claim rewards associated to a gauge where this contract stakes
/// tokens.
function claimGaugeRewards(address gaugeAddress) public whenNotPaused {
function claimGaugeRewards(address token) public whenNotPaused {
address gaugeAddress = tokenToGauge[token];
require(
gaugeAddress != address(0),
"LiquidityGaugeManager: token has no gauge configured"
);

uint256 nTokens = ILiquidityGauge(gaugeAddress).reward_count();
address[] memory tokens = new address[](nTokens);
uint256[] memory amounts = new uint256[](nTokens);
Expand Down
4 changes: 4 additions & 0 deletions contracts/metagov/utils/VoteEscrowTokenManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ interface IVeToken {
function locked__end(address) external view returns (uint256);

function checkpoint() external;

function commit_smart_wallet_checker(address) external;

function apply_smart_wallet_checker() external;
}

/// @title Vote-escrowed Token Manager
Expand Down
9 changes: 9 additions & 0 deletions contracts/mock/MockGovernorVoter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.4;

import "./MockCoreRef.sol";
import "../metagov/utils/GovernorVoter.sol";

contract MockGovernorVoter is GovernorVoter, MockCoreRef {
constructor(address core) MockCoreRef(core) GovernorVoter() {}
}
9 changes: 0 additions & 9 deletions contracts/mock/MockOZGovernorVoter.sol

This file was deleted.

57 changes: 57 additions & 0 deletions contracts/pcv/angle/AngleGaugeLens.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.10;

import "../../metagov/utils/LiquidityGaugeManager.sol";
import "../IPCVDepositBalances.sol";

/// @title AngleGaugeLens
/// @author Fei Protocol
/// @notice a contract to read tokens held in a gauge.
/// Angle has a small modification in their Curve fork : they name a
/// variable staking_token() instead of lp_token() as in the original Curve code.
contract AngleGaugeLens is IPCVDepositBalances {
/// @notice FEI token address
address private constant FEI = 0x956F47F50A910163D8BF957Cf5846D573E7f87CA;

/// @notice the gauge inspected
address public immutable gaugeAddress;

/// @notice the address of the contract staking in the gauge
address public immutable stakerAddress;

/// @notice the token the lens reports balances in
address public immutable override balanceReportedIn;

constructor(address _gaugeAddress, address _stakerAddress) {
gaugeAddress = _gaugeAddress;
stakerAddress = _stakerAddress;
balanceReportedIn = ILiquidityGauge(_gaugeAddress).staking_token();
}

/// @notice returns the amount of tokens staked by stakerAddress in
/// the gauge gaugeAddress.
function balance() public view override returns (uint256) {
return ILiquidityGauge(gaugeAddress).balanceOf(stakerAddress);
}

/// @notice returns the amount of tokens staked by stakerAddress in
/// the gauge gaugeAddress.
/// In the case where an LP token between XYZ and FEI is staked in
/// the gauge, this lens reports the amount of LP tokens staked, not the
/// underlying amounts of XYZ and FEI tokens held within the LP tokens.
/// This lens can be coupled with another lens in order to compute the
/// underlying amounts of FEI and XYZ held inside the LP tokens.
function resistantBalanceAndFei()
public
view
override
returns (uint256, uint256)
{
uint256 stakedBalance = balance();
if (balanceReportedIn == FEI) {
return (stakedBalance, stakedBalance);
} else {
return (stakedBalance, 0);
}
}
}
Loading

0 comments on commit 36916ee

Please sign in to comment.