- Added contracts from v1 of the protocol.

This commit is contained in:
eboado 2020-05-29 18:45:37 +02:00
parent 5ccf06c1a8
commit b889cb25b6
105 changed files with 8456 additions and 1 deletions

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
contract AddressStorage {
mapping(bytes32 => address) private addresses;
function getAddress(bytes32 _key) public view returns (address) {
return addresses[_key];
}
function _setAddress(bytes32 _key, address _value) internal {
addresses[_key] = _value;
}
}

View File

@ -0,0 +1,239 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/access/Ownable.sol";
import "../libraries/openzeppelin-upgradeability/InitializableAdminUpgradeabilityProxy.sol";
import "./AddressStorage.sol";
import "../interfaces/ILendingPoolAddressesProvider.sol";
/**
* @title LendingPoolAddressesProvider contract
* @notice Is the main registry of the protocol. All the different components of the protocol are accessible
* through the addresses provider.
* @author Aave
**/
contract LendingPoolAddressesProvider is Ownable, ILendingPoolAddressesProvider, AddressStorage {
//events
event LendingPoolUpdated(address indexed newAddress);
event LendingPoolCoreUpdated(address indexed newAddress);
event LendingPoolParametersProviderUpdated(address indexed newAddress);
event LendingPoolManagerUpdated(address indexed newAddress);
event LendingPoolConfiguratorUpdated(address indexed newAddress);
event LendingPoolLiquidationManagerUpdated(address indexed newAddress);
event LendingPoolDataProviderUpdated(address indexed newAddress);
event EthereumAddressUpdated(address indexed newAddress);
event PriceOracleUpdated(address indexed newAddress);
event LendingRateOracleUpdated(address indexed newAddress);
event FeeProviderUpdated(address indexed newAddress);
event TokenDistributorUpdated(address indexed newAddress);
event ProxyCreated(bytes32 id, address indexed newAddress);
bytes32 private constant LENDING_POOL = "LENDING_POOL";
bytes32 private constant LENDING_POOL_CORE = "LENDING_POOL_CORE";
bytes32 private constant LENDING_POOL_CONFIGURATOR = "LENDING_POOL_CONFIGURATOR";
bytes32 private constant LENDING_POOL_PARAMETERS_PROVIDER = "PARAMETERS_PROVIDER";
bytes32 private constant LENDING_POOL_MANAGER = "LENDING_POOL_MANAGER";
bytes32 private constant LENDING_POOL_LIQUIDATION_MANAGER = "LIQUIDATION_MANAGER";
bytes32 private constant LENDING_POOL_FLASHLOAN_PROVIDER = "FLASHLOAN_PROVIDER";
bytes32 private constant DATA_PROVIDER = "DATA_PROVIDER";
bytes32 private constant ETHEREUM_ADDRESS = "ETHEREUM_ADDRESS";
bytes32 private constant PRICE_ORACLE = "PRICE_ORACLE";
bytes32 private constant LENDING_RATE_ORACLE = "LENDING_RATE_ORACLE";
bytes32 private constant FEE_PROVIDER = "FEE_PROVIDER";
bytes32 private constant WALLET_BALANCE_PROVIDER = "WALLET_BALANCE_PROVIDER";
bytes32 private constant TOKEN_DISTRIBUTOR = "TOKEN_DISTRIBUTOR";
/**
* @dev returns the address of the LendingPool proxy
* @return the lending pool proxy address
**/
function getLendingPool() public override view returns (address) {
return getAddress(LENDING_POOL);
}
/**
* @dev updates the implementation of the lending pool
* @param _pool the new lending pool implementation
**/
function setLendingPoolImpl(address _pool) public override onlyOwner {
updateImplInternal(LENDING_POOL, _pool);
emit LendingPoolUpdated(_pool);
}
/**
* @dev returns the address of the LendingPoolCore proxy
* @return the lending pool core proxy address
*/
function getLendingPoolCore() public override view returns (address payable) {
address payable core = payable(getAddress(LENDING_POOL_CORE));
return core;
}
/**
* @dev updates the implementation of the lending pool core
* @param _lendingPoolCore the new lending pool core implementation
**/
function setLendingPoolCoreImpl(address _lendingPoolCore) public override onlyOwner {
updateImplInternal(LENDING_POOL_CORE, _lendingPoolCore);
emit LendingPoolCoreUpdated(_lendingPoolCore);
}
/**
* @dev returns the address of the LendingPoolConfigurator proxy
* @return the lending pool configurator proxy address
**/
function getLendingPoolConfigurator() public override view returns (address) {
return getAddress(LENDING_POOL_CONFIGURATOR);
}
/**
* @dev updates the implementation of the lending pool configurator
* @param _configurator the new lending pool configurator implementation
**/
function setLendingPoolConfiguratorImpl(address _configurator) public override onlyOwner {
updateImplInternal(LENDING_POOL_CONFIGURATOR, _configurator);
emit LendingPoolConfiguratorUpdated(_configurator);
}
/**
* @dev returns the address of the LendingPoolDataProvider proxy
* @return the lending pool data provider proxy address
*/
function getLendingPoolDataProvider() public override view returns (address) {
return getAddress(DATA_PROVIDER);
}
/**
* @dev updates the implementation of the lending pool data provider
* @param _provider the new lending pool data provider implementation
**/
function setLendingPoolDataProviderImpl(address _provider) public override onlyOwner {
updateImplInternal(DATA_PROVIDER, _provider);
emit LendingPoolDataProviderUpdated(_provider);
}
/**
* @dev returns the address of the LendingPoolParametersProvider proxy
* @return the address of the Lending pool parameters provider proxy
**/
function getLendingPoolParametersProvider() public override view returns (address) {
return getAddress(LENDING_POOL_PARAMETERS_PROVIDER);
}
/**
* @dev updates the implementation of the lending pool parameters provider
* @param _parametersProvider the new lending pool parameters provider implementation
**/
function setLendingPoolParametersProviderImpl(address _parametersProvider) public override onlyOwner {
updateImplInternal(LENDING_POOL_PARAMETERS_PROVIDER, _parametersProvider);
emit LendingPoolParametersProviderUpdated(_parametersProvider);
}
/**
* @dev returns the address of the FeeProvider proxy
* @return the address of the Fee provider proxy
**/
function getFeeProvider() public override view returns (address) {
return getAddress(FEE_PROVIDER);
}
/**
* @dev updates the implementation of the FeeProvider proxy
* @param _feeProvider the new lending pool fee provider implementation
**/
function setFeeProviderImpl(address _feeProvider) public override onlyOwner {
updateImplInternal(FEE_PROVIDER, _feeProvider);
emit FeeProviderUpdated(_feeProvider);
}
/**
* @dev returns the address of the LendingPoolLiquidationManager. Since the manager is used
* through delegateCall within the LendingPool contract, the proxy contract pattern does not work properly hence
* the addresses are changed directly.
* @return the address of the Lending pool liquidation manager
**/
function getLendingPoolLiquidationManager() public override view returns (address) {
return getAddress(LENDING_POOL_LIQUIDATION_MANAGER);
}
/**
* @dev updates the address of the Lending pool liquidation manager
* @param _manager the new lending pool liquidation manager address
**/
function setLendingPoolLiquidationManager(address _manager) public override onlyOwner {
_setAddress(LENDING_POOL_LIQUIDATION_MANAGER, _manager);
emit LendingPoolLiquidationManagerUpdated(_manager);
}
/**
* @dev the functions below are storing specific addresses that are outside the context of the protocol
* hence the upgradable proxy pattern is not used
**/
function getLendingPoolManager() public override view returns (address) {
return getAddress(LENDING_POOL_MANAGER);
}
function setLendingPoolManager(address _lendingPoolManager) public override onlyOwner {
_setAddress(LENDING_POOL_MANAGER, _lendingPoolManager);
emit LendingPoolManagerUpdated(_lendingPoolManager);
}
function getPriceOracle() public override view returns (address) {
return getAddress(PRICE_ORACLE);
}
function setPriceOracle(address _priceOracle) public override onlyOwner {
_setAddress(PRICE_ORACLE, _priceOracle);
emit PriceOracleUpdated(_priceOracle);
}
function getLendingRateOracle() public override view returns (address) {
return getAddress(LENDING_RATE_ORACLE);
}
function setLendingRateOracle(address _lendingRateOracle) public override onlyOwner {
_setAddress(LENDING_RATE_ORACLE, _lendingRateOracle);
emit LendingRateOracleUpdated(_lendingRateOracle);
}
function getTokenDistributor() public override view returns (address) {
return getAddress(TOKEN_DISTRIBUTOR);
}
function setTokenDistributor(address _tokenDistributor) public override onlyOwner {
_setAddress(TOKEN_DISTRIBUTOR, _tokenDistributor);
emit TokenDistributorUpdated(_tokenDistributor);
}
/**
* @dev internal function to update the implementation of a specific component of the protocol
* @param _id the id of the contract to be updated
* @param _newAddress the address of the new implementation
**/
function updateImplInternal(bytes32 _id, address _newAddress) internal {
address payable proxyAddress = address(uint160(getAddress(_id)));
InitializableAdminUpgradeabilityProxy proxy = InitializableAdminUpgradeabilityProxy(proxyAddress);
bytes memory params = abi.encodeWithSignature("initialize(address)", address(this));
if (proxyAddress == address(0)) {
proxy = new InitializableAdminUpgradeabilityProxy();
proxy.initialize(_newAddress, address(this), params);
_setAddress(_id, address(proxy));
emit ProxyCreated(_id, address(proxy));
} else {
proxy.upgradeToAndCall(_newAddress, params);
}
}
}

View File

@ -0,0 +1,86 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/access/Ownable.sol";
import "../interfaces/ILendingPoolAddressesProviderRegistry.sol";
/**
* @title LendingPoolAddressesProviderRegistry contract
* @notice contains the list of active addresses providers
* @author Aave
**/
contract LendingPoolAddressesProviderRegistry is Ownable, ILendingPoolAddressesProviderRegistry {
//events
event AddressesProviderRegistered(address indexed newAddress);
event AddressesProviderUnregistered(address indexed newAddress);
mapping(address => uint256) addressesProviders;
address[] addressesProvidersList;
/**
* @dev returns if an addressesProvider is registered or not
* @param _provider the addresses provider
* @return true if the addressesProvider is registered, false otherwise
**/
function isAddressesProviderRegistered(address _provider) external override view returns(uint256) {
return addressesProviders[_provider];
}
/**
* @dev returns the list of active addressesProviders
* @return the list of addressesProviders
**/
function getAddressesProvidersList() external override view returns(address[] memory) {
uint256 maxLength = addressesProvidersList.length;
address[] memory activeProviders = new address[](maxLength);
for(uint256 i = 0; i<addressesProvidersList.length; i++){
if(addressesProviders[addressesProvidersList[i]] > 0){
activeProviders[i] = addressesProvidersList[i];
}
}
return activeProviders;
}
/**
* @dev adds a lending pool to the list of registered lending pools
* @param _provider the pool address to be registered
**/
function registerAddressesProvider(address _provider, uint256 _id) public override onlyOwner {
addressesProviders[_provider] = _id;
addToAddressesProvidersListInternal(_provider);
emit AddressesProviderRegistered(_provider);
}
/**
* @dev removes a lending pool from the list of registered lending pools
* @param _provider the pool address to be unregistered
**/
function unregisterAddressesProvider(address _provider) public override onlyOwner {
require(addressesProviders[_provider] > 0, "Provider is not registered");
addressesProviders[_provider] = 0;
emit AddressesProviderUnregistered(_provider);
}
/**
* @dev adds to the list of the addresses providers, if it wasn't already added before
* @param _provider the pool address to be added
**/
function addToAddressesProvidersListInternal(address _provider) internal {
for(uint256 i = 0; i < addressesProvidersList.length; i++) {
if(addressesProvidersList[i] == _provider){
return;
}
}
addressesProvidersList.push(_provider);
}
}

View File

@ -0,0 +1,54 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
import "./UintStorage.sol";
/**
* @title LendingPoolParametersProvider
* @author Aave
* @notice stores the configuration parameters of the Lending Pool contract
**/
contract LendingPoolParametersProvider is VersionedInitializable {
uint256 private constant MAX_STABLE_RATE_BORROW_SIZE_PERCENT = 25;
uint256 private constant REBALANCE_DOWN_RATE_DELTA = (1e27)/5;
uint256 private constant FLASHLOAN_FEE_TOTAL = 9;
uint256 private constant FLASHLOAN_FEE_PROTOCOL = 3000;
uint256 constant private DATA_PROVIDER_REVISION = 0x2;
function getRevision() internal override pure returns(uint256) {
return DATA_PROVIDER_REVISION;
}
/**
* @dev initializes the LendingPoolParametersProvider after it's added to the proxy
* @param _addressesProvider the address of the LendingPoolAddressesProvider
*/
function initialize(address _addressesProvider) public initializer {
}
/**
* @dev returns the maximum stable rate borrow size, in percentage of the available liquidity.
**/
function getMaxStableRateBorrowSizePercent() external pure returns (uint256) {
return MAX_STABLE_RATE_BORROW_SIZE_PERCENT;
}
/**
* @dev returns the delta between the current stable rate and the user stable rate at
* which the borrow position of the user will be rebalanced (scaled down)
**/
function getRebalanceDownRateDelta() external pure returns (uint256) {
return REBALANCE_DOWN_RATE_DELTA;
}
/**
* @dev returns the fee applied to a flashloan and the portion to redirect to the protocol, in basis points.
**/
function getFlashLoanFeesInBips() external pure returns (uint256, uint256) {
return (FLASHLOAN_FEE_TOTAL, FLASHLOAN_FEE_PROTOCOL);
}
}

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
contract UintStorage {
mapping(bytes32 => uint256) private uints;
function getUint(bytes32 _key) public view returns (uint256) {
return uints[_key];
}
function _setUint(bytes32 _key, uint256 _value) internal {
uints[_key] = _value;
}
}

View File

@ -0,0 +1,52 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
import "../interfaces/IFeeProvider.sol";
import "../libraries/WadRayMath.sol";
/**
* @title FeeProvider contract
* @notice Implements calculation for the fees applied by the protocol
* @author Aave
**/
contract FeeProvider is IFeeProvider, VersionedInitializable {
using WadRayMath for uint256;
// percentage of the fee to be calculated on the loan amount
uint256 public originationFeePercentage;
uint256 constant public FEE_PROVIDER_REVISION = 0x1;
function getRevision() internal override pure returns(uint256) {
return FEE_PROVIDER_REVISION;
}
/**
* @dev initializes the FeeProvider after it's added to the proxy
* @param _addressesProvider the address of the LendingPoolAddressesProvider
*/
function initialize(address _addressesProvider) public initializer {
/// @notice origination fee is set as default as 25 basis points of the loan amount (0.0025%)
originationFeePercentage = 0.0025 * 1e18;
}
/**
* @dev calculates the origination fee for every loan executed on the platform.
* @param _user can be used in the future to apply discount to the origination fee based on the
* _user account (eg. stake AAVE tokens in the lending pool, or deposit > 1M USD etc.)
* @param _amount the amount of the loan
**/
function calculateLoanOriginationFee(address _user, uint256 _amount) external override view returns (uint256) {
return _amount.wadMul(originationFeePercentage);
}
/**
* @dev returns the origination fee percentage
**/
function getLoanOriginationFeePercentage() external override view returns (uint256) {
return originationFeePercentage;
}
}

View File

@ -0,0 +1,45 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "../libraries/EthAddressLib.sol";
import "../mocks/tokens/MintableERC20.sol";
/// @title MockKyberProxy
/// @author Aave
/// @notice Mock contract to simulate the behaviour of the Kyber DEX
/// - Receives ETH/tokens
/// - Mints the tokenToBurn
/// - Sends back the tokenToBurn
contract MockKyberProxy {
using SafeERC20 for IERC20;
using SafeERC20 for MintableERC20;
/// @notice The token which the msg.sender of tradeWithHint will burn
MintableERC20 public tokenToBurn;
constructor(MintableERC20 _tokenToBurn) public {
tokenToBurn = _tokenToBurn;
}
/// @notice Simulates the function with the same name on the Kyber Proxy contract
function tradeWithHint(
IERC20 _fromToken,
uint256 _amount,
IERC20 _toToken,
address _receiver,
uint256 _maxAmount,
uint minConversionRate,
address _referral,
bytes calldata _filtering
) external payable returns(uint256) {
require(tokenToBurn.mint(1 ether), "TRADE_WITH_HINT. Reverted mint()");
if (address(_fromToken) != EthAddressLib.ethAddress()) {
_fromToken.safeTransferFrom(msg.sender, address(this), _amount);
}
tokenToBurn.safeTransfer(msg.sender, 1 ether);
return 1 ether;
}
}

