From 26e4b27a3c6c360be0da317620ba7ad052893b71 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 16 Jun 2021 16:50:33 -0400 Subject: [PATCH 01/33] Upgrade to AAVE v2. --- contracts/contracts/mocks/MockAave.sol | 20 +++++++--- .../contracts/strategies/AaveStrategy.sol | 4 +- contracts/contracts/strategies/IAave.sol | 39 +++++++++++++++---- 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/contracts/contracts/mocks/MockAave.sol b/contracts/contracts/mocks/MockAave.sol index ab109e72de..e9d72925db 100644 --- a/contracts/contracts/mocks/MockAave.sol +++ b/contracts/contracts/mocks/MockAave.sol @@ -47,11 +47,11 @@ contract MockAToken is ERC20Mintable, ERC20Detailed { addMinter(_lendingPool); } - function redeem(uint256 _amount) external { + function poolRedeem(uint256 _amount, address _to) external { // Redeem these a Tokens - _burn(msg.sender, _amount); + _burn(_to, _amount); // For the underlying - underlyingToken.safeTransferFrom(lendingPool, msg.sender, _amount); + underlyingToken.safeTransferFrom(lendingPool, _to, _amount); } } @@ -80,7 +80,8 @@ contract MockAave is IAaveLendingPool, ILendingPoolAddressesProvider { function deposit( address _reserve, uint256 _amount, - uint16 /*_referralCode*/ + address _to, + uint16 /*referralCode*/ ) external { uint256 previousBal = IERC20(reserveToAToken[_reserve]).balanceOf( msg.sender @@ -90,7 +91,16 @@ contract MockAave is IAaveLendingPool, ILendingPoolAddressesProvider { // Take their reserve IERC20(_reserve).safeTransferFrom(msg.sender, address(this), _amount); // Credit them with aToken - ERC20Mintable(reserveToAToken[_reserve]).mint(msg.sender, _amount); + ERC20Mintable(reserveToAToken[_reserve]).mint(_to, _amount); + } + + function withdraw( + address asset, + uint256 amount, + address to + ) external returns (uint256) { + MockAToken atoken = MockAToken(reserveToAToken[asset]); + atoken.poolRedeem(amount, to); } function getLendingPool() external view returns (address) { diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index 01a6cb156d..48ffd04205 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -38,7 +38,7 @@ contract AaveStrategy is InitializableAbstractStrategy { require(_amount > 0, "Must deposit something"); IAaveAToken aToken = _getATokenFor(_asset); emit Deposit(_asset, address(aToken), _amount); - _getLendingPool().deposit(_asset, _amount, referralCode); + _getLendingPool().deposit(_asset, _amount, address(this), referralCode); } /** @@ -70,7 +70,7 @@ contract AaveStrategy is InitializableAbstractStrategy { IAaveAToken aToken = _getATokenFor(_asset); emit Withdrawal(_asset, address(aToken), _amount); - aToken.redeem(_amount); + _getLendingPool().withdraw(_asset, _amount, address(this)); IERC20(_asset).safeTransfer(_recipient, _amount); } diff --git a/contracts/contracts/strategies/IAave.sol b/contracts/contracts/strategies/IAave.sol index 723b6d15c8..9ca1f99d73 100644 --- a/contracts/contracts/strategies/IAave.sol +++ b/contracts/contracts/strategies/IAave.sol @@ -26,16 +26,39 @@ interface IAaveAToken { */ interface IAaveLendingPool { /** - * @notice Deposits a certain _amount of an asset specified by the _reserve parameter. - * @dev The caller receives a certain amount of corresponding aTokens in exchange. - * The amount of aTokens received depends on the corresponding aToken exchange rate. - * LendingPoolCore must be approved to spend this reserve - */ + * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. + * - E.g. User deposits 100 USDC and gets in return 100 aUSDC + * @param asset The address of the underlying asset to deposit + * @param amount The amount to be deposited + * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user + * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens + * is a different wallet + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + **/ function deposit( - address _reserve, - uint256 _amount, - uint16 _referralCode + address asset, + uint256 amount, + address onBehalfOf, + uint16 referralCode ) external; + + /** + * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned + * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC + * @param asset The address of the underlying asset to withdraw + * @param amount The underlying amount to be withdrawn + * - Send the value type(uint256).max in order to withdraw the whole aToken balance + * @param to Address that will receive the underlying, same as msg.sender if the user + * wants to receive it on his own wallet, or a different address if the beneficiary is a + * different wallet + * @return The final amount withdrawn + **/ + function withdraw( + address asset, + uint256 amount, + address to + ) external returns (uint256); } /** From a36ffbd916760ab23890d65929eaff348120bf89 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 16 Jun 2021 16:55:54 -0400 Subject: [PATCH 02/33] =?UTF-8?q?It=20is=20causing=20me=20pain=20to=20leav?= =?UTF-8?q?e=20this=20out.=20Even=20though=20I=20know=20it=E2=80=99s=20jus?= =?UTF-8?q?t=20a=20mock=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contracts/contracts/mocks/MockAave.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/contracts/mocks/MockAave.sol b/contracts/contracts/mocks/MockAave.sol index e9d72925db..089ffdcfe2 100644 --- a/contracts/contracts/mocks/MockAave.sol +++ b/contracts/contracts/mocks/MockAave.sol @@ -48,6 +48,7 @@ contract MockAToken is ERC20Mintable, ERC20Detailed { } function poolRedeem(uint256 _amount, address _to) external { + require(msg.sender == lendingPool, "pool only"); // Redeem these a Tokens _burn(_to, _amount); // For the underlying From a20ccd37624b6b189ffe58d89406637053fcf6c3 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 16 Jun 2021 17:08:24 -0400 Subject: [PATCH 03/33] Undo accidental edit --- contracts/contracts/mocks/MockAave.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/contracts/mocks/MockAave.sol b/contracts/contracts/mocks/MockAave.sol index 089ffdcfe2..ad24b3d4e4 100644 --- a/contracts/contracts/mocks/MockAave.sol +++ b/contracts/contracts/mocks/MockAave.sol @@ -82,7 +82,7 @@ contract MockAave is IAaveLendingPool, ILendingPoolAddressesProvider { address _reserve, uint256 _amount, address _to, - uint16 /*referralCode*/ + uint16 /*_referralCode*/ ) external { uint256 previousBal = IERC20(reserveToAToken[_reserve]).balanceOf( msg.sender From 62765b26993c7364d250c2cd1067d97f573143f8 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 16 Jun 2021 17:22:12 -0400 Subject: [PATCH 04/33] Verify amount actually withdrawn. --- contracts/contracts/mocks/MockAave.sol | 1 + contracts/contracts/strategies/AaveStrategy.sol | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/contracts/contracts/mocks/MockAave.sol b/contracts/contracts/mocks/MockAave.sol index ad24b3d4e4..ba1211357e 100644 --- a/contracts/contracts/mocks/MockAave.sol +++ b/contracts/contracts/mocks/MockAave.sol @@ -102,6 +102,7 @@ contract MockAave is IAaveLendingPool, ILendingPoolAddressesProvider { ) external returns (uint256) { MockAToken atoken = MockAToken(reserveToAToken[asset]); atoken.poolRedeem(amount, to); + return amount; } function getLendingPool() external view returns (address) { diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index 48ffd04205..8c9a3bb4e6 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -70,7 +70,12 @@ contract AaveStrategy is InitializableAbstractStrategy { IAaveAToken aToken = _getATokenFor(_asset); emit Withdrawal(_asset, address(aToken), _amount); - _getLendingPool().withdraw(_asset, _amount, address(this)); + uint256 actual = _getLendingPool().withdraw( + _asset, + _amount, + address(this) + ); + require(actual >= _amount, "Did not withdraw enough"); IERC20(_asset).safeTransfer(_recipient, _amount); } From d6276fd5221afc2e607c6e21f94f8172c574d205 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Fri, 2 Jul 2021 16:49:38 -0400 Subject: [PATCH 05/33] Add untested AAVE rewards collection to strategy --- .../contracts/strategies/AaveStrategy.sol | 92 +++++++++++ .../strategies/IAaveIncentivesController.sol | 145 ++++++++++++++++++ .../contracts/strategies/IAaveStakeToken.sol | 13 ++ contracts/deploy/001_core.js | 4 +- 4 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 contracts/contracts/strategies/IAaveIncentivesController.sol create mode 100644 contracts/contracts/strategies/IAaveStakeToken.sol diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index 8c9a3bb4e6..47b2fbc6d1 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -11,9 +11,47 @@ import { InitializableAbstractStrategy } from "../utils/InitializableAbstractStrategy.sol"; +import { IAaveStakedToken } from "./IAaveStakedToken.sol"; +import { IAaveIncentivesController } from "./IAaveIncentivesController.sol"; + contract AaveStrategy is InitializableAbstractStrategy { uint16 constant referralCode = 92; + IAaveIncentivesController incentivesController; + IAaveStakedToken stkAave; + + /** + * Initializer for setting up strategy internal state. This overrides the + * InitializableAbstractStrategy initializer as AAVE needs several extra + * addresses for the rewards program. + * @param _platformAddress Address of the AAVE pool + * @param _vaultAddress Address of the vault + * @param _rewardTokenAddress Address of the AAVE token + * @param _assets Addresses of supported assets + * @param _pTokens Platform Token corresponding addresses + * @param _incentivesAddress Address of the AAVE incentives controller + * @param _stkAaveAddress Address of the stkAave contract + */ + function initialize( + address _platformAddress, // AAVE pool + address _vaultAddress, + address _rewardTokenAddress, // AAVE + address[] calldata _assets, + address[] calldata _pTokens, + address _incentivesAddress, + address _stkAaveAddress + ) external onlyGovernor initializer { + incentivesController = IAaveIncentivesController(_incentivesAddress); + stkAave = IAaveStakedToken(_stkAaveAddress); + InitializableAbstractStrategy._initialize( + _platformAddress, + _vaultAddress, + _rewardTokenAddress, + _assets, + _pTokens + ); + } + /** * @dev Deposit asset into Aave * @param _asset Address of asset to deposit @@ -190,4 +228,58 @@ contract AaveStrategy is InitializableAbstractStrategy { ); return lendingPoolCore; } + + /** + * @dev Collect stkAAVE, convert it to AAVE send to Vault. + */ + function collectRewardToken() external onlyVault nonReentrant { + if(stkAAVE == address(0)){ + return; + } + + // Check staked AAVE cooldown timer + uint256 cooldown = stkAAVE.stakersCooldowns(address(this)); + uint256 windowStart = cooldown + stkAAVE.COOLDOWN_SECONDS(); + uint256 windowEnd = windowStart + stkAAVE.UNSTAKE_WINDOW(); + uint256 currentTimestamp = now; + + // If inside the unlock window, then we can redeem stkAAVE + // for AAVE and send it to the vault. + if (currentTimestamp > windowStart && currentTimestamp < windowEnd) { + // Redeem to AAVE + uint256 stkAaveBalance = stkAave.balanceOf(address(this)); + if (stkAaveBalance > rewardLiquidationThreshold) { + stkAave.redeem(address(this), stkAaveBalance); + } + // Transfer AAVE to vaultAddress + uint256 aaveBalance = _rewardTokenAddress.balanceOf(address(this)); + if (aaveBalance > 0) { + _rewardTokenAddress.transfer(vault, aaveBalance); + } + } + + // If we were past the start of the window, + // or if the cooldown counter is not running, + // then start the unlock cooldown. + if (currentTimestamp > windowStart || cooldown == 0) { + uint256 pendingRewards = incentivesController.getRewardsBalance( + assetsMapped, + address(this) + ); + if (pendingRewards > 0) { + // claimRewards() may pause or push the cooldown time + // into the future. It needs to be run after any rewards would be + // collected, but before the cooldown is restarted. + incentivesController.claimRewards( + assetsMapped, + pendingRewards, + address(this) + ); + } + // Cooldown call reverts if no stkAAVE balance + if (stkAave.balanceOf(address(this)) > 0) { + stkAave.cooldown(); + } + } + } } diff --git a/contracts/contracts/strategies/IAaveIncentivesController.sol b/contracts/contracts/strategies/IAaveIncentivesController.sol new file mode 100644 index 0000000000..4ec76aa4e7 --- /dev/null +++ b/contracts/contracts/strategies/IAaveIncentivesController.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.5.11; +pragma experimental ABIEncoderV2; + +interface IAaveIncentivesController { + event RewardsAccrued(address indexed user, uint256 amount); + + event RewardsClaimed( + address indexed user, + address indexed to, + uint256 amount + ); + + event RewardsClaimed( + address indexed user, + address indexed to, + address indexed claimer, + uint256 amount + ); + + event ClaimerSet(address indexed user, address indexed claimer); + + /* + * @dev Returns the configuration of the distribution for a certain asset + * @param asset The address of the reference asset of the distribution + * @return The asset index, the emission per second and the last updated timestamp + **/ + function getAssetData(address asset) + external + view + returns ( + uint256, + uint256, + uint256 + ); + + /** + * @dev Whitelists an address to claim the rewards on behalf of another address + * @param user The address of the user + * @param claimer The address of the claimer + */ + function setClaimer(address user, address claimer) external; + + /** + * @dev Returns the whitelisted claimer for a certain address (0x0 if not set) + * @param user The address of the user + * @return The claimer address + */ + function getClaimer(address user) external view returns (address); + + /** + * @dev Configure assets for a certain rewards emission + * @param assets The assets to incentivize + * @param emissionsPerSecond The emission for each asset + */ + function configureAssets( + address[] calldata assets, + uint256[] calldata emissionsPerSecond + ) external; + + /** + * @dev Called by the corresponding asset on any update that affects the rewards distribution + * @param asset The address of the user + * @param userBalance The balance of the user of the asset in the lending pool + * @param totalSupply The total supply of the asset in the lending pool + **/ + function handleAction( + address asset, + uint256 userBalance, + uint256 totalSupply + ) external; + + /** + * @dev Returns the total of rewards of an user, already accrued + not yet accrued + * @param user The address of the user + * @return The rewards + **/ + function getRewardsBalance(address[] calldata assets, address user) + external + view + returns (uint256); + + /** + * @dev Claims reward for an user, on all the assets of the lending pool, accumulating the pending rewards + * @param amount Amount of rewards to claim + * @param to Address that will be receiving the rewards + * @return Rewards claimed + **/ + function claimRewards( + address[] calldata assets, + uint256 amount, + address to + ) external returns (uint256); + + /** + * @dev Claims reward for an user on behalf, on all the assets of the lending pool, accumulating the pending rewards. The caller must + * be whitelisted via "allowClaimOnBehalf" function by the RewardsAdmin role manager + * @param amount Amount of rewards to claim + * @param user Address to check and claim rewards + * @param to Address that will be receiving the rewards + * @return Rewards claimed + **/ + function claimRewardsOnBehalf( + address[] calldata assets, + uint256 amount, + address user, + address to + ) external returns (uint256); + + /** + * @dev returns the unclaimed rewards of the user + * @param user the address of the user + * @return the unclaimed user rewards + */ + function getUserUnclaimedRewards(address user) + external + view + returns (uint256); + + /** + * @dev returns the unclaimed rewards of the user + * @param user the address of the user + * @param asset The asset to incentivize + * @return the user index for the asset + */ + function getUserAssetData(address user, address asset) + external + view + returns (uint256); + + /** + * @dev for backward compatibility with previous implementation of the Incentives controller + */ + function REWARD_TOKEN() external view returns (address); + + /** + * @dev for backward compatibility with previous implementation of the Incentives controller + */ + function PRECISION() external view returns (uint8); + + /** + * @dev Gets the distribution end timestamp of the emissions + */ + function DISTRIBUTION_END() external view returns (uint256); +} diff --git a/contracts/contracts/strategies/IAaveStakeToken.sol b/contracts/contracts/strategies/IAaveStakeToken.sol new file mode 100644 index 0000000000..a0836bb1c1 --- /dev/null +++ b/contracts/contracts/strategies/IAaveStakeToken.sol @@ -0,0 +1,13 @@ +pragma solidity 0.5.11; + +interface IAaveStakedToken { + function balanceOf(address addr) external returns (uint256); + + function redeem(address to, uint256 amount) external; + + function stakersCooldowns(address addr) external returns (uint256); + + function COOLDOWN_SECONDS() external returns (uint256); + + function UNSTAKE_WINDOW() external returns (uint256); +} diff --git a/contracts/deploy/001_core.js b/contracts/deploy/001_core.js index 4bdc4b1fd2..7fe21ada34 100644 --- a/contracts/deploy/001_core.js +++ b/contracts/deploy/001_core.js @@ -52,7 +52,9 @@ const deployAaveStrategy = async () => { cVaultProxy.address, addresses.zero, // No reward token for Aave [assetAddresses.DAI], - [assetAddresses.aDAI] + [assetAddresses.aDAI], + addresses.zero, + addresses.zero, ) ); log("Initialized AaveStrategy"); From d54af5b08269ad851ff39c38a75e6d3e874e5270 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Sun, 11 Jul 2021 19:56:03 -0400 Subject: [PATCH 06/33] Add mock for stakedAAVE, and add unit tests foundation. --- contracts/contracts/mocks/MockStkAave.sol | 64 +++++++++++++++++++ .../contracts/strategies/AaveStrategy.sol | 27 ++++---- .../contracts/strategies/IAaveStakeToken.sol | 8 ++- contracts/deploy/000_mock.js | 7 +- contracts/deploy/001_core.js | 6 +- contracts/test/_fixture.js | 7 +- contracts/test/helpers.js | 6 ++ contracts/test/strategies/aave.js | 59 ++++++++++++++++- 8 files changed, 165 insertions(+), 19 deletions(-) create mode 100644 contracts/contracts/mocks/MockStkAave.sol diff --git a/contracts/contracts/mocks/MockStkAave.sol b/contracts/contracts/mocks/MockStkAave.sol new file mode 100644 index 0000000000..76edda3c5f --- /dev/null +++ b/contracts/contracts/mocks/MockStkAave.sol @@ -0,0 +1,64 @@ +pragma solidity 0.5.11; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "./MintableERC20.sol"; + +contract MockStkAave is MintableERC20 { + uint256 public constant decimals = 18; + string public constant symbol = "stkAAVE"; + string public constant name = "Staked Aave"; + + uint256 public COOLDOWN_SECONDS = 864000; + uint256 public UNSTAKE_WINDOW = 172800; + address public STAKED_TOKEN; + + mapping(address => uint256) public stakerRewardsToClaim; + mapping(address => uint256) public stakersCooldowns; + + using SafeERC20 for IERC20; + + constructor(address _stakedToken) public { + STAKED_TOKEN = _stakedToken; + } + + /** + * @dev Redeems staked tokens, and stop earning rewards + * @param to Address to redeem to + * @param amount Amount to redeem + **/ + function redeem(address to, uint256 amount) external { + uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender]; + uint256 windowStart = cooldownStartTimestamp.add(COOLDOWN_SECONDS); + require(amount != 0, "INVALID_ZERO_AMOUNT"); + require(block.timestamp > windowStart, "INSUFFICIENT_COOLDOWN"); + require( + block.timestamp.sub(windowStart) <= UNSTAKE_WINDOW, + "UNSTAKE_WINDOW_FINISHED" + ); + uint256 balanceOfMessageSender = balanceOf(msg.sender); + uint256 amountToRedeem = (amount > balanceOfMessageSender) + ? balanceOfMessageSender + : amount; + + stakersCooldowns[msg.sender] = 0; + _burn(msg.sender, amountToRedeem); + IERC20(STAKED_TOKEN).safeTransfer(to, amountToRedeem); + } + + /** + * @dev Activates the cooldown period to unstake + * - It can't be called if the user is not staking + **/ + function cooldown() external { + require(balanceOf(msg.sender) != 0, "INVALID_BALANCE_ON_COOLDOWN"); + stakersCooldowns[msg.sender] = block.timestamp; + } + + /** + * @dev Test helper function to allow changing the cooldown + **/ + function setCooldown(address account, uint256 _cooldown) external { + stakersCooldowns[account] = _cooldown; + } +} diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index 47b2fbc6d1..96f96c34a5 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -11,7 +11,7 @@ import { InitializableAbstractStrategy } from "../utils/InitializableAbstractStrategy.sol"; -import { IAaveStakedToken } from "./IAaveStakedToken.sol"; +import { IAaveStakedToken } from "./IAaveStakeToken.sol"; import { IAaveIncentivesController } from "./IAaveIncentivesController.sol"; contract AaveStrategy is InitializableAbstractStrategy { @@ -114,7 +114,7 @@ contract AaveStrategy is InitializableAbstractStrategy { address(this) ); require(actual >= _amount, "Did not withdraw enough"); - IERC20(_asset).safeTransfer(_recipient, _amount); + IERC20(_asset).transfer(_recipient, _amount); } /** @@ -230,20 +230,20 @@ contract AaveStrategy is InitializableAbstractStrategy { } /** - * @dev Collect stkAAVE, convert it to AAVE send to Vault. + * @dev Collect stkAave, convert it to AAVE send to Vault. */ function collectRewardToken() external onlyVault nonReentrant { - if(stkAAVE == address(0)){ + if (address(stkAave) == address(0)) { return; } // Check staked AAVE cooldown timer - uint256 cooldown = stkAAVE.stakersCooldowns(address(this)); - uint256 windowStart = cooldown + stkAAVE.COOLDOWN_SECONDS(); - uint256 windowEnd = windowStart + stkAAVE.UNSTAKE_WINDOW(); + uint256 cooldown = stkAave.stakersCooldowns(address(this)); + uint256 windowStart = cooldown + stkAave.COOLDOWN_SECONDS(); + uint256 windowEnd = windowStart + stkAave.UNSTAKE_WINDOW(); uint256 currentTimestamp = now; - // If inside the unlock window, then we can redeem stkAAVE + // If inside the unlock window, then we can redeem stkAave // for AAVE and send it to the vault. if (currentTimestamp > windowStart && currentTimestamp < windowEnd) { // Redeem to AAVE @@ -252,9 +252,14 @@ contract AaveStrategy is InitializableAbstractStrategy { stkAave.redeem(address(this), stkAaveBalance); } // Transfer AAVE to vaultAddress - uint256 aaveBalance = _rewardTokenAddress.balanceOf(address(this)); + uint256 aaveBalance = IERC20(rewardTokenAddress).balanceOf( + address(this) + ); if (aaveBalance > 0) { - _rewardTokenAddress.transfer(vault, aaveBalance); + IERC20(rewardTokenAddress).safeTransfer( + vaultAddress, + aaveBalance + ); } } @@ -276,7 +281,7 @@ contract AaveStrategy is InitializableAbstractStrategy { address(this) ); } - // Cooldown call reverts if no stkAAVE balance + // Cooldown call reverts if no stkAave balance if (stkAave.balanceOf(address(this)) > 0) { stkAave.cooldown(); } diff --git a/contracts/contracts/strategies/IAaveStakeToken.sol b/contracts/contracts/strategies/IAaveStakeToken.sol index a0836bb1c1..00d2b08801 100644 --- a/contracts/contracts/strategies/IAaveStakeToken.sol +++ b/contracts/contracts/strategies/IAaveStakeToken.sol @@ -1,13 +1,15 @@ pragma solidity 0.5.11; interface IAaveStakedToken { + function COOLDOWN_SECONDS() external returns (uint256); + + function UNSTAKE_WINDOW() external returns (uint256); + function balanceOf(address addr) external returns (uint256); function redeem(address to, uint256 amount) external; function stakersCooldowns(address addr) external returns (uint256); - function COOLDOWN_SECONDS() external returns (uint256); - - function UNSTAKE_WINDOW() external returns (uint256); + function cooldown() external; } diff --git a/contracts/deploy/000_mock.js b/contracts/deploy/000_mock.js index 2cad451d6e..e50cff7a2a 100644 --- a/contracts/deploy/000_mock.js +++ b/contracts/deploy/000_mock.js @@ -71,11 +71,11 @@ const deployMocks = async ({ getNamedAccounts, deployments }) => { from: governorAddr, }); - // Deploy mock uniswap pair oracles. const weth = await ethers.getContract("MockWETH"); const dai = await ethers.getContract("MockDAI"); const usdc = await ethers.getContract("MockUSDC"); const usdt = await ethers.getContract("MockUSDT"); + const aave = await ethers.getContract("MockAave"); // Deploy mock aTokens (Aave) // MockAave is the mock lendingPool @@ -172,6 +172,11 @@ const deployMocks = async ({ getNamedAccounts, deployments }) => { args: [[dai.address, usdc.address, usdt.address], threePoolToken.address], }); + await deploy("MockStkAave", { + from: deployerAddr, + args: [aave.address], + }); + await deploy("MockNonRebasing", { from: deployerAddr, }); diff --git a/contracts/deploy/001_core.js b/contracts/deploy/001_core.js index 7fe21ada34..b10c3cec4f 100644 --- a/contracts/deploy/001_core.js +++ b/contracts/deploy/001_core.js @@ -46,15 +46,17 @@ const deployAaveStrategy = async () => { ) ); log("Initialized AaveStrategyProxy"); + const initFunctionName = + "initialize(address,address,address,address[],address[],address,address)"; await withConfirmation( - cAaveStrategy.connect(sDeployer).initialize( + cAaveStrategy.connect(sDeployer)[initFunctionName]( assetAddresses.AAVE_ADDRESS_PROVIDER, cVaultProxy.address, addresses.zero, // No reward token for Aave [assetAddresses.DAI], [assetAddresses.aDAI], addresses.zero, - addresses.zero, + assetAddresses.STKAAVE ) ); log("Initialized AaveStrategy"); diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index 346ddce41d..0eb743a5da 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -93,6 +93,8 @@ async function defaultFixture() { cusdc, comp, adai, + aave, + stkAave, mockNonRebasing, mockNonRebasingTwo; @@ -145,12 +147,13 @@ async function defaultFixture() { adai = await ethers.getContract("MockADAI"); - const aave = await ethers.getContract("MockAave"); + aave = await ethers.getContract("MockAave"); // currently in test the mockAave is itself the address provder aaveAddressProvider = await ethers.getContractAt( "ILendingPoolAddressesProvider", aave.address ); + stkAave = await ethers.getContract("MockStkAave"); uniswapPairOUSD_USDT = await ethers.getContract("MockUniswapPairOUSD_USDT"); @@ -246,6 +249,8 @@ async function defaultFixture() { threePoolStrategy, aaveStrategy, aaveAddressProvider, + aave, + stkAave, uniswapPairOUSD_USDT, liquidityRewardOUSD_USDT, ognStaking, diff --git a/contracts/test/helpers.js b/contracts/test/helpers.js index 4cb454fc79..b5d44acf21 100644 --- a/contracts/test/helpers.js +++ b/contracts/test/helpers.js @@ -150,6 +150,10 @@ const advanceTime = async (seconds) => { await hre.ethers.provider.send("evm_mine"); }; +const getBlockTimestamp = async (seconds) => { + return (await hre.ethers.provider.getBlock("latest")).timestamp; +}; + const advanceBlocks = async (numBlocks) => { for (let i = 0; i < numBlocks; i++) { await hre.ethers.provider.send("evm_mine"); @@ -260,6 +264,7 @@ const getAssetAddresses = async (deployments) => { aUSDT: (await deployments.get("MockAUSDT")).address, AAVE: (await deployments.get("MockAave")).address, AAVE_ADDRESS_PROVIDER: (await deployments.get("MockAave")).address, + STKAAVE: (await deployments.get("MockStkAave")).address, OGN: isRinkeby ? addresses.rinkeby.OGN : (await deployments.get("MockOGN")).address, @@ -343,6 +348,7 @@ module.exports = { humanBalance, expectApproxSupply, advanceTime, + getBlockTimestamp, isMainnet, isRinkeby, isFork, diff --git a/contracts/test/strategies/aave.js b/contracts/test/strategies/aave.js index a35b02abb2..7eda51a858 100644 --- a/contracts/test/strategies/aave.js +++ b/contracts/test/strategies/aave.js @@ -1,13 +1,14 @@ const { expect } = require("chai"); const { utils } = require("ethers"); -const { aaveVaultFixture } = require("../_fixture"); +const { aaveVaultFixture, defaultFixture } = require("../_fixture"); const { daiUnits, ousdUnits, units, loadFixture, expectApproxSupply, + getBlockTimestamp, isFork, } = require("../helpers"); @@ -128,4 +129,60 @@ describe("Aave Strategy", function () { ).to.be.revertedWith("Caller is not the Governor"); }); }); + + describe("Rewards", function () { + let aaveStrategy, stkAave, aave, vault, governor; + let currentTimestamp; + const STAKE_AMOUNT = "10000000000"; + + const collectRewards = function (opts) { + return async function () { + const fixture = await loadFixture(aaveVaultFixture); + aaveStrategy = fixture.aaveStrategy; + aave = fixture.aave; + stkAave = fixture.stkAave; + vault = fixture.vault; + governor = fixture.governor; + + let { staked } = opts; + await vault.connect(governor)["harvest()"](); + + currentTimestamp = await getBlockTimestamp(); + }; + }; + + const cooldownWasReset = async () => { + const cooldown = await stkAave.stakersCooldowns(aaveStrategy.address); + expect(currentTimestamp).to.equal(cooldown); + }; + const cooldownUnchanged = async () => { + const cooldown = await stkAave.stakersCooldowns(aaveStrategy.address); + expect(currentTimestamp).to.not.equal(cooldown); + }; + + describe("In cooldown window", async function () { + // before(collectRewards({})) + // it("Should set the cooldown", cooldownWasReset) + }); + describe("Before cooldown window", async function () { + // before(collectRewards({})) + // it("Should not change cooldown", cooldownUnchanged) + }); + describe("No cooldown set", async function () { + // before(collectRewards({})) + // it("Should set the cooldown", cooldownWasReset) + }); + describe("After window", async function () { + // before(collectRewards({})) + // it("Should set the cooldown", cooldownWasReset) + }); + describe("No pending rewards", async function () { + // before(collectRewards({})) + // it("Should set the cooldown", cooldownWasReset) + }); + describe("No stakeAave claimed or avaialbe", async function () { + // before(collectRewards({})) + // it("Should not change cooldown", cooldownUnchanged) + }); + }); }); From c4a4b67748b16d85145279f382917ef608e05b06 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Sun, 11 Jul 2021 20:10:30 -0400 Subject: [PATCH 07/33] Check return values. --- contracts/contracts/strategies/AaveStrategy.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index 96f96c34a5..67a645c59f 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -114,7 +114,7 @@ contract AaveStrategy is InitializableAbstractStrategy { address(this) ); require(actual >= _amount, "Did not withdraw enough"); - IERC20(_asset).transfer(_recipient, _amount); + IERC20(_asset).safeTransfer(_recipient, _amount); } /** @@ -275,11 +275,12 @@ contract AaveStrategy is InitializableAbstractStrategy { // claimRewards() may pause or push the cooldown time // into the future. It needs to be run after any rewards would be // collected, but before the cooldown is restarted. - incentivesController.claimRewards( + uint256 collected = incentivesController.claimRewards( assetsMapped, pendingRewards, address(this) ); + require(collected == pendingRewards, "AAVE reward difference"); } // Cooldown call reverts if no stkAave balance if (stkAave.balanceOf(address(this)) > 0) { From dfefb38de27e4c4dee32ceae12a0976527501384 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 14 Jul 2021 10:30:15 -0400 Subject: [PATCH 08/33] Full mocks for everything needed by AAVE Strategy --- contracts/contracts/mocks/MockAAVEToken.sol | 9 + .../mocks/MockAaveIncentivesController.sol | 50 +++++ contracts/contracts/mocks/MockStkAave.sol | 4 + contracts/deploy/000_mock.js | 16 +- contracts/deploy/001_core.js | 25 ++- contracts/test/_fixture.js | 8 +- contracts/test/helpers.js | 1 + contracts/test/strategies/aave.js | 206 ++++++++++++++---- 8 files changed, 264 insertions(+), 55 deletions(-) create mode 100644 contracts/contracts/mocks/MockAAVEToken.sol create mode 100644 contracts/contracts/mocks/MockAaveIncentivesController.sol diff --git a/contracts/contracts/mocks/MockAAVEToken.sol b/contracts/contracts/mocks/MockAAVEToken.sol new file mode 100644 index 0000000000..2262dba861 --- /dev/null +++ b/contracts/contracts/mocks/MockAAVEToken.sol @@ -0,0 +1,9 @@ +pragma solidity 0.5.11; + +import "./MintableERC20.sol"; + +contract MockAAVEToken is MintableERC20 { + uint256 public constant decimals = 18; + string public constant symbol = "AAVE"; + string public constant name = "AAVE"; +} diff --git a/contracts/contracts/mocks/MockAaveIncentivesController.sol b/contracts/contracts/mocks/MockAaveIncentivesController.sol new file mode 100644 index 0000000000..fcbc844037 --- /dev/null +++ b/contracts/contracts/mocks/MockAaveIncentivesController.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.5.11; +pragma experimental ABIEncoderV2; + +import { MockStkAave } from "./MockStkAave.sol"; + +contract MockAaveIncentivesController { + mapping(address => uint256) private rewards; + MockStkAave public REWARD_TOKEN; + + constructor(address _reward_token) public { + REWARD_TOKEN = MockStkAave(_reward_token); + } + + function setRewardsBalance(address user, uint256 amount) external { + rewards[user] = amount; + } + + /** + * @dev Returns the total of rewards of an user, already accrued + not yet accrued + * @param user The address of the user + * @return The rewards + **/ + function getRewardsBalance(address[] calldata assets, address user) + external + view + returns (uint256) + { + return rewards[user]; + } + + /** + * @dev Claims reward for an user, on all the assets of the lending pool, accumulating the pending rewards + * @param amount Amount of rewards to claim + * @param to Address that will be receiving the rewards + * @return Rewards claimed + **/ + function claimRewards( + address[] calldata assets, + uint256 amount, + address to + ) external returns (uint256) { + require(amount > 0); + require(rewards[to] == amount); + REWARD_TOKEN.mint(amount); + require(REWARD_TOKEN.transfer(to, amount)); + rewards[to] = 0; + return amount; + } +} diff --git a/contracts/contracts/mocks/MockStkAave.sol b/contracts/contracts/mocks/MockStkAave.sol index 76edda3c5f..fd042bbcc4 100644 --- a/contracts/contracts/mocks/MockStkAave.sol +++ b/contracts/contracts/mocks/MockStkAave.sol @@ -22,6 +22,10 @@ contract MockStkAave is MintableERC20 { STAKED_TOKEN = _stakedToken; } + function setStakedToken(address _stakedToken) external { + STAKED_TOKEN = _stakedToken; + } + /** * @dev Redeems staked tokens, and stop earning rewards * @param to Address to redeem to diff --git a/contracts/deploy/000_mock.js b/contracts/deploy/000_mock.js index e50cff7a2a..d83a93959c 100644 --- a/contracts/deploy/000_mock.js +++ b/contracts/deploy/000_mock.js @@ -172,9 +172,23 @@ const deployMocks = async ({ getNamedAccounts, deployments }) => { args: [[dai.address, usdc.address, usdt.address], threePoolToken.address], }); + await deploy("MockAAVEToken", { + from: deployerAddr, + args: [], + }); + + const mockAaveToken = await ethers.getContract("MockAAVEToken"); + await deploy("MockStkAave", { from: deployerAddr, - args: [aave.address], + args: [mockAaveToken.address], + }); + + const mockStkAave = await ethers.getContract("MockStkAave"); + + await deploy("MockAaveIncentivesController", { + from: deployerAddr, + args: [mockStkAave.address], }); await deploy("MockNonRebasing", { diff --git a/contracts/deploy/001_core.js b/contracts/deploy/001_core.js index b10c3cec4f..8857eafe1e 100644 --- a/contracts/deploy/001_core.js +++ b/contracts/deploy/001_core.js @@ -45,19 +45,26 @@ const deployAaveStrategy = async () => { [] ) ); + + const cAaveIncentivesController = await ethers.getContract( + "MockAaveIncentivesController" + ); + log("Initialized AaveStrategyProxy"); const initFunctionName = "initialize(address,address,address,address[],address[],address,address)"; await withConfirmation( - cAaveStrategy.connect(sDeployer)[initFunctionName]( - assetAddresses.AAVE_ADDRESS_PROVIDER, - cVaultProxy.address, - addresses.zero, // No reward token for Aave - [assetAddresses.DAI], - [assetAddresses.aDAI], - addresses.zero, - assetAddresses.STKAAVE - ) + cAaveStrategy + .connect(sDeployer) + [initFunctionName]( + assetAddresses.AAVE_ADDRESS_PROVIDER, + cVaultProxy.address, + assetAddresses.AAVE_TOKEN, + [assetAddresses.DAI], + [assetAddresses.aDAI], + cAaveIncentivesController.address, + assetAddresses.STKAAVE + ) ); log("Initialized AaveStrategy"); await withConfirmation( diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index 0eb743a5da..8d3bcec0e7 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -53,6 +53,9 @@ async function defaultFixture() { "AaveStrategy", aaveStrategyProxy.address ); + const aaveIncentivesController = await ethers.getContract( + "MockAaveIncentivesController" + ); const liquidityRewardOUSD_USDT = await ethers.getContractAt( "LiquidityReward", @@ -94,6 +97,7 @@ async function defaultFixture() { comp, adai, aave, + aaveToken, stkAave, mockNonRebasing, mockNonRebasingTwo; @@ -146,7 +150,7 @@ async function defaultFixture() { threePoolGauge = await ethers.getContract("MockCurveGauge"); adai = await ethers.getContract("MockADAI"); - + aaveToken = await ethers.getContract("MockAAVEToken"); aave = await ethers.getContract("MockAave"); // currently in test the mockAave is itself the address provder aaveAddressProvider = await ethers.getContractAt( @@ -248,7 +252,9 @@ async function defaultFixture() { threePoolToken, threePoolStrategy, aaveStrategy, + aaveToken, aaveAddressProvider, + aaveIncentivesController, aave, stkAave, uniswapPairOUSD_USDT, diff --git a/contracts/test/helpers.js b/contracts/test/helpers.js index b5d44acf21..b9c398a5d7 100644 --- a/contracts/test/helpers.js +++ b/contracts/test/helpers.js @@ -263,6 +263,7 @@ const getAssetAddresses = async (deployments) => { aUSDC: (await deployments.get("MockAUSDC")).address, aUSDT: (await deployments.get("MockAUSDT")).address, AAVE: (await deployments.get("MockAave")).address, + AAVE_TOKEN: (await deployments.get("MockAAVEToken")).address, AAVE_ADDRESS_PROVIDER: (await deployments.get("MockAave")).address, STKAAVE: (await deployments.get("MockStkAave")).address, OGN: isRinkeby diff --git a/contracts/test/strategies/aave.js b/contracts/test/strategies/aave.js index 7eda51a858..032ecb8027 100644 --- a/contracts/test/strategies/aave.js +++ b/contracts/test/strategies/aave.js @@ -130,59 +130,177 @@ describe("Aave Strategy", function () { }); }); - describe("Rewards", function () { - let aaveStrategy, stkAave, aave, vault, governor; - let currentTimestamp; + describe.only("Rewards", function () { const STAKE_AMOUNT = "10000000000"; + const REWARD_AMOUNT = "70000000000"; + const ZERO_COOLDOWN = -1; + const DAY = 24 * 60 * 60; - const collectRewards = function (opts) { + const collectRewards = function (setupOpts, verificationOpts) { return async function () { + let currentTimestamp; + const fixture = await loadFixture(aaveVaultFixture); - aaveStrategy = fixture.aaveStrategy; - aave = fixture.aave; - stkAave = fixture.stkAave; - vault = fixture.vault; - governor = fixture.governor; + const aaveStrategy = fixture.aaveStrategy; + const aaveIncentives = fixture.aaveIncentivesController; + const aave = fixture.aaveToken; + const stkAave = fixture.stkAave; + const vault = fixture.vault; + const governor = fixture.governor; - let { staked } = opts; - await vault.connect(governor)["harvest()"](); + let { cooldownAgo, hasStkAave, hasRewards } = setupOpts; + // Options + stkAaveAmount = hasStkAave ? STAKE_AMOUNT : 0; + cooldownAgo = cooldownAgo == ZERO_COOLDOWN ? 0 : cooldownAgo; + rewardsAmount = hasRewards ? REWARD_AMOUNT : 0; + + // Configure + // ---- + // - Give some AAVE to stkAAVE so that we can redeem the stkAAVE + await aave.connect(governor).mint(stkAaveAmount); + await aave.connect(governor).transfer(stkAave.address, stkAaveAmount); + // Setup for test + // ---- + if (cooldownAgo > 0) { + currentTimestamp = await getBlockTimestamp(); + cooldown = currentTimestamp - cooldownAgo; + await stkAave.setCooldown(aaveStrategy.address, cooldown); + } + if (stkAaveAmount > 0) { + await stkAave.connect(governor).mint(stkAaveAmount); + await stkAave + .connect(governor) + .transfer(aaveStrategy.address, stkAaveAmount); + } + if (rewardsAmount > 0) { + await aaveIncentives.setRewardsBalance( + aaveStrategy.address, + rewardsAmount + ); + } + + // Run + // ---- + await vault.connect(governor)["harvest()"](); currentTimestamp = await getBlockTimestamp(); - }; - }; - const cooldownWasReset = async () => { - const cooldown = await stkAave.stakersCooldowns(aaveStrategy.address); - expect(currentTimestamp).to.equal(cooldown); - }; - const cooldownUnchanged = async () => { - const cooldown = await stkAave.stakersCooldowns(aaveStrategy.address); - expect(currentTimestamp).to.not.equal(cooldown); + // Verification + // ---- + const { + shouldConvertStkAAVEToAAVE, + shouldResetCooldown, + shouldClaimRewards, + } = verificationOpts; + if (shouldConvertStkAAVEToAAVE) { + const stratAave = await aave.balanceOf(aaveStrategy.address); + expect(stratAave).to.equal("0", "AAVE:Strategy"); + const vaultAave = await aave.balanceOf(vault.address); + expect(vaultAave).to.equal(STAKE_AMOUNT, "AAVE:Vault"); + } else { + const stratAave = await aave.balanceOf(aaveStrategy.address); + expect(stratAave).to.equal("0", "AAVE:Strategy"); + const vaultAave = await aave.balanceOf(vault.address); + expect(vaultAave).to.equal("0", "AAVE:Vault"); + } + + if (shouldResetCooldown) { + const cooldown = await stkAave.stakersCooldowns(aaveStrategy.address); + expect(currentTimestamp).to.equal(cooldown, "Cooldown should reset"); + } else { + const cooldown = await stkAave.stakersCooldowns(aaveStrategy.address); + expect(currentTimestamp).to.not.equal(cooldown, "Cooldown not reset"); + } + + if (shouldClaimRewards === true) { + const stratStkAave = await stkAave.balanceOf(aaveStrategy.address); + expect(stratStkAave).to.be.at.least( + REWARD_AMOUNT, + "StkAAVE:Strategy" + ); + } else if (shouldClaimRewards === false) { + const stratStkAave = await stkAave.balanceOf(aaveStrategy.address); + expect(stratStkAave).to.be.below(REWARD_AMOUNT, "StkAAVE:Strategy"); + } else { + expect(false).to.be.true("shouldclaimRewards is not defined"); + } + }; }; - describe("In cooldown window", async function () { - // before(collectRewards({})) - // it("Should set the cooldown", cooldownWasReset) - }); - describe("Before cooldown window", async function () { - // before(collectRewards({})) - // it("Should not change cooldown", cooldownUnchanged) - }); - describe("No cooldown set", async function () { - // before(collectRewards({})) - // it("Should set the cooldown", cooldownWasReset) - }); - describe("After window", async function () { - // before(collectRewards({})) - // it("Should set the cooldown", cooldownWasReset) - }); - describe("No pending rewards", async function () { - // before(collectRewards({})) - // it("Should set the cooldown", cooldownWasReset) - }); - describe("No stakeAave claimed or avaialbe", async function () { - // before(collectRewards({})) - // it("Should not change cooldown", cooldownUnchanged) - }); + it( + "In cooldown window", + collectRewards( + { + cooldownAgo: 11 * DAY, + hasStkAave: true, + hasRewards: true, + }, + { + shouldConvertStkAAVEToAAVE: true, + shouldResetCooldown: true, + shouldClaimRewards: true, + } + ) + ); + it( + "Before cooldown window", + collectRewards( + { + cooldownAgo: 2 * DAY, + hasStkAave: true, + hasRewards: true, + }, + { + shouldConvertStkAAVEToAAVE: false, + shouldResetCooldown: false, + shouldClaimRewards: false, + } + ) + ); + it( + "No cooldown set", + collectRewards( + { + cooldownAgo: ZERO_COOLDOWN, + hasStkAave: true, + hasRewards: true, + }, + { + shouldConvertStkAAVEToAAVE: false, + shouldResetCooldown: true, + shouldClaimRewards: true, + } + ) + ); + it( + "After window", + collectRewards( + { + cooldownAgo: 13 * DAY, + hasStkAave: true, + hasRewards: true, + }, + { + shouldConvertStkAAVEToAAVE: false, + shouldResetCooldown: true, + shouldClaimRewards: true, + } + ) + ); + it( + "No pending rewards", + collectRewards( + { + cooldownAgo: 11 * DAY, + hasStkAave: true, + hasRewards: false, + }, + { + shouldConvertStkAAVEToAAVE: true, + shouldResetCooldown: false, + shouldClaimRewards: false, + } + ) + ); }); }); From 17014ad5912fbdb905e2c74db3e2ab826906a259 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 14 Jul 2021 10:40:25 -0400 Subject: [PATCH 09/33] Remove unused variables. --- contracts/deploy/000_mock.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/contracts/deploy/000_mock.js b/contracts/deploy/000_mock.js index d83a93959c..2870f1a538 100644 --- a/contracts/deploy/000_mock.js +++ b/contracts/deploy/000_mock.js @@ -71,11 +71,9 @@ const deployMocks = async ({ getNamedAccounts, deployments }) => { from: governorAddr, }); - const weth = await ethers.getContract("MockWETH"); const dai = await ethers.getContract("MockDAI"); const usdc = await ethers.getContract("MockUSDC"); const usdt = await ethers.getContract("MockUSDT"); - const aave = await ethers.getContract("MockAave"); // Deploy mock aTokens (Aave) // MockAave is the mock lendingPool From 51e70c82883a16cc3aff1c55b9018e0ca8d7e783 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Thu, 15 Jul 2021 14:07:08 -0400 Subject: [PATCH 10/33] Draft deployment for AAVE v2 upgrade --- contracts/deploy/020_aave_v2.js | 77 +++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 contracts/deploy/020_aave_v2.js diff --git a/contracts/deploy/020_aave_v2.js b/contracts/deploy/020_aave_v2.js new file mode 100644 index 0000000000..f1e589eea5 --- /dev/null +++ b/contracts/deploy/020_aave_v2.js @@ -0,0 +1,77 @@ +const { deploymentWithProposal } = require("../utils/deploy"); + +module.exports = deploymentWithProposal( + { deployName: "020_aave_v2" }, + async ({ ethers, deployWithConfirmation }) => { + const { deployerAddr, governorAddr } = await getNamedAccounts(); + // Signers + const sDeployer = await ethers.provider.getSigner(deployerAddr); + const sGovernor = await ethers.provider.getSigner(governorAddr); + // Current contracts + const cVaultProxy = await ethers.getContract("VaultProxy"); + const cOldAaveStrategy = await ethers.getContract("AaveStrategyProxy"); + + // Deployer Actions + // ---------------- + + // 1. Deploy new proxy + // New strategy will be living at a clean address + const dAaveStrategyProxy = await deployWithConfirmation( + "AaveStrategyProxy", + [], + "InitializeGovernedUpgradeabilityProxy" + ); + // 2. Deploy new implimentation + await deployWithConfirmation("AaveStrategy"); + const cAaveStrategy = await ethers.getContractAt( + "AaveStrategy", + dAaveStrategyProxy.address + ); + // 3. Init and configure new AAVE strategy + const initFunction = + "initialize(address,address,address,address[],address[],address,address)"; + await withConfirmation( + cAaveStrategy.connect(sDeployer)[initFunction]( + assetAddresses.AAVE_ADDRESS_PROVIDER, // TODO Check + cVaultProxy.address, + assetAddresses.AAVE_TOKEN, // TODO + [assetAddresses.DAI], + [assetAddresses.aDAI], + assetAddresses.AAVE_INCENTIVES_CONTROLLER, // TODO + assetAddresses.STKAAVE // TODO + ) + ); + // 4. Transfer governance + await withConfirmation( + cAaveStrategy + .connect(sDeployer) + .transferGovernance(governorAddr, await getTxOpts()) + ); + + // Governance Actions + // ---------------- + return { + name: "Switch to new AAVEv2 strategy", + actions: [ + // 1. Accept governance of new AAVEStrategy + { + contract: cAaveStrategy, + signature: "claimGovernance()", + args: [], + }, + // 2. Remove old AAVE strategy from vault + { + contract: cVaultProxy, + signature: "removeStrategy(address)", + args: [cOldAaveStrategy.address], + }, + // 3. Add new AAVE v2 strategy to vault + { + contract: cVaultProxy, + signature: "approveStrategy(address)", + args: [cAaveStrategy.address], + }, + ], + }; + } +); From 2b0dda433c899b2ac186af384f5e8412798e705e Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Fri, 16 Jul 2021 15:32:23 -0400 Subject: [PATCH 11/33] AAVE v2 deploy script --- contracts/deploy/020_aave_v2.js | 37 ++++++++++++++++++++----------- contracts/test/helpers.js | 3 +++ contracts/test/strategies/aave.js | 2 +- contracts/utils/addresses.js | 15 ++++++++----- contracts/utils/deploy.js | 9 +++++++- 5 files changed, 45 insertions(+), 21 deletions(-) diff --git a/contracts/deploy/020_aave_v2.js b/contracts/deploy/020_aave_v2.js index f1e589eea5..bae0f4a6e8 100644 --- a/contracts/deploy/020_aave_v2.js +++ b/contracts/deploy/020_aave_v2.js @@ -2,15 +2,24 @@ const { deploymentWithProposal } = require("../utils/deploy"); module.exports = deploymentWithProposal( { deployName: "020_aave_v2" }, - async ({ ethers, deployWithConfirmation }) => { + async ({ + assetAddresses, + deployWithConfirmation, + ethers, + getTxOpts, + withConfirmation, + }) => { const { deployerAddr, governorAddr } = await getNamedAccounts(); // Signers const sDeployer = await ethers.provider.getSigner(deployerAddr); const sGovernor = await ethers.provider.getSigner(governorAddr); // Current contracts const cVaultProxy = await ethers.getContract("VaultProxy"); + const cVaultAdmin = await ethers.getContractAt( + "VaultAdmin", + cVaultProxy.address + ); const cOldAaveStrategy = await ethers.getContract("AaveStrategyProxy"); - // Deployer Actions // ---------------- @@ -31,15 +40,17 @@ module.exports = deploymentWithProposal( const initFunction = "initialize(address,address,address,address[],address[],address,address)"; await withConfirmation( - cAaveStrategy.connect(sDeployer)[initFunction]( - assetAddresses.AAVE_ADDRESS_PROVIDER, // TODO Check - cVaultProxy.address, - assetAddresses.AAVE_TOKEN, // TODO - [assetAddresses.DAI], - [assetAddresses.aDAI], - assetAddresses.AAVE_INCENTIVES_CONTROLLER, // TODO - assetAddresses.STKAAVE // TODO - ) + cAaveStrategy + .connect(sDeployer) + [initFunction]( + assetAddresses.AAVE_ADDRESS_PROVIDER, + cVaultAdmin.address, + assetAddresses.AAVE, + [assetAddresses.DAI], + [assetAddresses.aDAI], + assetAddresses.AAVE_INCENTIVES_CONTROLLER, + assetAddresses.STKAAVE + ) ); // 4. Transfer governance await withConfirmation( @@ -61,13 +72,13 @@ module.exports = deploymentWithProposal( }, // 2. Remove old AAVE strategy from vault { - contract: cVaultProxy, + contract: cVaultAdmin, signature: "removeStrategy(address)", args: [cOldAaveStrategy.address], }, // 3. Add new AAVE v2 strategy to vault { - contract: cVaultProxy, + contract: cVaultAdmin, signature: "approveStrategy(address)", args: [cAaveStrategy.address], }, diff --git a/contracts/test/helpers.js b/contracts/test/helpers.js index b9c398a5d7..5734cd3cf3 100644 --- a/contracts/test/helpers.js +++ b/contracts/test/helpers.js @@ -235,10 +235,13 @@ const getAssetAddresses = async (deployments) => { CRV: addresses.mainnet.CRV, CRVMinter: addresses.mainnet.CRVMinter, aDAI: addresses.mainnet.aDAI, + aDAI_v2: addresses.mainnet.aDAI_v2, aUSDC: addresses.mainnet.aUSDC, aUSDT: addresses.mainnet.aUSDT, AAVE: addresses.mainnet.Aave, AAVE_ADDRESS_PROVIDER: addresses.mainnet.AAVE_ADDRESS_PROVIDER, + AAVE_INCENTIVES_CONTROLLER: addresses.mainnet.AAVE_INCENTIVES_CONTROLLER, + STKAAVE: addresses.mainnet.STKAAVE, OGN: addresses.mainnet.OGN, uniswapRouter: addresses.mainnet.uniswapRouter, }; diff --git a/contracts/test/strategies/aave.js b/contracts/test/strategies/aave.js index 032ecb8027..872e58763d 100644 --- a/contracts/test/strategies/aave.js +++ b/contracts/test/strategies/aave.js @@ -130,7 +130,7 @@ describe("Aave Strategy", function () { }); }); - describe.only("Rewards", function () { + describe("Rewards", function () { const STAKE_AMOUNT = "10000000000"; const REWARD_AMOUNT = "70000000000"; const ZERO_COOLDOWN = -1; diff --git a/contracts/utils/addresses.js b/contracts/utils/addresses.js index f42cea3cb7..2d6a89de2d 100644 --- a/contracts/utils/addresses.js +++ b/contracts/utils/addresses.js @@ -17,12 +17,15 @@ addresses.mainnet.USDT = "0xdAC17F958D2ee523a2206206994597C13D831ec7"; addresses.mainnet.TUSD = "0x0000000000085d4780B73119b644AE5ecd22b376"; // AAVE addresses.mainnet.AAVE_ADDRESS_PROVIDER = - "0x24a42fD28C976A61Df5D00D0599C34c4f90748c8"; -addresses.mainnet.Aave = "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9"; -addresses.mainnet.aTUSD = "0x4DA9b813057D04BAef4e5800E36083717b4a0341"; -addresses.mainnet.aUSDT = "0x71fc860F7D3A592A4a98740e39dB31d25db65ae8"; -addresses.mainnet.aDAI = "0xfC1E690f61EFd961294b3e1Ce3313fBD8aa4f85d"; -addresses.mainnet.aUSDC = "0x9bA00D6856a4eDF4665BcA2C2309936572473B7E"; + "0xb53c1a33016b2dc2ff3653530bff1848a515c8c5"; // v2 +addresses.mainnet.Aave = "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9"; // v1-v2 +addresses.mainnet.aTUSD = "--"; // Todo: use v2 +addresses.mainnet.aUSDT = "--"; // Todo: use v2 +addresses.mainnet.aDAI = "0x028171bca77440897b824ca71d1c56cac55b68a3"; // v2 +addresses.mainnet.aUSDC = "--"; // Todo: use v2 +addresses.mainnet.STKAAVE = "0x4da27a545c0c5b758a6ba100e3a049001de870f5"; // v1-v2 +addresses.mainnet.AAVE_INCENTIVES_CONTROLLER = + "0xd784927Ff2f95ba542BfC824c8a8a98F3495f6b5"; // v2 // Compound addresses.mainnet.COMP = "0xc00e94Cb662C3520282E6f5717214004A7f26888"; diff --git a/contracts/utils/deploy.js b/contracts/utils/deploy.js index 646b8e774f..21d57c64a7 100644 --- a/contracts/utils/deploy.js +++ b/contracts/utils/deploy.js @@ -10,6 +10,7 @@ const { isFork, isRinkeby, isMainnetOrRinkebyOrFork, + getAssetAddresses, } = require("../test/helpers.js"); const { @@ -19,6 +20,7 @@ const { const addresses = require("../utils/addresses.js"); const { getTxOpts } = require("../utils/tx"); +const { proposeArgs } = require("../utils/governor"); // Wait for 3 blocks confirmation on Mainnet/Rinkeby. const NUM_CONFIRMATIONS = isMainnet || isRinkeby ? 3 : 0; @@ -252,10 +254,15 @@ const sendProposal = async (proposalArgs, description, opts = {}) => { */ function deploymentWithProposal(opts, fn) { const { deployName, dependencies } = opts; + const isSmokeTest = process.env.SMOKE_TEST === "true"; const runDeployment = async (hre) => { + const assetAddresses = await getAssetAddresses(hre); const tools = { - ethers, + assetAddresses, deployWithConfirmation, + ethers, + getTxOpts, + withConfirmation, }; const proposal = await fn(tools); From e8ae06e863f081cb7d06332ce847aacecbe26360 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 28 Jul 2021 16:00:12 -0400 Subject: [PATCH 12/33] getLendingPoolCore() no longer exists in AAVE v2. --- contracts/contracts/mocks/MockAave.sol | 4 --- .../contracts/strategies/AaveStrategy.sol | 29 ++++--------------- contracts/contracts/strategies/IAave.sol | 6 ---- 3 files changed, 6 insertions(+), 33 deletions(-) diff --git a/contracts/contracts/mocks/MockAave.sol b/contracts/contracts/mocks/MockAave.sol index ba1211357e..f7482288f2 100644 --- a/contracts/contracts/mocks/MockAave.sol +++ b/contracts/contracts/mocks/MockAave.sol @@ -108,8 +108,4 @@ contract MockAave is IAaveLendingPool, ILendingPoolAddressesProvider { function getLendingPool() external view returns (address) { return pool; } - - function getLendingPoolCore() external view returns (address payable) { - return core; - } } diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index 67a645c59f..5c190781f8 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -166,13 +166,13 @@ contract AaveStrategy is InitializableAbstractStrategy { */ function safeApproveAllTokens() external onlyGovernor nonReentrant { uint256 assetCount = assetsMapped.length; - address lendingPoolVault = _getLendingPoolCore(); + address lendingPool = address(_getLendingPool()); // approve the pool to spend the bAsset for (uint256 i = 0; i < assetCount; i++) { address asset = assetsMapped[i]; // Safe approval - IERC20(asset).safeApprove(lendingPoolVault, 0); - IERC20(asset).safeApprove(lendingPoolVault, uint256(-1)); + IERC20(asset).safeApprove(lendingPool, 0); + IERC20(asset).safeApprove(lendingPool, uint256(-1)); } } @@ -183,9 +183,9 @@ contract AaveStrategy is InitializableAbstractStrategy { * @param _aToken This aToken has the approval approval */ function _abstractSetPToken(address _asset, address _aToken) internal { - address lendingPoolVault = _getLendingPoolCore(); - IERC20(_asset).safeApprove(lendingPoolVault, 0); - IERC20(_asset).safeApprove(lendingPoolVault, uint256(-1)); + address lendingPool = address(_getLendingPool()); + IERC20(_asset).safeApprove(lendingPool, 0); + IERC20(_asset).safeApprove(lendingPool, uint256(-1)); } /** @@ -212,23 +212,6 @@ contract AaveStrategy is InitializableAbstractStrategy { return IAaveLendingPool(lendingPool); } - /** - * @dev Get the current address of the Aave lending pool core, which stores all the - * reserve tokens in its vault. - * @return Current lending pool core address - */ - function _getLendingPoolCore() internal view returns (address payable) { - address payable lendingPoolCore = ILendingPoolAddressesProvider( - platformAddress - ) - .getLendingPoolCore(); - require( - lendingPoolCore != address(uint160(address(0))), - "Lending pool core does not exist" - ); - return lendingPoolCore; - } - /** * @dev Collect stkAave, convert it to AAVE send to Vault. */ diff --git a/contracts/contracts/strategies/IAave.sol b/contracts/contracts/strategies/IAave.sol index 9ca1f99d73..bb2365fd6e 100644 --- a/contracts/contracts/strategies/IAave.sol +++ b/contracts/contracts/strategies/IAave.sol @@ -71,10 +71,4 @@ interface ILendingPoolAddressesProvider { * @dev Lending pool is the core contract on which to call deposit */ function getLendingPool() external view returns (address); - - /** - * @notice Get the address for lendingPoolCore - * @dev IMPORTANT - this is where _reserve must be approved before deposit - */ - function getLendingPoolCore() external view returns (address payable); } From 6e5b3bd7911a38340c765ada08bf4490807acc93 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 28 Jul 2021 16:01:49 -0400 Subject: [PATCH 13/33] Must initialize the proxy to point to the implemention before running the implementation initialization code. --- contracts/deploy/020_aave_v2.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/contracts/deploy/020_aave_v2.js b/contracts/deploy/020_aave_v2.js index bae0f4a6e8..0a8b57beab 100644 --- a/contracts/deploy/020_aave_v2.js +++ b/contracts/deploy/020_aave_v2.js @@ -30,13 +30,27 @@ module.exports = deploymentWithProposal( [], "InitializeGovernedUpgradeabilityProxy" ); - // 2. Deploy new implimentation - await deployWithConfirmation("AaveStrategy"); + const cAaveStrategyProxy = await ethers.getContractAt( + "InitializeGovernedUpgradeabilityProxy", + dAaveStrategyProxy.address + ); + // 2. Deploy new implementation + const dAaveStrategyImpl = await deployWithConfirmation("AaveStrategy"); const cAaveStrategy = await ethers.getContractAt( "AaveStrategy", dAaveStrategyProxy.address ); - // 3. Init and configure new AAVE strategy + // 3. Init the proxy to point at the implementation + await withConfirmation( + cAaveStrategyProxy + .connect(sDeployer) + ["initialize(address,address,bytes)"]( + dAaveStrategyImpl.address, + deployerAddr, + [] + ) + ); + // 4. Init and configure new AAVE strategy const initFunction = "initialize(address,address,address,address[],address[],address,address)"; await withConfirmation( From dc4cf463ca900b1eed9d063ecd4116000c3857dc Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 28 Jul 2021 16:02:26 -0400 Subject: [PATCH 14/33] Make addresses used by strategy public for easier debugging --- contracts/contracts/strategies/AaveStrategy.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index 5c190781f8..afdf85d369 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -17,8 +17,8 @@ import { IAaveIncentivesController } from "./IAaveIncentivesController.sol"; contract AaveStrategy is InitializableAbstractStrategy { uint16 constant referralCode = 92; - IAaveIncentivesController incentivesController; - IAaveStakedToken stkAave; + IAaveIncentivesController public incentivesController; + IAaveStakedToken public stkAave; /** * Initializer for setting up strategy internal state. This overrides the From 9b3a05c313ead3ec28bf50c9ec0e13a1a77b40ca Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 28 Jul 2021 16:02:41 -0400 Subject: [PATCH 15/33] Incentives system works off rewards tokens. --- contracts/contracts/strategies/AaveStrategy.sol | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index afdf85d369..0976aa4f3f 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -250,8 +250,14 @@ contract AaveStrategy is InitializableAbstractStrategy { // or if the cooldown counter is not running, // then start the unlock cooldown. if (currentTimestamp > windowStart || cooldown == 0) { + // Incentives controller needs aToken addresses + address[] memory aTokens = new address[](assetsMapped.length); + for (uint256 i = 0; i < assetsMapped.length; i++) { + aTokens[i] = address(_getATokenFor(assetsMapped[i])); + } + uint256 pendingRewards = incentivesController.getRewardsBalance( - assetsMapped, + aTokens, address(this) ); if (pendingRewards > 0) { @@ -259,7 +265,7 @@ contract AaveStrategy is InitializableAbstractStrategy { // into the future. It needs to be run after any rewards would be // collected, but before the cooldown is restarted. uint256 collected = incentivesController.claimRewards( - assetsMapped, + aTokens, pendingRewards, address(this) ); From 10223f39ad77bf403e77be1dd012804e7496de00 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 28 Jul 2021 16:14:14 -0400 Subject: [PATCH 16/33] Prettier. --- contracts/deploy/020_aave_v2.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/deploy/020_aave_v2.js b/contracts/deploy/020_aave_v2.js index 0a8b57beab..53f3a3978f 100644 --- a/contracts/deploy/020_aave_v2.js +++ b/contracts/deploy/020_aave_v2.js @@ -43,12 +43,12 @@ module.exports = deploymentWithProposal( // 3. Init the proxy to point at the implementation await withConfirmation( cAaveStrategyProxy - .connect(sDeployer) - ["initialize(address,address,bytes)"]( - dAaveStrategyImpl.address, - deployerAddr, - [] - ) + .connect(sDeployer) + ["initialize(address,address,bytes)"]( + dAaveStrategyImpl.address, + deployerAddr, + [] + ) ); // 4. Init and configure new AAVE strategy const initFunction = From 57507f85609d06765cf1a235b8c4516db18c93c8 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 28 Jul 2021 16:24:24 -0400 Subject: [PATCH 17/33] Missed a lending pool core removal. --- contracts/test/strategies/aave.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/test/strategies/aave.js b/contracts/test/strategies/aave.js index 872e58763d..6f81545700 100644 --- a/contracts/test/strategies/aave.js +++ b/contracts/test/strategies/aave.js @@ -56,7 +56,7 @@ describe("Aave Strategy", function () { usdc = fixture.usdc; dai = fixture.dai; aaveAddressProvider = fixture.aaveAddressProvider; - aaveCoreAddress = await aaveAddressProvider.getLendingPoolCore(); + aaveCoreAddress = await aaveAddressProvider.getLendingPool(); }); describe("Mint", function () { From 15a3325ab19bbf8ba5d08f8455a7b74c555b8588 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 28 Jul 2021 16:29:07 -0400 Subject: [PATCH 18/33] Better comments --- contracts/contracts/strategies/AaveStrategy.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index 0976aa4f3f..588cd3c707 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -250,12 +250,13 @@ contract AaveStrategy is InitializableAbstractStrategy { // or if the cooldown counter is not running, // then start the unlock cooldown. if (currentTimestamp > windowStart || cooldown == 0) { - // Incentives controller needs aToken addresses + // aToken addresses for incentives controller address[] memory aTokens = new address[](assetsMapped.length); for (uint256 i = 0; i < assetsMapped.length; i++) { aTokens[i] = address(_getATokenFor(assetsMapped[i])); } + // 1. If we have rewards availabile, collect them uint256 pendingRewards = incentivesController.getRewardsBalance( aTokens, address(this) @@ -271,8 +272,10 @@ contract AaveStrategy is InitializableAbstractStrategy { ); require(collected == pendingRewards, "AAVE reward difference"); } - // Cooldown call reverts if no stkAave balance + + // 2. Start cooldown counting down. if (stkAave.balanceOf(address(this)) > 0) { + // Cooldown call would revert if no stkAave balance. stkAave.cooldown(); } } From 57c42996e3e8924f46642dea0bc4fd8e59eb03f4 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Thu, 29 Jul 2021 12:08:48 -0400 Subject: [PATCH 19/33] Comments update --- contracts/contracts/strategies/AaveStrategy.sol | 7 ++++--- contracts/deploy/020_aave_v2.js | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index 588cd3c707..f1e7f3468f 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -262,9 +262,10 @@ contract AaveStrategy is InitializableAbstractStrategy { address(this) ); if (pendingRewards > 0) { - // claimRewards() may pause or push the cooldown time - // into the future. It needs to be run after any rewards would be - // collected, but before the cooldown is restarted. + // Because getting more stkAAVE from the incentives controller + // with claimRewards() may push the stkAAVE cooldown time + // forward, it is called after stakedAAVE has been turned into + // AAVE. uint256 collected = incentivesController.claimRewards( aTokens, pendingRewards, diff --git a/contracts/deploy/020_aave_v2.js b/contracts/deploy/020_aave_v2.js index 53f3a3978f..3a061f707c 100644 --- a/contracts/deploy/020_aave_v2.js +++ b/contracts/deploy/020_aave_v2.js @@ -66,7 +66,7 @@ module.exports = deploymentWithProposal( assetAddresses.STKAAVE ) ); - // 4. Transfer governance + // 5. Transfer governance await withConfirmation( cAaveStrategy .connect(sDeployer) From da3f0aea4a598ac4a1f0d20ef9ce1e9335da6632 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Thu, 29 Jul 2021 12:08:48 -0400 Subject: [PATCH 20/33] Add getTxOpts --- contracts/deploy/020_aave_v2.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contracts/deploy/020_aave_v2.js b/contracts/deploy/020_aave_v2.js index 53f3a3978f..d528bc0270 100644 --- a/contracts/deploy/020_aave_v2.js +++ b/contracts/deploy/020_aave_v2.js @@ -47,7 +47,8 @@ module.exports = deploymentWithProposal( ["initialize(address,address,bytes)"]( dAaveStrategyImpl.address, deployerAddr, - [] + [], + await getTxOpts() ) ); // 4. Init and configure new AAVE strategy @@ -63,7 +64,8 @@ module.exports = deploymentWithProposal( [assetAddresses.DAI], [assetAddresses.aDAI], assetAddresses.AAVE_INCENTIVES_CONTROLLER, - assetAddresses.STKAAVE + assetAddresses.STKAAVE, + await getTxOpts() ) ); // 4. Transfer governance From 86a6203ad39a6bbf4502076daabd9329050b82ea Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Thu, 29 Jul 2021 14:20:20 -0400 Subject: [PATCH 21/33] Update comments. --- contracts/contracts/strategies/AaveStrategy.sol | 7 ++++--- contracts/deploy/020_aave_v2.js | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index 588cd3c707..f1e7f3468f 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -262,9 +262,10 @@ contract AaveStrategy is InitializableAbstractStrategy { address(this) ); if (pendingRewards > 0) { - // claimRewards() may pause or push the cooldown time - // into the future. It needs to be run after any rewards would be - // collected, but before the cooldown is restarted. + // Because getting more stkAAVE from the incentives controller + // with claimRewards() may push the stkAAVE cooldown time + // forward, it is called after stakedAAVE has been turned into + // AAVE. uint256 collected = incentivesController.claimRewards( aTokens, pendingRewards, diff --git a/contracts/deploy/020_aave_v2.js b/contracts/deploy/020_aave_v2.js index d528bc0270..1cab73be96 100644 --- a/contracts/deploy/020_aave_v2.js +++ b/contracts/deploy/020_aave_v2.js @@ -68,7 +68,7 @@ module.exports = deploymentWithProposal( await getTxOpts() ) ); - // 4. Transfer governance + // 5. Transfer governance await withConfirmation( cAaveStrategy .connect(sDeployer) From ebe364185a507a83ede4c77f1a56ffad62e2ac17 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Fri, 30 Jul 2021 14:48:34 -0400 Subject: [PATCH 22/33] Include last second of window. --- contracts/contracts/strategies/AaveStrategy.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index f1e7f3468f..39210a41a8 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -224,11 +224,10 @@ contract AaveStrategy is InitializableAbstractStrategy { uint256 cooldown = stkAave.stakersCooldowns(address(this)); uint256 windowStart = cooldown + stkAave.COOLDOWN_SECONDS(); uint256 windowEnd = windowStart + stkAave.UNSTAKE_WINDOW(); - uint256 currentTimestamp = now; // If inside the unlock window, then we can redeem stkAave // for AAVE and send it to the vault. - if (currentTimestamp > windowStart && currentTimestamp < windowEnd) { + if (now > windowStart && now <= windowEnd) { // Redeem to AAVE uint256 stkAaveBalance = stkAave.balanceOf(address(this)); if (stkAaveBalance > rewardLiquidationThreshold) { From 733a5d3c39f90898a5fbddb0c960cfef7179e49c Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Fri, 30 Jul 2021 14:48:34 -0400 Subject: [PATCH 23/33] Include last second of window. --- contracts/contracts/strategies/AaveStrategy.sol | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index f1e7f3468f..7af9d133bc 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -224,11 +224,10 @@ contract AaveStrategy is InitializableAbstractStrategy { uint256 cooldown = stkAave.stakersCooldowns(address(this)); uint256 windowStart = cooldown + stkAave.COOLDOWN_SECONDS(); uint256 windowEnd = windowStart + stkAave.UNSTAKE_WINDOW(); - uint256 currentTimestamp = now; // If inside the unlock window, then we can redeem stkAave // for AAVE and send it to the vault. - if (currentTimestamp > windowStart && currentTimestamp < windowEnd) { + if (now > windowStart && now <= windowEnd) { // Redeem to AAVE uint256 stkAaveBalance = stkAave.balanceOf(address(this)); if (stkAaveBalance > rewardLiquidationThreshold) { @@ -249,7 +248,7 @@ contract AaveStrategy is InitializableAbstractStrategy { // If we were past the start of the window, // or if the cooldown counter is not running, // then start the unlock cooldown. - if (currentTimestamp > windowStart || cooldown == 0) { + if (now > windowStart || cooldown == 0) { // aToken addresses for incentives controller address[] memory aTokens = new address[](assetsMapped.length); for (uint256 i = 0; i < assetsMapped.length; i++) { From 4165d98165040259a756deb5be22d453ee08d5d1 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Fri, 13 Aug 2021 09:14:27 -0400 Subject: [PATCH 24/33] Better comment --- contracts/contracts/strategies/AaveStrategy.sol | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index 7af9d133bc..ec3e5c4003 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -245,9 +245,8 @@ contract AaveStrategy is InitializableAbstractStrategy { } } - // If we were past the start of the window, - // or if the cooldown counter is not running, - // then start the unlock cooldown. + // Collect avaiable rewards and restart the cooldown timer, if either of + // those should be run. if (now > windowStart || cooldown == 0) { // aToken addresses for incentives controller address[] memory aTokens = new address[](assetsMapped.length); From 5e27ca13634014197fe2e3737214e3bcec801538 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Mon, 16 Aug 2021 12:14:02 -0400 Subject: [PATCH 25/33] Add test for AAVE withdrawAll, and update code to match v2 --- contracts/contracts/strategies/AaveStrategy.sol | 3 ++- contracts/test/strategies/aave.js | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index ec3e5c4003..c6e023ab2a 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -123,10 +123,11 @@ contract AaveStrategy is InitializableAbstractStrategy { function withdrawAll() external onlyVaultOrGovernor nonReentrant { for (uint256 i = 0; i < assetsMapped.length; i++) { // Redeem entire balance of aToken + address _asset = assetsMapped[i]; IAaveAToken aToken = _getATokenFor(assetsMapped[i]); uint256 balance = aToken.balanceOf(address(this)); if (balance > 0) { - aToken.redeem(balance); + _getLendingPool().withdraw(_asset, balance, address(this)); // Transfer entire balance to Vault IERC20 asset = IERC20(assetsMapped[i]); asset.safeTransfer( diff --git a/contracts/test/strategies/aave.js b/contracts/test/strategies/aave.js index 6f81545700..9269cb5c98 100644 --- a/contracts/test/strategies/aave.js +++ b/contracts/test/strategies/aave.js @@ -94,6 +94,15 @@ describe("Aave Strategy", function () { await expect(anna).to.have.a.balanceOf("10000", ousd); }); + it("Should be able to withdrawAll", async function () { + await expectApproxSupply(ousd, ousdUnits("200")); + await mint("30000.00", dai); + await vault + .connect(governor) + .withdrawAllFromStrategy(aaveStrategy.address); + await expect(aaveStrategy).to.have.a.balanceOf("0", dai); + }); + it("Should be able to redeem and return assets after multiple mints", async function () { await mint("30000.00", usdt); await mint("30000.00", usdc); From 28bc85d3822e007c660937ec983c040df51d8236 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Mon, 16 Aug 2021 12:19:41 -0400 Subject: [PATCH 26/33] Make the code flow --- contracts/contracts/strategies/AaveStrategy.sol | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index c6e023ab2a..4e944debd5 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -123,16 +123,15 @@ contract AaveStrategy is InitializableAbstractStrategy { function withdrawAll() external onlyVaultOrGovernor nonReentrant { for (uint256 i = 0; i < assetsMapped.length; i++) { // Redeem entire balance of aToken - address _asset = assetsMapped[i]; + address asset = assetsMapped[i]; IAaveAToken aToken = _getATokenFor(assetsMapped[i]); uint256 balance = aToken.balanceOf(address(this)); if (balance > 0) { - _getLendingPool().withdraw(_asset, balance, address(this)); + _getLendingPool().withdraw(asset, balance, address(this)); // Transfer entire balance to Vault - IERC20 asset = IERC20(assetsMapped[i]); - asset.safeTransfer( + IERC20(asset).safeTransfer( vaultAddress, - asset.balanceOf(address(this)) + IERC20(asset).balanceOf(address(this)) ); } } From fd8c00c9446d375191ecc074a965425317c4251a Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Mon, 16 Aug 2021 12:26:40 -0400 Subject: [PATCH 27/33] Check returned transaction amount --- contracts/contracts/strategies/AaveStrategy.sol | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index 4e944debd5..b9a266866b 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -113,7 +113,7 @@ contract AaveStrategy is InitializableAbstractStrategy { _amount, address(this) ); - require(actual >= _amount, "Did not withdraw enough"); + require(actual == _amount, "Did not withdraw enough"); IERC20(_asset).safeTransfer(_recipient, _amount); } @@ -123,15 +123,20 @@ contract AaveStrategy is InitializableAbstractStrategy { function withdrawAll() external onlyVaultOrGovernor nonReentrant { for (uint256 i = 0; i < assetsMapped.length; i++) { // Redeem entire balance of aToken - address asset = assetsMapped[i]; + IERC20 asset = IERC20(assetsMapped[i]); IAaveAToken aToken = _getATokenFor(assetsMapped[i]); uint256 balance = aToken.balanceOf(address(this)); if (balance > 0) { - _getLendingPool().withdraw(asset, balance, address(this)); + uint256 actual = _getLendingPool().withdraw( + address(asset), + balance, + address(this) + ); + require(actual == balance, "Did not withdraw enough"); // Transfer entire balance to Vault - IERC20(asset).safeTransfer( + asset.safeTransfer( vaultAddress, - IERC20(asset).balanceOf(address(this)) + asset.balanceOf(address(this)) ); } } From 0b4937a36e44156c8a0a5894972ec0fe170c8fd8 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Tue, 17 Aug 2021 15:33:00 -0400 Subject: [PATCH 28/33] Remove old AAVE v1 aToken interface. --- contracts/contracts/mocks/MockAave.sol | 1 - .../contracts/strategies/AaveStrategy.sol | 20 +++++++++---------- contracts/contracts/strategies/IAave.sol | 20 ------------------- 3 files changed, 9 insertions(+), 32 deletions(-) diff --git a/contracts/contracts/mocks/MockAave.sol b/contracts/contracts/mocks/MockAave.sol index f7482288f2..3d91ac2f92 100644 --- a/contracts/contracts/mocks/MockAave.sol +++ b/contracts/contracts/mocks/MockAave.sol @@ -1,7 +1,6 @@ pragma solidity 0.5.11; import { - IAaveAToken, IAaveLendingPool, ILendingPoolAddressesProvider } from "../strategies/IAave.sol"; diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index b9a266866b..201c46a905 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -74,8 +74,7 @@ contract AaveStrategy is InitializableAbstractStrategy { */ function _deposit(address _asset, uint256 _amount) internal { require(_amount > 0, "Must deposit something"); - IAaveAToken aToken = _getATokenFor(_asset); - emit Deposit(_asset, address(aToken), _amount); + emit Deposit(_asset, _getATokenFor(_asset), _amount); _getLendingPool().deposit(_asset, _amount, address(this), referralCode); } @@ -106,8 +105,7 @@ contract AaveStrategy is InitializableAbstractStrategy { require(_amount > 0, "Must withdraw something"); require(_recipient != address(0), "Must specify recipient"); - IAaveAToken aToken = _getATokenFor(_asset); - emit Withdrawal(_asset, address(aToken), _amount); + emit Withdrawal(_asset, _getATokenFor(_asset), _amount); uint256 actual = _getLendingPool().withdraw( _asset, _amount, @@ -124,8 +122,8 @@ contract AaveStrategy is InitializableAbstractStrategy { for (uint256 i = 0; i < assetsMapped.length; i++) { // Redeem entire balance of aToken IERC20 asset = IERC20(assetsMapped[i]); - IAaveAToken aToken = _getATokenFor(assetsMapped[i]); - uint256 balance = aToken.balanceOf(address(this)); + address aToken = _getATokenFor(assetsMapped[i]); + uint256 balance = IERC20(aToken).balanceOf(address(this)); if (balance > 0) { uint256 actual = _getLendingPool().withdraw( address(asset), @@ -153,8 +151,8 @@ contract AaveStrategy is InitializableAbstractStrategy { returns (uint256 balance) { // Balance is always with token aToken decimals - IAaveAToken aToken = _getATokenFor(_asset); - balance = aToken.balanceOf(address(this)); + address aToken = _getATokenFor(_asset); + balance = IRERC20(aToken).balanceOf(address(this)); } /** @@ -199,10 +197,10 @@ contract AaveStrategy is InitializableAbstractStrategy { * @param _asset Address of the asset * @return Corresponding aToken to this asset */ - function _getATokenFor(address _asset) internal view returns (IAaveAToken) { + function _getATokenFor(address _asset) internal view returns (address) { address aToken = assetToPToken[_asset]; require(aToken != address(0), "aToken does not exist"); - return IAaveAToken(aToken); + return aToken; } /** @@ -256,7 +254,7 @@ contract AaveStrategy is InitializableAbstractStrategy { // aToken addresses for incentives controller address[] memory aTokens = new address[](assetsMapped.length); for (uint256 i = 0; i < assetsMapped.length; i++) { - aTokens[i] = address(_getATokenFor(assetsMapped[i])); + aTokens[i] = _getATokenFor(assetsMapped[i]); } // 1. If we have rewards availabile, collect them diff --git a/contracts/contracts/strategies/IAave.sol b/contracts/contracts/strategies/IAave.sol index bb2365fd6e..909ec5523e 100644 --- a/contracts/contracts/strategies/IAave.sol +++ b/contracts/contracts/strategies/IAave.sol @@ -1,25 +1,5 @@ pragma solidity 0.5.11; -/** - * @dev Interface for Aaves A Token - * Documentation: https://developers.aave.com/#atokens - */ -interface IAaveAToken { - /** - * @notice Non-standard ERC20 function to redeem an _amount of aTokens for the underlying - * asset, burning the aTokens during the process. - * @param _amount Amount of aTokens - */ - function redeem(uint256 _amount) external; - - /** - * @notice returns the current total aToken balance of _user all interest collected included. - * To obtain the user asset principal balance with interests excluded , ERC20 non-standard - * method principalBalanceOf() can be used. - */ - function balanceOf(address _user) external view returns (uint256); -} - /** * @dev Interface for Aaves Lending Pool * Documentation: https://developers.aave.com/#lendingpool From 5afd7a8de7633a4d42a1f928c7bf95a5a3ccdb80 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Tue, 17 Aug 2021 15:36:54 -0400 Subject: [PATCH 29/33] =?UTF-8?q?Remove=20wrong=20comments=20about=20retur?= =?UTF-8?q?n=20values=20=F0=9F=A5=8F=20[N02]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contracts/contracts/strategies/AaveStrategy.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index 201c46a905..acb3f2150a 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -56,7 +56,6 @@ contract AaveStrategy is InitializableAbstractStrategy { * @dev Deposit asset into Aave * @param _asset Address of asset to deposit * @param _amount Amount of asset to deposit - * @return amountDeposited Amount of asset that was deposited */ function deposit(address _asset, uint256 _amount) external @@ -70,7 +69,6 @@ contract AaveStrategy is InitializableAbstractStrategy { * @dev Deposit asset into Aave * @param _asset Address of asset to deposit * @param _amount Amount of asset to deposit - * @return amountDeposited Amount of asset that was deposited */ function _deposit(address _asset, uint256 _amount) internal { require(_amount > 0, "Must deposit something"); @@ -95,7 +93,6 @@ contract AaveStrategy is InitializableAbstractStrategy { * @param _recipient Address to receive withdrawn asset * @param _asset Address of asset to withdraw * @param _amount Amount of asset to withdraw - * @return amountWithdrawn Amount of asset that was withdrawn */ function withdraw( address _recipient, From 77abd7b7fcac377365e1d5449f206ae65c05f9cc Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Tue, 17 Aug 2021 15:49:46 -0400 Subject: [PATCH 30/33] Fix typo --- contracts/contracts/strategies/AaveStrategy.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index acb3f2150a..af67724dab 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -149,7 +149,7 @@ contract AaveStrategy is InitializableAbstractStrategy { { // Balance is always with token aToken decimals address aToken = _getATokenFor(_asset); - balance = IRERC20(aToken).balanceOf(address(this)); + balance = IERC20(aToken).balanceOf(address(this)); } /** From 45b36f9860542670b91c758dff9a31fd4567964b Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 18 Aug 2021 09:14:38 -0400 Subject: [PATCH 31/33] Safemath --- contracts/contracts/strategies/AaveStrategy.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index af67724dab..40ad7eef42 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -222,8 +222,8 @@ contract AaveStrategy is InitializableAbstractStrategy { // Check staked AAVE cooldown timer uint256 cooldown = stkAave.stakersCooldowns(address(this)); - uint256 windowStart = cooldown + stkAave.COOLDOWN_SECONDS(); - uint256 windowEnd = windowStart + stkAave.UNSTAKE_WINDOW(); + uint256 windowStart = cooldown.add(stkAave.COOLDOWN_SECONDS()); + uint256 windowEnd = windowStart.add(stkAave.UNSTAKE_WINDOW()); // If inside the unlock window, then we can redeem stkAave // for AAVE and send it to the vault. From 4930a262d46cf93e301c579cc394a14a90468bfd Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 18 Aug 2021 09:14:59 -0400 Subject: [PATCH 32/33] Better flavor --- contracts/contracts/strategies/AaveStrategy.sol | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index 40ad7eef42..e1ec165b89 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -165,10 +165,9 @@ contract AaveStrategy is InitializableAbstractStrategy { * if for some reason is it necessary. */ function safeApproveAllTokens() external onlyGovernor nonReentrant { - uint256 assetCount = assetsMapped.length; address lendingPool = address(_getLendingPool()); - // approve the pool to spend the bAsset - for (uint256 i = 0; i < assetCount; i++) { + // approve the pool to spend the Asset + for (uint256 i = 0; i < assetsMapped.length; i++) { address asset = assetsMapped[i]; // Safe approval IERC20(asset).safeApprove(lendingPool, 0); From 014be0e1eccec4966edc443dd1077b05958716d2 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 18 Aug 2021 09:39:59 -0400 Subject: [PATCH 33/33] Update comments --- contracts/contracts/strategies/AaveStrategy.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contracts/contracts/strategies/AaveStrategy.sol b/contracts/contracts/strategies/AaveStrategy.sol index e1ec165b89..82c83a97f3 100644 --- a/contracts/contracts/strategies/AaveStrategy.sol +++ b/contracts/contracts/strategies/AaveStrategy.sol @@ -72,6 +72,8 @@ contract AaveStrategy is InitializableAbstractStrategy { */ function _deposit(address _asset, uint256 _amount) internal { require(_amount > 0, "Must deposit something"); + // Following line also doubles as a check that we are depositing + // an asset that we support. emit Deposit(_asset, _getATokenFor(_asset), _amount); _getLendingPool().deposit(_asset, _amount, address(this), referralCode); } @@ -273,7 +275,8 @@ contract AaveStrategy is InitializableAbstractStrategy { // 2. Start cooldown counting down. if (stkAave.balanceOf(address(this)) > 0) { - // Cooldown call would revert if no stkAave balance. + // Protected with if since cooldown call would revert + // if no stkAave balance. stkAave.cooldown(); } }