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 {
|
interface ICurveFeeDistributor {
|
||||||
function claim() external;
|
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
|
// SPDX-License-Identifier: agpl-3.0
|
||||||
pragma solidity 0.6.12;
|
pragma solidity 0.6.12;
|
||||||
|
|
||||||
import {ILendingPool} from '../../interfaces/ILendingPool.sol';
|
import {ILendingPool} from '../../../interfaces/ILendingPool.sol';
|
||||||
import {RewardsAwareAToken} from '../../protocol/tokenization/RewardsAwareAToken.sol';
|
import {RewardsAwareAToken} from '../../../protocol/tokenization/RewardsAwareAToken.sol';
|
||||||
import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||||
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
|
import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||||
import {ICurveMinter} from '../interfaces/curve/ICurveMinter.sol';
|
import {ICurveMinter} from '../../interfaces/curve/ICurveMinter.sol';
|
||||||
import {ICurveGauge, ICurveGaugeView} from '../interfaces/curve/ICurveGauge.sol';
|
import {ICurveGauge, ICurveGaugeView} from '../../interfaces/curve/ICurveGauge.sol';
|
||||||
import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol';
|
import {IAaveIncentivesController} from '../../../interfaces/IAaveIncentivesController.sol';
|
||||||
|
import {ICurveTreasury} from '../../interfaces/curve/ICurveTreasury.sol';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @title Curve Rewards Aware AToken
|
* @title Curve Rewards Aware AToken
|
||||||
|
@ -17,6 +18,7 @@ import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesControl
|
||||||
contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
|
contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
|
||||||
// CRV token address
|
// CRV token address
|
||||||
address internal immutable CRV_TOKEN;
|
address internal immutable CRV_TOKEN;
|
||||||
|
address internal immutable CURVE_TREASURY;
|
||||||
|
|
||||||
// Gauge contract address
|
// Gauge contract address
|
||||||
address internal _gaugeController;
|
address internal _gaugeController;
|
||||||
|
@ -29,8 +31,9 @@ contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
|
||||||
/**
|
/**
|
||||||
* @param crvToken The address of the $CRV token
|
* @param crvToken The address of the $CRV token
|
||||||
*/
|
*/
|
||||||
constructor(address crvToken) public {
|
constructor(address crvToken, address crvTreasury) public {
|
||||||
CRV_TOKEN = crvToken;
|
CRV_TOKEN = crvToken;
|
||||||
|
CURVE_TREASURY = crvTreasury;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -79,18 +82,21 @@ contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
|
||||||
_underlyingAsset = underlyingAsset;
|
_underlyingAsset = underlyingAsset;
|
||||||
_incentivesController = incentivesController;
|
_incentivesController = incentivesController;
|
||||||
|
|
||||||
|
_gaugeController = _decodeParams(params);
|
||||||
|
|
||||||
// Initialize Curve Gauge rewards
|
// Initialize Curve Gauge rewards
|
||||||
_gaugeController = underlyingAsset;
|
|
||||||
_rewardTokens[0] = CRV_TOKEN;
|
_rewardTokens[0] = CRV_TOKEN;
|
||||||
|
|
||||||
// Start for loop with index = 1 to not rewrite first element of rewardTokens
|
// Start for loop with index = 1 to not rewrite first element of rewardTokens
|
||||||
for (uint256 index = 1; index < MAX_REWARD_TOKENS; index++) {
|
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;
|
if (reward == address(0)) break;
|
||||||
|
|
||||||
_rewardTokens[index] = reward;
|
_rewardTokens[index] = reward;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IERC20(underlyingAsset).safeApprove(CURVE_TREASURY, type(uint256).max);
|
||||||
|
|
||||||
emit Initialized(
|
emit Initialized(
|
||||||
underlyingAsset,
|
underlyingAsset,
|
||||||
address(pool),
|
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
|
* @dev External call to retrieve the lifetime accrued $CRV token rewards from the external Rewards Controller contract
|
||||||
*/
|
*/
|
||||||
function _getExternalLifetimeCurve() internal view returns (uint256) {
|
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
|
* @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) {
|
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 */
|
/** End of Curve Specific functions */
|
||||||
|
@ -154,7 +160,7 @@ contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
|
||||||
function _computeExternalLifetimeRewards(address token) internal override returns (uint256) {
|
function _computeExternalLifetimeRewards(address token) internal override returns (uint256) {
|
||||||
// The Curve Gauge can give exact lifetime rewards and accrued rewards for the CRV token
|
// The Curve Gauge can give exact lifetime rewards and accrued rewards for the CRV token
|
||||||
if (token == CRV_TOKEN) {
|
if (token == CRV_TOKEN) {
|
||||||
ICurveGauge(_gaugeController).user_checkpoint(address(this));
|
ICurveGauge(_gaugeController).user_checkpoint(CURVE_TREASURY);
|
||||||
return _getExternalLifetimeCurve();
|
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.
|
// 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
|
* @dev External call to claim the lifetime accrued rewards of the aToken contract to the external Rewards Controller contract
|
||||||
*/
|
*/
|
||||||
function _claimRewardsFromController() internal override {
|
function _claimRewardsFromController() internal override {
|
||||||
// Mint CRV to aToken
|
|
||||||
ICurveMinter(ICurveGaugeView(_gaugeController).minter()).mint(_gaugeController);
|
|
||||||
|
|
||||||
_updateRewards();
|
_updateRewards();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +200,7 @@ contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
|
||||||
priorTokenBalances[index] = IERC20(rewardToken).balanceOf(address(this));
|
priorTokenBalances[index] = IERC20(rewardToken).balanceOf(address(this));
|
||||||
}
|
}
|
||||||
// Mint other rewards to aToken
|
// 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++) {
|
for (uint256 index = 1; index < MAX_REWARD_TOKENS; index++) {
|
||||||
address rewardToken = _getRewardsTokenAddress(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 */
|
/** End of Rewards Aware AToken functions */
|
||||||
|
|
||||||
/** Start of External getters */
|
/** 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