View File

@ -0,0 +1,63 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "../libraries/EthAddressLib.sol";
import "../mocks/tokens/MintableERC20.sol";
import "../interfaces/IOneSplit.sol";
contract MockOneSplit is IOneSplit {
using SafeERC20 for IERC20;
using SafeERC20 for MintableERC20;
MintableERC20 public tokenToBurn;
constructor(MintableERC20 _tokenToBurn) public {
tokenToBurn = _tokenToBurn;
}
function getExpectedReturn(
IERC20 fromToken,
IERC20 toToken,
uint256 amount,
uint256 parts,
uint256 disableFlags // 1 - Uniswap, 2 - Kyber, 4 - Bancor, 8 - Oasis, 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI
)
public
override
view
returns(
uint256 returnAmount,
uint256[] memory distribution // [Uniswap, Kyber, Bancor, Oasis]
) {
return (0, new uint256[](0));
}
function swap(
IERC20 fromToken,
IERC20 toToken,
uint256 amount,
uint256 minReturn,
uint256[] memory distribution, // [Uniswap, Kyber, Bancor, Oasis]
uint256 disableFlags // 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI
) public override payable {
}
function goodSwap(
IERC20 fromToken,
IERC20 toToken,
uint256 amount,
uint256 minReturn,
uint256 parts,
uint256 disableFlags
) public override payable {
require(tokenToBurn.mint(10000 ether), "TRADE_WITH_HINT. Reverted mint()");
if (address(fromToken) != EthAddressLib.ethAddress()) {
fromToken.safeTransferFrom(msg.sender, address(this), amount);
}
tokenToBurn.safeTransfer(msg.sender, 10000 ether);
}
}

View File

@ -0,0 +1,79 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/IOneSplit.sol";
import "../interfaces/IPriceOracleGetter.sol";
import "../interfaces/IExchangeAdapter.sol";
/// @title OneSplitAdapter
/// @author Aave
/// @notice Implements the logic to exchange assets through 1Split
/// The hardcoded parameters are:
/// 0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476: ONE_SPLIT. The address of the 1Split exchange
/// 0x76B47460d7F7c5222cFb6b6A75615ab10895DDe4: AAVE_PRICES_PROVIDER. Contract providing prices of the assets
/// in the Aave protocol, in token/ETH.
/// 512 : MULTI_PATH_ETH_FLAG. By using this flag on OneSplit, the swap sequence will introduce a step to ETH
/// in the middle, resulting in a sequence like FROM-to-ETH -> ETH-to-TO.
/// This is optimal for cases where the pair FROM/TO is not liquid enough in the
/// underlying exchanges used by OneSplit, reducing this way the slippage.
/// 10: SPLIT_PARTS. It defines in how many chunks the amount to swap will be splitted to then
/// divide the chunks amongst the underlying exchanges.
/// For example, using 10 as SPLIT_PARTS and having 4 underlying exchanges on 1Split,
/// the division amongst could look like [4,4,0,2].
contract OneSplitAdapter is IExchangeAdapter {
using SafeMath for uint256;
event OneSplitAdapterSetup(address oneSplit, address priceOracle, uint256 splitParts);
constructor() public {
emit OneSplitAdapterSetup(0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476, 0x76B47460d7F7c5222cFb6b6A75615ab10895DDe4, 10);
}
/// @notice "Infinite" approval for all the tokens initialized
/// @param _tokens the list of token addresses to approve
function approveExchange(IERC20[] calldata _tokens) external override {
for (uint256 i = 0; i < _tokens.length; i++) {
if (address(_tokens[i]) != EthAddressLib.ethAddress()) {
_tokens[i].safeApprove(0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476, UintConstants.maxUintMinus1());
}
}
}
/// @notice Exchanges _amount of _from token (or ETH) to _to token (or ETH)
/// - Uses EthAddressLib.ethAddress() as the reference on 1Split of ETH
/// @param _from The asset to exchange from
/// @param _to The asset to exchange to
/// @param _amount The amount to exchange
/// @param _maxSlippage Max slippage acceptable, taken into account after the goodSwap()
function exchange(address _from, address _to, uint256 _amount, uint256 _maxSlippage) external override returns(uint256) {
uint256 _value = (_from == EthAddressLib.ethAddress()) ? _amount : 0;
uint256 _fromAssetPriceInWei = IPriceOracleGetter(0x76B47460d7F7c5222cFb6b6A75615ab10895DDe4).getAssetPrice(_from);
uint256 _toAssetPriceInWei = IPriceOracleGetter(0x76B47460d7F7c5222cFb6b6A75615ab10895DDe4).getAssetPrice(_to);
uint256 _toBalanceBefore = IERC20(_to).balanceOf(address(this));
IOneSplit(0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476).goodSwap{value: _value}(
IERC20(_from),
IERC20(_to),
_amount,
0,
10,
512
);
uint256 _toReceivedAmount = IERC20(_to).balanceOf(address(this)).sub(_toBalanceBefore);
require(
(_toAssetPriceInWei.mul(_toReceivedAmount).mul(100))
.div(_fromAssetPriceInWei.mul(_amount)) >= (100 - _maxSlippage),
"INVALID_SLIPPAGE"
);
emit Exchange(_from, _to, 0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476, _amount, _toReceivedAmount);
return _toReceivedAmount;
}
}

View File

@ -0,0 +1,218 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Burnable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
import "../interfaces/IKyberNetworkProxyInterface.sol";
import "../interfaces/IExchangeAdapter.sol";
import "../libraries/EthAddressLib.sol";
/// @title TokenDistributor
/// @author Aave
/// @notice Receives tokens and manages the distribution amongst receivers
/// The usage is as follows:
/// - The distribution addresses and percentages are set up on construction
/// - The Kyber Proxy is approved for a list of tokens in construction, which will be later burnt
/// - At any moment, anyone can call distribute() with a list of token addresses in order to distribute
/// the accumulated token amounts and/or ETH in this contract to all the receivers with percentages
/// - If the address(0) is used as receiver, this contract will trade in Kyber to tokenToBurn (LEND)
/// and burn it (sending to address(0) the tokenToBurn)
contract TokenDistributor is ReentrancyGuard, VersionedInitializable {
using SafeMath for uint256;
using SafeERC20 for IERC20;
struct Distribution {
address[] receivers;
uint256[] percentages;
}
event DistributionUpdated(address[] receivers, uint256[] percentages);
event Distributed(address receiver, uint256 percentage, uint256 amount);
event Setup(address tokenToBurn, IExchangeAdapter exchangeAdapter, address _recipientBurn);
event Burn(uint256 amount);
uint256 public constant IMPLEMENTATION_REVISION = 0x3;
/// @notice DEPRECATED
uint256 public constant MAX_UINT = 2**256 - 1;
/// @notice DEPRECATED
uint256 public constant MAX_UINT_MINUS_ONE = (2**256 - 1) - 1;
/// @notice DEPRECATED
uint256 public constant MIN_CONVERSION_RATE = 1;
/// @notice DEPRECATED
address public constant KYBER_ETH_MOCK_ADDRESS = address(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);
/// @notice Defines how tokens and ETH are distributed on each call to .distribute()
Distribution private distribution;
/// @notice Instead of using 100 for percentages, higher base to have more precision in the distribution
uint256 public constant DISTRIBUTION_BASE = 10000;
/// @notice DEPRECATED
IKyberNetworkProxyInterface public kyberProxy;
/// @notice The address of the token to burn (LEND token)
address public tokenToBurn;
/// @notice Address to send tokens to "burn".
/// Because of limitations on OZ ERC20, on dev it's needed to use the 0x00000...1 address instead of address(0)
/// So this param needs to be received on construction
address public recipientBurn;
/// @notice Smart contract implementing the logic to interact with a particular exchange.
/// Will be called by DELEGATECALL
IExchangeAdapter public exchangeAdapter;
/// @notice Called by the proxy when setting this contract as implementation
function initialize(
address _recipientBurn,
address _tokenToBurn,
IExchangeAdapter _exchangeAdapter,
address[] memory _receivers,
uint256[] memory _percentages,
IERC20[] memory _tokens
) public initializer {
recipientBurn = _recipientBurn;
tokenToBurn = _tokenToBurn;
exchangeAdapter = _exchangeAdapter;
internalSetTokenDistribution(_receivers, _percentages);
approveExchange(_tokens);
emit Setup(_tokenToBurn, _exchangeAdapter, _recipientBurn);
}
/// @notice In order to receive ETH transfers
receive() external payable {}
/// @notice "Infinite" approval for all the tokens initialized
/// @param _tokens List of IERC20 to approve
function approveExchange(IERC20[] memory _tokens) public {
(bool _success, ) = address(exchangeAdapter).delegatecall(
abi.encodeWithSelector(exchangeAdapter.approveExchange.selector, _tokens)
);
}
/// @notice Distributes the whole balance of a list of _tokens balances in this contract
/// @param _tokens list of ERC20 tokens to distribute
function distribute(IERC20[] memory _tokens) public {
for (uint256 i = 0; i < _tokens.length; i++) {
uint256 _balanceToDistribute = (address(_tokens[i]) != EthAddressLib.ethAddress())
? _tokens[i].balanceOf(address(this))
: address(this).balance;
if (_balanceToDistribute <= 0) {
continue;
}
internalDistributeTokenWithAmount(_tokens[i], _balanceToDistribute);
}
}
/// @notice Distributes specific amounts of a list of _tokens
/// @param _tokens list of ERC20 tokens to distribute
/// @param _amounts list of amounts to distribute per token
function distributeWithAmounts(IERC20[] memory _tokens, uint256[] memory _amounts) public {
for (uint256 i = 0; i < _tokens.length; i++) {
internalDistributeTokenWithAmount(_tokens[i], _amounts[i]);
}
}
/// @notice Distributes specific total balance's percentages of a list of _tokens
/// @param _tokens list of ERC20 tokens to distribute
/// @param _percentages list of percentages to distribute per token
function distributeWithPercentages(IERC20[] memory _tokens, uint256[] memory _percentages) public {
for (uint256 i = 0; i < _tokens.length; i++) {
uint256 _amountToDistribute = (address(_tokens[i]) != EthAddressLib.ethAddress())
? _tokens[i].balanceOf(address(this)).mul(_percentages[i]).div(100)
: address(this).balance.mul(_percentages[i]).div(100);
if (_amountToDistribute <= 0) {
continue;
}
internalDistributeTokenWithAmount(_tokens[i], _amountToDistribute);
}
}
/// @notice Sets _receivers addresses with _percentages for each one
/// @param _receivers Array of addresses receiving a percentage of the distribution, both user addresses
/// or contracts
/// @param _percentages Array of percentages each _receivers member will get
function internalSetTokenDistribution(address[] memory _receivers, uint256[] memory _percentages) internal {
require(_receivers.length == _percentages.length, "Array lengths should be equal");
distribution = Distribution({receivers: _receivers, percentages: _percentages});
emit DistributionUpdated(_receivers, _percentages);
}
/// @notice Distributes a specific amount of a token owned by this contract
/// @param _token The ERC20 token to distribute
/// @param _amountToDistribute The specific amount to distribute
function internalDistributeTokenWithAmount(IERC20 _token, uint256 _amountToDistribute) internal {
address _tokenAddress = address(_token);
Distribution memory _distribution = distribution;
for (uint256 j = 0; j < _distribution.receivers.length; j++) {
uint256 _amount = _amountToDistribute.mul(_distribution.percentages[j]).div(DISTRIBUTION_BASE);
//avoid transfers/burns of 0 tokens
if(_amount == 0){
continue;
}
if (_distribution.receivers[j] != address(0)) {
if (_tokenAddress != EthAddressLib.ethAddress()) {
_token.safeTransfer(_distribution.receivers[j], _amount);
} else {
//solium-disable-next-line
(bool _success,) = _distribution.receivers[j].call{value: _amount}("");
require(_success, "Reverted ETH transfer");
}
emit Distributed(_distribution.receivers[j], _distribution.percentages[j], _amount);
} else {
uint256 _amountToBurn = _amount;
// If the token to burn is already tokenToBurn, we don't trade, burning directly
if (_tokenAddress != tokenToBurn) {
(bool _success, bytes memory _result) = address(exchangeAdapter).delegatecall(
abi.encodeWithSelector(
exchangeAdapter.exchange.selector,
_tokenAddress,
tokenToBurn,
_amount,
10
)
);
require(_success, "ERROR_ON_EXCHANGE");
_amountToBurn = abi.decode(_result, (uint256));
}
internalBurn(_amountToBurn);
}
}
}
/// @notice Internal function to send _amount of tokenToBurn to the 0x0 address
/// @param _amount The amount to burn
function internalBurn(uint256 _amount) internal {
require(IERC20(tokenToBurn).transfer(recipientBurn, _amount), "INTERNAL_BURN. Reverted transfer to recipientBurn address");
emit Burn(_amount);
}
/// @notice Returns the receivers and percentages of the contract Distribution
/// @return receivers array of addresses and percentages array on uints
function getDistribution() public view returns(address[] memory receivers, uint256[] memory percentages) {
receivers = distribution.receivers;
percentages = distribution.percentages;
}
/// @notice Gets the revision number of the contract
/// @return The revision numeric reference
function getRevision() internal override pure returns (uint256) {
return IMPLEMENTATION_REVISION;
}
}

View File

@ -0,0 +1,52 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "../interfaces/IFlashLoanReceiver.sol";
import "../../interfaces/ILendingPoolAddressesProvider.sol";
import "../../libraries/EthAddressLib.sol";
abstract contract FlashLoanReceiverBase is IFlashLoanReceiver {
using SafeERC20 for IERC20;
using SafeMath for uint256;
ILendingPoolAddressesProvider public addressesProvider;
constructor(ILendingPoolAddressesProvider _provider) public {
addressesProvider = _provider;
}
receive() external payable {}
function transferFundsBackToPoolInternal(address _reserve, uint256 _amount) internal {
address payable core = addressesProvider.getLendingPoolCore();
transferInternal(core,_reserve, _amount);
}
function transferInternal(address payable _destination, address _reserve, uint256 _amount) internal {
if(_reserve == EthAddressLib.ethAddress()) {
//solium-disable-next-line
_destination.call{value: _amount}("");
return;
}
IERC20(_reserve).safeTransfer(_destination, _amount);
}
function getBalanceInternal(address _target, address _reserve) internal view returns(uint256) {
if(_reserve == EthAddressLib.ethAddress()) {
return _target.balance;
}
return IERC20(_reserve).balanceOf(_target);
}
}

View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
/**
* @title IFlashLoanReceiver interface
* @notice Interface for the Aave fee IFlashLoanReceiver.
* @author Aave
* @dev implement this interface to develop a flashloan-compatible flashLoanReceiver contract
**/
interface IFlashLoanReceiver {
function executeOperation(address _reserve, uint256 _amount, uint256 _fee, bytes calldata _params) external;
}

View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
interface IChainlinkAggregator {
function latestAnswer() external view returns (int256);
function latestTimestamp() external view returns (uint256);
function latestRound() external view returns (uint256);
function getAnswer(uint256 roundId) external view returns (int256);
function getTimestamp(uint256 roundId) external view returns (uint256);
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp);
event NewRound(uint256 indexed roundId, address indexed startedBy);
}

View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IERC20Detailed is IERC20 {
function name() external view returns(string memory);
function symbol() external view returns(string memory);
function decimals() external view returns(uint8);
}

View File

@ -0,0 +1,24 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "../libraries/EthAddressLib.sol";
import "../libraries/UintConstants.sol";
interface IExchangeAdapter {
using SafeERC20 for IERC20;
event Exchange(
address indexed from,
address indexed to,
address indexed platform,
uint256 fromAmount,
uint256 toAmount
);
function approveExchange(IERC20[] calldata _tokens) external;
function exchange(address _from, address _to, uint256 _amount, uint256 _maxSlippage) external returns(uint256);
}

View File

@ -0,0 +1,12 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
/**
* @title IFeeProvider interface
* @notice Interface for the Aave fee provider.
**/
interface IFeeProvider {
function calculateLoanOriginationFee(address _user, uint256 _amount) external view returns (uint256);
function getLoanOriginationFeePercentage() external view returns (uint256);
}

View File

