mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
feat: Adapt reward tokens to Curve Treasury.
This commit is contained in:
parent
d178cc1cea
commit
8d52a520b0
|
@ -3,4 +3,6 @@ pragma solidity 0.6.12;
|
|||
|
||||
interface ICurveFeeDistributor {
|
||||
function claim() external;
|
||||
|
||||
function token() external view returns (address);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.12;
|
||||
|
||||
interface ICurveGaugeController {
|
||||
function vote_for_gauge_weights(address _gauge_addr, uint256 _user_weight) external;
|
||||
|
||||
function token() external view returns (address);
|
||||
}
|
102
contracts/adapters/interfaces/curve/ICurveTreasury.sol
Normal file
102
contracts/adapters/interfaces/curve/ICurveTreasury.sol
Normal file
|
@ -0,0 +1,102 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.12;
|
||||
|
||||
/**
|
||||
* @title Curve Treasury that holds Curve LP and Gauge tokens
|
||||
* @notice The treasury holds Curve assets like LP or Gauge tokens and can lock veCRV for boosting Curve yields
|
||||
* @author Aave
|
||||
*/
|
||||
interface ICurveTreasury {
|
||||
/**
|
||||
* @dev Deposit Curve LP or Gauge LP tokens into the treasury from a whitelisted entity
|
||||
* @param token Curve LP or Gauge LP token address
|
||||
* @param amount Amount of tokens to deposit in base unit
|
||||
* @param useGauge Flag to determine if the deposit should be staked
|
||||
*/
|
||||
function deposit(
|
||||
address token,
|
||||
uint256 amount,
|
||||
bool useGauge
|
||||
) external;
|
||||
|
||||
/**
|
||||
* @dev Withdraw Curve LP or Gauge LP tokens into the treasury from a whitelisted entity
|
||||
* @param token Curve LP or Gauge LP token address
|
||||
* @param amount Amount of tokens to withdraw in base unit
|
||||
* @param useGauge Flag to determine if the withdraw should be unstaked
|
||||
*/
|
||||
function withdraw(
|
||||
address token,
|
||||
uint256 amount,
|
||||
bool useGauge
|
||||
) external;
|
||||
|
||||
/**
|
||||
* @dev Register entities to enable or disable deposits and withdraws from the treasury
|
||||
* @param entities Entities addresses list
|
||||
* @param tokens Curve LP Token addresses list
|
||||
* @param gauges Curve Gauge Staking tokens list, use zero address to disable staking
|
||||
* @param whitelisted Flag to determine if the entity should be enabled or disabled
|
||||
*/
|
||||
function setWhitelist(
|
||||
address[] calldata entities,
|
||||
address[] calldata tokens,
|
||||
address[] calldata gauges,
|
||||
bool[] memory whitelisted
|
||||
) external;
|
||||
|
||||
/**
|
||||
* @dev Claim all available rewards from Curve Gauge Staking contract
|
||||
* @param gaugeUnderlyingToken The gauge underlying stake token to claim rewards
|
||||
*/
|
||||
function claimGaugeRewards(address gaugeUnderlyingToken) external;
|
||||
|
||||
/**
|
||||
* @dev Claim Curve Distributor fees and send to protocol treasury
|
||||
*/
|
||||
function claimCurveDistributorFees() external;
|
||||
|
||||
/**
|
||||
* @dev Lock the CRV at the veCRV contract up to 4 years to boost rewards from Curve
|
||||
* @param amount Amount of CRV to lock inside the Lock Contract
|
||||
* @param unlockTime Time where the CRV will be unlocked, maximum 4 years
|
||||
*/
|
||||
function lockCrv(uint256 amount, uint256 unlockTime) external;
|
||||
|
||||
/**
|
||||
* @dev Withdraw the unlocked CRV at the veCRV contract after the time lock
|
||||
*/
|
||||
function unlockCrv() external;
|
||||
|
||||
/**
|
||||
* @dev Increase the CRV amount inside the veCRV contract
|
||||
* @param amount The amount of CRV token to add to the current time lock
|
||||
*/
|
||||
function increaseLockedCrv(uint256 amount) external;
|
||||
|
||||
/**
|
||||
* @dev Extend the CRV time lock inside the veCRV ontract
|
||||
* @param unlockTime Next time where the CRV will be unlocked, maximum 4 years
|
||||
*/
|
||||
function increaseUnlockTimeCrv(uint256 unlockTime) external;
|
||||
|
||||
/** Owner methods related with Gauge Controller Voting contract */
|
||||
|
||||
/**
|
||||
* @dev Vote to set the gauge weights using veCRV
|
||||
* @param gauge Gauge address to set the voting weight
|
||||
* @param weight Percentage of voting with two decimal points, 100% equals 10000
|
||||
*/
|
||||
function voteForGaugeWeights(address gauge, uint256 weight) external;
|
||||
|
||||
/**
|
||||
* @dev Get the current owner of this contract
|
||||
*/
|
||||
function owner() external view returns (address);
|
||||
|
||||
/**
|
||||
* @dev Transfers ownership of the contract to a new account (`newOwner`).
|
||||
* @param newOwner addess of the new owner
|
||||
*/
|
||||
function transferOwnership(address newOwner) external;
|
||||
}
|
|
@ -1,184 +0,0 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.12;
|
||||
|
||||
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
import {ICurveGauge, ICurveGaugeView} from '../interfaces/curve/ICurveGauge.sol';
|
||||
import {IVotingEscrow} from '../interfaces/curve/IVotingEscrow.sol';
|
||||
import {
|
||||
VersionedInitializable
|
||||
} from '../../protocol/libraries/aave-upgradeability/VersionedInitializable.sol';
|
||||
import {ICurveFeeDistributor} from '../interfaces/curve/ICurveFeeDistributor.sol';
|
||||
|
||||
/**
|
||||
* @title Curve Treasury that holds Curve LP and Gauge tokens
|
||||
* @notice The treasury holds Curve assets like LP or Gauge tokens and can lock veCRV for boosting Curve yields
|
||||
* @author Aave
|
||||
*/
|
||||
contract CurveTreasury is VersionedInitializable {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
address immutable VOTING_ESCROW;
|
||||
address immutable CRV_TOKEN;
|
||||
address immutable FEE_DISTRIBUTOR;
|
||||
address private _owner;
|
||||
|
||||
uint256 public constant TREASURY_REVISION = 0x1;
|
||||
|
||||
mapping(address => mapping(address => bool)) internal _entityTokenWhitelist;
|
||||
mapping(address => mapping(address => address)) internal _entityTokenGauge;
|
||||
|
||||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
||||
|
||||
constructor(
|
||||
address _votingEscrow,
|
||||
address _crvToken,
|
||||
address _curveFeeDistributor
|
||||
) public {
|
||||
VOTING_ESCROW = _votingEscrow;
|
||||
CRV_TOKEN = _crvToken;
|
||||
FEE_DISTRIBUTOR = _curveFeeDistributor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Throws if called by any account other than the owner.
|
||||
*/
|
||||
modifier onlyOwner() {
|
||||
require(_owner == msg.sender, 'Ownable: caller is not the owner');
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyWhitelistedEntity(address token) {
|
||||
require(_entityTokenWhitelist[msg.sender][token] == true, 'ENTITY_NOT_WHITELISTED');
|
||||
_;
|
||||
}
|
||||
|
||||
function initialize(
|
||||
address[] calldata entities,
|
||||
address[] calldata tokens,
|
||||
address[] calldata gauges,
|
||||
address owner
|
||||
) external virtual initializer {
|
||||
_owner = owner;
|
||||
bool[] memory whitelisted = new bool[](entities.length);
|
||||
_setWhitelist(entities, tokens, gauges, whitelisted);
|
||||
}
|
||||
|
||||
function getRevision() internal pure virtual override returns (uint256) {
|
||||
return TREASURY_REVISION;
|
||||
}
|
||||
|
||||
function deposit(
|
||||
address token,
|
||||
uint256 amount,
|
||||
bool useGauge
|
||||
) external onlyWhitelistedEntity(token) {
|
||||
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
|
||||
|
||||
if (useGauge && _entityTokenGauge[msg.sender][token] != address(0)) {
|
||||
stakeGauge(_entityTokenGauge[msg.sender][token], amount);
|
||||
}
|
||||
}
|
||||
|
||||
function withdraw(
|
||||
address token,
|
||||
uint256 amount,
|
||||
bool useGauge
|
||||
) external onlyWhitelistedEntity(token) {
|
||||
if (useGauge && _entityTokenGauge[msg.sender][token] != address(0)) {
|
||||
unstakeGauge(_entityTokenGauge[msg.sender][token], amount);
|
||||
}
|
||||
IERC20(token).safeTransfer(msg.sender, amount);
|
||||
}
|
||||
|
||||
function stakeGauge(address gauge, uint256 amount) internal {
|
||||
ICurveGauge(gauge).deposit(amount);
|
||||
}
|
||||
|
||||
function unstakeGauge(address gauge, uint256 amount) internal {
|
||||
ICurveGauge(gauge).withdraw(amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the address of the current owner.
|
||||
*/
|
||||
function owner() external view returns (address) {
|
||||
return _owner;
|
||||
}
|
||||
|
||||
/** Owner methods */
|
||||
function approve(
|
||||
address token,
|
||||
address to,
|
||||
uint256 amount
|
||||
) external onlyOwner {
|
||||
IERC20(token).safeApprove(to, amount);
|
||||
}
|
||||
|
||||
function transferFrom(
|
||||
address token,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
) external onlyOwner {
|
||||
IERC20(token).safeTransferFrom(from, to, amount);
|
||||
}
|
||||
|
||||
function setWhitelist(
|
||||
address[] calldata entities,
|
||||
address[] calldata tokens,
|
||||
address[] calldata gauges,
|
||||
bool[] memory whitelisted
|
||||
) external onlyOwner {
|
||||
_setWhitelist(entities, tokens, gauges, whitelisted);
|
||||
}
|
||||
|
||||
function _setWhitelist(
|
||||
address[] calldata entities,
|
||||
address[] calldata tokens,
|
||||
address[] calldata gauges,
|
||||
bool[] memory whitelisted
|
||||
) internal {
|
||||
for (uint256 e; e < entities.length; e++) {
|
||||
_entityTokenWhitelist[entities[e]][tokens[e]] = whitelisted[e];
|
||||
IERC20(tokens[e]).safeApprove(entities[e], type(uint256).max);
|
||||
if (gauges[e] != address(0)) {
|
||||
IERC20(tokens[e]).safeApprove(gauges[e], type(uint256).max);
|
||||
_entityTokenGauge[entities[e]][tokens[e]] = gauges[e];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function claimCurveFees() external onlyOwner {
|
||||
ICurveFeeDistributor(FEE_DISTRIBUTOR).claim();
|
||||
}
|
||||
|
||||
/** Owner methods related with veCRV to interact with Voting Escrow Curve contract */
|
||||
function lockCrv(uint256 amount, uint256 unlockTime) external onlyOwner {
|
||||
IERC20(CRV_TOKEN).safeApprove(VOTING_ESCROW, amount);
|
||||
IVotingEscrow(VOTING_ESCROW).create_lock(amount, unlockTime);
|
||||
}
|
||||
|
||||
function unlockCrv(uint256 amount, uint256 unlockTime) external onlyOwner {
|
||||
IVotingEscrow(VOTING_ESCROW).withdraw();
|
||||
}
|
||||
|
||||
function increaseLockedCrv(uint256 amount) external onlyOwner {
|
||||
IERC20(CRV_TOKEN).safeApprove(VOTING_ESCROW, amount);
|
||||
IVotingEscrow(VOTING_ESCROW).increase_amount(amount);
|
||||
}
|
||||
|
||||
function increaseUnlockTimeCrv(uint256 unlockTime) external onlyOwner {
|
||||
IVotingEscrow(VOTING_ESCROW).increase_unlock_time(unlockTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Transfers ownership of the contract to a new account (`newOwner`).
|
||||
* Can only be called by the current owner.
|
||||
*/
|
||||
function transferOwnership(address newOwner) external onlyOwner {
|
||||
require(newOwner != address(0), 'Ownable: new owner is the zero address');
|
||||
emit OwnershipTransferred(_owner, newOwner);
|
||||
_owner = newOwner;
|
||||
}
|
||||
}
|
|
@ -1,13 +1,14 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.12;
|
||||
|
||||
import {ILendingPool} from '../../interfaces/ILendingPool.sol';
|
||||
import {RewardsAwareAToken} from '../../protocol/tokenization/RewardsAwareAToken.sol';
|
||||
import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
import {ICurveMinter} from '../interfaces/curve/ICurveMinter.sol';
|
||||
import {ICurveGauge, ICurveGaugeView} from '../interfaces/curve/ICurveGauge.sol';
|
||||
import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol';
|
||||
import {ILendingPool} from '../../../interfaces/ILendingPool.sol';
|
||||
import {RewardsAwareAToken} from '../../../protocol/tokenization/RewardsAwareAToken.sol';
|
||||
import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
import {ICurveMinter} from '../../interfaces/curve/ICurveMinter.sol';
|
||||
import {ICurveGauge, ICurveGaugeView} from '../../interfaces/curve/ICurveGauge.sol';
|
||||
import {IAaveIncentivesController} from '../../../interfaces/IAaveIncentivesController.sol';
|
||||
import {ICurveTreasury} from '../../interfaces/curve/ICurveTreasury.sol';
|
||||
|
||||
/**
|
||||
* @title Curve Rewards Aware AToken
|
||||
|
@ -17,6 +18,7 @@ import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesControl
|
|||
contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
|
||||
// CRV token address
|
||||
address internal immutable CRV_TOKEN;
|
||||
address internal immutable CURVE_TREASURY;
|
||||
|
||||
// Gauge contract address
|
||||
address internal _gaugeController;
|
||||
|
@ -29,8 +31,9 @@ contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
|
|||
/**
|
||||
* @param crvToken The address of the $CRV token
|
||||
*/
|
||||
constructor(address crvToken) public {
|
||||
constructor(address crvToken, address crvTreasury) public {
|
||||
CRV_TOKEN = crvToken;
|
||||
CURVE_TREASURY = crvTreasury;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,18 +82,21 @@ contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
|
|||
_underlyingAsset = underlyingAsset;
|
||||
_incentivesController = incentivesController;
|
||||
|
||||
_gaugeController = _decodeParams(params);
|
||||
|
||||
// Initialize Curve Gauge rewards
|
||||
_gaugeController = underlyingAsset;
|
||||
_rewardTokens[0] = CRV_TOKEN;
|
||||
|
||||
// Start for loop with index = 1 to not rewrite first element of rewardTokens
|
||||
for (uint256 index = 1; index < MAX_REWARD_TOKENS; index++) {
|
||||
address reward = ICurveGaugeView(underlyingAsset).reward_tokens(index - 1);
|
||||
address reward = ICurveGaugeView(_decodeParams(params)).reward_tokens(index - 1);
|
||||
if (reward == address(0)) break;
|
||||
|
||||
_rewardTokens[index] = reward;
|
||||
}
|
||||
|
||||
IERC20(underlyingAsset).safeApprove(CURVE_TREASURY, type(uint256).max);
|
||||
|
||||
emit Initialized(
|
||||
underlyingAsset,
|
||||
address(pool),
|
||||
|
@ -109,14 +115,14 @@ contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
|
|||
* @dev External call to retrieve the lifetime accrued $CRV token rewards from the external Rewards Controller contract
|
||||
*/
|
||||
function _getExternalLifetimeCurve() internal view returns (uint256) {
|
||||
return ICurveGaugeView(_gaugeController).integrate_fraction(address(this));
|
||||
return ICurveGaugeView(_gaugeController).integrate_fraction(CURVE_TREASURY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev External call to retrieve the current claimable extra token rewards from the external Rewards Controller contract
|
||||
*/
|
||||
function _getExternalClaimableCurveExtraRewards(address token) internal view returns (uint256) {
|
||||
return ICurveGaugeView(_gaugeController).claimable_reward(address(this), token);
|
||||
return ICurveGaugeView(_gaugeController).claimable_reward(CURVE_TREASURY, token);
|
||||
}
|
||||
|
||||
/** End of Curve Specific functions */
|
||||
|
@ -154,7 +160,7 @@ contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
|
|||
function _computeExternalLifetimeRewards(address token) internal override returns (uint256) {
|
||||
// The Curve Gauge can give exact lifetime rewards and accrued rewards for the CRV token
|
||||
if (token == CRV_TOKEN) {
|
||||
ICurveGauge(_gaugeController).user_checkpoint(address(this));
|
||||
ICurveGauge(_gaugeController).user_checkpoint(CURVE_TREASURY);
|
||||
return _getExternalLifetimeCurve();
|
||||
}
|
||||
// Other rewards from the Curve Gauge can not get the lifetime rewards externally, only at the moment of claim, due they are external rewards outside the Curve ecosystem.
|
||||
|
@ -178,9 +184,6 @@ contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
|
|||
* @dev External call to claim the lifetime accrued rewards of the aToken contract to the external Rewards Controller contract
|
||||
*/
|
||||
function _claimRewardsFromController() internal override {
|
||||
// Mint CRV to aToken
|
||||
ICurveMinter(ICurveGaugeView(_gaugeController).minter()).mint(_gaugeController);
|
||||
|
||||
_updateRewards();
|
||||
}
|
||||
|
||||
|
@ -197,7 +200,7 @@ contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
|
|||
priorTokenBalances[index] = IERC20(rewardToken).balanceOf(address(this));
|
||||
}
|
||||
// Mint other rewards to aToken
|
||||
ICurveGauge(_gaugeController).claim_rewards(address(this));
|
||||
ICurveTreasury(CURVE_TREASURY).claimGaugeRewards(_gaugeController);
|
||||
|
||||
for (uint256 index = 1; index < MAX_REWARD_TOKENS; index++) {
|
||||
address rewardToken = _getRewardsTokenAddress(index);
|
||||
|
@ -211,6 +214,37 @@ contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Deposit LP tokens at the Curve Treasury and stake into the Gauge Contract from the Treasury
|
||||
* @param token Address of the LP Curve token
|
||||
* @param amount Amount of tokens to deposit
|
||||
*/
|
||||
function _stake(address token, uint256 amount) internal override returns (uint256) {
|
||||
ICurveTreasury(CURVE_TREASURY).deposit(token, amount, true);
|
||||
return amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Withdraw LP tokens at the Curve Treasury and stake into the Gauge Contract from the Treasury
|
||||
* @param token Address of the LP Curve token
|
||||
* @param amount Amount of tokens to withdraw
|
||||
*/
|
||||
function _unstake(address token, uint256 amount) internal override returns (uint256) {
|
||||
ICurveTreasury(CURVE_TREASURY).withdraw(token, amount, true);
|
||||
return amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Param decoder to get Gauge Staking address
|
||||
* @param params Additional variadic field to include extra params. Expected parameters:
|
||||
* @return address of gauge
|
||||
*/
|
||||
function _decodeParams(bytes memory params) internal pure returns (address) {
|
||||
address gauge = abi.decode(params, (address));
|
||||
|
||||
return gauge;
|
||||
}
|
||||
|
||||
/** End of Rewards Aware AToken functions */
|
||||
|
||||
/** Start of External getters */
|
320
contracts/adapters/rewards/curve/CurveTreasury.sol
Normal file
320
contracts/adapters/rewards/curve/CurveTreasury.sol
Normal file
|
@ -0,0 +1,320 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity 0.6.12;
|
||||
|
||||
import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol';
|
||||
import {ICurveGauge, ICurveGaugeView} from '../../interfaces/curve/ICurveGauge.sol';
|
||||
import {ICurveMinter} from '../../interfaces/curve/ICurveMinter.sol';
|
||||
import {ICurveGaugeController} from '../../interfaces/curve/ICurveGaugeController.sol';
|
||||
import {IVotingEscrow} from '../../interfaces/curve/IVotingEscrow.sol';
|
||||
import {
|
||||
VersionedInitializable
|
||||
} from '../../../protocol/libraries/aave-upgradeability/VersionedInitializable.sol';
|
||||
import {ICurveFeeDistributor} from '../../interfaces/curve/ICurveFeeDistributor.sol';
|
||||
import {ICurveTreasury} from '../../interfaces/curve/ICurveTreasury.sol';
|
||||
|
||||
/**
|
||||
* @title Curve Treasury that holds Curve LP and Gauge tokens
|
||||
* @notice The treasury holds Curve assets like LP or Gauge tokens and can lock veCRV for boosting Curve yields
|
||||
* @author Aave
|
||||
*/
|
||||
contract CurveTreasury is ICurveTreasury, VersionedInitializable {
|
||||
using SafeMath for uint256;
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
uint256 public constant MAX_REWARD_TOKENS = 10;
|
||||
|
||||
address immutable VOTING_ESCROW;
|
||||
address immutable CRV_TOKEN;
|
||||
address immutable FEE_DISTRIBUTOR;
|
||||
address immutable GAUGE_CONTROLLER;
|
||||
address immutable AAVE_COLLECTOR;
|
||||
|
||||
address private _owner;
|
||||
|
||||
uint256 public constant TREASURY_REVISION = 0x1;
|
||||
|
||||
mapping(address => mapping(address => bool)) internal _entityTokenWhitelist;
|
||||
mapping(address => mapping(address => address)) internal _entityTokenGauge;
|
||||
|
||||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
||||
|
||||
constructor(
|
||||
address _votingEscrow,
|
||||
address _crvToken,
|
||||
address _curveFeeDistributor,
|
||||
address _gaugeController,
|
||||
address _aaveCollector
|
||||
) public {
|
||||
VOTING_ESCROW = _votingEscrow;
|
||||
CRV_TOKEN = _crvToken;
|
||||
FEE_DISTRIBUTOR = _curveFeeDistributor;
|
||||
GAUGE_CONTROLLER = _gaugeController;
|
||||
AAVE_COLLECTOR = _aaveCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Revert if caller is not the owner of this contract
|
||||
*/
|
||||
modifier onlyOwner() {
|
||||
require(_owner == msg.sender, 'Ownable: caller is not the owner');
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Revert if caller and selected token is not a whitelisted entity
|
||||
*/
|
||||
modifier onlyWhitelistedEntity(address token) {
|
||||
require(_entityTokenWhitelist[msg.sender][token] == true, 'ENTITY_NOT_WHITELISTED');
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Revert if caller gauge token is not whitelisted
|
||||
*/
|
||||
modifier onlyWhitelistedGauge(address token) {
|
||||
require(_entityTokenGauge[msg.sender][token] != address(0), 'ENTITY_GAUGE_NOT_WHITELISTED');
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Initializes the contract with an owner and optional entities whitelist
|
||||
* @param owner Sets the owner of the contract
|
||||
* @param entities Entities addresses list, optional argument
|
||||
* @param tokens Curve LP Token addresses list, optional argument
|
||||
* @param gauges Curve Gauge Staking tokens list, use zero address to disable staking, optional argument
|
||||
*/
|
||||
function initialize(
|
||||
address owner,
|
||||
address[] calldata entities,
|
||||
address[] calldata tokens,
|
||||
address[] calldata gauges
|
||||
) external virtual initializer {
|
||||
_owner = owner;
|
||||
if (entities.length > 0) {
|
||||
bool[] memory whitelisted = new bool[](entities.length);
|
||||
for (uint256 x; x < whitelisted.length; x++) {
|
||||
whitelisted[x] = true;
|
||||
}
|
||||
_setWhitelist(entities, tokens, gauges, whitelisted);
|
||||
}
|
||||
}
|
||||
|
||||
/// @inheritdoc ICurveTreasury
|
||||
function deposit(
|
||||
address token,
|
||||
uint256 amount,
|
||||
bool useGauge
|
||||
) external override onlyWhitelistedEntity(token) {
|
||||
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
|
||||
|
||||
if (useGauge && _entityTokenGauge[msg.sender][token] != address(0)) {
|
||||
_stakeGauge(_entityTokenGauge[msg.sender][token], amount);
|
||||
}
|
||||
}
|
||||
|
||||
/// @inheritdoc ICurveTreasury
|
||||
function withdraw(
|
||||
address token,
|
||||
uint256 amount,
|
||||
bool useGauge
|
||||
) external override onlyWhitelistedEntity(token) {
|
||||
if (useGauge && _entityTokenGauge[msg.sender][token] != address(0)) {
|
||||
_unstakeGauge(_entityTokenGauge[msg.sender][token], amount);
|
||||
}
|
||||
IERC20(token).safeTransfer(msg.sender, amount);
|
||||
}
|
||||
|
||||
/// @inheritdoc ICurveTreasury
|
||||
function setWhitelist(
|
||||
address[] calldata entities,
|
||||
address[] calldata tokens,
|
||||
address[] calldata gauges,
|
||||
bool[] memory whitelisted
|
||||
) external override onlyOwner {
|
||||
_setWhitelist(entities, tokens, gauges, whitelisted);
|
||||
}
|
||||
|
||||
/// @inheritdoc ICurveTreasury
|
||||
function claimGaugeRewards(address gaugeUnderlyingToken)
|
||||
external
|
||||
override
|
||||
onlyWhitelistedEntity(gaugeUnderlyingToken)
|
||||
{
|
||||
address gauge = _entityTokenGauge[msg.sender][gaugeUnderlyingToken];
|
||||
|
||||
// Claim CRV from Curve Minter
|
||||
uint256 priorCrvBalance = IERC20(CRV_TOKEN).balanceOf(address(this));
|
||||
ICurveMinter(ICurveGaugeView(gauge).minter()).mint(gauge);
|
||||
uint256 afterCrvBalance = IERC20(CRV_TOKEN).balanceOf(address(this));
|
||||
|
||||
// Transfer CRV to entity
|
||||
uint256 crvRewards = afterCrvBalance.sub(priorCrvBalance);
|
||||
if (crvRewards > 0) {
|
||||
IERC20(CRV_TOKEN).safeTransfer(msg.sender, crvRewards);
|
||||
}
|
||||
|
||||
// Claim the extra rewards from Gauge Staking
|
||||
uint256[] memory priorRewardsBalance = new uint256[](MAX_REWARD_TOKENS);
|
||||
uint256[] memory afterRewardsBalance = new uint256[](MAX_REWARD_TOKENS);
|
||||
|
||||
// Calculate balances prior claiming rewards
|
||||
for (uint256 index = 1; index < MAX_REWARD_TOKENS; index++) {
|
||||
address rewardToken = ICurveGaugeView(gauge).reward_tokens(index - 1);
|
||||
if (rewardToken == address(0)) break;
|
||||
priorRewardsBalance[index] = IERC20(rewardToken).balanceOf(address(this));
|
||||
}
|
||||
|
||||
// Claim extra rewards
|
||||
ICurveGauge(gauge).claim_rewards();
|
||||
|
||||
// Transfer extra rewards to entity
|
||||
for (uint256 index = 1; index < MAX_REWARD_TOKENS; index++) {
|
||||
address rewardToken = ICurveGaugeView(gauge).reward_tokens(index - 1);
|
||||
if (rewardToken == address(0)) break;
|
||||
afterRewardsBalance[index] = IERC20(rewardToken).balanceOf(address(this));
|
||||
uint256 rewardsAmount = afterRewardsBalance[index].sub(priorRewardsBalance[index]);
|
||||
if (rewardsAmount > 0) {
|
||||
IERC20(rewardToken).safeTransfer(msg.sender, rewardsAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @inheritdoc ICurveTreasury
|
||||
function claimCurveDistributorFees() external override onlyOwner {
|
||||
address rewardsToken = ICurveFeeDistributor(FEE_DISTRIBUTOR).token();
|
||||
uint256 priorBalance = IERC20(rewardsToken).balanceOf(address(this));
|
||||
ICurveFeeDistributor(FEE_DISTRIBUTOR).claim();
|
||||
uint256 afterBalance = IERC20(rewardsToken).balanceOf(address(this));
|
||||
uint256 rewards = afterBalance.sub(priorBalance);
|
||||
if (rewards > 0) {
|
||||
IERC20(rewardsToken).safeTransfer(AAVE_COLLECTOR, rewards);
|
||||
}
|
||||
}
|
||||
|
||||
/** Owner methods related with veCRV to interact with Voting Escrow Curve contract */
|
||||
|
||||
/// @inheritdoc ICurveTreasury
|
||||
function lockCrv(uint256 amount, uint256 unlockTime) external override onlyOwner {
|
||||
IERC20(CRV_TOKEN).safeApprove(VOTING_ESCROW, 0);
|
||||
IERC20(CRV_TOKEN).safeApprove(VOTING_ESCROW, amount);
|
||||
IVotingEscrow(VOTING_ESCROW).create_lock(amount, unlockTime);
|
||||
}
|
||||
|
||||
/// @inheritdoc ICurveTreasury
|
||||
function unlockCrv() external override onlyOwner {
|
||||
IVotingEscrow(VOTING_ESCROW).withdraw();
|
||||
}
|
||||
|
||||
/// @inheritdoc ICurveTreasury
|
||||
function increaseLockedCrv(uint256 amount) external override onlyOwner {
|
||||
IERC20(CRV_TOKEN).safeApprove(VOTING_ESCROW, 0);
|
||||
IERC20(CRV_TOKEN).safeApprove(VOTING_ESCROW, amount);
|
||||
IVotingEscrow(VOTING_ESCROW).increase_amount(amount);
|
||||
}
|
||||
|
||||
/// @inheritdoc ICurveTreasury
|
||||
function increaseUnlockTimeCrv(uint256 unlockTime) external override onlyOwner {
|
||||
IVotingEscrow(VOTING_ESCROW).increase_unlock_time(unlockTime);
|
||||
}
|
||||
|
||||
/** Owner methods related with Gauge Controller Voting contract */
|
||||
|
||||
/// @inheritdoc ICurveTreasury
|
||||
function voteForGaugeWeights(address gauge, uint256 weight) external override onlyOwner {
|
||||
ICurveGaugeController(GAUGE_CONTROLLER).vote_for_gauge_weights(gauge, weight);
|
||||
}
|
||||
|
||||
/// @inheritdoc ICurveTreasury
|
||||
function transferOwnership(address newOwner) external override onlyOwner {
|
||||
require(newOwner != address(0), 'New owner cant be the zero address');
|
||||
emit OwnershipTransferred(_owner, newOwner);
|
||||
_owner = newOwner;
|
||||
}
|
||||
|
||||
/// @inheritdoc ICurveTreasury
|
||||
function owner() external view override returns (address) {
|
||||
return _owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Get the implementation revision
|
||||
* @return uin256 - The current version of the implementation
|
||||
*/
|
||||
function getRevision() internal pure virtual override returns (uint256) {
|
||||
return TREASURY_REVISION;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Register entities to enable or disable deposits and withdraws from the treasury
|
||||
* @param entities Entities addresses list
|
||||
* @param tokens Curve LP Token addresses list
|
||||
* @param gauges Curve Gauge Staking tokens list, use zero address to disable staking
|
||||
* @param whitelisted Flag to determine if the entity should be enabled or disabled
|
||||
*/
|
||||
function _setWhitelist(
|
||||
address[] calldata entities,
|
||||
address[] calldata tokens,
|
||||
address[] calldata gauges,
|
||||
bool[] memory whitelisted
|
||||
) internal {
|
||||
for (uint256 e; e < entities.length; e++) {
|
||||
_entityTokenWhitelist[entities[e]][tokens[e]] = whitelisted[e];
|
||||
if (whitelisted[e] == true) {
|
||||
if (gauges[e] != address(0)) {
|
||||
_entityTokenGauge[entities[e]][tokens[e]] = gauges[e];
|
||||
}
|
||||
_approveEntityTokens(entities[e], tokens[e], gauges[e], type(uint256).max);
|
||||
} else {
|
||||
_entityTokenGauge[entities[e]][tokens[e]] = address(0);
|
||||
_approveEntityTokens(entities[e], tokens[e], gauges[e], 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev ERC20 approval to allow entity and gauge staking contracts to pull whitelisted tokens and rewards from the treasury
|
||||
* @param entity Entity address
|
||||
* @param token Curve LP Token contract address
|
||||
* @param gauge Curve Gauge Staking contract address
|
||||
* @param amount Amount of tokens
|
||||
*/
|
||||
function _approveEntityTokens(
|
||||
address entity,
|
||||
address token,
|
||||
address gauge,
|
||||
uint256 amount
|
||||
) internal {
|
||||
IERC20(token).safeApprove(entity, 0);
|
||||
IERC20(token).safeApprove(entity, amount);
|
||||
if (gauge != address(0)) {
|
||||
IERC20(token).safeApprove(gauge, 0);
|
||||
IERC20(token).safeApprove(gauge, amount);
|
||||
for (uint256 index = 0; index < MAX_REWARD_TOKENS; index++) {
|
||||
address reward = ICurveGaugeView(gauge).reward_tokens(index - 1);
|
||||
if (reward == address(0)) break;
|
||||
IERC20(reward).safeApprove(entity, 0);
|
||||
IERC20(reward).safeApprove(entity, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Stake underliying token inside the Gauge Staking contract
|
||||
* @param gauge Address of the Gauge Staking contract
|
||||
* @param amount Amount of the underlying token to stake inside the contract
|
||||
*/
|
||||
function _stakeGauge(address gauge, uint256 amount) internal {
|
||||
ICurveGauge(gauge).deposit(amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Unstake underliying token from the Gauge Staking contract
|
||||
* @param gauge Address of the Gauge Staking contract
|
||||
* @param amount Amount of the underlying token to withdraw from the contract
|
||||
*/
|
||||
function _unstakeGauge(address gauge, uint256 amount) internal {
|
||||
ICurveGauge(gauge).withdraw(amount);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user