feat: adapted prior curve reward aware token tests. Added 3pool support.

This commit is contained in:
David Racero 2021-06-22 19:10:44 +02:00
parent b5ba303e8f
commit 5b56182157
7 changed files with 149 additions and 125 deletions

View File

@ -36,12 +36,14 @@ interface ICurveTreasury {
* @param entities Entities addresses list * @param entities Entities addresses list
* @param tokens Curve LP Token addresses list * @param tokens Curve LP Token addresses list
* @param gauges Curve Gauge Staking tokens list, use zero address to disable staking * @param gauges Curve Gauge Staking tokens list, use zero address to disable staking
* @param areGaugesV2 Curve version list, if true gauge is V2 compatible, false if not.
* @param whitelisted Flag to determine if the entity should be enabled or disabled * @param whitelisted Flag to determine if the entity should be enabled or disabled
*/ */
function setWhitelist( function setWhitelist(
address[] calldata entities, address[] calldata entities,
address[] calldata tokens, address[] calldata tokens,
address[] calldata gauges, address[] calldata gauges,
bool[] memory areGaugesV2,
bool[] memory whitelisted bool[] memory whitelisted
) external; ) external;

View File

@ -9,6 +9,7 @@ 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'; import {ICurveTreasury} from '../../interfaces/curve/ICurveTreasury.sol';
import 'hardhat/console.sol';
/** /**
* @title Curve Rewards Aware AToken * @title Curve Rewards Aware AToken
@ -82,17 +83,20 @@ contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
_underlyingAsset = underlyingAsset; _underlyingAsset = underlyingAsset;
_incentivesController = incentivesController; _incentivesController = incentivesController;
_gaugeController = _decodeParams(params); _gaugeController = _decodeGaugeAddress(params);
// Initialize Curve Gauge rewards // Initialize Curve Gauge rewards
_rewardTokens[0] = CRV_TOKEN; _rewardTokens[0] = CRV_TOKEN;
// Start for loop with index = 1 to not rewrite first element of rewardTokens // If Gauge is version V2, them discover reward tokens
for (uint256 index = 1; index < MAX_REWARD_TOKENS; index++) { if (_decodeGaugeVersion(params) == true) {
address reward = ICurveGaugeView(_decodeParams(params)).reward_tokens(index - 1); // Start for loop with index = 1 to not rewrite first element of rewardTokens
if (reward == address(0)) break; for (uint256 index = 1; index < MAX_REWARD_TOKENS; index++) {
address reward = ICurveGaugeView(_decodeGaugeAddress(params)).reward_tokens(index - 1);
if (reward == address(0)) break;
_rewardTokens[index] = reward; _rewardTokens[index] = reward;
}
} }
IERC20(underlyingAsset).safeApprove(CURVE_TREASURY, type(uint256).max); IERC20(underlyingAsset).safeApprove(CURVE_TREASURY, type(uint256).max);
@ -160,7 +164,6 @@ 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(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.
@ -200,7 +203,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
ICurveTreasury(CURVE_TREASURY).claimGaugeRewards(_gaugeController); ICurveTreasury(CURVE_TREASURY).claimGaugeRewards(_underlyingAsset);
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);
@ -220,7 +223,9 @@ contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
* @param amount Amount of tokens to deposit * @param amount Amount of tokens to deposit
*/ */
function _stake(address token, uint256 amount) internal override returns (uint256) { function _stake(address token, uint256 amount) internal override returns (uint256) {
ICurveTreasury(CURVE_TREASURY).deposit(token, amount, true); if (token == UNDERLYING_ASSET_ADDRESS()) {
ICurveTreasury(CURVE_TREASURY).deposit(token, amount, true);
}
return amount; return amount;
} }
@ -230,21 +235,34 @@ contract CurveGaugeRewardsAwareAToken is RewardsAwareAToken {
* @param amount Amount of tokens to withdraw * @param amount Amount of tokens to withdraw
*/ */
function _unstake(address token, uint256 amount) internal override returns (uint256) { function _unstake(address token, uint256 amount) internal override returns (uint256) {
ICurveTreasury(CURVE_TREASURY).withdraw(token, amount, true); if (token == UNDERLYING_ASSET_ADDRESS()) {
ICurveTreasury(CURVE_TREASURY).withdraw(token, amount, true);
}
return amount; return amount;
} }
/** /**
* @dev Param decoder to get Gauge Staking address * @dev Param decoder to get Gauge Staking address. The decoder function is splitted due "Stack too deep" at constructor.
* @param params Additional variadic field to include extra params. Expected parameters: * @param params Additional variadic field to include extra params. Expected parameters:
* @return address of gauge * @return address of gauge
*/ */
function _decodeParams(bytes memory params) internal pure returns (address) { function _decodeGaugeAddress(bytes memory params) internal pure returns (address) {
address gauge = abi.decode(params, (address)); (address gauge, bool _isGaugeV2) = abi.decode(params, (address, bool));
return gauge; return gauge;
} }
/**
* @dev Param decoder to get Gauge Staking address. The decoder function is splitted due "Stack too deep" at constructor.
* @param params Additional variadic field to include extra params. Expected parameters:
* @return bool true if Gauge follows Gauge V2 interface, false otherwise
*/
function _decodeGaugeVersion(bytes memory params) internal pure returns (bool) {
(address _gauge, bool isGaugeV2) = abi.decode(params, (address, bool));
return isGaugeV2;
}
/** End of Rewards Aware AToken functions */ /** End of Rewards Aware AToken functions */
/** Start of External getters */ /** Start of External getters */

View File

@ -13,6 +13,7 @@ import {
} from '../../../protocol/libraries/aave-upgradeability/VersionedInitializable.sol'; } from '../../../protocol/libraries/aave-upgradeability/VersionedInitializable.sol';
import {ICurveFeeDistributor} from '../../interfaces/curve/ICurveFeeDistributor.sol'; import {ICurveFeeDistributor} from '../../interfaces/curve/ICurveFeeDistributor.sol';
import {ICurveTreasury} from '../../interfaces/curve/ICurveTreasury.sol'; import {ICurveTreasury} from '../../interfaces/curve/ICurveTreasury.sol';
import 'hardhat/console.sol';
/** /**
* @title Curve Treasury that holds Curve LP and Gauge tokens * @title Curve Treasury that holds Curve LP and Gauge tokens
@ -37,6 +38,7 @@ contract CurveTreasury is ICurveTreasury, VersionedInitializable {
mapping(address => mapping(address => bool)) internal _entityTokenWhitelist; mapping(address => mapping(address => bool)) internal _entityTokenWhitelist;
mapping(address => mapping(address => address)) internal _entityTokenGauge; mapping(address => mapping(address => address)) internal _entityTokenGauge;
mapping(address => bool) internal _isGaugeV2Compatible;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
@ -66,6 +68,7 @@ contract CurveTreasury is ICurveTreasury, VersionedInitializable {
* @dev Revert if caller and selected token is not a whitelisted entity * @dev Revert if caller and selected token is not a whitelisted entity
*/ */
modifier onlyWhitelistedEntity(address token) { modifier onlyWhitelistedEntity(address token) {
console.log(msg.sender, token, _entityTokenWhitelist[msg.sender][token]);
require(_entityTokenWhitelist[msg.sender][token] == true, 'ENTITY_NOT_WHITELISTED'); require(_entityTokenWhitelist[msg.sender][token] == true, 'ENTITY_NOT_WHITELISTED');
_; _;
} }
@ -79,26 +82,11 @@ contract CurveTreasury is ICurveTreasury, VersionedInitializable {
} }
/** /**
* @dev Initializes the contract with an owner and optional entities whitelist * @dev Initializes the contract with an owner that allows to whitelist new entities inside the treasury contract
* @param owner Sets the owner of the contract * @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( function initialize(address owner) external virtual initializer {
address owner,
address[] calldata entities,
address[] calldata tokens,
address[] calldata gauges
) external virtual initializer {
_owner = owner; _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 /// @inheritdoc ICurveTreasury
@ -131,9 +119,23 @@ contract CurveTreasury is ICurveTreasury, VersionedInitializable {
address[] calldata entities, address[] calldata entities,
address[] calldata tokens, address[] calldata tokens,
address[] calldata gauges, address[] calldata gauges,
bool[] memory areGaugesV2,
bool[] memory whitelisted bool[] memory whitelisted
) external override onlyOwner { ) external override onlyOwner {
_setWhitelist(entities, tokens, gauges, whitelisted); 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];
_isGaugeV2Compatible[gauges[e]] = areGaugesV2[e];
}
_approveEntityTokens(entities[e], tokens[e], gauges[e], areGaugesV2[e], type(uint256).max);
} else {
_entityTokenGauge[entities[e]][tokens[e]] = address(0);
_approveEntityTokens(entities[e], tokens[e], gauges[e], areGaugesV2[e], 0);
_isGaugeV2Compatible[gauges[e]] = false;
}
}
} }
/// @inheritdoc ICurveTreasury /// @inheritdoc ICurveTreasury
@ -155,28 +157,30 @@ contract CurveTreasury is ICurveTreasury, VersionedInitializable {
IERC20(CRV_TOKEN).safeTransfer(msg.sender, crvRewards); IERC20(CRV_TOKEN).safeTransfer(msg.sender, crvRewards);
} }
// Claim the extra rewards from Gauge Staking if (_isGaugeV2Compatible[gauge] == true) {
uint256[] memory priorRewardsBalance = new uint256[](MAX_REWARD_TOKENS); // Claim the extra rewards from Gauge Staking
uint256[] memory afterRewardsBalance = new uint256[](MAX_REWARD_TOKENS); uint256[] memory priorRewardsBalance = new uint256[](MAX_REWARD_TOKENS);
uint256[] memory afterRewardsBalance = new uint256[](MAX_REWARD_TOKENS);
// Calculate balances prior claiming rewards // Calculate balances prior claiming rewards
for (uint256 index = 1; index < MAX_REWARD_TOKENS; index++) { for (uint256 index = 1; index < MAX_REWARD_TOKENS; index++) {
address rewardToken = ICurveGaugeView(gauge).reward_tokens(index - 1); address rewardToken = ICurveGaugeView(gauge).reward_tokens(index - 1);
if (rewardToken == address(0)) break; if (rewardToken == address(0)) break;
priorRewardsBalance[index] = IERC20(rewardToken).balanceOf(address(this)); priorRewardsBalance[index] = IERC20(rewardToken).balanceOf(address(this));
} }
// Claim extra rewards // Claim extra rewards
ICurveGauge(gauge).claim_rewards(); ICurveGauge(gauge).claim_rewards();
// Transfer extra rewards to entity // Transfer extra rewards to entity
for (uint256 index = 1; index < MAX_REWARD_TOKENS; index++) { for (uint256 index = 1; index < MAX_REWARD_TOKENS; index++) {
address rewardToken = ICurveGaugeView(gauge).reward_tokens(index - 1); address rewardToken = ICurveGaugeView(gauge).reward_tokens(index - 1);
if (rewardToken == address(0)) break; if (rewardToken == address(0)) break;
afterRewardsBalance[index] = IERC20(rewardToken).balanceOf(address(this)); afterRewardsBalance[index] = IERC20(rewardToken).balanceOf(address(this));
uint256 rewardsAmount = afterRewardsBalance[index].sub(priorRewardsBalance[index]); uint256 rewardsAmount = afterRewardsBalance[index].sub(priorRewardsBalance[index]);
if (rewardsAmount > 0) { if (rewardsAmount > 0) {
IERC20(rewardToken).safeTransfer(msg.sender, rewardsAmount); IERC20(rewardToken).safeTransfer(msg.sender, rewardsAmount);
}
} }
} }
} }
@ -246,33 +250,6 @@ contract CurveTreasury is ICurveTreasury, VersionedInitializable {
return TREASURY_REVISION; 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 * @dev ERC20 approval to allow entity and gauge staking contracts to pull whitelisted tokens and rewards from the treasury
* @param entity Entity address * @param entity Entity address
@ -284,6 +261,7 @@ contract CurveTreasury is ICurveTreasury, VersionedInitializable {
address entity, address entity,
address token, address token,
address gauge, address gauge,
bool isGaugeV2,
uint256 amount uint256 amount
) internal { ) internal {
IERC20(token).safeApprove(entity, 0); IERC20(token).safeApprove(entity, 0);
@ -291,11 +269,15 @@ contract CurveTreasury is ICurveTreasury, VersionedInitializable {
if (gauge != address(0)) { if (gauge != address(0)) {
IERC20(token).safeApprove(gauge, 0); IERC20(token).safeApprove(gauge, 0);
IERC20(token).safeApprove(gauge, amount); IERC20(token).safeApprove(gauge, amount);
for (uint256 index = 0; index < MAX_REWARD_TOKENS; index++) { IERC20(CRV_TOKEN).safeApprove(entity, 0);
address reward = ICurveGaugeView(gauge).reward_tokens(index - 1); IERC20(CRV_TOKEN).safeApprove(entity, amount);
if (reward == address(0)) break; if (isGaugeV2 == true) {
IERC20(reward).safeApprove(entity, 0); for (uint256 index = 0; index < MAX_REWARD_TOKENS; index++) {
IERC20(reward).safeApprove(entity, amount); address reward = ICurveGaugeView(gauge).reward_tokens(index);
if (reward == address(0)) break;
IERC20(reward).safeApprove(entity, 0);
IERC20(reward).safeApprove(entity, amount);
}
} }
} }
} }

View File

@ -115,7 +115,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
reserve.updateState(); reserve.updateState();
reserve.updateInterestRates(asset, aToken, amount, 0); reserve.updateInterestRates(asset, aToken, amount, 0);
IERC20(asset).safeTransferFrom(msg.sender, aToken, amount); IERC20(asset).safeTransferFrom(msg.sender, aToken, amount);
bool isFirstDeposit = IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex); bool isFirstDeposit = IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex);
@ -713,7 +712,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
} }
/** /**
* @dev Returns the fee on flash loans * @dev Returns the fee on flash loans
*/ */
function FLASHLOAN_PREMIUM_TOTAL() public view returns (uint256) { function FLASHLOAN_PREMIUM_TOTAL() public view returns (uint256) {
return _flashLoanPremiumTotal; return _flashLoanPremiumTotal;

View File

@ -84,12 +84,12 @@ export const CURVE_TREASURY = {
export const CURVE_CONFIG = { export const CURVE_CONFIG = {
votingEscrow: { votingEscrow: {
[eEthereumNetwork.main]: ZERO_ADDRESS, [eEthereumNetwork.main]: '0x5f3b5DfEb7B28CDbD7FAba78963EE202a494e2A2',
}, },
curveFeeDistributor: { curveFeeDistributor: {
[eEthereumNetwork.main]: ZERO_ADDRESS, [eEthereumNetwork.main]: '0xA464e6DCda8AC41e03616F95f4BC98a13b8922Dc',
}, },
gaugeController: { gaugeController: {
[eEthereumNetwork.main]: ZERO_ADDRESS, [eEthereumNetwork.main]: '0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB',
}, },
}; };

View File

@ -15,7 +15,7 @@ task(`deploy-curve-treasury`, `Deploys the CurveTreasury contract`)
.setAction(async ({ verify, proxyAdmin, treasuryAdmin, collector }, localBRE) => { .setAction(async ({ verify, proxyAdmin, treasuryAdmin, collector }, localBRE) => {
await localBRE.run('set-DRE'); await localBRE.run('set-DRE');
const net = localBRE.network.name; const net = process.env.FORK ? process.env.FORK : localBRE.network.name;
console.log(`\n- Curve Treasury deployment`); console.log(`\n- Curve Treasury deployment`);
const aaveCollector = collector || ZERO_ADDRESS; const aaveCollector = collector || ZERO_ADDRESS;
@ -30,18 +30,15 @@ task(`deploy-curve-treasury`, `Deploys the CurveTreasury contract`)
); );
// Freeze implementatation // Freeze implementatation
await waitForTx(await implementation.initialize(ZERO_ADDRESS, [], [], [])); await waitForTx(await implementation.initialize(ZERO_ADDRESS));
// Deploy proxy // Deploy proxy
const proxy = await deployInitializableAdminUpgradeabilityProxy(verify); const proxy = await deployInitializableAdminUpgradeabilityProxy(verify);
const encoded = implementation.interface.encodeFunctionData('initialize', [ const encoded = implementation.interface.encodeFunctionData('initialize', [treasuryAdmin]);
treasuryAdmin, await waitForTx(
[], await proxy['initialize(address,address,bytes)'](implementation.address, proxyAdmin, encoded)
[], );
[],
]);
await waitForTx(await proxy.initialize(implementation.address, proxyAdmin, encoded));
console.log(`\tFinished CurveTreasury deployment`); console.log(`\tFinished CurveTreasury deployment`);
console.log(`\tProxy:`, proxy.address); console.log(`\tProxy:`, proxy.address);

View File

@ -15,20 +15,21 @@ import {
getLendingPoolAddressesProvider, getLendingPoolAddressesProvider,
getLendingPoolConfiguratorProxy, getLendingPoolConfiguratorProxy,
} from '../../../helpers/contracts-getters'; } from '../../../helpers/contracts-getters';
import { import { deployCurveGaugeReserveInterestRateStrategy } from '../../../helpers/contracts-deployments';
deployCurveGaugeReserveInterestRateStrategy,
deployDefaultReserveInterestRateStrategy,
} from '../../../helpers/contracts-deployments';
import { IERC20Factory } from '../../../types/IERC20Factory'; import { IERC20Factory } from '../../../types/IERC20Factory';
import BigNumberJs from 'bignumber.js'; import BigNumberJs from 'bignumber.js';
import { CurveGaugeRewardsAwareATokenFactory } from '../../../types'; import {
CurveGaugeRewardsAwareATokenFactory,
CurveTreasury,
CurveTreasuryFactory,
} from '../../../types';
import { eContractid, eEthereumNetwork, tEthereumAddress } from '../../../helpers/types'; import { eContractid, eEthereumNetwork, tEthereumAddress } from '../../../helpers/types';
import { strategyWBTC } from '../../../markets/aave/reservesConfigs'; import { strategyWBTC } from '../../../markets/aave/reservesConfigs';
import { checkRewards } from '../helpers/rewards-distribution/verify'; import { checkRewards } from '../helpers/rewards-distribution/verify';
import { IRewardsAwareAToken } from '../../../types/IRewardsAwareAToken'; import { IRewardsAwareAToken } from '../../../types/IRewardsAwareAToken';
import { IRewardsAwareATokenFactory } from '../../../types/IRewardsAwareATokenFactory'; import { IRewardsAwareATokenFactory } from '../../../types/IRewardsAwareATokenFactory';
import { BigNumber } from 'ethers'; import { BigNumber } from 'ethers';
import { AbiCoder, defaultAbiCoder, parseEther } from 'ethers/lib/utils'; import { defaultAbiCoder, formatEther, parseEther } from 'ethers/lib/utils';
import { IERC20 } from '../../../types/IERC20'; import { IERC20 } from '../../../types/IERC20';
import { import {
getContractAddressWithJsonFallback, getContractAddressWithJsonFallback,
@ -46,7 +47,6 @@ interface GaugeInfo {
symbol: string; symbol: string;
rewardTokens: tEthereumAddress[]; rewardTokens: tEthereumAddress[];
} }
const BLOCK_HEIGHT = '';
const USER_ADDRESS = '0x9c5083dd4838E120Dbeac44C052179692Aa5dAC5'; const USER_ADDRESS = '0x9c5083dd4838E120Dbeac44C052179692Aa5dAC5';
const CRV_TOKEN = '0xd533a949740bb3306d119cc777fa900ba034cd52'; const CRV_TOKEN = '0xd533a949740bb3306d119cc777fa900ba034cd52';
@ -86,12 +86,18 @@ const GAUGE_ANKR: GaugeInfo = {
'0x8290333ceF9e6D528dD5618Fb97a76f268f3EDD4', '0x8290333ceF9e6D528dD5618Fb97a76f268f3EDD4',
], ],
}; };
const isGaugeV2 = (address: tEthereumAddress) =>
GAUGE_3POOL.address.toLowerCase() !== address.toLowerCase();
const unstakeAllGauges = async (key: SignerWithAddress, gauges: tEthereumAddress[]) => { const unstakeAllGauges = async (key: SignerWithAddress, gauges: tEthereumAddress[]) => {
for (let x = 0; x < gauges.length; x++) { for (let x = 0; x < gauges.length; x++) {
await waitForTx( if (isGaugeV2(gauges[x])) {
await ICurveGaugeFactory.connect(gauges[x], key.signer).withdraw(MAX_UINT_AMOUNT) await waitForTx(
); await IERC20Factory.connect(gauges[x], key.signer).approve(gauges[x], MAX_UINT_AMOUNT)
);
}
const balance = IERC20Factory.connect(gauges[x], key.signer).balanceOf(key.address);
await waitForTx(await ICurveGaugeFactory.connect(gauges[x], key.signer).withdraw(balance));
} }
}; };
@ -140,7 +146,10 @@ const listCurveLPToken = async (gauge: GaugeInfo, curveTreasury: tEthereumAddres
false false
); );
const interestRateStrategyAddress = interestStrategy.address; const interestRateStrategyAddress = interestStrategy.address;
const encodedParams = defaultAbiCoder.encode(['string'], [gauge.address]); const encodedParams = defaultAbiCoder.encode(
['address', 'bool'],
[gauge.address, isGaugeV2(gauge.address)]
);
const curveReserveInitParams = [ const curveReserveInitParams = [
{ {
aTokenImpl, aTokenImpl,
@ -175,7 +184,8 @@ const depositPoolToken = async (
const pool = await getLendingPool(); const pool = await getLendingPool();
const curveReserveToken = IERC20Factory.connect(gauge.underlying, key.signer); const curveReserveToken = IERC20Factory.connect(gauge.underlying, key.signer);
await curveReserveToken.connect(key.signer).approve(pool.address, amount); await waitForTx(await curveReserveToken.connect(key.signer).approve(pool.address, 0));
await waitForTx(await curveReserveToken.connect(key.signer).approve(pool.address, amount));
const txDeposit = await waitForTx( const txDeposit = await waitForTx(
await pool.connect(key.signer).deposit(gauge.underlying, amount, key.address, '0') await pool.connect(key.signer).deposit(gauge.underlying, amount, key.address, '0')
@ -249,6 +259,8 @@ makeSuite('Curve Rewards Aware aToken', (testEnv: TestEnv) => {
let crvToken: IERC20; let crvToken: IERC20;
let snxToken: IERC20; let snxToken: IERC20;
let curveTreasury: CurveTreasury;
before('Initializing configuration', async () => { before('Initializing configuration', async () => {
// Sets BigNumber for this suite, instead of globally // Sets BigNumber for this suite, instead of globally
BigNumberJs.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumberJs.ROUND_DOWN }); BigNumberJs.config({ DECIMAL_PLACES: 0, ROUNDING_MODE: BigNumberJs.ROUND_DOWN });
@ -272,10 +284,6 @@ makeSuite('Curve Rewards Aware aToken', (testEnv: TestEnv) => {
// Depositor should have 3pool, EURS, AAVE3, and ANKR balance // Depositor should have 3pool, EURS, AAVE3, and ANKR balance
const curve3poolBalance = await curve3poolErc20.balanceOf(USER_ADDRESS); const curve3poolBalance = await curve3poolErc20.balanceOf(USER_ADDRESS);
const gaugeCurve3poolBalance = await IERC20Factory.connect(
GAUGE_3POOL.address,
depositor.signer
).balanceOf(USER_ADDRESS);
const curveEursBalance = await curveEursErc20.balanceOf(USER_ADDRESS); const curveEursBalance = await curveEursErc20.balanceOf(USER_ADDRESS);
const curveAave3Balance = await curveAave3Erc20.balanceOf(USER_ADDRESS); const curveAave3Balance = await curveAave3Erc20.balanceOf(USER_ADDRESS);
const curveAnkrBalance = await curveAnkrErc20.balanceOf(USER_ADDRESS); const curveAnkrBalance = await curveAnkrErc20.balanceOf(USER_ADDRESS);
@ -292,16 +300,17 @@ makeSuite('Curve Rewards Aware aToken', (testEnv: TestEnv) => {
eEthereumNetwork.main eEthereumNetwork.main
); );
const { proxy: curveTreasury } = await DRE.run('deploy-curve-treasury', { const { proxy: curveTreasuryAddress } = await DRE.run('deploy-curve-treasury', {
proxyAdmin: ZERO_ADDRESS, proxyAdmin: testEnv.users[1].address,
treasuryAdmin: ZERO_ADDRESS, treasuryAdmin: testEnv.users[0].address,
collector, collector,
}); });
// Gauge tokens should be listed at Aave test deployment // Gauge tokens should be listed at Aave test deployment
await listCurveLPToken(GAUGE_3POOL, curveTreasury); await listCurveLPToken(GAUGE_3POOL, curveTreasuryAddress);
await listCurveLPToken(GAUGE_EURS, curveTreasury); await listCurveLPToken(GAUGE_EURS, curveTreasuryAddress);
await listCurveLPToken(GAUGE_AAVE3, curveTreasury); await listCurveLPToken(GAUGE_AAVE3, curveTreasuryAddress);
await listCurveLPToken(GAUGE_ANKR, curveTreasury); await listCurveLPToken(GAUGE_ANKR, curveTreasuryAddress);
const allTokens = await testEnv.helpersContract.getAllATokens(); const allTokens = await testEnv.helpersContract.getAllATokens();
@ -319,9 +328,26 @@ makeSuite('Curve Rewards Aware aToken', (testEnv: TestEnv) => {
); );
aANKR = IRewardsAwareATokenFactory.connect( aANKR = IRewardsAwareATokenFactory.connect(
allTokens.find((aToken) => aToken.symbol.includes('ankr'))?.tokenAddress || ZERO_ADDRESS, allTokens.find((aToken) => aToken.symbol.includes('ankr'))?.tokenAddress || ZERO_ADDRESS,
await getFirstSigner() await getFirstSigner()
); );
curveTreasury = CurveTreasuryFactory.connect(curveTreasuryAddress, testEnv.users[0].signer);
// Enable atoken entities into Curve Treasury
console.log(a3POOL.address, aEURS.address, aAAVE3.address, aANKR.address);
await waitForTx(
await curveTreasury.setWhitelist(
[a3POOL.address, aEURS.address, aAAVE3.address, aANKR.address],
[
GAUGE_3POOL.underlying,
GAUGE_EURS.underlying,
GAUGE_AAVE3.underlying,
GAUGE_ANKR.underlying,
],
[GAUGE_3POOL.address, GAUGE_EURS.address, GAUGE_AAVE3.address, GAUGE_ANKR.address],
[false, true, true, true],
[true, true, true, true]
)
);
}); });
after('Reset', () => { after('Reset', () => {
@ -390,7 +416,7 @@ makeSuite('Curve Rewards Aware aToken', (testEnv: TestEnv) => {
it('Deposit and generate user reward checkpoints', async () => { it('Deposit and generate user reward checkpoints', async () => {
// Deposits // Deposits
await depositPoolToken(depositor, GAUGE_3POOL, a3POOL.address, parseEther('100000')); await depositPoolToken(depositor, GAUGE_3POOL, a3POOL.address, parseEther('2000'));
const curveATokenBalance = await crvToken.balanceOf(a3POOL.address); const curveATokenBalance = await crvToken.balanceOf(a3POOL.address);
expect(curveATokenBalance).to.be.eq('0', 'CRV rewards should be zero'); expect(curveATokenBalance).to.be.eq('0', 'CRV rewards should be zero');
}); });
@ -408,19 +434,19 @@ makeSuite('Curve Rewards Aware aToken', (testEnv: TestEnv) => {
); );
}); });
it('Pass time and withdraw Staked AAVE3', async () => { it('Pass time and withdraw Staked 3pool', async () => {
// Pass time to generate rewards // Pass time to generate rewards
await increaseTime(ONE_DAY); await increaseTime(ONE_DAY);
// Withdraw // Withdraw
await withdrawPoolToken(depositor, GAUGE_3POOL, a3POOL.address); await withdrawPoolToken(depositor, GAUGE_3POOL, a3POOL.address, true);
const curveATokenBalance = await crvToken.balanceOf(a3POOL.address); const curveATokenBalance = await crvToken.balanceOf(a3POOL.address);
expect(curveATokenBalance).to.be.eq('0', 'CRV rewards should be zero'); expect(curveATokenBalance).to.be.eq('0', 'CRV rewards should be zero');
}); });
it('Claim the remaining CRV', async () => { it('Claim the remaining CRV, should not reward', async () => {
// Claim // Claim
await claimFromGauge(depositor, GAUGE_AAVE3, aAAVE3.address); await claimFromGauge(depositor, GAUGE_AAVE3, aAAVE3.address, false);
const curveATokenBalance = await crvToken.balanceOf(a3POOL.address); const curveATokenBalance = await crvToken.balanceOf(a3POOL.address);
expect(curveATokenBalance).to.be.eq( expect(curveATokenBalance).to.be.eq(
'0', '0',