@ -0,0 +1,23 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IKyberNetworkProxyInterface {
function maxGasPrice() external view returns(uint);
function getUserCapInWei(address user) external view returns(uint);
function getUserCapInTokenWei(address user, IERC20 token) external view returns(uint);
function enabled() external view returns(bool);
function info(bytes32 id) external view returns(uint);
function getExpectedRate(IERC20 src, IERC20 dest, uint srcQty)
external view returns (uint expectedRate, uint slippageRate);
function tradeWithHint(
IERC20 src,
uint srcAmount,
IERC20 dest,
address destAddress,
uint maxDestAmount,
uint minConversionRate,
address walletId,
bytes calldata hint) external payable returns(uint);
}

View File

@ -0,0 +1,45 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
/**
@title ILendingPoolAddressesProvider interface
@notice provides the interface to fetch the LendingPoolCore address
*/
interface ILendingPoolAddressesProvider {
function getLendingPool() external view returns (address);
function setLendingPoolImpl(address _pool) external;
function getLendingPoolCore() external view returns (address payable);
function setLendingPoolCoreImpl(address _lendingPoolCore) external;
function getLendingPoolConfigurator() external view returns (address);
function setLendingPoolConfiguratorImpl(address _configurator) external;
function getLendingPoolDataProvider() external view returns (address);
function setLendingPoolDataProviderImpl(address _provider) external;
function getLendingPoolParametersProvider() external view returns (address);
function setLendingPoolParametersProviderImpl(address _parametersProvider) external;
function getTokenDistributor() external view returns (address);
function setTokenDistributor(address _tokenDistributor) external;
function getFeeProvider() external view returns (address);
function setFeeProviderImpl(address _feeProvider) external;
function getLendingPoolLiquidationManager() external view returns (address);
function setLendingPoolLiquidationManager(address _manager) external;
function getLendingPoolManager() external view returns (address);
function setLendingPoolManager(address _lendingPoolManager) external;
function getPriceOracle() external view returns (address);
function setPriceOracle(address _priceOracle) external;
function getLendingRateOracle() external view returns (address);
function setLendingRateOracle(address _lendingRateOracle) external;
}

View File

@ -0,0 +1,16 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
/**
* @title ILendingPoolAddressesProvider interface
* @notice provides the interface to fetch the LendingPoolCore address
**/
interface ILendingPoolAddressesProviderRegistry {
function getAddressesProvidersList() external view returns (address[] memory);
function isAddressesProviderRegistered(address _provider) external view returns (uint256);
function registerAddressesProvider(address _provider, uint256 _id) external;
function unregisterAddressesProvider(address _provider) external;
}

View File

@ -0,0 +1,19 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
/**
* @title ILendingRateOracle interface
* @notice Interface for the Aave borrow rate oracle. Provides the average market borrow rate to be used as a base for the stable borrow rate calculations
**/
interface ILendingRateOracle {
/**
@dev returns the market borrow rate in ray
**/
function getMarketBorrowRate(address _asset) external view returns (uint256);
/**
@dev sets the market borrow rate. Rate value must be in ray
**/
function setMarketBorrowRate(address _asset, uint256 _rate) external;
}

View File

@ -0,0 +1,60 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
abstract contract IOneSplitView {
// disableFlags = FLAG_UNISWAP + FLAG_KYBER + ...
uint256 public constant FLAG_UNISWAP = 0x01;
uint256 public constant FLAG_KYBER = 0x02;
uint256 public constant FLAG_KYBER_UNISWAP_RESERVE = 0x100000000; // Turned off by default
uint256 public constant FLAG_KYBER_OASIS_RESERVE = 0x200000000; // Turned off by default
uint256 public constant FLAG_KYBER_BANCOR_RESERVE = 0x400000000; // Turned off by default
uint256 public constant FLAG_BANCOR = 0x04;
uint256 public constant FLAG_OASIS = 0x08;
uint256 public constant FLAG_COMPOUND = 0x10;
uint256 public constant FLAG_FULCRUM = 0x20;
uint256 public constant FLAG_CHAI = 0x40;
uint256 public constant FLAG_AAVE = 0x80;
uint256 public constant FLAG_SMART_TOKEN = 0x100;
uint256 public constant FLAG_MULTI_PATH_ETH = 0x200; // Turned off by default
uint256 public constant FLAG_BDAI = 0x400;
uint256 public constant FLAG_IEARN = 0x800;
function getExpectedReturn(
IERC20 fromToken,
IERC20 toToken,
uint256 amount,
uint256 parts,
uint256 disableFlags // 1 - Uniswap, 2 - Kyber, 4 - Bancor, 8 - Oasis, 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI
)
public
virtual
view
returns(
uint256 returnAmount,
uint256[] memory distribution // [Uniswap, Kyber, Bancor, Oasis]
);
}
abstract contract IOneSplit is IOneSplitView {
function swap(
IERC20 fromToken,
IERC20 toToken,
uint256 amount,
uint256 minReturn,
uint256[] memory distribution, // [Uniswap, Kyber, Bancor, Oasis]
uint256 disableFlags // 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI
) public virtual payable;
function goodSwap(
IERC20 fromToken,
IERC20 toToken,
uint256 amount,
uint256 minReturn,
uint256 parts,
uint256 disableFlags // 1 - Uniswap, 2 - Kyber, 4 - Bancor, 8 - Oasis, 16 - Compound, 32 - Fulcrum, 64 - Chai, 128 - Aave, 256 - SmartToken, 1024 - bDAI
) public virtual payable;
}

View File

@ -0,0 +1,18 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
/************
@title IPriceOracle interface
@notice Interface for the Aave price oracle.*/
interface IPriceOracle {
/***********
@dev returns the asset price in ETH
*/
function getAssetPrice(address _asset) external view returns (uint256);
/***********
@dev sets the asset price, in wei
*/
function setAssetPrice(address _asset, uint256 _price) external;
}

View File

@ -0,0 +1,16 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
/**
* @title IPriceOracleGetter interface
* @notice Interface for the Aave price oracle.
**/
interface IPriceOracleGetter {
/**
* @dev returns the asset price in ETH
* @param _asset the address of the asset
* @return the ETH price of the asset
**/
function getAssetPrice(address _asset) external view returns (uint256);
}

View File

@ -0,0 +1,30 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
/**
@title IReserveInterestRateStrategyInterface interface
@notice Interface for the calculation of the interest rates.
*/
interface IReserveInterestRateStrategy {
/**
* @dev returns the base variable borrow rate, in rays
*/
function getBaseVariableBorrowRate() external view returns (uint256);
/**
* @dev calculates the liquidity, stable, and variable rates depending on the current utilization rate
* and the base parameters
*
*/
function calculateInterestRates(
address _reserve,
uint256 _utilizationRate,
uint256 _totalBorrowsStable,
uint256 _totalBorrowsVariable,
uint256 _averageStableBorrowRate)
external
view
returns (uint256 liquidityRate, uint256 stableBorrowRate, uint256 variableBorrowRate);
}

View File

@ -0,0 +1,9 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
interface IUniswapExchange {
event TokenPurchase(address indexed buyer, uint256 indexed eth_sold, uint256 indexed tokens_bought);
event EthPurchase(address indexed buyer, uint256 indexed tokens_sold, uint256 indexed eth_bought);
event AddLiquidity(address indexed provider, uint256 indexed eth_amount, uint256 indexed token_amount);
event RemoveLiquidity(address indexed provider, uint256 indexed eth_amount, uint256 indexed token_amount);
}

View File

