diff --git a/contracts/deployments/ATokensAndRatesHelper.sol b/contracts/deployments/ATokensAndRatesHelper.sol index a29dc6f2..400f7ca6 100644 --- a/contracts/deployments/ATokensAndRatesHelper.sol +++ b/contracts/deployments/ATokensAndRatesHelper.sol @@ -20,6 +20,20 @@ contract ATokensAndRatesHelper is Ownable { address private poolConfigurator; event deployedContracts(address aToken, address strategy); + struct InitDeploymentInput { + address asset; + uint256[6] rates; + } + + struct ConfigureReserveInput { + address asset; + uint256 baseLTV; + uint256 liquidationThreshold; + uint256 liquidationBonus; + uint256 reserveFactor; + bool stableBorrowingEnabled; + } + constructor( address payable _pool, address _addressesProvider, @@ -30,93 +44,40 @@ contract ATokensAndRatesHelper is Ownable { poolConfigurator = _poolConfigurator; } - function initDeployment( - address[] calldata assets, - string[] calldata symbols, - uint256[6][] calldata rates, - address treasuryAddress, - address incentivesController - ) external onlyOwner { - require(assets.length == symbols.length, 't Arrays not same length'); - require(rates.length == symbols.length, 'r Arrays not same length'); - for (uint256 i = 0; i < assets.length; i++) { + function initDeployment(InitDeploymentInput[] calldata inputParams) external onlyOwner { + for (uint256 i = 0; i < inputParams.length; i++) { emit deployedContracts( - address( - new AToken( - LendingPool(pool), - assets[i], - treasuryAddress, - StringLib.concat('Aave interest bearing ', symbols[i]), - StringLib.concat('a', symbols[i]), - incentivesController - ) - ), + address(new AToken()), address( new DefaultReserveInterestRateStrategy( LendingPoolAddressesProvider(addressesProvider), - rates[i][0], - rates[i][1], - rates[i][2], - rates[i][3], - rates[i][4], - rates[i][5] + inputParams[i].rates[0], + inputParams[i].rates[1], + inputParams[i].rates[2], + inputParams[i].rates[3], + inputParams[i].rates[4], + inputParams[i].rates[5] ) ) ); } } - function initReserve( - address[] calldata stables, - address[] calldata variables, - address[] calldata aTokens, - address[] calldata strategies, - uint8[] calldata reserveDecimals - ) external onlyOwner { - require(variables.length == stables.length); - require(aTokens.length == stables.length); - require(strategies.length == stables.length); - require(reserveDecimals.length == stables.length); - - for (uint256 i = 0; i < stables.length; i++) { - LendingPoolConfigurator(poolConfigurator).initReserve( - aTokens[i], - stables[i], - variables[i], - reserveDecimals[i], - strategies[i] - ); - } - } - - function configureReserves( - address[] calldata assets, - uint256[] calldata baseLTVs, - uint256[] calldata liquidationThresholds, - uint256[] calldata liquidationBonuses, - uint256[] calldata reserveFactors, - bool[] calldata stableBorrowingEnabled - ) external onlyOwner { - require(baseLTVs.length == assets.length); - require(liquidationThresholds.length == assets.length); - require(liquidationBonuses.length == assets.length); - require(stableBorrowingEnabled.length == assets.length); - require(reserveFactors.length == assets.length); - + function configureReserves(ConfigureReserveInput[] calldata inputParams) external onlyOwner { LendingPoolConfigurator configurator = LendingPoolConfigurator(poolConfigurator); - for (uint256 i = 0; i < assets.length; i++) { + for (uint256 i = 0; i < inputParams.length; i++) { configurator.configureReserveAsCollateral( - assets[i], - baseLTVs[i], - liquidationThresholds[i], - liquidationBonuses[i] + inputParams[i].asset, + inputParams[i].baseLTV, + inputParams[i].liquidationThreshold, + inputParams[i].liquidationBonus ); configurator.enableBorrowingOnReserve( - assets[i], - stableBorrowingEnabled[i] + inputParams[i].asset, + inputParams[i].stableBorrowingEnabled ); - configurator.setReserveFactor(assets[i], reserveFactors[i]); + configurator.setReserveFactor(inputParams[i].asset, inputParams[i].reserveFactor); } } } diff --git a/contracts/deployments/StableAndVariableTokensHelper.sol b/contracts/deployments/StableAndVariableTokensHelper.sol index 8f76bbe1..e31651f3 100644 --- a/contracts/deployments/StableAndVariableTokensHelper.sol +++ b/contracts/deployments/StableAndVariableTokensHelper.sol @@ -18,34 +18,11 @@ contract StableAndVariableTokensHelper is Ownable { addressesProvider = _addressesProvider; } - function initDeployment( - address[] calldata tokens, - string[] calldata symbols, - address incentivesController - ) external onlyOwner { + function initDeployment(address[] calldata tokens, string[] calldata symbols) external onlyOwner { require(tokens.length == symbols.length, 'Arrays not same length'); require(pool != address(0), 'Pool can not be zero address'); for (uint256 i = 0; i < tokens.length; i++) { - emit deployedContracts( - address( - new StableDebtToken( - pool, - tokens[i], - StringLib.concat('Aave stable debt bearing ', symbols[i]), - StringLib.concat('stableDebt', symbols[i]), - incentivesController - ) - ), - address( - new VariableDebtToken( - pool, - tokens[i], - StringLib.concat('Aave variable debt bearing ', symbols[i]), - StringLib.concat('variableDebt', symbols[i]), - incentivesController - ) - ) - ); + emit deployedContracts(address(new StableDebtToken()), address(new VariableDebtToken())); } } diff --git a/contracts/interfaces/IAToken.sol b/contracts/interfaces/IAToken.sol index 5fa1a4f0..ca338270 100644 --- a/contracts/interfaces/IAToken.sol +++ b/contracts/interfaces/IAToken.sol @@ -3,8 +3,10 @@ pragma solidity 0.6.12; import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol'; import {IScaledBalanceToken} from './IScaledBalanceToken.sol'; +import {IInitializableAToken} from './IInitializableAToken.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; -interface IAToken is IERC20, IScaledBalanceToken { +interface IAToken is IERC20, IScaledBalanceToken, IInitializableAToken { /** * @dev Emitted after the mint action * @param from The address performing the mint @@ -85,4 +87,9 @@ interface IAToken is IERC20, IScaledBalanceToken { * @return The amount transferred **/ function transferUnderlyingTo(address user, uint256 amount) external returns (uint256); + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view returns (IAaveIncentivesController); } diff --git a/contracts/interfaces/IInitializableAToken.sol b/contracts/interfaces/IInitializableAToken.sol new file mode 100644 index 00000000..9cffe6ce --- /dev/null +++ b/contracts/interfaces/IInitializableAToken.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {ILendingPool} from './ILendingPool.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + +/** + * @title IInitializableAToken + * @notice Interface for the initialize function on AToken + * @author Aave + **/ +interface IInitializableAToken { + /** + * @dev Initializes the aToken + * @param pool The address of the lending pool where this aToken will be used + * @param treasury The address of the Aave treasury, receiving the fees on this aToken + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param aTokenDecimals The decimals of the aToken, same as the underlying asset's + * @param aTokenName The name of the aToken + * @param aTokenSymbol The symbol of the aToken + */ + function initialize( + ILendingPool pool, + address treasury, + address underlyingAsset, + IAaveIncentivesController incentivesController, + uint8 aTokenDecimals, + string calldata aTokenName, + string calldata aTokenSymbol + ) external; +} diff --git a/contracts/interfaces/IInitializableDebtToken.sol b/contracts/interfaces/IInitializableDebtToken.sol new file mode 100644 index 00000000..0a85c968 --- /dev/null +++ b/contracts/interfaces/IInitializableDebtToken.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; + +import {ILendingPool} from './ILendingPool.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + +/** + * @title IInitializableDebtToken + * @notice Interface for the initialize function common between debt tokens + * @author Aave + **/ +interface IInitializableDebtToken { + /** + * @dev Initializes the debt token. + * @param pool The address of the lending pool where this aToken will be used + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's + * @param debtTokenName The name of the token + * @param debtTokenSymbol The symbol of the token + */ + function initialize( + ILendingPool pool, + address underlyingAsset, + IAaveIncentivesController incentivesController, + uint8 debtTokenDecimals, + string memory debtTokenName, + string memory debtTokenSymbol + ) external; +} diff --git a/contracts/interfaces/ILendingPoolConfigurator.sol b/contracts/interfaces/ILendingPoolConfigurator.sol new file mode 100644 index 00000000..8981db6c --- /dev/null +++ b/contracts/interfaces/ILendingPoolConfigurator.sol @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +interface ILendingPoolConfigurator { + struct InitReserveInput { + address aTokenImpl; + address stableDebtTokenImpl; + address variableDebtTokenImpl; + uint8 underlyingAssetDecimals; + address interestRateStrategyAddress; + address underlyingAsset; + address treasury; + address incentivesController; + string underlyingAssetName; + string aTokenName; + string aTokenSymbol; + string variableDebtTokenName; + string variableDebtTokenSymbol; + string stableDebtTokenName; + string stableDebtTokenSymbol; + } + + struct UpdateATokenInput { + address asset; + address treasury; + address incentivesController; + string name; + string symbol; + address implementation; + } + + struct UpdateDebtTokenInput { + address asset; + address incentivesController; + string name; + string symbol; + address implementation; + } + + /** + * @dev Emitted when a reserve is initialized. + * @param asset The address of the underlying asset of the reserve + * @param aToken The address of the associated aToken contract + * @param stableDebtToken The address of the associated stable rate debt token + * @param variableDebtToken The address of the associated variable rate debt token + * @param interestRateStrategyAddress The address of the interest rate strategy for the reserve + **/ + event ReserveInitialized( + address indexed asset, + address indexed aToken, + address stableDebtToken, + address variableDebtToken, + address interestRateStrategyAddress + ); + + /** + * @dev Emitted when borrowing is enabled on a reserve + * @param asset The address of the underlying asset of the reserve + * @param stableRateEnabled True if stable rate borrowing is enabled, false otherwise + **/ + event BorrowingEnabledOnReserve(address indexed asset, bool stableRateEnabled); + + /** + * @dev Emitted when borrowing is disabled on a reserve + * @param asset The address of the underlying asset of the reserve + **/ + event BorrowingDisabledOnReserve(address indexed asset); + + /** + * @dev Emitted when the collateralization risk parameters for the specified asset are updated. + * @param asset The address of the underlying asset of the reserve + * @param ltv The loan to value of the asset when used as collateral + * @param liquidationThreshold The threshold at which loans using this asset as collateral will be considered undercollateralized + * @param liquidationBonus The bonus liquidators receive to liquidate this asset + **/ + event CollateralConfigurationChanged( + address indexed asset, + uint256 ltv, + uint256 liquidationThreshold, + uint256 liquidationBonus + ); + + /** + * @dev Emitted when stable rate borrowing is enabled on a reserve + * @param asset The address of the underlying asset of the reserve + **/ + event StableRateEnabledOnReserve(address indexed asset); + + /** + * @dev Emitted when stable rate borrowing is disabled on a reserve + * @param asset The address of the underlying asset of the reserve + **/ + event StableRateDisabledOnReserve(address indexed asset); + + /** + * @dev Emitted when a reserve is activated + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveActivated(address indexed asset); + + /** + * @dev Emitted when a reserve is deactivated + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveDeactivated(address indexed asset); + + /** + * @dev Emitted when a reserve is frozen + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveFrozen(address indexed asset); + + /** + * @dev Emitted when a reserve is unfrozen + * @param asset The address of the underlying asset of the reserve + **/ + event ReserveUnfrozen(address indexed asset); + + /** + * @dev Emitted when a reserve factor is updated + * @param asset The address of the underlying asset of the reserve + * @param factor The new reserve factor + **/ + event ReserveFactorChanged(address indexed asset, uint256 factor); + + /** + * @dev Emitted when the reserve decimals are updated + * @param asset The address of the underlying asset of the reserve + * @param decimals The new decimals + **/ + event ReserveDecimalsChanged(address indexed asset, uint256 decimals); + + /** + * @dev Emitted when a reserve interest strategy contract is updated + * @param asset The address of the underlying asset of the reserve + * @param strategy The new address of the interest strategy contract + **/ + event ReserveInterestRateStrategyChanged(address indexed asset, address strategy); + + /** + * @dev Emitted when an aToken implementation is upgraded + * @param asset The address of the underlying asset of the reserve + * @param proxy The aToken proxy address + * @param implementation The new aToken implementation + **/ + event ATokenUpgraded( + address indexed asset, + address indexed proxy, + address indexed implementation + ); + + /** + * @dev Emitted when the implementation of a stable debt token is upgraded + * @param asset The address of the underlying asset of the reserve + * @param proxy The stable debt token proxy address + * @param implementation The new aToken implementation + **/ + event StableDebtTokenUpgraded( + address indexed asset, + address indexed proxy, + address indexed implementation + ); + + /** + * @dev Emitted when the implementation of a variable debt token is upgraded + * @param asset The address of the underlying asset of the reserve + * @param proxy The variable debt token proxy address + * @param implementation The new aToken implementation + **/ + event VariableDebtTokenUpgraded( + address indexed asset, + address indexed proxy, + address indexed implementation + ); +} diff --git a/contracts/interfaces/IStableDebtToken.sol b/contracts/interfaces/IStableDebtToken.sol index c24750bc..e39cf8b5 100644 --- a/contracts/interfaces/IStableDebtToken.sol +++ b/contracts/interfaces/IStableDebtToken.sol @@ -1,6 +1,9 @@ // SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; +import {IInitializableDebtToken} from './IInitializableDebtToken.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; + /** * @title IStableDebtToken * @notice Defines the interface for the stable debt token @@ -8,7 +11,7 @@ pragma solidity 0.6.12; * @author Aave **/ -interface IStableDebtToken { +interface IStableDebtToken is IInitializableDebtToken { /** * @dev Emitted when new stable debt is minted * @param user The address of the user who triggered the minting @@ -122,4 +125,9 @@ interface IStableDebtToken { * @return The debt balance of the user since the last burn/mint action **/ function principalBalanceOf(address user) external view returns (uint256); + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view returns (IAaveIncentivesController); } diff --git a/contracts/interfaces/ITokenConfiguration.sol b/contracts/interfaces/ITokenConfiguration.sol deleted file mode 100644 index 94607bcd..00000000 --- a/contracts/interfaces/ITokenConfiguration.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: agpl-3.0 -pragma solidity ^0.6.12; - -/** - * @title ITokenConfiguration - * @author Aave - * @dev Common interface between aTokens and debt tokens to fetch the - * token configuration - **/ -interface ITokenConfiguration { - function UNDERLYING_ASSET_ADDRESS() external view returns (address); - - function POOL() external view returns (address); -} diff --git a/contracts/interfaces/IVariableDebtToken.sol b/contracts/interfaces/IVariableDebtToken.sol index 6a0c1555..d88c25fc 100644 --- a/contracts/interfaces/IVariableDebtToken.sol +++ b/contracts/interfaces/IVariableDebtToken.sol @@ -2,13 +2,15 @@ pragma solidity 0.6.12; import {IScaledBalanceToken} from './IScaledBalanceToken.sol'; +import {IInitializableDebtToken} from './IInitializableDebtToken.sol'; +import {IAaveIncentivesController} from './IAaveIncentivesController.sol'; /** * @title IVariableDebtToken * @author Aave * @notice Defines the basic interface for a variable debt token. **/ -interface IVariableDebtToken is IScaledBalanceToken { +interface IVariableDebtToken is IScaledBalanceToken, IInitializableDebtToken { /** * @dev Emitted after the mint action * @param from The address performing the mint @@ -52,4 +54,9 @@ interface IVariableDebtToken is IScaledBalanceToken { uint256 amount, uint256 index ) external; + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view returns (IAaveIncentivesController); } diff --git a/contracts/mocks/upgradeability/MockAToken.sol b/contracts/mocks/upgradeability/MockAToken.sol index f0eda38c..7e7b6f2a 100644 --- a/contracts/mocks/upgradeability/MockAToken.sol +++ b/contracts/mocks/upgradeability/MockAToken.sol @@ -2,39 +2,11 @@ pragma solidity 0.6.12; import {AToken} from '../../protocol/tokenization/AToken.sol'; -import {LendingPool} from '../../protocol/lendingpool/LendingPool.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; contract MockAToken is AToken { - constructor( - LendingPool pool, - address underlyingAssetAddress, - address reserveTreasury, - string memory tokenName, - string memory tokenSymbol, - address incentivesController - ) - public - AToken( - pool, - underlyingAssetAddress, - reserveTreasury, - tokenName, - tokenSymbol, - incentivesController - ) - {} - function getRevision() internal pure override returns (uint256) { return 0x2; } - - function initialize( - uint8 _underlyingAssetDecimals, - string calldata _tokenName, - string calldata _tokenSymbol - ) external virtual override initializer { - _setName(_tokenName); - _setSymbol(_tokenSymbol); - _setDecimals(_underlyingAssetDecimals); - } } diff --git a/contracts/mocks/upgradeability/MockStableDebtToken.sol b/contracts/mocks/upgradeability/MockStableDebtToken.sol index 5030c171..cbc66664 100644 --- a/contracts/mocks/upgradeability/MockStableDebtToken.sol +++ b/contracts/mocks/upgradeability/MockStableDebtToken.sol @@ -4,17 +4,6 @@ pragma solidity 0.6.12; import {StableDebtToken} from '../../protocol/tokenization/StableDebtToken.sol'; contract MockStableDebtToken is StableDebtToken { - constructor( - address _pool, - address _underlyingAssetAddress, - string memory _tokenName, - string memory _tokenSymbol, - address incentivesController - ) - public - StableDebtToken(_pool, _underlyingAssetAddress, _tokenName, _tokenSymbol, incentivesController) - {} - function getRevision() internal pure override returns (uint256) { return 0x2; } diff --git a/contracts/mocks/upgradeability/MockVariableDebtToken.sol b/contracts/mocks/upgradeability/MockVariableDebtToken.sol index 0a9a03aa..497682c3 100644 --- a/contracts/mocks/upgradeability/MockVariableDebtToken.sol +++ b/contracts/mocks/upgradeability/MockVariableDebtToken.sol @@ -4,23 +4,6 @@ pragma solidity 0.6.12; import {VariableDebtToken} from '../../protocol/tokenization/VariableDebtToken.sol'; contract MockVariableDebtToken is VariableDebtToken { - constructor( - address _pool, - address _underlyingAssetAddress, - string memory _tokenName, - string memory _tokenSymbol, - address incentivesController - ) - public - VariableDebtToken( - _pool, - _underlyingAssetAddress, - _tokenName, - _tokenSymbol, - incentivesController - ) - {} - function getRevision() internal pure override returns (uint256) { return 0x2; } diff --git a/contracts/protocol/lendingpool/LendingPool.sol b/contracts/protocol/lendingpool/LendingPool.sol index 7c48f237..75e80230 100644 --- a/contracts/protocol/lendingpool/LendingPool.sol +++ b/contracts/protocol/lendingpool/LendingPool.sol @@ -49,10 +49,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage using PercentageMath for uint256; using SafeERC20 for IERC20; - //main configuration parameters - uint256 public constant MAX_STABLE_RATE_BORROW_SIZE_PERCENT = 2500; - uint256 public constant FLASHLOAN_PREMIUM_TOTAL = 9; - uint256 public constant MAX_NUMBER_RESERVES = 128; uint256 public constant LENDINGPOOL_REVISION = 0x2; modifier whenNotPaused() { @@ -86,9 +82,20 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage * - Caching the address of the LendingPoolAddressesProvider in order to reduce gas consumption * on subsequent operations * @param provider The address of the LendingPoolAddressesProvider + * @param maxStableRateBorrowSizePercent The percentage of available liquidity that can be borrowed at once at stable rate + * @param flashLoanPremiumTotal The fee on flash loans + * @param maxNumberOfReserves Maximum number of reserves supported to be listed in this LendingPool **/ - function initialize(ILendingPoolAddressesProvider provider) public initializer { + function initialize( + ILendingPoolAddressesProvider provider, + uint256 maxStableRateBorrowSizePercent, + uint256 flashLoanPremiumTotal, + uint256 maxNumberOfReserves + ) public initializer { _addressesProvider = provider; + _maxStableRateBorrowSizePercent = maxStableRateBorrowSizePercent; + _flashLoanPremiumTotal = flashLoanPremiumTotal; + _maxNumberOfReserves = maxNumberOfReserves; } /** @@ -144,7 +151,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage address asset, uint256 amount, address to - ) external override whenNotPaused returns (uint256) { + ) external override whenNotPaused returns (uint256) { DataTypes.ReserveData storage reserve = _reserves[asset]; address aToken = reserve.aTokenAddress; @@ -499,7 +506,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage for (vars.i = 0; vars.i < assets.length; vars.i++) { aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress; - premiums[vars.i] = amounts[vars.i].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000); + premiums[vars.i] = amounts[vars.i].mul(_flashLoanPremiumTotal).div(10000); IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]); } @@ -703,6 +710,27 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage return _addressesProvider; } + /** + * @dev Returns the percentage of available liquidity that can be borrowed at once at stable rate + */ + function MAX_STABLE_RATE_BORROW_SIZE_PERCENT() public view returns (uint256) { + return _maxStableRateBorrowSizePercent; + } + + /** + * @dev Returns the fee on flash loans + */ + function FLASHLOAN_PREMIUM_TOTAL() public view returns (uint256) { + return _flashLoanPremiumTotal; + } + + /** + * @dev Returns the maximum number of reserves supported to be listed in this LendingPool + */ + function MAX_NUMBER_RESERVES() public view returns (uint256) { + return _maxNumberOfReserves; + } + /** * @dev Validates and finalizes an aToken transfer * - Only callable by the overlying aToken of the `asset` @@ -847,7 +875,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage vars.amount, amountInETH, vars.interestRateMode, - MAX_STABLE_RATE_BORROW_SIZE_PERCENT, + _maxStableRateBorrowSizePercent, _reserves, userConfig, _reservesList, @@ -909,7 +937,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage function _addReserveToList(address asset) internal { uint256 reservesCount = _reservesCount; - require(reservesCount < MAX_NUMBER_RESERVES, Errors.LP_NO_MORE_RESERVES_ALLOWED); + require(reservesCount < _maxNumberOfReserves, Errors.LP_NO_MORE_RESERVES_ALLOWED); bool reserveAlreadyAdded = _reserves[asset].id != 0 || _reservesList[0] == asset; diff --git a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol index bb3fb774..f42415ca 100644 --- a/contracts/protocol/lendingpool/LendingPoolConfigurator.sol +++ b/contracts/protocol/lendingpool/LendingPoolConfigurator.sol @@ -10,11 +10,14 @@ import { import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol'; import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol'; import {ILendingPool} from '../../interfaces/ILendingPool.sol'; -import {ITokenConfiguration} from '../../interfaces/ITokenConfiguration.sol'; import {IERC20Detailed} from '../../dependencies/openzeppelin/contracts/IERC20Detailed.sol'; import {Errors} from '../libraries/helpers/Errors.sol'; import {PercentageMath} from '../libraries/math/PercentageMath.sol'; import {DataTypes} from '../libraries/types/DataTypes.sol'; +import {IInitializableDebtToken} from '../../interfaces/IInitializableDebtToken.sol'; +import {IInitializableAToken} from '../../interfaces/IInitializableAToken.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; +import {ILendingPoolConfigurator} from '../../interfaces/ILendingPoolConfigurator.sol'; /** * @title LendingPoolConfigurator contract @@ -22,147 +25,11 @@ import {DataTypes} from '../libraries/types/DataTypes.sol'; * @dev Implements the configuration methods for the Aave protocol **/ -contract LendingPoolConfigurator is VersionedInitializable { +contract LendingPoolConfigurator is VersionedInitializable, ILendingPoolConfigurator { using SafeMath for uint256; using PercentageMath for uint256; using ReserveConfiguration for DataTypes.ReserveConfigurationMap; - /** - * @dev Emitted when a reserve is initialized. - * @param asset The address of the underlying asset of the reserve - * @param aToken The address of the associated aToken contract - * @param stableDebtToken The address of the associated stable rate debt token - * @param variableDebtToken The address of the associated variable rate debt token - * @param interestRateStrategyAddress The address of the interest rate strategy for the reserve - **/ - event ReserveInitialized( - address indexed asset, - address indexed aToken, - address stableDebtToken, - address variableDebtToken, - address interestRateStrategyAddress - ); - - /** - * @dev Emitted when borrowing is enabled on a reserve - * @param asset The address of the underlying asset of the reserve - * @param stableRateEnabled True if stable rate borrowing is enabled, false otherwise - **/ - event BorrowingEnabledOnReserve(address indexed asset, bool stableRateEnabled); - - /** - * @dev Emitted when borrowing is disabled on a reserve - * @param asset The address of the underlying asset of the reserve - **/ - event BorrowingDisabledOnReserve(address indexed asset); - - /** - * @dev Emitted when the collateralization risk parameters for the specified asset are updated. - * @param asset The address of the underlying asset of the reserve - * @param ltv The loan to value of the asset when used as collateral - * @param liquidationThreshold The threshold at which loans using this asset as collateral will be considered undercollateralized - * @param liquidationBonus The bonus liquidators receive to liquidate this asset - **/ - event CollateralConfigurationChanged( - address indexed asset, - uint256 ltv, - uint256 liquidationThreshold, - uint256 liquidationBonus - ); - - /** - * @dev Emitted when stable rate borrowing is enabled on a reserve - * @param asset The address of the underlying asset of the reserve - **/ - event StableRateEnabledOnReserve(address indexed asset); - - /** - * @dev Emitted when stable rate borrowing is disabled on a reserve - * @param asset The address of the underlying asset of the reserve - **/ - event StableRateDisabledOnReserve(address indexed asset); - - /** - * @dev Emitted when a reserve is activated - * @param asset The address of the underlying asset of the reserve - **/ - event ReserveActivated(address indexed asset); - - /** - * @dev Emitted when a reserve is deactivated - * @param asset The address of the underlying asset of the reserve - **/ - event ReserveDeactivated(address indexed asset); - - /** - * @dev Emitted when a reserve is frozen - * @param asset The address of the underlying asset of the reserve - **/ - event ReserveFrozen(address indexed asset); - - /** - * @dev Emitted when a reserve is unfrozen - * @param asset The address of the underlying asset of the reserve - **/ - event ReserveUnfrozen(address indexed asset); - - /** - * @dev Emitted when a reserve factor is updated - * @param asset The address of the underlying asset of the reserve - * @param factor The new reserve factor - **/ - event ReserveFactorChanged(address indexed asset, uint256 factor); - - /** - * @dev Emitted when the reserve decimals are updated - * @param asset The address of the underlying asset of the reserve - * @param decimals The new decimals - **/ - event ReserveDecimalsChanged(address indexed asset, uint256 decimals); - - /** - * @dev Emitted when a reserve interest strategy contract is updated - * @param asset The address of the underlying asset of the reserve - * @param strategy The new address of the interest strategy contract - **/ - event ReserveInterestRateStrategyChanged(address indexed asset, address strategy); - - /** - * @dev Emitted when an aToken implementation is upgraded - * @param asset The address of the underlying asset of the reserve - * @param proxy The aToken proxy address - * @param implementation The new aToken implementation - **/ - event ATokenUpgraded( - address indexed asset, - address indexed proxy, - address indexed implementation - ); - - /** - * @dev Emitted when the implementation of a stable debt token is upgraded - * @param asset The address of the underlying asset of the reserve - * @param proxy The stable debt token proxy address - * @param implementation The new aToken implementation - **/ - event StableDebtTokenUpgraded( - address indexed asset, - address indexed proxy, - address indexed implementation - ); - - /** - * @dev Emitted when the implementation of a variable debt token is upgraded - * @param asset The address of the underlying asset of the reserve - * @param proxy The variable debt token proxy address - * @param implementation The new aToken implementation - **/ - event VariableDebtTokenUpgraded( - address indexed asset, - address indexed proxy, - address indexed implementation - ); - ILendingPoolAddressesProvider internal addressesProvider; ILendingPool internal pool; @@ -191,114 +58,186 @@ contract LendingPoolConfigurator is VersionedInitializable { } /** - * @dev Initializes a reserve - * @param aTokenImpl The address of the aToken contract implementation - * @param stableDebtTokenImpl The address of the stable debt token contract - * @param variableDebtTokenImpl The address of the variable debt token contract - * @param underlyingAssetDecimals The decimals of the reserve underlying asset - * @param interestRateStrategyAddress The address of the interest rate strategy contract for this reserve + * @dev Initializes reserves in batch **/ - function initReserve( - address aTokenImpl, - address stableDebtTokenImpl, - address variableDebtTokenImpl, - uint8 underlyingAssetDecimals, - address interestRateStrategyAddress - ) public onlyPoolAdmin { - address asset = ITokenConfiguration(aTokenImpl).UNDERLYING_ASSET_ADDRESS(); + function batchInitReserve(InitReserveInput[] calldata inputParams) public onlyPoolAdmin { + ILendingPool cachedPool = pool; + for (uint256 i = 0; i < inputParams.length; i++) { + _initReserve(cachedPool, inputParams[i]); + } + } - require( - address(pool) == ITokenConfiguration(aTokenImpl).POOL(), - Errors.LPC_INVALID_ATOKEN_POOL_ADDRESS - ); - require( - address(pool) == ITokenConfiguration(stableDebtTokenImpl).POOL(), - Errors.LPC_INVALID_STABLE_DEBT_TOKEN_POOL_ADDRESS - ); - require( - address(pool) == ITokenConfiguration(variableDebtTokenImpl).POOL(), - Errors.LPC_INVALID_VARIABLE_DEBT_TOKEN_POOL_ADDRESS - ); - require( - asset == ITokenConfiguration(stableDebtTokenImpl).UNDERLYING_ASSET_ADDRESS(), - Errors.LPC_INVALID_STABLE_DEBT_TOKEN_UNDERLYING_ADDRESS - ); - require( - asset == ITokenConfiguration(variableDebtTokenImpl).UNDERLYING_ASSET_ADDRESS(), - Errors.LPC_INVALID_VARIABLE_DEBT_TOKEN_UNDERLYING_ADDRESS - ); - - address aTokenProxyAddress = _initTokenWithProxy(aTokenImpl, underlyingAssetDecimals); + function _initReserve(ILendingPool pool, InitReserveInput calldata inputParams) internal { + address aTokenProxyAddress = + _initTokenWithProxy( + inputParams.aTokenImpl, + abi.encodeWithSelector( + IInitializableAToken.initialize.selector, + pool, + inputParams.treasury, + inputParams.underlyingAsset, + IAaveIncentivesController(inputParams.incentivesController), + inputParams.underlyingAssetDecimals, + inputParams.aTokenName, + inputParams.aTokenSymbol + ) + ); address stableDebtTokenProxyAddress = - _initTokenWithProxy(stableDebtTokenImpl, underlyingAssetDecimals); + _initTokenWithProxy( + inputParams.stableDebtTokenImpl, + abi.encodeWithSelector( + IInitializableDebtToken.initialize.selector, + pool, + inputParams.underlyingAsset, + IAaveIncentivesController(inputParams.incentivesController), + inputParams.underlyingAssetDecimals, + inputParams.stableDebtTokenName, + inputParams.stableDebtTokenSymbol + ) + ); address variableDebtTokenProxyAddress = - _initTokenWithProxy(variableDebtTokenImpl, underlyingAssetDecimals); + _initTokenWithProxy( + inputParams.variableDebtTokenImpl, + abi.encodeWithSelector( + IInitializableDebtToken.initialize.selector, + pool, + inputParams.underlyingAsset, + IAaveIncentivesController(inputParams.incentivesController), + inputParams.underlyingAssetDecimals, + inputParams.variableDebtTokenName, + inputParams.variableDebtTokenSymbol + ) + ); pool.initReserve( - asset, + inputParams.underlyingAsset, aTokenProxyAddress, stableDebtTokenProxyAddress, variableDebtTokenProxyAddress, - interestRateStrategyAddress + inputParams.interestRateStrategyAddress ); - DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset); + DataTypes.ReserveConfigurationMap memory currentConfig = + pool.getConfiguration(inputParams.underlyingAsset); - currentConfig.setDecimals(underlyingAssetDecimals); + currentConfig.setDecimals(inputParams.underlyingAssetDecimals); currentConfig.setActive(true); currentConfig.setFrozen(false); - pool.setConfiguration(asset, currentConfig.data); + pool.setConfiguration(inputParams.underlyingAsset, currentConfig.data); emit ReserveInitialized( - asset, + inputParams.underlyingAsset, aTokenProxyAddress, stableDebtTokenProxyAddress, variableDebtTokenProxyAddress, - interestRateStrategyAddress + inputParams.interestRateStrategyAddress ); } /** * @dev Updates the aToken implementation for the reserve - * @param asset The address of the underlying asset of the reserve to be updated - * @param implementation The address of the new aToken implementation **/ - function updateAToken(address asset, address implementation) external onlyPoolAdmin { - DataTypes.ReserveData memory reserveData = pool.getReserveData(asset); + function updateAToken(UpdateATokenInput calldata inputParams) external onlyPoolAdmin { + ILendingPool cachedPool = pool; - _upgradeTokenImplementation(asset, reserveData.aTokenAddress, implementation); + DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(inputParams.asset); - emit ATokenUpgraded(asset, reserveData.aTokenAddress, implementation); + DataTypes.ReserveConfigurationMap memory configuration = + cachedPool.getConfiguration(inputParams.asset); + + (, , , uint256 decimals, ) = configuration.getParamsMemory(); + + _upgradeTokenImplementation( + reserveData.aTokenAddress, + inputParams.implementation, + abi.encodeWithSelector( + IInitializableAToken.initialize.selector, + cachedPool, + inputParams.treasury, + inputParams.asset, + inputParams.incentivesController, + decimals, + inputParams.name, + inputParams.symbol + ) + ); + + emit ATokenUpgraded(inputParams.asset, reserveData.aTokenAddress, inputParams.implementation); } /** * @dev Updates the stable debt token implementation for the reserve - * @param asset The address of the underlying asset of the reserve to be updated - * @param implementation The address of the new aToken implementation **/ - function updateStableDebtToken(address asset, address implementation) external onlyPoolAdmin { - DataTypes.ReserveData memory reserveData = pool.getReserveData(asset); + function updateStableDebtToken(UpdateDebtTokenInput calldata inputParams) external onlyPoolAdmin { + ILendingPool cachedPool = pool; - _upgradeTokenImplementation(asset, reserveData.stableDebtTokenAddress, implementation); + DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(inputParams.asset); - emit StableDebtTokenUpgraded(asset, reserveData.stableDebtTokenAddress, implementation); + DataTypes.ReserveConfigurationMap memory configuration = + cachedPool.getConfiguration(inputParams.asset); + + (, , , uint256 decimals, ) = configuration.getParamsMemory(); + + _upgradeTokenImplementation( + reserveData.stableDebtTokenAddress, + inputParams.implementation, + abi.encodeWithSelector( + IInitializableDebtToken.initialize.selector, + cachedPool, + inputParams.asset, + inputParams.incentivesController, + decimals, + inputParams.name, + inputParams.symbol + ) + ); + + emit StableDebtTokenUpgraded( + inputParams.asset, + reserveData.stableDebtTokenAddress, + inputParams.implementation + ); } /** * @dev Updates the variable debt token implementation for the asset - * @param asset The address of the underlying asset of the reserve to be updated - * @param implementation The address of the new aToken implementation **/ - function updateVariableDebtToken(address asset, address implementation) external onlyPoolAdmin { - DataTypes.ReserveData memory reserveData = pool.getReserveData(asset); + function updateVariableDebtToken(UpdateDebtTokenInput calldata inputParams) + external + onlyPoolAdmin + { + ILendingPool cachedPool = pool; - _upgradeTokenImplementation(asset, reserveData.variableDebtTokenAddress, implementation); + DataTypes.ReserveData memory reserveData = cachedPool.getReserveData(inputParams.asset); - emit VariableDebtTokenUpgraded(asset, reserveData.variableDebtTokenAddress, implementation); + DataTypes.ReserveConfigurationMap memory configuration = + cachedPool.getConfiguration(inputParams.asset); + + (, , , uint256 decimals, ) = configuration.getParamsMemory(); + + _upgradeTokenImplementation( + reserveData.variableDebtTokenAddress, + inputParams.implementation, + abi.encodeWithSelector( + IInitializableDebtToken.initialize.selector, + cachedPool, + inputParams.asset, + inputParams.incentivesController, + decimals, + inputParams.name, + inputParams.symbol + ) + ); + + emit VariableDebtTokenUpgraded( + inputParams.asset, + reserveData.variableDebtTokenAddress, + inputParams.implementation + ); } /** @@ -509,44 +448,27 @@ contract LendingPoolConfigurator is VersionedInitializable { pool.setPause(val); } - function _initTokenWithProxy(address implementation, uint8 decimals) internal returns (address) { + function _initTokenWithProxy(address implementation, bytes memory initParams) + internal + returns (address) + { InitializableImmutableAdminUpgradeabilityProxy proxy = new InitializableImmutableAdminUpgradeabilityProxy(address(this)); - bytes memory params = - abi.encodeWithSignature( - 'initialize(uint8,string,string)', - decimals, - IERC20Detailed(implementation).name(), - IERC20Detailed(implementation).symbol() - ); - - proxy.initialize(implementation, params); + proxy.initialize(implementation, initParams); return address(proxy); } function _upgradeTokenImplementation( - address asset, address proxyAddress, - address implementation + address implementation, + bytes memory initParams ) internal { InitializableImmutableAdminUpgradeabilityProxy proxy = InitializableImmutableAdminUpgradeabilityProxy(payable(proxyAddress)); - DataTypes.ReserveConfigurationMap memory configuration = pool.getConfiguration(asset); - - (, , , uint256 decimals, ) = configuration.getParamsMemory(); - - bytes memory params = - abi.encodeWithSignature( - 'initialize(uint8,string,string)', - uint8(decimals), - IERC20Detailed(implementation).name(), - IERC20Detailed(implementation).symbol() - ); - - proxy.upgradeToAndCall(implementation, params); + proxy.upgradeToAndCall(implementation, initParams); } function _checkNoLiquidity(address asset) internal view { diff --git a/contracts/protocol/lendingpool/LendingPoolStorage.sol b/contracts/protocol/lendingpool/LendingPoolStorage.sol index 4edff4a3..198a3eea 100644 --- a/contracts/protocol/lendingpool/LendingPoolStorage.sol +++ b/contracts/protocol/lendingpool/LendingPoolStorage.sol @@ -23,4 +23,10 @@ contract LendingPoolStorage { uint256 internal _reservesCount; bool internal _paused; + + uint256 internal _maxStableRateBorrowSizePercent; + + uint256 internal _flashLoanPremiumTotal; + + uint256 internal _maxNumberOfReserves; } diff --git a/contracts/protocol/tokenization/AToken.sol b/contracts/protocol/tokenization/AToken.sol index 00df0b61..1a47ebf4 100644 --- a/contracts/protocol/tokenization/AToken.sol +++ b/contracts/protocol/tokenization/AToken.sol @@ -9,13 +9,18 @@ import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {Errors} from '../libraries/helpers/Errors.sol'; import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol'; import {IncentivizedERC20} from './IncentivizedERC20.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; /** * @title Aave ERC20 AToken * @dev Implementation of the interest bearing token for the Aave protocol * @author Aave */ -contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { +contract AToken is + VersionedInitializable, + IncentivizedERC20('ATOKEN_IMPL', 'ATOKEN_IMPL', 0), + IAToken +{ using WadRayMath for uint256; using SafeERC20 for IERC20; @@ -25,44 +30,46 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { bytes32 public constant PERMIT_TYPEHASH = keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)'); - uint256 public constant UINT_MAX_VALUE = uint256(-1); uint256 public constant ATOKEN_REVISION = 0x1; - address public immutable UNDERLYING_ASSET_ADDRESS; - address public immutable RESERVE_TREASURY_ADDRESS; - ILendingPool public immutable POOL; /// @dev owner => next valid nonce to submit with permit() mapping(address => uint256) public _nonces; bytes32 public DOMAIN_SEPARATOR; - modifier onlyLendingPool { - require(_msgSender() == address(POOL), Errors.CT_CALLER_MUST_BE_LENDING_POOL); - _; - } + ILendingPool internal _pool; + address internal _treasury; + address internal _underlyingAsset; + IAaveIncentivesController internal _incentivesController; - constructor( - ILendingPool pool, - address underlyingAssetAddress, - address reserveTreasuryAddress, - string memory tokenName, - string memory tokenSymbol, - address incentivesController - ) public IncentivizedERC20(tokenName, tokenSymbol, 18, incentivesController) { - POOL = pool; - UNDERLYING_ASSET_ADDRESS = underlyingAssetAddress; - RESERVE_TREASURY_ADDRESS = reserveTreasuryAddress; + modifier onlyLendingPool { + require(_msgSender() == address(_pool), Errors.CT_CALLER_MUST_BE_LENDING_POOL); + _; } function getRevision() internal pure virtual override returns (uint256) { return ATOKEN_REVISION; } + /** + * @dev Initializes the aToken + * @param pool The address of the lending pool where this aToken will be used + * @param treasury The address of the Aave treasury, receiving the fees on this aToken + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param aTokenDecimals The decimals of the aToken, same as the underlying asset's + * @param aTokenName The name of the aToken + * @param aTokenSymbol The symbol of the aToken + */ function initialize( - uint8 underlyingAssetDecimals, - string calldata tokenName, - string calldata tokenSymbol - ) external virtual initializer { + ILendingPool pool, + address treasury, + address underlyingAsset, + IAaveIncentivesController incentivesController, + uint8 aTokenDecimals, + string calldata aTokenName, + string calldata aTokenSymbol + ) external override initializer { uint256 chainId; //solium-disable-next-line @@ -73,16 +80,21 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { DOMAIN_SEPARATOR = keccak256( abi.encode( EIP712_DOMAIN, - keccak256(bytes(tokenName)), + keccak256(bytes(aTokenName)), keccak256(EIP712_REVISION), chainId, address(this) ) ); - _setName(tokenName); - _setSymbol(tokenSymbol); - _setDecimals(underlyingAssetDecimals); + _setName(aTokenName); + _setSymbol(aTokenSymbol); + _setDecimals(aTokenDecimals); + + _pool = pool; + _treasury = treasury; + _underlyingAsset = underlyingAsset; + _incentivesController = incentivesController; } /** @@ -103,7 +115,7 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT); _burn(user, amountScaled); - IERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(receiverOfUnderlying, amount); + IERC20(_underlyingAsset).safeTransfer(receiverOfUnderlying, amount); emit Transfer(user, address(0), amount); emit Burn(user, receiverOfUnderlying, amount, index); @@ -145,14 +157,16 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { return; } + address treasury = _treasury; + // Compared to the normal mint, we don't check for rounding errors. // The amount to mint can easily be very small since it is a fraction of the interest ccrued. // In that case, the treasury will experience a (very small) loss, but it // wont cause potentially valid transactions to fail. - _mint(RESERVE_TREASURY_ADDRESS, amount.rayDiv(index)); + _mint(treasury, amount.rayDiv(index)); - emit Transfer(address(0), RESERVE_TREASURY_ADDRESS, amount); - emit Mint(RESERVE_TREASURY_ADDRESS, amount, index); + emit Transfer(address(0), treasury, amount); + emit Mint(treasury, amount, index); } /** @@ -185,7 +199,7 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { override(IncentivizedERC20, IERC20) returns (uint256) { - return super.balanceOf(user).rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS)); + return super.balanceOf(user).rayMul(_pool.getReserveNormalizedIncome(_underlyingAsset)); } /** @@ -226,7 +240,7 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { return 0; } - return currentSupplyScaled.rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS)); + return currentSupplyScaled.rayMul(_pool.getReserveNormalizedIncome(_underlyingAsset)); } /** @@ -237,6 +251,41 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { return super.totalSupply(); } + /** + * @dev Returns the address of the Aave treasury, receiving the fees on this aToken + **/ + function RESERVE_TREASURY_ADDRESS() public view returns (address) { + return _treasury; + } + + /** + * @dev Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH) + **/ + function UNDERLYING_ASSET_ADDRESS() public view returns (address) { + return _underlyingAsset; + } + + /** + * @dev Returns the address of the lending pool where this aToken is used + **/ + function POOL() public view returns (ILendingPool) { + return _pool; + } + + /** + * @dev For internal usage in the logic of the parent contract IncentivizedERC20 + **/ + function _getIncentivesController() internal view override returns (IAaveIncentivesController) { + return _incentivesController; + } + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view override returns (IAaveIncentivesController) { + return _getIncentivesController(); + } + /** * @dev Transfers the underlying asset to `target`. Used by the LendingPool to transfer * assets in borrow(), withdraw() and flashLoan() @@ -250,7 +299,7 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { onlyLendingPool returns (uint256) { - IERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(target, amount); + IERC20(_underlyingAsset).safeTransfer(target, amount); return amount; } @@ -305,7 +354,10 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { uint256 amount, bool validate ) internal { - uint256 index = POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS); + address underlyingAsset = _underlyingAsset; + ILendingPool pool = _pool; + + uint256 index = pool.getReserveNormalizedIncome(underlyingAsset); uint256 fromBalanceBefore = super.balanceOf(from).rayMul(index); uint256 toBalanceBefore = super.balanceOf(to).rayMul(index); @@ -313,14 +365,7 @@ contract AToken is VersionedInitializable, IncentivizedERC20, IAToken { super._transfer(from, to, amount.rayDiv(index)); if (validate) { - POOL.finalizeTransfer( - UNDERLYING_ASSET_ADDRESS, - from, - to, - amount, - fromBalanceBefore, - toBalanceBefore - ); + pool.finalizeTransfer(underlyingAsset, from, to, amount, fromBalanceBefore, toBalanceBefore); } emit BalanceTransfer(from, to, amount, index); diff --git a/contracts/protocol/tokenization/DelegationAwareAToken.sol b/contracts/protocol/tokenization/DelegationAwareAToken.sol index 4e49f2b6..443e10fb 100644 --- a/contracts/protocol/tokenization/DelegationAwareAToken.sol +++ b/contracts/protocol/tokenization/DelegationAwareAToken.sol @@ -14,36 +14,17 @@ import {AToken} from './AToken.sol'; contract DelegationAwareAToken is AToken { modifier onlyPoolAdmin { require( - _msgSender() == ILendingPool(POOL).getAddressesProvider().getPoolAdmin(), + _msgSender() == ILendingPool(_pool).getAddressesProvider().getPoolAdmin(), Errors.CALLER_NOT_POOL_ADMIN ); _; } - constructor( - ILendingPool pool, - address underlyingAssetAddress, - address reserveTreasury, - string memory tokenName, - string memory tokenSymbol, - address incentivesController - ) - public - AToken( - pool, - underlyingAssetAddress, - reserveTreasury, - tokenName, - tokenSymbol, - incentivesController - ) - {} - /** * @dev Delegates voting power of the underlying asset to a `delegatee` address * @param delegatee The address that will receive the delegation **/ function delegateUnderlyingTo(address delegatee) external onlyPoolAdmin { - IDelegationToken(UNDERLYING_ASSET_ADDRESS).delegate(delegatee); + IDelegationToken(_underlyingAsset).delegate(delegatee); } } diff --git a/contracts/protocol/tokenization/IncentivizedERC20.sol b/contracts/protocol/tokenization/IncentivizedERC20.sol index 101eaf9a..fb831769 100644 --- a/contracts/protocol/tokenization/IncentivizedERC20.sol +++ b/contracts/protocol/tokenization/IncentivizedERC20.sol @@ -12,11 +12,9 @@ import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesControl * @notice Basic ERC20 implementation * @author Aave, inspired by the Openzeppelin ERC20 implementation **/ -contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { +abstract contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { using SafeMath for uint256; - IAaveIncentivesController internal immutable _incentivesController; - mapping(address => uint256) internal _balances; mapping(address => mapping(address => uint256)) private _allowances; @@ -28,13 +26,11 @@ contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { constructor( string memory name, string memory symbol, - uint8 decimals, - address incentivesController + uint8 decimals ) public { _name = name; _symbol = symbol; _decimals = decimals; - _incentivesController = IAaveIncentivesController(incentivesController); } /** @@ -72,6 +68,12 @@ contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { return _balances[account]; } + /** + * @return Abstract function implemented by the child aToken/debtToken. + * Done this way in order to not break compatibility with previous versions of aTokens/debtTokens + **/ + function _getIncentivesController() internal view virtual returns(IAaveIncentivesController); + /** * @dev Executes a transfer of tokens from _msgSender() to recipient * @param recipient The recipient of the tokens @@ -180,11 +182,11 @@ contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { uint256 oldRecipientBalance = _balances[recipient]; _balances[recipient] = _balances[recipient].add(amount); - if (address(_incentivesController) != address(0)) { + if (address(_getIncentivesController()) != address(0)) { uint256 currentTotalSupply = _totalSupply; - _incentivesController.handleAction(sender, currentTotalSupply, oldSenderBalance); + _getIncentivesController().handleAction(sender, currentTotalSupply, oldSenderBalance); if (sender != recipient) { - _incentivesController.handleAction(recipient, currentTotalSupply, oldRecipientBalance); + _getIncentivesController().handleAction(recipient, currentTotalSupply, oldRecipientBalance); } } } @@ -200,8 +202,8 @@ contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { uint256 oldAccountBalance = _balances[account]; _balances[account] = oldAccountBalance.add(amount); - if (address(_incentivesController) != address(0)) { - _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance); + if (address(_getIncentivesController()) != address(0)) { + _getIncentivesController().handleAction(account, oldTotalSupply, oldAccountBalance); } } @@ -216,8 +218,8 @@ contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { uint256 oldAccountBalance = _balances[account]; _balances[account] = oldAccountBalance.sub(amount, 'ERC20: burn amount exceeds balance'); - if (address(_incentivesController) != address(0)) { - _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance); + if (address(_getIncentivesController()) != address(0)) { + _getIncentivesController().handleAction(account, oldTotalSupply, oldAccountBalance); } } diff --git a/contracts/protocol/tokenization/StableDebtToken.sol b/contracts/protocol/tokenization/StableDebtToken.sol index 78401405..f52c4931 100644 --- a/contracts/protocol/tokenization/StableDebtToken.sol +++ b/contracts/protocol/tokenization/StableDebtToken.sol @@ -5,6 +5,8 @@ import {DebtTokenBase} from './base/DebtTokenBase.sol'; import {MathUtils} from '../libraries/math/MathUtils.sol'; import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {IStableDebtToken} from '../../interfaces/IStableDebtToken.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; import {Errors} from '../libraries/helpers/Errors.sol'; /** @@ -23,13 +25,35 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { mapping(address => uint256) internal _usersStableRate; uint40 internal _totalSupplyTimestamp; - constructor( - address pool, + ILendingPool internal _pool; + address internal _underlyingAsset; + IAaveIncentivesController internal _incentivesController; + + /** + * @dev Initializes the debt token. + * @param pool The address of the lending pool where this aToken will be used + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's + * @param debtTokenName The name of the token + * @param debtTokenSymbol The symbol of the token + */ + function initialize( + ILendingPool pool, address underlyingAsset, - string memory name, - string memory symbol, - address incentivesController - ) public DebtTokenBase(pool, underlyingAsset, name, symbol, incentivesController) {} + IAaveIncentivesController incentivesController, + uint8 debtTokenDecimals, + string memory debtTokenName, + string memory debtTokenSymbol + ) public override initializer { + _setName(debtTokenName); + _setSymbol(debtTokenSymbol); + _setDecimals(debtTokenDecimals); + + _pool = pool; + _underlyingAsset = underlyingAsset; + _incentivesController = incentivesController; + } /** * @dev Gets the revision of the stable debt token implementation @@ -300,6 +324,48 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { return super.balanceOf(user); } + /** + * @dev Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH) + **/ + function UNDERLYING_ASSET_ADDRESS() public view returns (address) { + return _underlyingAsset; + } + + /** + * @dev Returns the address of the lending pool where this aToken is used + **/ + function POOL() public view returns (ILendingPool) { + return _pool; + } + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view override returns (IAaveIncentivesController) { + return _getIncentivesController(); + } + + /** + * @dev For internal usage in the logic of the parent contracts + **/ + function _getIncentivesController() internal view override returns (IAaveIncentivesController) { + return _incentivesController; + } + + /** + * @dev For internal usage in the logic of the parent contracts + **/ + function _getUnderlyingAssetAddress() internal view override returns (address) { + return _underlyingAsset; + } + + /** + * @dev For internal usage in the logic of the parent contracts + **/ + function _getLendingPool() internal view override returns (ILendingPool) { + return _pool; + } + /** * @dev Calculates the total supply * @param avgRate The average rate at which the total supply increases diff --git a/contracts/protocol/tokenization/VariableDebtToken.sol b/contracts/protocol/tokenization/VariableDebtToken.sol index d2e27ac3..60a47e12 100644 --- a/contracts/protocol/tokenization/VariableDebtToken.sol +++ b/contracts/protocol/tokenization/VariableDebtToken.sol @@ -5,6 +5,8 @@ import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol'; import {WadRayMath} from '../libraries/math/WadRayMath.sol'; import {Errors} from '../libraries/helpers/Errors.sol'; import {DebtTokenBase} from './base/DebtTokenBase.sol'; +import {ILendingPool} from '../../interfaces/ILendingPool.sol'; +import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol'; /** * @title VariableDebtToken @@ -17,13 +19,35 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { uint256 public constant DEBT_TOKEN_REVISION = 0x1; - constructor( - address pool, + ILendingPool internal _pool; + address internal _underlyingAsset; + IAaveIncentivesController internal _incentivesController; + + /** + * @dev Initializes the debt token. + * @param pool The address of the lending pool where this aToken will be used + * @param underlyingAsset The address of the underlying asset of this aToken (E.g. WETH for aWETH) + * @param incentivesController The smart contract managing potential incentives distribution + * @param debtTokenDecimals The decimals of the debtToken, same as the underlying asset's + * @param debtTokenName The name of the token + * @param debtTokenSymbol The symbol of the token + */ + function initialize( + ILendingPool pool, address underlyingAsset, - string memory name, - string memory symbol, - address incentivesController - ) public DebtTokenBase(pool, underlyingAsset, name, symbol, incentivesController) {} + IAaveIncentivesController incentivesController, + uint8 debtTokenDecimals, + string memory debtTokenName, + string memory debtTokenSymbol + ) public override initializer { + _setName(debtTokenName); + _setSymbol(debtTokenSymbol); + _setDecimals(debtTokenDecimals); + + _pool = pool; + _underlyingAsset = underlyingAsset; + _incentivesController = incentivesController; + } /** * @dev Gets the revision of the stable debt token implementation @@ -44,7 +68,7 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { return 0; } - return scaledBalance.rayMul(POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET_ADDRESS)); + return scaledBalance.rayMul(_pool.getReserveNormalizedVariableDebt(_underlyingAsset)); } /** @@ -113,8 +137,7 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { * @return The total supply **/ function totalSupply() public view virtual override returns (uint256) { - return - super.totalSupply().rayMul(POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET_ADDRESS)); + return super.totalSupply().rayMul(_pool.getReserveNormalizedVariableDebt(_underlyingAsset)); } /** @@ -139,4 +162,37 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken { { return (super.balanceOf(user), super.totalSupply()); } + + /** + * @dev Returns the address of the underlying asset of this aToken (E.g. WETH for aWETH) + **/ + function UNDERLYING_ASSET_ADDRESS() public view returns (address) { + return _underlyingAsset; + } + + /** + * @dev Returns the address of the incentives controller contract + **/ + function getIncentivesController() external view override returns (IAaveIncentivesController) { + return _getIncentivesController(); + } + + /** + * @dev Returns the address of the lending pool where this aToken is used + **/ + function POOL() public view returns (ILendingPool) { + return _pool; + } + + function _getIncentivesController() internal view override returns (IAaveIncentivesController) { + return _incentivesController; + } + + function _getUnderlyingAssetAddress() internal view override returns (address) { + return _underlyingAsset; + } + + function _getLendingPool() internal view override returns (ILendingPool) { + return _pool; + } } diff --git a/contracts/protocol/tokenization/base/DebtTokenBase.sol b/contracts/protocol/tokenization/base/DebtTokenBase.sol index 07bdef22..4d75bc2f 100644 --- a/contracts/protocol/tokenization/base/DebtTokenBase.sol +++ b/contracts/protocol/tokenization/base/DebtTokenBase.sol @@ -16,54 +16,20 @@ import {Errors} from '../../libraries/helpers/Errors.sol'; */ abstract contract DebtTokenBase is - IncentivizedERC20, + IncentivizedERC20('DEBTTOKEN_IMPL', 'DEBTTOKEN_IMPL', 0), VersionedInitializable, ICreditDelegationToken { - address public immutable UNDERLYING_ASSET_ADDRESS; - ILendingPool public immutable POOL; - mapping(address => mapping(address => uint256)) internal _borrowAllowances; /** * @dev Only lending pool can call functions marked by this modifier **/ modifier onlyLendingPool { - require(_msgSender() == address(POOL), Errors.CT_CALLER_MUST_BE_LENDING_POOL); + require(_msgSender() == address(_getLendingPool()), Errors.CT_CALLER_MUST_BE_LENDING_POOL); _; } - /** - * @dev The metadata of the token will be set on the proxy, that the reason of - * passing "NULL" and 0 as metadata - */ - constructor( - address pool, - address underlyingAssetAddress, - string memory name, - string memory symbol, - address incentivesController - ) public IncentivizedERC20(name, symbol, 18, incentivesController) { - POOL = ILendingPool(pool); - UNDERLYING_ASSET_ADDRESS = underlyingAssetAddress; - } - - /** - * @dev Initializes the debt token. - * @param name The name of the token - * @param symbol The symbol of the token - * @param decimals The decimals of the token - */ - function initialize( - uint8 decimals, - string memory name, - string memory symbol - ) public initializer { - _setName(name); - _setSymbol(symbol); - _setDecimals(decimals); - } - /** * @dev delegates borrowing power to a user on the specific debt token * @param delegatee the address receiving the delegated borrowing power @@ -73,7 +39,7 @@ abstract contract DebtTokenBase is **/ function approveDelegation(address delegatee, uint256 amount) external override { _borrowAllowances[_msgSender()][delegatee] = amount; - emit BorrowAllowanceDelegated(_msgSender(), delegatee, UNDERLYING_ASSET_ADDRESS, amount); + emit BorrowAllowanceDelegated(_msgSender(), delegatee, _getUnderlyingAssetAddress(), amount); } /** @@ -162,6 +128,10 @@ abstract contract DebtTokenBase is _borrowAllowances[delegator][delegatee] = newAllowance; - emit BorrowAllowanceDelegated(delegator, delegatee, UNDERLYING_ASSET_ADDRESS, newAllowance); + emit BorrowAllowanceDelegated(delegator, delegatee, _getUnderlyingAssetAddress(), newAllowance); } + + function _getUnderlyingAssetAddress() internal view virtual returns (address); + + function _getLendingPool() internal view virtual returns (ILendingPool); }