@ -0,0 +1,203 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "../interfaces/IReserveInterestRateStrategy.sol";
import "../libraries/WadRayMath.sol";
import "../configuration/LendingPoolAddressesProvider.sol";
import "./LendingPoolCore.sol";
import "../interfaces/ILendingRateOracle.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
/**
* @title DefaultReserveInterestRateStrategy contract
* @notice implements the calculation of the interest rates depending on the reserve parameters.
* @dev if there is need to update the calculation of the interest rates for a specific reserve,
* a new version of this contract will be deployed.
* @author Aave
**/
contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy {
using WadRayMath for uint256;
using SafeMath for uint256;
/**
* @dev this constant represents the utilization rate at which the pool aims to obtain most competitive borrow rates
* expressed in ray
**/
uint256 public constant OPTIMAL_UTILIZATION_RATE = 0.8 * 1e27;
/**
* @dev this constant represents the excess utilization rate above the optimal. It's always equal to
* 1-optimal utilization rate. Added as a constant here for gas optimizations
* expressed in ray
**/
uint256 public constant EXCESS_UTILIZATION_RATE = 0.2 * 1e27;
LendingPoolAddressesProvider public addressesProvider;
//base variable borrow rate when Utilization rate = 0. Expressed in ray
uint256 public baseVariableBorrowRate;
//slope of the variable interest curve when utilization rate > 0 and <= OPTIMAL_UTILIZATION_RATE. Expressed in ray
uint256 public variableRateSlope1;
//slope of the variable interest curve when utilization rate > OPTIMAL_UTILIZATION_RATE. Expressed in ray
uint256 public variableRateSlope2;
//slope of the stable interest curve when utilization rate > 0 and <= OPTIMAL_UTILIZATION_RATE. Expressed in ray
uint256 public stableRateSlope1;
//slope of the stable interest curve when utilization rate > OPTIMAL_UTILIZATION_RATE. Expressed in ray
uint256 public stableRateSlope2;
address public reserve;
constructor(
address _reserve,
LendingPoolAddressesProvider _provider,
uint256 _baseVariableBorrowRate,
uint256 _variableRateSlope1,
uint256 _variableRateSlope2,
uint256 _stableRateSlope1,
uint256 _stableRateSlope2
) public {
addressesProvider = _provider;
baseVariableBorrowRate = _baseVariableBorrowRate;
variableRateSlope1 = _variableRateSlope1;
variableRateSlope2 = _variableRateSlope2;
stableRateSlope1 = _stableRateSlope1;
stableRateSlope2 = _stableRateSlope2;
reserve = _reserve;
}
/**
@dev accessors
*/
function getBaseVariableBorrowRate() external override view returns (uint256) {
return baseVariableBorrowRate;
}
function getVariableRateSlope1() external view returns (uint256) {
return variableRateSlope1;
}
function getVariableRateSlope2() external view returns (uint256) {
return variableRateSlope2;
}
function getStableRateSlope1() external view returns (uint256) {
return stableRateSlope1;
}
function getStableRateSlope2() external view returns (uint256) {
return stableRateSlope2;
}
/**
* @dev calculates the interest rates depending on the available liquidity and the total borrowed.
* @param _reserve the address of the reserve
* @param _availableLiquidity the liquidity available in the reserve
* @param _totalBorrowsStable the total borrowed from the reserve a stable rate
* @param _totalBorrowsVariable the total borrowed from the reserve at a variable rate
* @param _averageStableBorrowRate the weighted average of all the stable rate borrows
* @return currentLiquidityRate calculated from the input parameters TODO: add description
* @return currentStableBorrowRate calculated from the input parameters TODO: add description
* @return currentVariableBorrowRate calculated from the input parameters TODO: add description
**/
function calculateInterestRates(
address _reserve,
uint256 _availableLiquidity,
uint256 _totalBorrowsStable,
uint256 _totalBorrowsVariable,
uint256 _averageStableBorrowRate
)
external
override
view
returns (
uint256 currentLiquidityRate,
uint256 currentStableBorrowRate,
uint256 currentVariableBorrowRate
)
{
uint256 totalBorrows = _totalBorrowsStable.add(_totalBorrowsVariable);
uint256 utilizationRate = (totalBorrows == 0 && _availableLiquidity == 0)
? 0
: totalBorrows.rayDiv(_availableLiquidity.add(totalBorrows));
currentStableBorrowRate = ILendingRateOracle(addressesProvider.getLendingRateOracle())
.getMarketBorrowRate(_reserve);
if (utilizationRate > OPTIMAL_UTILIZATION_RATE) {
uint256 excessUtilizationRateRatio = utilizationRate
.sub(OPTIMAL_UTILIZATION_RATE)
.rayDiv(EXCESS_UTILIZATION_RATE);
currentStableBorrowRate = currentStableBorrowRate.add(stableRateSlope1).add(
stableRateSlope2.rayMul(excessUtilizationRateRatio)
);
currentVariableBorrowRate = baseVariableBorrowRate.add(variableRateSlope1).add(
variableRateSlope2.rayMul(excessUtilizationRateRatio)
);
} else {
currentStableBorrowRate = currentStableBorrowRate.add(
stableRateSlope1.rayMul(
utilizationRate.rayDiv(
OPTIMAL_UTILIZATION_RATE
)
)
);
currentVariableBorrowRate = baseVariableBorrowRate.add(
utilizationRate.rayDiv(OPTIMAL_UTILIZATION_RATE).rayMul(variableRateSlope1)
);
}
currentLiquidityRate = getOverallBorrowRateInternal(
_totalBorrowsStable,
_totalBorrowsVariable,
currentVariableBorrowRate,
_averageStableBorrowRate
)
.rayMul(utilizationRate);
}
/**
* @dev calculates the overall borrow rate as the weighted average between the total variable borrows and total stable borrows.
* @param _totalBorrowsStable the total borrowed from the reserve a stable rate
* @param _totalBorrowsVariable the total borrowed from the reserve at a variable rate
* @param _currentVariableBorrowRate the current variable borrow rate
* @param _currentAverageStableBorrowRate the weighted average of all the stable rate borrows
* @return the weighted averaged borrow rate
**/
function getOverallBorrowRateInternal(
uint256 _totalBorrowsStable,
uint256 _totalBorrowsVariable,
uint256 _currentVariableBorrowRate,
uint256 _currentAverageStableBorrowRate
) internal pure returns (uint256) {
uint256 totalBorrows = _totalBorrowsStable.add(_totalBorrowsVariable);
if (totalBorrows == 0) return 0;
uint256 weightedVariableRate = _totalBorrowsVariable.wadToRay().rayMul(
_currentVariableBorrowRate
);
uint256 weightedStableRate = _totalBorrowsStable.wadToRay().rayMul(
_currentAverageStableBorrowRate
);
uint256 overallBorrowRate = weightedVariableRate.add(weightedStableRate).rayDiv(
totalBorrows.wadToRay()
);
return overallBorrowRate;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,451 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../interfaces/IERC20Detailed.sol";
import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
import "../configuration/LendingPoolAddressesProvider.sol";
import "./LendingPoolCore.sol";
import "../tokenization/AToken.sol";
/**
* @title LendingPoolConfigurator contract
* @author Aave
* @notice Executes configuration methods on the LendingPoolCore contract. Allows to enable/disable reserves,
* and set different protocol parameters.
**/
contract LendingPoolConfigurator is VersionedInitializable {
using SafeMath for uint256;
/**
* @dev emitted when a reserve is initialized.
* @param _reserve the address of the reserve
* @param _aToken the address of the overlying aToken contract
* @param _interestRateStrategyAddress the address of the interest rate strategy for the reserve
**/
event ReserveInitialized(
address indexed _reserve,
address indexed _aToken,
address _interestRateStrategyAddress
);
/**
* @dev emitted when a reserve is removed.
* @param _reserve the address of the reserve
**/
event ReserveRemoved(
address indexed _reserve
);
/**
* @dev emitted when borrowing is enabled on a reserve
* @param _reserve the address of the reserve
* @param _stableRateEnabled true if stable rate borrowing is enabled, false otherwise
**/
event BorrowingEnabledOnReserve(address _reserve, bool _stableRateEnabled);
/**
* @dev emitted when borrowing is disabled on a reserve
* @param _reserve the address of the reserve
**/
event BorrowingDisabledOnReserve(address indexed _reserve);
/**
* @dev emitted when a reserve is enabled as collateral.
* @param _reserve the address 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 ReserveEnabledAsCollateral(
address indexed _reserve,
uint256 _ltv,
uint256 _liquidationThreshold,
uint256 _liquidationBonus
);
/**
* @dev emitted when a reserve is disabled as collateral
* @param _reserve the address of the reserve
**/
event ReserveDisabledAsCollateral(address indexed _reserve);
/**
* @dev emitted when stable rate borrowing is enabled on a reserve
* @param _reserve the address of the reserve
**/
event StableRateEnabledOnReserve(address indexed _reserve);
/**
* @dev emitted when stable rate borrowing is disabled on a reserve
* @param _reserve the address of the reserve
**/
event StableRateDisabledOnReserve(address indexed _reserve);
/**
* @dev emitted when a reserve is activated
* @param _reserve the address of the reserve
**/
event ReserveActivated(address indexed _reserve);
/**
* @dev emitted when a reserve is deactivated
* @param _reserve the address of the reserve
**/
event ReserveDeactivated(address indexed _reserve);
/**
* @dev emitted when a reserve is freezed
* @param _reserve the address of the reserve
**/
event ReserveFreezed(address indexed _reserve);
/**
* @dev emitted when a reserve is unfreezed
* @param _reserve the address of the reserve
**/
event ReserveUnfreezed(address indexed _reserve);
/**
* @dev emitted when a reserve loan to value is updated
* @param _reserve the address of the reserve
* @param _ltv the new value for the loan to value
**/
event ReserveBaseLtvChanged(address _reserve, uint256 _ltv);
/**
* @dev emitted when a reserve liquidation threshold is updated
* @param _reserve the address of the reserve
* @param _threshold the new value for the liquidation threshold
**/
event ReserveLiquidationThresholdChanged(address _reserve, uint256 _threshold);
/**
* @dev emitted when a reserve liquidation bonus is updated
* @param _reserve the address of the reserve
* @param _bonus the new value for the liquidation bonus
**/
event ReserveLiquidationBonusChanged(address _reserve, uint256 _bonus);
/**
* @dev emitted when the reserve decimals are updated
* @param _reserve the address of the reserve
* @param _decimals the new decimals
**/
event ReserveDecimalsChanged(address _reserve, uint256 _decimals);
/**
* @dev emitted when a reserve interest strategy contract is updated
* @param _reserve the address of the reserve
* @param _strategy the new address of the interest strategy contract
**/
event ReserveInterestRateStrategyChanged(address _reserve, address _strategy);
LendingPoolAddressesProvider public poolAddressesProvider;
/**
* @dev only the lending pool manager can call functions affected by this modifier
**/
modifier onlyLendingPoolManager {
require(
poolAddressesProvider.getLendingPoolManager() == msg.sender,
"The caller must be a lending pool manager"
);
_;
}
uint256 public constant CONFIGURATOR_REVISION = 0x3;
function getRevision() internal override pure returns (uint256) {
return CONFIGURATOR_REVISION;
}
function initialize(LendingPoolAddressesProvider _poolAddressesProvider) public initializer {
poolAddressesProvider = _poolAddressesProvider;
}
/**
* @dev initializes a reserve
* @param _reserve the address of the reserve to be initialized
* @param _underlyingAssetDecimals the decimals of the reserve underlying asset
* @param _interestRateStrategyAddress the address of the interest rate strategy contract for this reserve
**/
function initReserve(
address _reserve,
uint8 _underlyingAssetDecimals,
address _interestRateStrategyAddress
) external onlyLendingPoolManager {
IERC20Detailed asset = IERC20Detailed(_reserve);
string memory aTokenName = string(abi.encodePacked("Aave Interest bearing ", asset.name()));
string memory aTokenSymbol = string(abi.encodePacked("a", asset.symbol()));
initReserveWithData(
_reserve,
aTokenName,
aTokenSymbol,
_underlyingAssetDecimals,
_interestRateStrategyAddress
);
}
/**
* @dev initializes a reserve using aTokenData provided externally (useful if the underlying ERC20 contract doesn't expose name or decimals)
* @param _reserve the address of the reserve to be initialized
* @param _aTokenName the name of the aToken contract
* @param _aTokenSymbol the symbol of the aToken contract
* @param _underlyingAssetDecimals the decimals of the reserve underlying asset
* @param _interestRateStrategyAddress the address of the interest rate strategy contract for this reserve
**/
function initReserveWithData(
address _reserve,
string memory _aTokenName,
string memory _aTokenSymbol,
uint8 _underlyingAssetDecimals,
address _interestRateStrategyAddress
) public onlyLendingPoolManager {
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
AToken aTokenInstance = new AToken(
poolAddressesProvider,
_reserve,
_underlyingAssetDecimals,
_aTokenName,
_aTokenSymbol
);
core.initReserve(
_reserve,
address(aTokenInstance),
_underlyingAssetDecimals,
_interestRateStrategyAddress
);
emit ReserveInitialized(
_reserve,
address(aTokenInstance),
_interestRateStrategyAddress
);
}
/**
* @dev removes the last added reserve in the list of the reserves
* @param _reserveToRemove the address of the reserve
**/
function removeLastAddedReserve( address _reserveToRemove) external onlyLendingPoolManager {
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.removeLastAddedReserve(_reserveToRemove);
emit ReserveRemoved(_reserveToRemove);
}
/**
* @dev enables borrowing on a reserve
* @param _reserve the address of the reserve
* @param _stableBorrowRateEnabled true if stable borrow rate needs to be enabled by default on this reserve
**/
function enableBorrowingOnReserve(address _reserve, bool _stableBorrowRateEnabled)
external
onlyLendingPoolManager
{
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.enableBorrowingOnReserve(_reserve, _stableBorrowRateEnabled);
emit BorrowingEnabledOnReserve(_reserve, _stableBorrowRateEnabled);
}
/**
* @dev disables borrowing on a reserve
* @param _reserve the address of the reserve
**/
function disableBorrowingOnReserve(address _reserve) external onlyLendingPoolManager {
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.disableBorrowingOnReserve(_reserve);
emit BorrowingDisabledOnReserve(_reserve);
}
/**
* @dev enables a reserve to be used as collateral
* @param _reserve the address of the reserve
* @param _baseLTVasCollateral 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
**/
function enableReserveAsCollateral(
address _reserve,
uint256 _baseLTVasCollateral,
uint256 _liquidationThreshold,
uint256 _liquidationBonus
) external onlyLendingPoolManager {
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.enableReserveAsCollateral(
_reserve,
_baseLTVasCollateral,
_liquidationThreshold,
_liquidationBonus
);
emit ReserveEnabledAsCollateral(
_reserve,
_baseLTVasCollateral,
_liquidationThreshold,
_liquidationBonus
);
}
/**
* @dev disables a reserve as collateral
* @param _reserve the address of the reserve
**/
function disableReserveAsCollateral(address _reserve) external onlyLendingPoolManager {
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.disableReserveAsCollateral(_reserve);
emit ReserveDisabledAsCollateral(_reserve);
}
/**
* @dev enable stable rate borrowing on a reserve
* @param _reserve the address of the reserve
**/
function enableReserveStableBorrowRate(address _reserve) external onlyLendingPoolManager {
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.enableReserveStableBorrowRate(_reserve);
emit StableRateEnabledOnReserve(_reserve);
}
/**
* @dev disable stable rate borrowing on a reserve
* @param _reserve the address of the reserve
**/
function disableReserveStableBorrowRate(address _reserve) external onlyLendingPoolManager {
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.disableReserveStableBorrowRate(_reserve);
emit StableRateDisabledOnReserve(_reserve);
}
/**
* @dev activates a reserve
* @param _reserve the address of the reserve
**/
function activateReserve(address _reserve) external onlyLendingPoolManager {
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.activateReserve(_reserve);
emit ReserveActivated(_reserve);
}
/**
* @dev deactivates a reserve
* @param _reserve the address of the reserve
**/
function deactivateReserve(address _reserve) external onlyLendingPoolManager {
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
require(core.getReserveTotalLiquidity(_reserve) == 0, "The liquidity of the reserve needs to be 0");
core.deactivateReserve(_reserve);
emit ReserveDeactivated(_reserve);
}
/**
* @dev freezes a reserve. A freezed reserve doesn't accept any new deposit, borrow or rate swap, but can accept repayments, liquidations, rate rebalances and redeems
* @param _reserve the address of the reserve
**/
function freezeReserve(address _reserve) external onlyLendingPoolManager {
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.freezeReserve(_reserve);
emit ReserveFreezed(_reserve);
}
/**
* @dev unfreezes a reserve
* @param _reserve the address of the reserve
**/
function unfreezeReserve(address _reserve) external onlyLendingPoolManager {
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.unfreezeReserve(_reserve);
emit ReserveUnfreezed(_reserve);
}
/**
* @dev emitted when a reserve loan to value is updated
* @param _reserve the address of the reserve
* @param _ltv the new value for the loan to value
**/
function setReserveBaseLTVasCollateral(address _reserve, uint256 _ltv)
external
onlyLendingPoolManager
{
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.setReserveBaseLTVasCollateral(_reserve, _ltv);
emit ReserveBaseLtvChanged(_reserve, _ltv);
}
/**
* @dev updates the liquidation threshold of a reserve.
* @param _reserve the address of the reserve
* @param _threshold the new value for the liquidation threshold
**/
function setReserveLiquidationThreshold(address _reserve, uint256 _threshold)
external
onlyLendingPoolManager
{
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.setReserveLiquidationThreshold(_reserve, _threshold);
emit ReserveLiquidationThresholdChanged(_reserve, _threshold);
}
/**
* @dev updates the liquidation bonus of a reserve
* @param _reserve the address of the reserve
* @param _bonus the new value for the liquidation bonus
**/
function setReserveLiquidationBonus(address _reserve, uint256 _bonus)
external
onlyLendingPoolManager
{
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.setReserveLiquidationBonus(_reserve, _bonus);
emit ReserveLiquidationBonusChanged(_reserve, _bonus);
}
/**
* @dev updates the reserve decimals
* @param _reserve the address of the reserve
* @param _decimals the new number of decimals
**/
function setReserveDecimals(address _reserve, uint256 _decimals)
external
onlyLendingPoolManager
{
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.setReserveDecimals(_reserve, _decimals);
emit ReserveDecimalsChanged(_reserve, _decimals);
}
/**
* @dev sets the interest rate strategy of a reserve
* @param _reserve the address of the reserve
* @param _rateStrategyAddress the new address of the interest strategy contract
**/
function setReserveInterestRateStrategyAddress(address _reserve, address _rateStrategyAddress)
external
onlyLendingPoolManager
{
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.setReserveInterestRateStrategyAddress(_reserve, _rateStrategyAddress);
emit ReserveInterestRateStrategyChanged(_reserve, _rateStrategyAddress);
}
/**
* @dev refreshes the lending pool core configuration to update the cached address
**/
function refreshLendingPoolCoreConfiguration() external onlyLendingPoolManager {
LendingPoolCore core = LendingPoolCore(poolAddressesProvider.getLendingPoolCore());
core.refreshConfiguration();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,482 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
import "../libraries/CoreLibrary.sol";
import "../configuration/LendingPoolAddressesProvider.sol";
import "../libraries/WadRayMath.sol";
import "../interfaces/IPriceOracleGetter.sol";
import "../interfaces/IFeeProvider.sol";
import "../tokenization/AToken.sol";
import "./LendingPoolCore.sol";
/**
* @title LendingPoolDataProvider contract
* @author Aave
* @notice Implements functions to fetch data from the core, and aggregate them in order to allow computation
* on the compounded balances and the account balances in ETH
**/
contract LendingPoolDataProvider is VersionedInitializable {
using SafeMath for uint256;
using WadRayMath for uint256;
LendingPoolCore public core;
LendingPoolAddressesProvider public addressesProvider;
/**
* @dev specifies the health factor threshold at which the user position is liquidated.
* 1e18 by default, if the health factor drops below 1e18, the loan can be liquidated.
**/
uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1e18;
uint256 public constant DATA_PROVIDER_REVISION = 0x1;
function getRevision() internal override pure returns (uint256) {
return DATA_PROVIDER_REVISION;
}
function initialize(LendingPoolAddressesProvider _addressesProvider) public initializer {
addressesProvider = _addressesProvider;
core = LendingPoolCore(_addressesProvider.getLendingPoolCore());
}
/**
* @dev struct to hold calculateUserGlobalData() local computations
**/
struct UserGlobalDataLocalVars {
uint256 reserveUnitPrice;
uint256 tokenUnit;
uint256 compoundedLiquidityBalance;
uint256 compoundedBorrowBalance;
uint256 reserveDecimals;
uint256 baseLtv;
uint256 liquidationThreshold;
uint256 originationFee;
bool usageAsCollateralEnabled;
bool userUsesReserveAsCollateral;
address currentReserve;
}
/**
* @dev calculates the user data across the reserves.
* this includes the total liquidity/collateral/borrow balances in ETH,
* the average Loan To Value, the average Liquidation Ratio, and the Health factor.
* @param _user the address of the user
* @return totalLiquidityBalanceETH of the user in ETH
* @return totalCollateralBalanceETH of the user in ETH
* @return totalBorrowBalanceETH of the user in ETH
* @return totalFeesETH of the user in ETH
* @return currentLtv average Ltv
* @return currentLiquidationThreshold liquidation threshold
* @return healthFactor health factor
* @return healthFactorBelowThreshold is the health factor below threshold
**/
function calculateUserGlobalData(address _user)
public
view
returns (
uint256 totalLiquidityBalanceETH,
uint256 totalCollateralBalanceETH,
uint256 totalBorrowBalanceETH,
uint256 totalFeesETH,
uint256 currentLtv,
uint256 currentLiquidationThreshold,
uint256 healthFactor,
bool healthFactorBelowThreshold
)
{
IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle());
// Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables
UserGlobalDataLocalVars memory vars;
address[] memory reserves = core.getReserves();
for (uint256 i = 0; i < reserves.length; i++) {
vars.currentReserve = reserves[i];
(
vars.compoundedLiquidityBalance,
vars.compoundedBorrowBalance,
vars.originationFee,
vars.userUsesReserveAsCollateral
) = core.getUserBasicReserveData(vars.currentReserve, _user);
if (vars.compoundedLiquidityBalance == 0 && vars.compoundedBorrowBalance == 0) {
continue;
}
//fetch reserve data
(
vars.reserveDecimals,
vars.baseLtv,
vars.liquidationThreshold,
vars.usageAsCollateralEnabled
) = core.getReserveConfiguration(vars.currentReserve);
vars.tokenUnit = 10 ** vars.reserveDecimals;
vars.reserveUnitPrice = oracle.getAssetPrice(vars.currentReserve);
//liquidity and collateral balance
if (vars.compoundedLiquidityBalance > 0) {
uint256 liquidityBalanceETH = vars
.reserveUnitPrice
.mul(vars.compoundedLiquidityBalance)
.div(vars.tokenUnit);
totalLiquidityBalanceETH = totalLiquidityBalanceETH.add(liquidityBalanceETH);
if (vars.usageAsCollateralEnabled && vars.userUsesReserveAsCollateral) {
totalCollateralBalanceETH = totalCollateralBalanceETH.add(liquidityBalanceETH);
currentLtv = currentLtv.add(liquidityBalanceETH.mul(vars.baseLtv));
currentLiquidationThreshold = currentLiquidationThreshold.add(
liquidityBalanceETH.mul(vars.liquidationThreshold)
);
}
}
if (vars.compoundedBorrowBalance > 0) {
totalBorrowBalanceETH = totalBorrowBalanceETH.add(
vars.reserveUnitPrice.mul(vars.compoundedBorrowBalance).div(vars.tokenUnit)
);
totalFeesETH = totalFeesETH.add(
vars.originationFee.mul(vars.reserveUnitPrice).div(vars.tokenUnit)
);
}
}
currentLtv = totalCollateralBalanceETH > 0 ? currentLtv.div(totalCollateralBalanceETH) : 0;
currentLiquidationThreshold = totalCollateralBalanceETH > 0
? currentLiquidationThreshold.div(totalCollateralBalanceETH)
: 0;
healthFactor = calculateHealthFactorFromBalancesInternal(
totalCollateralBalanceETH,
totalBorrowBalanceETH,
totalFeesETH,
currentLiquidationThreshold
);
healthFactorBelowThreshold = healthFactor < HEALTH_FACTOR_LIQUIDATION_THRESHOLD;
}
struct balanceDecreaseAllowedLocalVars {
uint256 decimals;
uint256 collateralBalanceETH;
uint256 borrowBalanceETH;
uint256 totalFeesETH;
uint256 currentLiquidationThreshold;
uint256 reserveLiquidationThreshold;
uint256 amountToDecreaseETH;
uint256 collateralBalancefterDecrease;
uint256 liquidationThresholdAfterDecrease;
uint256 healthFactorAfterDecrease;
bool reserveUsageAsCollateralEnabled;
}
/**
* @dev check if a specific balance decrease is allowed (i.e. doesn't bring the user borrow position health factor under 1e18)
* @param _reserve the address of the reserve
* @param _user the address of the user
* @param _amount the amount to decrease
* @return true if the decrease of the balance is allowed
**/
function balanceDecreaseAllowed(address _reserve, address _user, uint256 _amount)
external
view
returns (bool)
{
// Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables
balanceDecreaseAllowedLocalVars memory vars;
(
vars.decimals,
,
vars.reserveLiquidationThreshold,
vars.reserveUsageAsCollateralEnabled
) = core.getReserveConfiguration(_reserve);
if (
!vars.reserveUsageAsCollateralEnabled ||
!core.isUserUseReserveAsCollateralEnabled(_reserve, _user)
) {
return true; //if reserve is not used as collateral, no reasons to block the transfer
}
(
,
vars.collateralBalanceETH,
vars.borrowBalanceETH,
vars.totalFeesETH,
,
vars.currentLiquidationThreshold,
,
) = calculateUserGlobalData(_user);
if (vars.borrowBalanceETH == 0) {
return true; //no borrows - no reasons to block the transfer
}
IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle());
vars.amountToDecreaseETH = oracle.getAssetPrice(_reserve).mul(_amount).div(
10 ** vars.decimals
);
vars.collateralBalancefterDecrease = vars.collateralBalanceETH.sub(
vars.amountToDecreaseETH
);
//if there is a borrow, there can't be 0 collateral
if (vars.collateralBalancefterDecrease == 0) {
return false;
}
vars.liquidationThresholdAfterDecrease = vars
.collateralBalanceETH
.mul(vars.currentLiquidationThreshold)
.sub(vars.amountToDecreaseETH.mul(vars.reserveLiquidationThreshold))
.div(vars.collateralBalancefterDecrease);
uint256 healthFactorAfterDecrease = calculateHealthFactorFromBalancesInternal(
vars.collateralBalancefterDecrease,
vars.borrowBalanceETH,
vars.totalFeesETH,
vars.liquidationThresholdAfterDecrease
);
return healthFactorAfterDecrease > HEALTH_FACTOR_LIQUIDATION_THRESHOLD;
}
/**
* @notice calculates the amount of collateral needed in ETH to cover a new borrow.
* @param _reserve the reserve from which the user wants to borrow
* @param _amount the amount the user wants to borrow
* @param _fee the fee for the amount that the user needs to cover
* @param _userCurrentBorrowBalanceTH the current borrow balance of the user (before the borrow)
* @param _userCurrentLtv the average ltv of the user given his current collateral
* @return the total amount of collateral in ETH to cover the current borrow balance + the new amount + fee
**/
function calculateCollateralNeededInETH(
address _reserve,
uint256 _amount,
uint256 _fee,
uint256 _userCurrentBorrowBalanceTH,
uint256 _userCurrentFeesETH,
uint256 _userCurrentLtv
) external view returns (uint256) {
uint256 reserveDecimals = core.getReserveDecimals(_reserve);
IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle());
uint256 requestedBorrowAmountETH = oracle
.getAssetPrice(_reserve)
.mul(_amount.add(_fee))
.div(10 ** reserveDecimals); //price is in ether
//add the current already borrowed amount to the amount requested to calculate the total collateral needed.
uint256 collateralNeededInETH = _userCurrentBorrowBalanceTH
.add(_userCurrentFeesETH)
.add(requestedBorrowAmountETH)
.mul(100)
.div(_userCurrentLtv); //LTV is calculated in percentage
return collateralNeededInETH;
}
/**
* @dev calculates the equivalent amount in ETH that an user can borrow, depending on the available collateral and the
* average Loan To Value.
* @param collateralBalanceETH the total collateral balance
* @param borrowBalanceETH the total borrow balance
* @param totalFeesETH the total fees
* @param ltv the average loan to value
* @return the amount available to borrow in ETH for the user
**/
function calculateAvailableBorrowsETHInternal(
uint256 collateralBalanceETH,
uint256 borrowBalanceETH,
uint256 totalFeesETH,
uint256 ltv
) internal view returns (uint256) {
uint256 availableBorrowsETH = collateralBalanceETH.mul(ltv).div(100); //ltv is in percentage
if (availableBorrowsETH < borrowBalanceETH) {
return 0;
}
availableBorrowsETH = availableBorrowsETH.sub(borrowBalanceETH.add(totalFeesETH));
//calculate fee
uint256 borrowFee = IFeeProvider(addressesProvider.getFeeProvider())
.calculateLoanOriginationFee(msg.sender, availableBorrowsETH);
return availableBorrowsETH.sub(borrowFee);
}
/**
* @dev calculates the health factor from the corresponding balances
* @param collateralBalanceETH the total collateral balance in ETH
* @param borrowBalanceETH the total borrow balance in ETH
* @param totalFeesETH the total fees in ETH
* @param liquidationThreshold the avg liquidation threshold
**/
function calculateHealthFactorFromBalancesInternal(
uint256 collateralBalanceETH,
uint256 borrowBalanceETH,
uint256 totalFeesETH,
uint256 liquidationThreshold
) internal pure returns (uint256) {
if (borrowBalanceETH == 0) return uint256(-1);
return
(collateralBalanceETH.mul(liquidationThreshold).div(100)).wadDiv(
borrowBalanceETH.add(totalFeesETH)
);
}
/**
* @dev returns the health factor liquidation threshold
**/
function getHealthFactorLiquidationThreshold() public pure returns (uint256) {
return HEALTH_FACTOR_LIQUIDATION_THRESHOLD;
}
/**
* @dev accessory functions to fetch data from the lendingPoolCore
**/
function getReserveConfigurationData(address _reserve)
external
view
returns (
uint256 ltv,
uint256 liquidationThreshold,
uint256 liquidationBonus,
address rateStrategyAddress,
bool usageAsCollateralEnabled,
bool borrowingEnabled,
bool stableBorrowRateEnabled,
bool isActive
)
{
(, ltv, liquidationThreshold, usageAsCollateralEnabled) = core.getReserveConfiguration(
_reserve
);
stableBorrowRateEnabled = core.getReserveIsStableBorrowRateEnabled(_reserve);
borrowingEnabled = core.isReserveBorrowingEnabled(_reserve);
isActive = core.getReserveIsActive(_reserve);
liquidationBonus = core.getReserveLiquidationBonus(_reserve);
rateStrategyAddress = core.getReserveInterestRateStrategyAddress(_reserve);
}
function getReserveData(address _reserve)
external
view
returns (
uint256 totalLiquidity,
uint256 availableLiquidity,
uint256 totalBorrowsStable,
uint256 totalBorrowsVariable,
uint256 liquidityRate,
uint256 variableBorrowRate,
uint256 stableBorrowRate,
uint256 averageStableBorrowRate,
uint256 utilizationRate,
uint256 liquidityIndex,
uint256 variableBorrowIndex,
address aTokenAddress,
uint40 lastUpdateTimestamp
)
{
totalLiquidity = core.getReserveTotalLiquidity(_reserve);
availableLiquidity = core.getReserveAvailableLiquidity(_reserve);
totalBorrowsStable = core.getReserveTotalBorrowsStable(_reserve);
totalBorrowsVariable = core.getReserveTotalBorrowsVariable(_reserve);
liquidityRate = core.getReserveCurrentLiquidityRate(_reserve);
variableBorrowRate = core.getReserveCurrentVariableBorrowRate(_reserve);
stableBorrowRate = core.getReserveCurrentStableBorrowRate(_reserve);
averageStableBorrowRate = core.getReserveCurrentAverageStableBorrowRate(_reserve);
utilizationRate = core.getReserveUtilizationRate(_reserve);
liquidityIndex = core.getReserveLiquidityCumulativeIndex(_reserve);
variableBorrowIndex = core.getReserveVariableBorrowsCumulativeIndex(_reserve);
aTokenAddress = core.getReserveATokenAddress(_reserve);
lastUpdateTimestamp = core.getReserveLastUpdate(_reserve);
}
function getUserAccountData(address _user)
external
view
returns (
uint256 totalLiquidityETH,
uint256 totalCollateralETH,
uint256 totalBorrowsETH,
uint256 totalFeesETH,
uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
)
{
(
totalLiquidityETH,
totalCollateralETH,
totalBorrowsETH,
totalFeesETH,
ltv,
currentLiquidationThreshold,
healthFactor,
) = calculateUserGlobalData(_user);
availableBorrowsETH = calculateAvailableBorrowsETHInternal(
totalCollateralETH,
totalBorrowsETH,
totalFeesETH,
ltv
);
}
function getUserReserveData(address _reserve, address _user)
external
view
returns (
uint256 currentATokenBalance,
uint256 currentBorrowBalance,
uint256 principalBorrowBalance,
uint256 borrowRateMode,
uint256 borrowRate,
uint256 liquidityRate,
uint256 originationFee,
uint256 variableBorrowIndex,
uint256 lastUpdateTimestamp,
bool usageAsCollateralEnabled
)
{
currentATokenBalance = AToken(core.getReserveATokenAddress(_reserve)).balanceOf(_user);
CoreLibrary.InterestRateMode mode = core.getUserCurrentBorrowRateMode(_reserve, _user);
(principalBorrowBalance, currentBorrowBalance, ) = core.getUserBorrowBalances(
_reserve,
_user
);
//default is 0, if mode == CoreLibrary.InterestRateMode.NONE
if (mode == CoreLibrary.InterestRateMode.STABLE) {
borrowRate = core.getUserCurrentStableBorrowRate(_reserve, _user);
} else if (mode == CoreLibrary.InterestRateMode.VARIABLE) {
borrowRate = core.getReserveCurrentVariableBorrowRate(_reserve);
}
borrowRateMode = uint256(mode);
liquidityRate = core.getReserveCurrentLiquidityRate(_reserve);
originationFee = core.getUserOriginationFee(_reserve, _user);
variableBorrowIndex = core.getUserVariableBorrowCumulativeIndex(_reserve, _user);
lastUpdateTimestamp = core.getUserLastUpdate(_reserve, _user);
usageAsCollateralEnabled = core.isUserUseReserveAsCollateralEnabled(_reserve, _user);
}
}

View File

@ -0,0 +1,360 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "../libraries/openzeppelin-upgradeability/VersionedInitializable.sol";
import "../configuration/LendingPoolAddressesProvider.sol";
import "../configuration/LendingPoolParametersProvider.sol";
import "../tokenization/AToken.sol";
import "../libraries/CoreLibrary.sol";
import "../libraries/WadRayMath.sol";
import "./LendingPoolCore.sol";
import "./LendingPoolDataProvider.sol";
import "../interfaces/IPriceOracleGetter.sol";
/**
* @title LendingPoolLiquidationManager contract
* @author Aave
* @notice Implements the liquidation function.
**/
contract LendingPoolLiquidationManager is ReentrancyGuard, VersionedInitializable {
using SafeMath for uint256;
using WadRayMath for uint256;
using Address for address;
LendingPoolAddressesProvider public addressesProvider;
LendingPoolCore core;
LendingPoolDataProvider dataProvider;
LendingPoolParametersProvider parametersProvider;
IFeeProvider feeProvider;
address ethereumAddress;
uint256 constant LIQUIDATION_CLOSE_FACTOR_PERCENT = 50;
/**
* @dev emitted when a borrow fee is liquidated
* @param _collateral the address of the collateral being liquidated
* @param _reserve the address of the reserve
* @param _user the address of the user being liquidated
* @param _feeLiquidated the total fee liquidated
* @param _liquidatedCollateralForFee the amount of collateral received by the protocol in exchange for the fee
* @param _timestamp the timestamp of the action
**/
event OriginationFeeLiquidated(
address indexed _collateral,
address indexed _reserve,
address indexed _user,
uint256 _feeLiquidated,
uint256 _liquidatedCollateralForFee,
uint256 _timestamp
);
/**
* @dev emitted when a borrower is liquidated
* @param _collateral the address of the collateral being liquidated
* @param _reserve the address of the reserve
* @param _user the address of the user being liquidated
* @param _purchaseAmount the total amount liquidated
* @param _liquidatedCollateralAmount the amount of collateral being liquidated
* @param _accruedBorrowInterest the amount of interest accrued by the borrower since the last action
* @param _liquidator the address of the liquidator
* @param _receiveAToken true if the liquidator wants to receive aTokens, false otherwise
* @param _timestamp the timestamp of the action
**/
event LiquidationCall(
address indexed _collateral,
address indexed _reserve,
address indexed _user,
uint256 _purchaseAmount,
uint256 _liquidatedCollateralAmount,
uint256 _accruedBorrowInterest,
address _liquidator,
bool _receiveAToken,
uint256 _timestamp
);
enum LiquidationErrors {
NO_ERROR,
NO_COLLATERAL_AVAILABLE,
COLLATERAL_CANNOT_BE_LIQUIDATED,
CURRRENCY_NOT_BORROWED,
HEALTH_FACTOR_ABOVE_THRESHOLD,
NOT_ENOUGH_LIQUIDITY
}
struct LiquidationCallLocalVars {
uint256 userCollateralBalance;
uint256 userCompoundedBorrowBalance;
uint256 borrowBalanceIncrease;
uint256 maxPrincipalAmountToLiquidate;
uint256 actualAmountToLiquidate;
uint256 liquidationRatio;
uint256 maxAmountCollateralToLiquidate;
uint256 originationFee;
uint256 feeLiquidated;
uint256 liquidatedCollateralForFee;
CoreLibrary.InterestRateMode borrowRateMode;
uint256 userStableRate;
bool isCollateralEnabled;
bool healthFactorBelowThreshold;
}
/**
* @dev as the contract extends the VersionedInitializable contract to match the state
* of the LendingPool contract, the getRevision() function is needed.
*/
function getRevision() internal override pure returns (uint256) {
return 0;
}
/**
* @dev users can invoke this function to liquidate an undercollateralized position.
* @param _reserve the address of the collateral to liquidated
* @param _reserve the address of the principal reserve
* @param _user the address of the borrower
* @param _purchaseAmount the amount of principal that the liquidator wants to repay
* @param _receiveAToken true if the liquidators wants to receive the aTokens, false if
* he wants to receive the underlying asset directly
**/
function liquidationCall(
address _collateral,
address _reserve,
address _user,
uint256 _purchaseAmount,
bool _receiveAToken
) external payable returns (uint256, string memory) {
// Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables
LiquidationCallLocalVars memory vars;
(, , , , , , , vars.healthFactorBelowThreshold) = dataProvider.calculateUserGlobalData(
_user
);
if (!vars.healthFactorBelowThreshold) {
return (
uint256(LiquidationErrors.HEALTH_FACTOR_ABOVE_THRESHOLD),
"Health factor is not below the threshold"
);
}
vars.userCollateralBalance = core.getUserUnderlyingAssetBalance(_collateral, _user);
//if _user hasn't deposited this specific collateral, nothing can be liquidated
if (vars.userCollateralBalance == 0) {
return (
uint256(LiquidationErrors.NO_COLLATERAL_AVAILABLE),
"Invalid collateral to liquidate"
);
}
vars.isCollateralEnabled =
core.isReserveUsageAsCollateralEnabled(_collateral) &&
core.isUserUseReserveAsCollateralEnabled(_collateral, _user);
//if _collateral isn't enabled as collateral by _user, it cannot be liquidated
if (!vars.isCollateralEnabled) {
return (
uint256(LiquidationErrors.COLLATERAL_CANNOT_BE_LIQUIDATED),
"The collateral chosen cannot be liquidated"
);
}
//if the user hasn't borrowed the specific currency defined by _reserve, it cannot be liquidated
(, vars.userCompoundedBorrowBalance, vars.borrowBalanceIncrease) = core
.getUserBorrowBalances(_reserve, _user);
if (vars.userCompoundedBorrowBalance == 0) {
return (
uint256(LiquidationErrors.CURRRENCY_NOT_BORROWED),
"User did not borrow the specified currency"
);
}
//all clear - calculate the max principal amount that can be liquidated
vars.maxPrincipalAmountToLiquidate = vars
.userCompoundedBorrowBalance
.mul(LIQUIDATION_CLOSE_FACTOR_PERCENT)
.div(100);
vars.actualAmountToLiquidate = _purchaseAmount > vars.maxPrincipalAmountToLiquidate
? vars.maxPrincipalAmountToLiquidate
: _purchaseAmount;
(uint256 maxCollateralToLiquidate, uint256 principalAmountNeeded) = calculateAvailableCollateralToLiquidate(
_collateral,
_reserve,
vars.actualAmountToLiquidate,
vars.userCollateralBalance
);
vars.originationFee = core.getUserOriginationFee(_reserve, _user);
//if there is a fee to liquidate, calculate the maximum amount of fee that can be liquidated
if (vars.originationFee > 0) {
(
vars.liquidatedCollateralForFee,
vars.feeLiquidated
) = calculateAvailableCollateralToLiquidate(
_collateral,
_reserve,
vars.originationFee,
vars.userCollateralBalance.sub(maxCollateralToLiquidate)
);
}
//if principalAmountNeeded < vars.ActualAmountToLiquidate, there isn't enough
//of _collateral to cover the actual amount that is being liquidated, hence we liquidate
//a smaller amount
if (principalAmountNeeded < vars.actualAmountToLiquidate) {
vars.actualAmountToLiquidate = principalAmountNeeded;
}
//if liquidator reclaims the underlying asset, we make sure there is enough available collateral in the reserve
if (!_receiveAToken) {
uint256 currentAvailableCollateral = core.getReserveAvailableLiquidity(_collateral);
if (currentAvailableCollateral < maxCollateralToLiquidate) {
return (
uint256(LiquidationErrors.NOT_ENOUGH_LIQUIDITY),
"There isn't enough liquidity available to liquidate"
);
}
}
core.updateStateOnLiquidation(
_reserve,
_collateral,
_user,
vars.actualAmountToLiquidate,
maxCollateralToLiquidate,
vars.feeLiquidated,
vars.liquidatedCollateralForFee,
vars.borrowBalanceIncrease,
_receiveAToken
);
AToken collateralAtoken = AToken(core.getReserveATokenAddress(_collateral));
//if liquidator reclaims the aToken, he receives the equivalent atoken amount
if (_receiveAToken) {
collateralAtoken.transferOnLiquidation(_user, msg.sender, maxCollateralToLiquidate);
} else {
//otherwise receives the underlying asset
//burn the equivalent amount of atoken
collateralAtoken.burnOnLiquidation(_user, maxCollateralToLiquidate);
core.transferToUser(_collateral, msg.sender, maxCollateralToLiquidate);
}
//transfers the principal currency to the pool
core.transferToReserve{value: msg.value}(_reserve, msg.sender, vars.actualAmountToLiquidate);
if (vars.feeLiquidated > 0) {
//if there is enough collateral to liquidate the fee, first transfer burn an equivalent amount of
//aTokens of the user
collateralAtoken.burnOnLiquidation(_user, vars.liquidatedCollateralForFee);
//then liquidate the fee by transferring it to the fee collection address
core.liquidateFee(
_collateral,
vars.liquidatedCollateralForFee,
addressesProvider.getTokenDistributor()
);
emit OriginationFeeLiquidated(
_collateral,
_reserve,
_user,
vars.feeLiquidated,
vars.liquidatedCollateralForFee,
//solium-disable-next-line
block.timestamp
);
}
emit LiquidationCall(
_collateral,
_reserve,
_user,
vars.actualAmountToLiquidate,
maxCollateralToLiquidate,
vars.borrowBalanceIncrease,
msg.sender,
_receiveAToken,
//solium-disable-next-line
block.timestamp
);
return (uint256(LiquidationErrors.NO_ERROR), "No errors");
}
struct AvailableCollateralToLiquidateLocalVars {
uint256 userCompoundedBorrowBalance;
uint256 liquidationBonus;
uint256 collateralPrice;
uint256 principalCurrencyPrice;
uint256 maxAmountCollateralToLiquidate;
uint256 principalDecimals;
uint256 collateralDecimals;
}
/**
* @dev calculates how much of a specific collateral can be liquidated, given
* a certain amount of principal currency. This function needs to be called after
* all the checks to validate the liquidation have been performed, otherwise it might fail.
* @param _collateral the collateral to be liquidated
* @param _principal the principal currency to be liquidated
* @param _purchaseAmount the amount of principal being liquidated
* @param _userCollateralBalance the collatera balance for the specific _collateral asset of the user being liquidated
* @return collateralAmount the maximum amount that is possible to liquidated given all the liquidation constraints (user balance, close factor)
* @return principalAmountNeeded the purchase amount
**/
function calculateAvailableCollateralToLiquidate(
address _collateral,
address _principal,
uint256 _purchaseAmount,
uint256 _userCollateralBalance
) internal view returns (uint256 collateralAmount, uint256 principalAmountNeeded) {
collateralAmount = 0;
principalAmountNeeded = 0;
IPriceOracleGetter oracle = IPriceOracleGetter(addressesProvider.getPriceOracle());
// Usage of a memory struct of vars to avoid "Stack too deep" errors due to local variables
AvailableCollateralToLiquidateLocalVars memory vars;
vars.collateralPrice = oracle.getAssetPrice(_collateral);
vars.principalCurrencyPrice = oracle.getAssetPrice(_principal);
vars.liquidationBonus = core.getReserveLiquidationBonus(_collateral);
vars.principalDecimals = core.getReserveDecimals(_principal);
vars.collateralDecimals = core.getReserveDecimals(_collateral);
//this is the maximum possible amount of the selected collateral that can be liquidated, given the
//max amount of principal currency that is available for liquidation.
vars.maxAmountCollateralToLiquidate = vars
.principalCurrencyPrice
.mul(_purchaseAmount)
.mul(10 ** vars.collateralDecimals)
.div(vars.collateralPrice.mul(10 ** vars.principalDecimals))
.mul(vars.liquidationBonus)
.div(100);
if (vars.maxAmountCollateralToLiquidate > _userCollateralBalance) {
collateralAmount = _userCollateralBalance;
principalAmountNeeded = vars
.collateralPrice
.mul(collateralAmount)
.mul(10 ** vars.principalDecimals)
.div(vars.principalCurrencyPrice.mul(10 ** vars.collateralDecimals))
.mul(100)
.div(vars.liquidationBonus);
} else {
collateralAmount = vars.maxAmountCollateralToLiquidate;
principalAmountNeeded = _purchaseAmount;
}
return (collateralAmount, principalAmountNeeded);
}
}

View File

@ -0,0 +1,440 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "./WadRayMath.sol";
/**
* @title CoreLibrary library
* @author Aave
* @notice Defines the data structures of the reserves and the user data
**/
library CoreLibrary {
using SafeMath for uint256;
using WadRayMath for uint256;
enum InterestRateMode {NONE, STABLE, VARIABLE}
uint256 internal constant SECONDS_PER_YEAR = 365 days;
struct UserReserveData {
//principal amount borrowed by the user.
uint256 principalBorrowBalance;
//cumulated variable borrow index for the user. Expressed in ray
uint256 lastVariableBorrowCumulativeIndex;
//origination fee cumulated by the user
uint256 originationFee;
// stable borrow rate at which the user has borrowed. Expressed in ray
uint256 stableBorrowRate;
uint40 lastUpdateTimestamp;
//defines if a specific deposit should or not be used as a collateral in borrows
bool useAsCollateral;
}
struct ReserveData {
/**
* @dev refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties.
**/
//the liquidity index. Expressed in ray
uint256 lastLiquidityCumulativeIndex;
//the current supply rate. Expressed in ray
uint256 currentLiquidityRate;
//the total borrows of the reserve at a stable rate. Expressed in the currency decimals
uint256 totalBorrowsStable;
//the total borrows of the reserve at a variable rate. Expressed in the currency decimals
uint256 totalBorrowsVariable;
//the current variable borrow rate. Expressed in ray
uint256 currentVariableBorrowRate;
//the current stable borrow rate. Expressed in ray
uint256 currentStableBorrowRate;
//the current average stable borrow rate (weighted average of all the different stable rate loans). Expressed in ray
uint256 currentAverageStableBorrowRate;
//variable borrow index. Expressed in ray
uint256 lastVariableBorrowCumulativeIndex;
//the ltv of the reserve. Expressed in percentage (0-100)
uint256 baseLTVasCollateral;
//the liquidation threshold of the reserve. Expressed in percentage (0-100)
uint256 liquidationThreshold;
//the liquidation bonus of the reserve. Expressed in percentage
uint256 liquidationBonus;
//the decimals of the reserve asset
uint256 decimals;
/**
* @dev address of the aToken representing the asset
**/
address aTokenAddress;
/**
* @dev address of the interest rate strategy contract
**/
address interestRateStrategyAddress;
uint40 lastUpdateTimestamp;
// borrowingEnabled = true means users can borrow from this reserve
bool borrowingEnabled;
// usageAsCollateralEnabled = true means users can use this reserve as collateral
bool usageAsCollateralEnabled;
// isStableBorrowRateEnabled = true means users can borrow at a stable rate
bool isStableBorrowRateEnabled;
// isActive = true means the reserve has been activated and properly configured
bool isActive;
// isFreezed = true means the reserve only allows repays and redeems, but not deposits, new borrowings or rate swap
bool isFreezed;
}
/**
* @dev returns the ongoing normalized income for the reserve.
* a value of 1e27 means there is no income. As time passes, the income is accrued.
* A value of 2*1e27 means that the income of the reserve is double the initial amount.
* @param _reserve the reserve object
* @return the normalized income. expressed in ray
**/
function getNormalizedIncome(CoreLibrary.ReserveData storage _reserve)
internal
view
returns (uint256)
{
uint256 cumulated = calculateLinearInterest(
_reserve
.currentLiquidityRate,
_reserve
.lastUpdateTimestamp
)
.rayMul(_reserve.lastLiquidityCumulativeIndex);
return cumulated;
}
/**
* @dev Updates the liquidity cumulative index Ci and variable borrow cumulative index Bvc. Refer to the whitepaper for
* a formal specification.
* @param _self the reserve object
**/
function updateCumulativeIndexes(ReserveData storage _self) internal {
uint256 totalBorrows = getTotalBorrows(_self);
if (totalBorrows > 0) {
//only cumulating if there is any income being produced
uint256 cumulatedLiquidityInterest = calculateLinearInterest(
_self.currentLiquidityRate,
_self.lastUpdateTimestamp
);
_self.lastLiquidityCumulativeIndex = cumulatedLiquidityInterest.rayMul(
_self.lastLiquidityCumulativeIndex
);
uint256 cumulatedVariableBorrowInterest = calculateCompoundedInterest(
_self.currentVariableBorrowRate,
_self.lastUpdateTimestamp
);
_self.lastVariableBorrowCumulativeIndex = cumulatedVariableBorrowInterest.rayMul(
_self.lastVariableBorrowCumulativeIndex
);
}
}
/**
* @dev accumulates a predefined amount of asset to the reserve as a fixed, one time income. Used for example to accumulate
* the flashloan fee to the reserve, and spread it through the depositors.
* @param _self the reserve object
* @param _totalLiquidity the total liquidity available in the reserve
* @param _amount the amount to accomulate
**/
function cumulateToLiquidityIndex(
ReserveData storage _self,
uint256 _totalLiquidity,
uint256 _amount
) internal {
uint256 amountToLiquidityRatio = _amount.wadToRay().rayDiv(_totalLiquidity.wadToRay());
uint256 cumulatedLiquidity = amountToLiquidityRatio.add(WadRayMath.ray());
_self.lastLiquidityCumulativeIndex = cumulatedLiquidity.rayMul(
_self.lastLiquidityCumulativeIndex
);
}
/**
* @dev initializes a reserve
* @param _self the reserve object
* @param _aTokenAddress the address of the overlying atoken contract
* @param _decimals the number of decimals of the underlying asset
* @param _interestRateStrategyAddress the address of the interest rate strategy contract
**/
function init(
ReserveData storage _self,
address _aTokenAddress,
uint256 _decimals,
address _interestRateStrategyAddress
) external {
require(_self.aTokenAddress == address(0), "Reserve has already been initialized");
if (_self.lastLiquidityCumulativeIndex == 0) {
//if the reserve has not been initialized yet
_self.lastLiquidityCumulativeIndex = WadRayMath.ray();
}
if (_self.lastVariableBorrowCumulativeIndex == 0) {
_self.lastVariableBorrowCumulativeIndex = WadRayMath.ray();
}
_self.aTokenAddress = _aTokenAddress;
_self.decimals = _decimals;
_self.interestRateStrategyAddress = _interestRateStrategyAddress;
_self.isActive = true;
_self.isFreezed = false;
}
/**
* @dev enables borrowing on a reserve
* @param _self the reserve object
* @param _stableBorrowRateEnabled true if the stable borrow rate must be enabled by default, false otherwise
**/
function enableBorrowing(ReserveData storage _self, bool _stableBorrowRateEnabled) external {
require(_self.borrowingEnabled == false, "Reserve is already enabled");
_self.borrowingEnabled = true;
_self.isStableBorrowRateEnabled = _stableBorrowRateEnabled;
}
/**
* @dev disables borrowing on a reserve
* @param _self the reserve object
**/
function disableBorrowing(ReserveData storage _self) external {
_self.borrowingEnabled = false;
}
/**
* @dev enables a reserve to be used as collateral
* @param _self the reserve object
* @param _baseLTVasCollateral 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
**/
function enableAsCollateral(
ReserveData storage _self,
uint256 _baseLTVasCollateral,
uint256 _liquidationThreshold,
uint256 _liquidationBonus
) external {
require(
_self.usageAsCollateralEnabled == false,
"Reserve is already enabled as collateral"
);
_self.usageAsCollateralEnabled = true;
_self.baseLTVasCollateral = _baseLTVasCollateral;
_self.liquidationThreshold = _liquidationThreshold;
_self.liquidationBonus = _liquidationBonus;
if (_self.lastLiquidityCumulativeIndex == 0)
_self.lastLiquidityCumulativeIndex = WadRayMath.ray();
}
/**
* @dev disables a reserve as collateral
* @param _self the reserve object
**/
function disableAsCollateral(ReserveData storage _self) external {
_self.usageAsCollateralEnabled = false;
}
/**
* @dev calculates the compounded borrow balance of a user
* @param _self the userReserve object
* @param _reserve the reserve object
* @return the user compounded borrow balance
**/
function getCompoundedBorrowBalance(
CoreLibrary.UserReserveData storage _self,
CoreLibrary.ReserveData storage _reserve
) internal view returns (uint256) {
if (_self.principalBorrowBalance == 0) return 0;
uint256 principalBorrowBalanceRay = _self.principalBorrowBalance.wadToRay();
uint256 compoundedBalance = 0;
uint256 cumulatedInterest = 0;
if (_self.stableBorrowRate > 0) {
cumulatedInterest = calculateCompoundedInterest(
_self.stableBorrowRate,
_self.lastUpdateTimestamp
);
} else {
//variable interest
cumulatedInterest = calculateCompoundedInterest(
_reserve
.currentVariableBorrowRate,
_reserve
.lastUpdateTimestamp
)
.rayMul(_reserve.lastVariableBorrowCumulativeIndex)
.rayDiv(_self.lastVariableBorrowCumulativeIndex);
}
compoundedBalance = principalBorrowBalanceRay.rayMul(cumulatedInterest).rayToWad();
if (compoundedBalance == _self.principalBorrowBalance) {
//solium-disable-next-line
if (_self.lastUpdateTimestamp != block.timestamp) {
//no interest cumulation because of the rounding - we add 1 wei
//as symbolic cumulated interest to avoid interest free loans.
return _self.principalBorrowBalance.add(1 wei);
}
}
return compoundedBalance;
}
/**
* @dev increases the total borrows at a stable rate on a specific reserve and updates the
* average stable rate consequently
* @param _reserve the reserve object
* @param _amount the amount to add to the total borrows stable
* @param _rate the rate at which the amount has been borrowed
**/
function increaseTotalBorrowsStableAndUpdateAverageRate(
ReserveData storage _reserve,
uint256 _amount,
uint256 _rate
) internal {
uint256 previousTotalBorrowStable = _reserve.totalBorrowsStable;
//updating reserve borrows stable
_reserve.totalBorrowsStable = _reserve.totalBorrowsStable.add(_amount);
//update the average stable rate
//weighted average of all the borrows
uint256 weightedLastBorrow = _amount.wadToRay().rayMul(_rate);
uint256 weightedPreviousTotalBorrows = previousTotalBorrowStable.wadToRay().rayMul(
_reserve.currentAverageStableBorrowRate
);
_reserve.currentAverageStableBorrowRate = weightedLastBorrow
.add(weightedPreviousTotalBorrows)
.rayDiv(_reserve.totalBorrowsStable.wadToRay());
}
/**
* @dev decreases the total borrows at a stable rate on a specific reserve and updates the
* average stable rate consequently
* @param _reserve the reserve object
* @param _amount the amount to substract to the total borrows stable
* @param _rate the rate at which the amount has been repaid
**/
function decreaseTotalBorrowsStableAndUpdateAverageRate(
ReserveData storage _reserve,
uint256 _amount,
uint256 _rate
) internal {
require(_reserve.totalBorrowsStable >= _amount, "Invalid amount to decrease");
uint256 previousTotalBorrowStable = _reserve.totalBorrowsStable;
//updating reserve borrows stable
_reserve.totalBorrowsStable = _reserve.totalBorrowsStable.sub(_amount);
if (_reserve.totalBorrowsStable == 0) {
_reserve.currentAverageStableBorrowRate = 0; //no income if there are no stable rate borrows
return;
}
//update the average stable rate
//weighted average of all the borrows
uint256 weightedLastBorrow = _amount.wadToRay().rayMul(_rate);
uint256 weightedPreviousTotalBorrows = previousTotalBorrowStable.wadToRay().rayMul(
_reserve.currentAverageStableBorrowRate
);
require(
weightedPreviousTotalBorrows >= weightedLastBorrow,
"The amounts to subtract don't match"
);
_reserve.currentAverageStableBorrowRate = weightedPreviousTotalBorrows
.sub(weightedLastBorrow)
.rayDiv(_reserve.totalBorrowsStable.wadToRay());
}
/**
* @dev increases the total borrows at a variable rate
* @param _reserve the reserve object
* @param _amount the amount to add to the total borrows variable
**/
function increaseTotalBorrowsVariable(ReserveData storage _reserve, uint256 _amount) internal {
_reserve.totalBorrowsVariable = _reserve.totalBorrowsVariable.add(_amount);
}
/**
* @dev decreases the total borrows at a variable rate
* @param _reserve the reserve object
* @param _amount the amount to substract to the total borrows variable
**/
function decreaseTotalBorrowsVariable(ReserveData storage _reserve, uint256 _amount) internal {
require(
_reserve.totalBorrowsVariable >= _amount,
"The amount that is being subtracted from the variable total borrows is incorrect"
);
_reserve.totalBorrowsVariable = _reserve.totalBorrowsVariable.sub(_amount);
}
/**
* @dev function to calculate the interest using a linear interest rate formula
* @param _rate the interest rate, in ray
* @param _lastUpdateTimestamp the timestamp of the last update of the interest
* @return the interest rate linearly accumulated during the timeDelta, in ray
**/
function calculateLinearInterest(uint256 _rate, uint40 _lastUpdateTimestamp)
internal
view
returns (uint256)
{
//solium-disable-next-line
uint256 timeDifference = block.timestamp.sub(uint256(_lastUpdateTimestamp));
uint256 timeDelta = timeDifference.wadToRay().rayDiv(SECONDS_PER_YEAR.wadToRay());
return _rate.rayMul(timeDelta).add(WadRayMath.ray());
}
/**
* @dev function to calculate the interest using a compounded interest rate formula
* @param _rate the interest rate, in ray
* @param _lastUpdateTimestamp the timestamp of the last update of the interest
* @return the interest rate compounded during the timeDelta, in ray
**/
function calculateCompoundedInterest(uint256 _rate, uint40 _lastUpdateTimestamp)
internal
view
returns (uint256)
{
//solium-disable-next-line
uint256 timeDifference = block.timestamp.sub(uint256(_lastUpdateTimestamp));
uint256 ratePerSecond = _rate.div(SECONDS_PER_YEAR);
return ratePerSecond.add(WadRayMath.ray()).rayPow(timeDifference);
}
/**
* @dev returns the total borrows on the reserve
* @param _reserve the reserve object
* @return the total borrows (stable + variable)
**/
function getTotalBorrows(CoreLibrary.ReserveData storage _reserve)
internal
view
returns (uint256)
{
return _reserve.totalBorrowsStable.add(_reserve.totalBorrowsVariable);
}
}

View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
library EthAddressLib {
/**
* @dev returns the address used within the protocol to identify ETH
* @return the address assigned to ETH
*/
function ethAddress() internal pure returns(address) {
return 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
}
}

View File

@ -0,0 +1,20 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
library UintConstants {
/**
* @dev returns max uint256
* @return max uint256
*/
function maxUint() internal pure returns(uint256) {
return uint256(-1);
}
/**
* @dev returns max uint256-1
* @return max uint256-1
*/
function maxUintMinus1() internal pure returns(uint256) {
return uint256(-1) - 1;
}
}

View File

@ -0,0 +1,134 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/math/SafeMath.sol";
/**
* @title WadRayMath library
* @author Aave
* @dev Provides mul and div function for wads (decimal numbers with 18 digits precision) and rays (decimals with 27 digits)
**/
library WadRayMath {
using SafeMath for uint256;
uint256 internal constant WAD = 1e18;
uint256 internal constant halfWAD = WAD / 2;
uint256 internal constant RAY = 1e27;
uint256 internal constant halfRAY = RAY / 2;
uint256 internal constant WAD_RAY_RATIO = 1e9;
/**
* @return one ray, 1e27
**/
function ray() internal pure returns (uint256) {
return RAY;
}
/**
* @return one wad, 1e18
**/
function wad() internal pure returns (uint256) {
return WAD;
}
/**
* @return half ray, 1e27/2
**/
function halfRay() internal pure returns (uint256) {
return halfRAY;
}
/**
* @return half ray, 1e18/2
**/
function halfWad() internal pure returns (uint256) {
return halfWAD;
}
/**
* @dev multiplies two wad, rounding half up to the nearest wad
* @param a wad
* @param b wad
* @return the result of a*b, in wad
**/
function wadMul(uint256 a, uint256 b) internal pure returns (uint256) {
return halfWAD.add(a.mul(b)).div(WAD);
}
/**
* @dev divides two wad, rounding half up to the nearest wad
* @param a wad
* @param b wad
* @return the result of a/b, in wad
**/
function wadDiv(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 halfB = b / 2;
return halfB.add(a.mul(WAD)).div(b);
}
/**
* @dev multiplies two ray, rounding half up to the nearest ray
* @param a ray
* @param b ray
* @return the result of a*b, in ray
**/
function rayMul(uint256 a, uint256 b) internal pure returns (uint256) {
return halfRAY.add(a.mul(b)).div(RAY);
}
/**
* @dev divides two ray, rounding half up to the nearest ray
* @param a ray
* @param b ray
* @return the result of a/b, in ray
**/
function rayDiv(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 halfB = b / 2;
return halfB.add(a.mul(RAY)).div(b);
}
/**
* @dev casts ray down to wad
* @param a ray
* @return a casted to wad, rounded half up to the nearest wad
**/
function rayToWad(uint256 a) internal pure returns (uint256) {
uint256 halfRatio = WAD_RAY_RATIO / 2;
return halfRatio.add(a).div(WAD_RAY_RATIO);
}
/**
* @dev convert wad up to ray
* @param a wad
* @return a converted in ray
**/
function wadToRay(uint256 a) internal pure returns (uint256) {
return a.mul(WAD_RAY_RATIO);
}
/**
* @dev calculates base^exp. The code uses the ModExp precompile
* @return z base^exp, in ray
*/
//solium-disable-next-line
function rayPow(uint256 x, uint256 n) internal pure returns (uint256 z) {
z = n % 2 != 0 ? x : RAY;
for (n /= 2; n != 0; n /= 2) {
x = rayMul(x, x);
if (n % 2 != 0) {
z = rayMul(z, x);
}
}
}
}

View File

@ -0,0 +1,33 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./BaseAdminUpgradeabilityProxy.sol";
/**
* @title AdminUpgradeabilityProxy
* @dev Extends from BaseAdminUpgradeabilityProxy with a constructor for
* initializing the implementation, admin, and init data.
*/
contract AdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, UpgradeabilityProxy {
/**
* Contract constructor.
* @param _logic address of the initial implementation.
* @param _admin Address of the proxy administrator.
* @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
* It should include the signature and the parameters of the function to be called, as described in
* https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
* This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
*/
constructor(address _logic, address _admin, bytes memory _data) public payable UpgradeabilityProxy(_logic, _data) {
assert(ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
_setAdmin(_admin);
}
/**
* @dev Only fall back when the sender is not the admin.
*/
function _willFallback() internal override(BaseAdminUpgradeabilityProxy, Proxy) {
BaseAdminUpgradeabilityProxy._willFallback();
}
}

View File

@ -0,0 +1,121 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./UpgradeabilityProxy.sol";
/**
* @title BaseAdminUpgradeabilityProxy
* @dev This contract combines an upgradeability proxy with an authorization
* mechanism for administrative tasks.
* All external functions in this contract must be guarded by the
* `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
* feature proposal that would enable this to be done automatically.
*/
contract BaseAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
/**
* @dev Emitted when the administration has been transferred.
* @param previousAdmin Address of the previous admin.
* @param newAdmin Address of the new admin.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Modifier to check whether the `msg.sender` is the admin.
* If it is, it will run the function. Otherwise, it will delegate the call
* to the implementation.
*/
modifier ifAdmin() {
if (msg.sender == _admin()) {
_;
} else {
_fallback();
}
}
/**
* @return The address of the proxy admin.
*/
function admin() external ifAdmin returns (address) {
return _admin();
}
/**
* @return The address of the implementation.
*/
function implementation() external ifAdmin returns (address) {
return _implementation();
}
/**
* @dev Changes the admin of the proxy.
* Only the current admin can call this function.
* @param newAdmin Address to transfer proxy administration to.
*/
function changeAdmin(address newAdmin) external ifAdmin {
require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address");
emit AdminChanged(_admin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev Upgrade the backing implementation of the proxy.
* Only the admin can call this function.
* @param newImplementation Address of the new implementation.
*/
function upgradeTo(address newImplementation) external ifAdmin {
_upgradeTo(newImplementation);
}
/**
* @dev Upgrade the backing implementation of the proxy and call a function
* on the new implementation.
* This is useful to initialize the proxied contract.
* @param newImplementation Address of the new implementation.
* @param data Data to send as msg.data in the low level call.
* It should include the signature and the parameters of the function to be called, as described in
* https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
*/
function upgradeToAndCall(address newImplementation, bytes calldata data) external payable ifAdmin {
_upgradeTo(newImplementation);
(bool success, ) = newImplementation.delegatecall(data);
require(success);
}
/**
* @return adm The admin slot.
*/
function _admin() internal view returns (address adm) {
bytes32 slot = ADMIN_SLOT;
//solium-disable-next-line
assembly {
adm := sload(slot)
}
}
/**
* @dev Sets the address of the proxy admin.
* @param newAdmin Address of the new proxy admin.
*/
function _setAdmin(address newAdmin) internal {
bytes32 slot = ADMIN_SLOT;
//solium-disable-next-line
assembly {
sstore(slot, newAdmin)
}
}
/**
* @dev Only fall back when the sender is not the admin.
*/
function _willFallback() internal override virtual {
require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin");
super._willFallback();
}
}

View File

@ -0,0 +1,65 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./Proxy.sol";
import "@openzeppelin/contracts/utils/Address.sol";
/**
* @title BaseUpgradeabilityProxy
* @dev This contract implements a proxy that allows to change the
* implementation address to which it will delegate.
* Such a change is called an implementation upgrade.
*/
contract BaseUpgradeabilityProxy is Proxy {
/**
* @dev Emitted when the implementation is upgraded.
* @param implementation Address of the new implementation.
*/
event Upgraded(address indexed implementation);
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev Returns the current implementation.
* @return impl Address of the current implementation
*/
function _implementation() internal override view returns (address impl) {
bytes32 slot = IMPLEMENTATION_SLOT;
//solium-disable-next-line
assembly {
impl := sload(slot)
}
}
/**
* @dev Upgrades the proxy to a new implementation.
* @param newImplementation Address of the new implementation.
*/
function _upgradeTo(address newImplementation) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
/**
* @dev Sets the implementation address of the proxy.
* @param newImplementation Address of the new implementation.
*/
function _setImplementation(address newImplementation) internal {
require(
Address.isContract(newImplementation),
"Cannot set a proxy implementation to a non-contract address"
);
bytes32 slot = IMPLEMENTATION_SLOT;
//solium-disable-next-line
assembly {
sstore(slot, newImplementation)
}
}
}

View File

@ -0,0 +1,63 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity >=0.4.24 <0.7.0;
/**
* @title Initializable
*
* @dev Helper contract to support initializer functions. To use it, replace
* the constructor with a function that has the `initializer` modifier.
* WARNING: Unlike constructors, initializer functions must be manually
* invoked. This applies both to deploying an Initializable contract, as well
* as extending an Initializable contract via inheritance.
* WARNING: When used with inheritance, manual care must be taken to not invoke
* a parent initializer twice, or ensure that all initializers are idempotent,
* because this is not dealt with automatically as with constructors.
*/
contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
*/
bool private initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private initializing;
/**
* @dev Modifier to use in the initializer function of a contract.
*/
modifier initializer() {
require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
bool isTopLevelCall = !initializing;
if (isTopLevelCall) {
initializing = true;
initialized = true;
}
_;
if (isTopLevelCall) {
initializing = false;
}
}
/// @dev Returns true if and only if the function is running in the constructor
function isConstructor() private view returns (bool) {
// extcodesize checks the size of the code stored in an address, and
// address returns the current address. Since the code is still not
// deployed when running a constructor, any checks on its code size will
// yield zero, making it an effective way to detect if a contract is
// under construction or not.
uint256 cs;
//solium-disable-next-line
assembly {
cs := extcodesize(address())
}
return cs == 0;
}
// Reserved storage space to allow for layout changes in the future.
uint256[50] private ______gap;
}

View File

@ -0,0 +1,36 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./BaseAdminUpgradeabilityProxy.sol";
import "./InitializableUpgradeabilityProxy.sol";
/**
* @title InitializableAdminUpgradeabilityProxy
* @dev Extends from BaseAdminUpgradeabilityProxy with an initializer for
* initializing the implementation, admin, and init data.
*/
contract InitializableAdminUpgradeabilityProxy is BaseAdminUpgradeabilityProxy, InitializableUpgradeabilityProxy {
/**
* Contract initializer.
* @param _logic address of the initial implementation.
* @param _admin Address of the proxy administrator.
* @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
* It should include the signature and the parameters of the function to be called, as described in
* https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
* This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
*/
function initialize(address _logic, address _admin, bytes memory _data) public payable {
require(_implementation() == address(0));
InitializableUpgradeabilityProxy.initialize(_logic, _data);
assert(ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
_setAdmin(_admin);
}
/**
* @dev Only fall back when the sender is not the admin.
*/
function _willFallback() internal override(BaseAdminUpgradeabilityProxy, Proxy) {
BaseAdminUpgradeabilityProxy._willFallback();
}
}

View File

@ -0,0 +1,29 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./BaseUpgradeabilityProxy.sol";
/**
* @title InitializableUpgradeabilityProxy
* @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
* implementation and init data.
*/
contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
/**
* @dev Contract initializer.
* @param _logic Address of the initial implementation.
* @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
* It should include the signature and the parameters of the function to be called, as described in
* https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
* This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
*/
function initialize(address _logic, bytes memory _data) public payable {
require(_implementation() == address(0));
assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
_setImplementation(_logic);
if (_data.length > 0) {
(bool success, ) = _logic.delegatecall(_data);
require(success);
}
}
}

View File

@ -0,0 +1,72 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.0;
/**
* @title Proxy
* @dev Implements delegation of calls to other contracts, with proper
* forwarding of return values and bubbling of failures.
* It defines a fallback function that delegates all calls to the address
* returned by the abstract _implementation() internal function.
*/
abstract contract Proxy {
/**
* @dev Fallback function.
* Implemented entirely in `_fallback`.
*/
receive() external payable {
_fallback();
}
/**
* @return The Address of the implementation.
*/
function _implementation() internal virtual view returns (address);
/**
* @dev Delegates execution to an implementation contract.
* This is a low level function that doesn't return to its internal call site.
* It will return to the external caller whatever the implementation returns.
* @param implementation Address to delegate.
*/
function _delegate(address implementation) internal {
//solium-disable-next-line
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @dev Function that is run as the first thing in the fallback function.
* Can be redefined in derived contracts to add functionality.
* Redefinitions must call super._willFallback().
*/
function _willFallback() internal virtual {}
/**
* @dev fallback implementation.
* Extracted to enable manual triggering.
*/
function _fallback() internal {
_willFallback();
_delegate(_implementation());
}
}

View File

@ -0,0 +1,28 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./BaseUpgradeabilityProxy.sol";
/**
* @title UpgradeabilityProxy
* @dev Extends BaseUpgradeabilityProxy with a constructor for initializing
* implementation and init data.
*/
contract UpgradeabilityProxy is BaseUpgradeabilityProxy {
/**
* @dev Contract constructor.
* @param _logic Address of the initial implementation.
* @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
* It should include the signature and the parameters of the function to be called, as described in
* https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
* This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
*/
constructor(address _logic, bytes memory _data) public payable {
assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1));
_setImplementation(_logic);
if (_data.length > 0) {
(bool success, ) = _logic.delegatecall(_data);
require(success);
}
}
}

View File

@ -0,0 +1,71 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.8;
/**
* @title VersionedInitializable
*
* @dev Helper contract to support initializer functions. To use it, replace
* the constructor with a function that has the `initializer` modifier.
* WARNING: Unlike constructors, initializer functions must be manually
* invoked. This applies both to deploying an Initializable contract, as well
* as extending an Initializable contract via inheritance.
* WARNING: When used with inheritance, manual care must be taken to not invoke
* a parent initializer twice, or ensure that all initializers are idempotent,
* because this is not dealt with automatically as with constructors.
*
* @author Aave, inspired by the OpenZeppelin Initializable contract
*/
abstract contract VersionedInitializable {
/**
* @dev Indicates that the contract has been initialized.
*/
uint256 private lastInitializedRevision = 0;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private initializing;
/**
* @dev Modifier to use in the initializer function of a contract.
*/
modifier initializer() {
uint256 revision = getRevision();
require(initializing || isConstructor() || revision > lastInitializedRevision, "Contract instance has already been initialized");
bool isTopLevelCall = !initializing;
if (isTopLevelCall) {
initializing = true;
lastInitializedRevision = revision;
}
_;
if (isTopLevelCall) {
initializing = false;
}
}
/// @dev returns the revision number of the contract.
/// Needs to be defined in the inherited class as a constant.
function getRevision() internal virtual pure returns(uint256);
/// @dev Returns true if and only if the function is running in the constructor
function isConstructor() private view returns (bool) {
// extcodesize checks the size of the code stored in an address, and
// address returns the current address. Since the code is still not
// deployed when running a constructor, any checks on its code size will
// yield zero, making it an effective way to detect if a contract is
// under construction or not.
uint256 cs;
//solium-disable-next-line
assembly {
cs := extcodesize(address())
}
return cs == 0;
}
// Reserved storage space to allow for layout changes in the future.
uint256[50] private ______gap;
}

View File

@ -0,0 +1,110 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/access/Ownable.sol";
import "../interfaces/IPriceOracleGetter.sol";
import "../interfaces/IChainlinkAggregator.sol";
import "../libraries/EthAddressLib.sol";
/// @title ChainlinkProxyPriceProvider
/// @author Aave
/// @notice Proxy smart contract to get the price of an asset from a price source, with Chainlink Aggregator
/// smart contracts as primary option
/// - If the returned price by a Chainlink aggregator is <= 0, the call is forwarded to a fallbackOracle
/// - Owned by the Aave governance system, allowed to add sources for assets, replace them
/// and change the fallbackOracle
contract ChainlinkProxyPriceProvider is IPriceOracleGetter, Ownable {
event AssetSourceUpdated(address indexed asset, address indexed source);
event FallbackOracleUpdated(address indexed fallbackOracle);
mapping(address => IChainlinkAggregator) private assetsSources;
IPriceOracleGetter private fallbackOracle;
/// @notice Constructor
/// @param _assets The addresses of the assets
/// @param _sources The address of the source of each asset
/// @param _fallbackOracle The address of the fallback oracle to use if the data of an
/// aggregator is not consistent
constructor(address[] memory _assets, address[] memory _sources, address _fallbackOracle) public {
internalSetFallbackOracle(_fallbackOracle);
internalSetAssetsSources(_assets, _sources);
}
/// @notice External function called by the Aave governance to set or replace sources of assets
/// @param _assets The addresses of the assets
/// @param _sources The address of the source of each asset
function setAssetSources(address[] calldata _assets, address[] calldata _sources) external onlyOwner {
internalSetAssetsSources(_assets, _sources);
}
/// @notice Sets the fallbackOracle
/// - Callable only by the Aave governance
/// @param _fallbackOracle The address of the fallbackOracle
function setFallbackOracle(address _fallbackOracle) external onlyOwner {
internalSetFallbackOracle(_fallbackOracle);
}
/// @notice Internal function to set the sources for each asset
/// @param _assets The addresses of the assets
/// @param _sources The address of the source of each asset
function internalSetAssetsSources(address[] memory _assets, address[] memory _sources) internal {
require(_assets.length == _sources.length, "INCONSISTENT_PARAMS_LENGTH");
for (uint256 i = 0; i < _assets.length; i++) {
assetsSources[_assets[i]] = IChainlinkAggregator(_sources[i]);
emit AssetSourceUpdated(_assets[i], _sources[i]);
}
}
/// @notice Internal function to set the fallbackOracle
/// @param _fallbackOracle The address of the fallbackOracle
function internalSetFallbackOracle(address _fallbackOracle) internal {
fallbackOracle = IPriceOracleGetter(_fallbackOracle);
emit FallbackOracleUpdated(_fallbackOracle);
}
/// @notice Gets an asset price by address
/// @param _asset The asset address
function getAssetPrice(address _asset) public override view returns(uint256) {
IChainlinkAggregator source = assetsSources[_asset];
if (_asset == EthAddressLib.ethAddress()) {
return 1 ether;
} else {
// If there is no registered source for the asset, call the fallbackOracle
if (address(source) == address(0)) {
return IPriceOracleGetter(fallbackOracle).getAssetPrice(_asset);
} else {
int256 _price = IChainlinkAggregator(source).latestAnswer();
if (_price > 0) {
return uint256(_price);
} else {
return IPriceOracleGetter(fallbackOracle).getAssetPrice(_asset);
}
}
}
}
/// @notice Gets a list of prices from a list of assets addresses
/// @param _assets The list of assets addresses
function getAssetsPrices(address[] calldata _assets) external view returns(uint256[] memory) {
uint256[] memory prices = new uint256[](_assets.length);
for (uint256 i = 0; i < _assets.length; i++) {
prices[i] = getAssetPrice(_assets[i]);
}
return prices;
}
/// @notice Gets the address of the source for an asset address
/// @param _asset The address of the asset
/// @return address The address of the source
function getSourceOfAsset(address _asset) external view returns(address) {
return address(assetsSources[_asset]);
}
/// @notice Gets the address of the fallback oracle
/// @return address The addres of the fallback oracle
function getFallbackOracle() external view returns(address) {
return address(fallbackOracle);
}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
contract IERC20DetailedBytes {
bytes32 public name;
bytes32 public symbol;
uint256 public decimals;
}

View File

@ -0,0 +1,103 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../configuration/LendingPoolAddressesProvider.sol";
import "../lendingpool/LendingPoolCore.sol";
import "../libraries/EthAddressLib.sol";
/**
* @title WalletBalanceProvider contract
* @author Aave, influenced by https://github.com/wbobeirne/eth-balance-checker/blob/master/contracts/BalanceChecker.sol
* @notice Implements a logic of getting multiple tokens balance for one user address
* @dev NOTE: THIS CONTRACT IS NOT USED WITHIN THE AAVE PROTOCOL. It's an accessory contract used to reduce the number of calls
* towards the blockchain from the Aave backend.
**/
contract WalletBalanceProvider {
using Address for address;
LendingPoolAddressesProvider provider;
constructor(LendingPoolAddressesProvider _provider) public {
provider = _provider;
}
/**
@dev Fallback function, don't accept any ETH
**/
receive() external payable {
revert("WalletBalanceProvider does not accept payments");
}
/**
@dev Check the token balance of a wallet in a token contract
Returns the balance of the token for user. Avoids possible errors:
- return 0 on non-contract address
**/
function balanceOf(address _user, address _token) public view returns (uint256) {
// check if token is actually a contract
if (_token.isContract()) {
return IERC20(_token).balanceOf(_user);
} else {
return 0;
}
}
/// @notice Fetches, for a list of _users and _tokens (ETH included with mock address), the balances
/// @param _users The list of users
/// @param _tokens The list of tokens
/// @return And array with the concatenation of, for each user, his/her balances
function batchBalanceOf(address[] memory _users, address[] memory _tokens) public view returns (uint256[] memory) {
uint256[] memory balances = new uint256[](_users.length * _tokens.length);
for (uint256 i = 0; i < _users.length; i++) {
for (uint256 j = 0; j < _tokens.length; j++) {
uint256 _offset = i * _tokens.length;
if (_tokens[j] == EthAddressLib.ethAddress()) {
balances[_offset + j] = _users[i].balance; // ETH balance
} else {
if (!_tokens[j].isContract()) {
revert("INVALID_TOKEN");
} else {
balances[_offset + j] = balanceOf(_users[i], _tokens[j]);
}
}
}
}
return balances;
}
/**
@dev provides balances of user wallet for all reserves available on the pool
*/
function getUserWalletBalances(address _user) public view returns (address[] memory, uint256[] memory) {
LendingPoolCore core = LendingPoolCore(provider.getLendingPoolCore());
address[] memory reserves = core.getReserves();
uint256[] memory balances = new uint256[](reserves.length);
for (uint256 j = 0; j < reserves.length; j++) {
if(!core.getReserveIsActive(reserves[j])){
balances[j] = 0;
continue;
}
if (reserves[j] != EthAddressLib.ethAddress()) {
balances[j] = balanceOf(_user, reserves[j]);
} else {
balances[j] = _user.balance; // ETH balance
}
}
return (reserves, balances);
}
}

View File

@ -0,0 +1,52 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../../flashloan/base/FlashLoanReceiverBase.sol";
import "../tokens/MintableERC20.sol";
contract MockFlashLoanReceiver is FlashLoanReceiverBase {
using SafeMath for uint256;
event ExecutedWithFail(address _reserve, uint256 _amount, uint256 _fee);
event ExecutedWithSuccess(address _reserve, uint256 _amount, uint256 _fee);
bool failExecution = false;
constructor(ILendingPoolAddressesProvider _provider) FlashLoanReceiverBase(_provider) public {
}
function setFailExecutionTransfer(bool _fail) public {
failExecution = _fail;
}
function executeOperation(
address _reserve,
uint256 _amount,
uint256 _fee,
bytes memory _params) public override {
//mint to this contract the specific amount
MintableERC20 token = MintableERC20(_reserve);
//check the contract has the specified balance
require(_amount <= getBalanceInternal(address(this), _reserve), "Invalid balance for the contract");
if(failExecution) {
emit ExecutedWithFail(_reserve, _amount, _fee);
return;
}
//execution does not fail - mint tokens and return them to the _destination
//note: if the reserve is eth, the mock contract must receive at least _fee ETH before calling executeOperation
if(_reserve != EthAddressLib.ethAddress()) {
token.mint(_fee);
}
//returning amount + fee to the destination
transferFundsBackToPoolInternal(_reserve, _amount.add(_fee));
emit ExecutedWithSuccess(_reserve, _amount, _fee);
}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorBAT is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorBUSD is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,17 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
contract MockAggregatorBase {
int256 private _latestAnswer;
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp);
constructor (int256 _initialAnswer) public {
_latestAnswer = _initialAnswer;
emit AnswerUpdated(_initialAnswer, 0, now);
}
function latestAnswer() external view returns (int256) {
return _latestAnswer;
}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorDAI is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorKNC is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorLEND is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorLINK is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorMANA is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorMKR is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorREP is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorSNX is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorSUSD is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorTUSD is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorUNI_DAI_ETH is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorUNI_LEND_ETH is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorUNI_LINK_ETH is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorUNI_MKR_ETH is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorUNI_SETH_ETH is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorUNI_USDC_ETH is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorUSD is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorUSDC is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorUSDT is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorWBTC is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MockAggregatorBase.sol";
contract MockAggregatorZRX is MockAggregatorBase {
constructor (int256 _initialAnswer) public MockAggregatorBase(_initialAnswer) {}
}

View File

@ -0,0 +1,7 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
interface ChainlinkUSDETHOracleI {
event AnswerUpdated(int256 indexed current, uint256 indexed answerId);
}

View File

@ -0,0 +1,20 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
interface GenericOracleI {
// ganache
event AssetPriceUpdated(address _asset, uint256 _price, uint256 timestamp);
event EthPriceUpdated(uint256 _price, uint256 timestamp);
// kovan
event ProphecySubmitted(
address indexed _sybil,
address indexed _asset,
uint96 _sybilProphecy,
uint96 _oracleProphecy
);
function getAssetPrice(address _asset) external view returns(uint256);
function getEthUsdPrice() external view returns(uint256);
}

View File

@ -0,0 +1,12 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
interface IExtendedPriceAggregator {
event AnswerUpdated(int256 indexed current, uint256 indexed roundId, uint256 timestamp);
function getToken() external view returns (address);
function getTokenType() external view returns (uint256);
function getPlatformId() external view returns (uint256);
function getSubTokens() external view returns(address[] memory);
function latestAnswer() external view returns (int256);
}

View File

@ -0,0 +1,28 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "../../interfaces/ILendingRateOracle.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract LendingRateOracle is ILendingRateOracle, Ownable {
mapping(address => uint256) borrowRates;
mapping(address => uint256) liquidityRates;
function getMarketBorrowRate(address _asset) external override view returns(uint256) {
return borrowRates[_asset];
}
function setMarketBorrowRate(address _asset, uint256 _rate) external override onlyOwner {
borrowRates[_asset] = _rate;
}
function getMarketLiquidityRate(address _asset) external view returns(uint256) {
return liquidityRates[_asset];
}
function setMarketLiquidityRate(address _asset, uint256 _rate) external onlyOwner {
liquidityRates[_asset] = _rate;
}
}

View File

@ -0,0 +1,32 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "../../interfaces/IPriceOracle.sol";
contract PriceOracle is IPriceOracle {
mapping(address => uint256) prices;
uint256 ethPriceUsd;
event AssetPriceUpdated(address _asset, uint256 _price, uint256 timestamp);
event EthPriceUpdated(uint256 _price, uint256 timestamp);
function getAssetPrice(address _asset) external override view returns(uint256) {
return prices[_asset];
}
function setAssetPrice(address _asset, uint256 _price) external override {
prices[_asset] = _price;
emit AssetPriceUpdated(_asset, _price, block.timestamp);
}
function getEthUsdPrice() external view returns(uint256) {
return ethPriceUsd;
}
function setEthUsdPrice(uint256 _price) external {
ethPriceUsd = _price;
emit EthPriceUpdated(_price, block.timestamp);
}
}

View File

@ -0,0 +1,23 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "../../tokenization/ERC20.sol";
/**
* @title ERC20Mintable
* @dev ERC20 minting logic
*/
contract MintableERC20 is ERC20 {
constructor(string memory name, string memory symbol, uint8 decimals) ERC20(name, symbol) public {
_setupDecimals(decimals);
}
/**
* @dev Function to mint tokens
* @param value The amount of tokens to mint.
* @return A boolean that indicates if the operation was successful.
*/
function mint(uint256 value) public returns (bool) {
_mint(msg.sender, value);
return true;
}
}

View File

@ -0,0 +1,9 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockBAT is MintableERC20("BAT", "Basic Attention Token", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockBUSD is MintableERC20("BUSD", "Binance USD", 18) {}

View File

@ -0,0 +1,9 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockDAI is MintableERC20("DAI", "DAI", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockKNC is MintableERC20("KNC", "Kyber Network", 18) {}

View File

@ -0,0 +1,9 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockLEND is MintableERC20("LEND", "LEND", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockLINK is MintableERC20("LINK", "ChainLink", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockMANA is MintableERC20("MANA", "Decentraland", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockMKR is MintableERC20("MKR", "Maker", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockREP is MintableERC20("REP", "Augur", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockSNX is MintableERC20("SNX", "SNX", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockSUSD is MintableERC20("SUSD", "Synthetix USD", 6) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockTUSD is MintableERC20("TUSD", "TrueUSD", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockUNI_DAI_ETH is MintableERC20("UNI_DAI_ETH", "UNI_DAI_ETH", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockUNI_LEND_ETH is MintableERC20("UNI_LEND_ETH", "UNI_LEND_ETH", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockUNI_LINK_ETH is MintableERC20("UNI_LINK_ETH", "UNI_LINK_ETH", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockUNI_MKR_ETH is MintableERC20("UNI_MKR_ETH", "UNI_MKR_ETH", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockUNI_SETH_ETH is MintableERC20("UNI_SETH_ETH", "UNI_SETH_ETH", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockUNI_USDC_ETH is MintableERC20("UNI_USDC_ETH", "UNI_USDC_ETH", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockUSD is MintableERC20("USD", "USD", 18) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockUSDC is MintableERC20("USDC", "USD Coin", 6) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockUSDT is MintableERC20("USDT", "USDT Coin", 6) {}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: agpl-3.0
pragma solidity ^0.6.8;
import "./MintableERC20.sol";
contract MockWBTC is MintableERC20("WBTC", "WBTC Coin", 18) {}

Some files were not shown because too many files have changed in this diff Show More