ETH Price: $1,947.74 (+1.06%)

Transaction Decoder

Block:
12944432 at Aug-02-2021 06:41:53 AM +UTC
Transaction Fee:
0.008047761 ETH $15.67
Gas Used:
277,509 Gas / 29 Gwei

Emitted Events:

332 InitializableImmutableAdminUpgradeabilityProxy.0xbb123b5c06d5408bbea3c4fef481578175cfb432e3b482c6186f02ed9086585b( 0xbb123b5c06d5408bbea3c4fef481578175cfb432e3b482c6186f02ed9086585b, 0x000000000000000000000000464c71f6c2f760dda6093dcb91c24c39e5d6e18c, 0x0000000000000000000000009ff58f4ffb29fa2266ab25e75e2a8b3503311656, 00000000000000000000000000000000000000000fa4613d6337c252eb7f4ec6 )
333 InitializableImmutableAdminUpgradeabilityProxy.0x2468f9268c60ad90e2d49edb0032c8a001e733ae888b3ab8e982edf535be1a76( 0x2468f9268c60ad90e2d49edb0032c8a001e733ae888b3ab8e982edf535be1a76, 0x000000000000000000000000464c71f6c2f760dda6093dcb91c24c39e5d6e18c, 000000000000000000000000000000000000000000000000000056ca92d1aa1b )
334 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000464c71f6c2f760dda6093dcb91c24c39e5d6e18c, 0000000000000000000000000000000000000000000000000000000000004921 )
335 InitializableImmutableAdminUpgradeabilityProxy.0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f( 0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f, 0x000000000000000000000000464c71f6c2f760dda6093dcb91c24c39e5d6e18c, 0000000000000000000000000000000000000000000000000000000000004921, 0000000000000000000000000000000000000000033c4fd6ed2096a8905f43e8 )
336 InitializableImmutableAdminUpgradeabilityProxy.0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a( 0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a, 0x0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599, 000000000000000000000000000000000000000000004db349a14550545c3af8, 0000000000000000000000000000000000000000002084098a18761bf5ce4c63, 000000000000000000000000000000000000000000062908397d2835aca509e9, 0000000000000000000000000000000000000000033c4fd6ed2096a8905f43e8, 0000000000000000000000000000000000000000034071abcabeca0aaf70e70c )
337 InitializableImmutableAdminUpgradeabilityProxy.0x44c58d81365b66dd4b1a7f36c25aa97b8c71c361ee4937adc1a00000227db5dd( 0x44c58d81365b66dd4b1a7f36c25aa97b8c71c361ee4937adc1a00000227db5dd, 0x0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599, 0x000000000000000000000000d2964675756a299360e0d87cd218b62fd62c36b3 )
338 WBTC.Transfer( from=InitializableImmutableAdminUpgradeabilityProxy, to=[Receiver] 0xd2964675756a299360e0d87cd218b62fd62c36b3, value=2154552396 )
339 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000d2964675756a299360e0d87cd218b62fd62c36b3, 0x0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000000000806bdc4c )
340 InitializableImmutableAdminUpgradeabilityProxy.0x5d624aa9c148153ab3446c1b154f660ee7701e549fe9b62dab7171b1c80e6fa2( 0x5d624aa9c148153ab3446c1b154f660ee7701e549fe9b62dab7171b1c80e6fa2, 0x000000000000000000000000d2964675756a299360e0d87cd218b62fd62c36b3, 0x000000000000000000000000d2964675756a299360e0d87cd218b62fd62c36b3, 00000000000000000000000000000000000000000000000000000000806bdc4c, 0000000000000000000000000000000000000000033c4fd6ed2096a8905f43e8 )
341 InitializableImmutableAdminUpgradeabilityProxy.0x3115d1449a7b732c986cba18244e897a450f61e1bb8d589cd2e69e6c8924f9f7( 0x3115d1449a7b732c986cba18244e897a450f61e1bb8d589cd2e69e6c8924f9f7, 0x0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599, 0x000000000000000000000000d2964675756a299360e0d87cd218b62fd62c36b3, 0x000000000000000000000000d2964675756a299360e0d87cd218b62fd62c36b3, 00000000000000000000000000000000000000000000000000000000806bdc4c )
342 0xd2964675756a299360e0d87cd218b62fd62c36b3.0xf6d9b29bbf2ae698de33670961ec53f895af65801d2cdaced431cc6129865347( 0xf6d9b29bbf2ae698de33670961ec53f895af65801d2cdaced431cc6129865347, 0x00000000000000000000000003d70891b8994feb6cca7022b25c32be92ee3725, 0x000000000000000000000000fac44bfdc053a9615c6b0527f44416914628a0c4, 0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000000000000000a0, 0000000000000000000000000000000000000000000000000000000000000120, 0000000000000000000000000000000000000000000000000000000000000160, 0000000000000000000000000000000000000000000000000000000000000200, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000009, 414156452d56322d410000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000001, 00000000000000000000000068b27a84101ac5120bbab7ce8d6b096c961df52c, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000020, 000000000000000000000000000000000000000000000000000000000000002c, 4c6f67576974686472617728616464726573732c75696e743235362c75696e74, 3235362c75696e74323536290000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000001, 0000000000000000000000000000000000000000000000000000000000000020, 0000000000000000000000000000000000000000000000000000000000000080, 0000000000000000000000002260fac5e5542a773aa44fbcfedf7c193bc2c599, 00000000000000000000000000000000000000000000000000000000806bdc4c, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x2260FAC5...93bc2C599
(Spark Pool)
20.190596897604409035 Eth20.198644658604409035 Eth0.008047761
0x7d2768dE...A69DDc7A9
(Aave: Lending Pool V2)
0x9ff58f4f...503311656
0xd784927F...F3495f6b5
(Aave: Default Incentives Controller V2)
0xfaC44BFd...14628A0C4
6.000164368415870758 Eth
Nonce: 1473
5.992116607415870758 Eth
Nonce: 1474
0.008047761

Execution Trace

0xd2964675756a299360e0d87cd218b62fd62c36b3.9304c934( )
  • InstaAccountV2.9304c934( )
    • InstaImplementations.getImplementation( _sig=System.Byte[] ) => ( 0x8a3462A50e1a9Fe8c9e7d9023CAcbD9a98D90021 )
    • InstaImplementationM1.cast( _targetNames=[AAVE-V2-A], _datas=[RTLXdgAAAAAAAAAAAAAAACJg+sXlVCp3OqRPvP7ffBk7wsWZ//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA], _origin=0x03d70891b8994feB6ccA7022B25c32be92ee3725 ) => ( 0000000000000000000000000000000000000000000000000000000000000000 )
      • InstaConnectorsV2.isConnectors( _connectorNames=[AAVE-V2-A] ) => ( isOk=True, _connectors=[0x68b27A84101ac5120bBAb7Ce8d6b096C961df52C] )
      • ConnectV2AaveV2.withdraw( token=0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, amt=115792089237316195423570985008687907853269984665640564039457584007913129639935, getId=0, setId=0 ) => ( _eventName=LogWithdraw(address,uint256,uint256,uint256), _eventParam=0x0000000000000000000000002260FAC5E5542A773AA44FBCFEDF7C193BC2C59900000000000000000000000000000000000000000000000000000000806BDC4C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 )
        • LendingPoolAddressesProvider.STATICCALL( )
        • WBTC.balanceOf( _owner=0xd2964675756a299360E0D87cd218b62fd62c36B3 ) => ( 0 )
        • InitializableImmutableAdminUpgradeabilityProxy.69328dec( )
          • LendingPool.withdraw( asset=0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, amount=115792089237316195423570985008687907853269984665640564039457584007913129639935, to=0xd2964675756a299360E0D87cd218b62fd62c36B3 ) => ( 2154552396 )
            • InitializableImmutableAdminUpgradeabilityProxy.70a08231( )
            • LendingPoolAddressesProvider.STATICCALL( )
            • ValidationLogic.d09db04a( )
            • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
            • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
            • InitializableImmutableAdminUpgradeabilityProxy.7df5bd3b( )
            • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
            • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
            • WBTC.balanceOf( _owner=0x9ff58f4fFB29fA2266Ab25e75e2A8b3503311656 ) => ( 3872578970298 )
            • DefaultReserveInterestRateStrategy.calculateInterestRates( reserve=0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, availableLiquidity=3870424417902, totalStableDebt=844491147, totalVariableDebt=248436864897, averageStableBorrowRate=46610543585138115330876839, reserveFactor=2000 ) => ( 366929491968204025379576, 39309154574936829280013411, 7447323659949463424010729 )
            • InitializableImmutableAdminUpgradeabilityProxy.d7020d0a( )
            • WBTC.balanceOf( _owner=0xd2964675756a299360E0D87cd218b62fd62c36B3 ) => ( 2154552396 )
              File 1 of 15: InitializableImmutableAdminUpgradeabilityProxy
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                  // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                  // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                  // for accounts without code, i.e. `keccak256('')`
                  bytes32 codehash;
                  bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                  // solhint-disable-next-line no-inline-assembly
                  assembly {
                    codehash := extcodehash(account)
                  }
                  return (codehash != accountHash && codehash != 0x0);
                }
              
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                  require(address(this).balance >= amount, 'Address: insufficient balance');
              
                  // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                  (bool success, ) = recipient.call{value: amount}('');
                  require(success, 'Address: unable to send value, recipient may have reverted');
                }
              }
              
              /**
               * @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`.
                 */
                fallback() external payable {
                  _fallback();
                }
              
                /**
                 * @return The Address of the implementation.
                 */
                function _implementation() internal view virtual 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());
                }
              }
              
              /**
               * @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 view override 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)
                  }
                }
              }
              
              /**
               * @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);
                  }
                }
              }
              
              /**
               * @title BaseImmutableAdminUpgradeabilityProxy
               * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
               * @dev This contract combines an upgradeability proxy with an authorization
               * mechanism for administrative tasks. The admin role is stored in an immutable, which
               * helps saving transactions costs
               * 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 BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
                address immutable ADMIN;
              
                constructor(address admin) public {
                  ADMIN = admin;
                }
              
                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 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);
                }
              
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal virtual override {
                  require(msg.sender != ADMIN, 'Cannot call fallback function from the proxy admin');
                  super._willFallback();
                }
              }
              
              /**
               * @title InitializableAdminUpgradeabilityProxy
               * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
               */
              contract InitializableImmutableAdminUpgradeabilityProxy is
                BaseImmutableAdminUpgradeabilityProxy,
                InitializableUpgradeabilityProxy
              {
                constructor(address admin) public BaseImmutableAdminUpgradeabilityProxy(admin) {}
              
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                  BaseImmutableAdminUpgradeabilityProxy._willFallback();
                }
              }

              File 2 of 15: InitializableImmutableAdminUpgradeabilityProxy
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import './BaseImmutableAdminUpgradeabilityProxy.sol';
              import '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
              /**
               * @title InitializableAdminUpgradeabilityProxy
               * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
               */
              contract InitializableImmutableAdminUpgradeabilityProxy is
                BaseImmutableAdminUpgradeabilityProxy,
                InitializableUpgradeabilityProxy
              {
                constructor(address admin) public BaseImmutableAdminUpgradeabilityProxy(admin) {}
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                  BaseImmutableAdminUpgradeabilityProxy._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
              /**
               * @title BaseImmutableAdminUpgradeabilityProxy
               * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
               * @dev This contract combines an upgradeability proxy with an authorization
               * mechanism for administrative tasks. The admin role is stored in an immutable, which
               * helps saving transactions costs
               * 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 BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
                address immutable ADMIN;
                constructor(address admin) public {
                  ADMIN = admin;
                }
                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 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);
                }
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal virtual override {
                  require(msg.sender != ADMIN, 'Cannot call fallback function from the proxy admin');
                  super._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import './Proxy.sol';
              import '../contracts/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)
                  }
                }
              }
              // 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`.
                 */
                fallback() 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());
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                  // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                  // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                  // for accounts without code, i.e. `keccak256('')`
                  bytes32 codehash;
                  bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                  // solhint-disable-next-line no-inline-assembly
                  assembly {
                    codehash := extcodehash(account)
                  }
                  return (codehash != accountHash && codehash != 0x0);
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                  require(address(this).balance >= amount, 'Address: insufficient balance');
                  // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                  (bool success, ) = recipient.call{value: amount}('');
                  require(success, 'Address: unable to send value, recipient may have reverted');
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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);
                  }
                }
              }
              

              File 3 of 15: InitializableImmutableAdminUpgradeabilityProxy
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import './BaseImmutableAdminUpgradeabilityProxy.sol';
              import '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
              /**
               * @title InitializableAdminUpgradeabilityProxy
               * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
               */
              contract InitializableImmutableAdminUpgradeabilityProxy is
                BaseImmutableAdminUpgradeabilityProxy,
                InitializableUpgradeabilityProxy
              {
                constructor(address admin) public BaseImmutableAdminUpgradeabilityProxy(admin) {}
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                  BaseImmutableAdminUpgradeabilityProxy._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
              /**
               * @title BaseImmutableAdminUpgradeabilityProxy
               * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
               * @dev This contract combines an upgradeability proxy with an authorization
               * mechanism for administrative tasks. The admin role is stored in an immutable, which
               * helps saving transactions costs
               * 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 BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
                address immutable ADMIN;
                constructor(address admin) public {
                  ADMIN = admin;
                }
                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 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);
                }
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal virtual override {
                  require(msg.sender != ADMIN, 'Cannot call fallback function from the proxy admin');
                  super._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import './Proxy.sol';
              import '../contracts/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)
                  }
                }
              }
              // 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`.
                 */
                fallback() 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());
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                  // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                  // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                  // for accounts without code, i.e. `keccak256('')`
                  bytes32 codehash;
                  bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                  // solhint-disable-next-line no-inline-assembly
                  assembly {
                    codehash := extcodehash(account)
                  }
                  return (codehash != accountHash && codehash != 0x0);
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                  require(address(this).balance >= amount, 'Address: insufficient balance');
                  // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                  (bool success, ) = recipient.call{value: amount}('');
                  require(success, 'Address: unable to send value, recipient may have reverted');
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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);
                  }
                }
              }
              

              File 4 of 15: WBTC
              pragma solidity 0.4.24;
              
              // File: openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol
              
              /**
               * @title ERC20Basic
               * @dev Simpler version of ERC20 interface
               * See https://github.com/ethereum/EIPs/issues/179
               */
              contract ERC20Basic {
                function totalSupply() public view returns (uint256);
                function balanceOf(address _who) public view returns (uint256);
                function transfer(address _to, uint256 _value) public returns (bool);
                event Transfer(address indexed from, address indexed to, uint256 value);
              }
              
              // File: openzeppelin-solidity/contracts/math/SafeMath.sol
              
              /**
               * @title SafeMath
               * @dev Math operations with safety checks that throw on error
               */
              library SafeMath {
              
                /**
                * @dev Multiplies two numbers, throws on overflow.
                */
                function mul(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
                  // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
                  if (_a == 0) {
                    return 0;
                  }
              
                  c = _a * _b;
                  assert(c / _a == _b);
                  return c;
                }
              
                /**
                * @dev Integer division of two numbers, truncating the quotient.
                */
                function div(uint256 _a, uint256 _b) internal pure returns (uint256) {
                  // assert(_b > 0); // Solidity automatically throws when dividing by 0
                  // uint256 c = _a / _b;
                  // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold
                  return _a / _b;
                }
              
                /**
                * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
                */
                function sub(uint256 _a, uint256 _b) internal pure returns (uint256) {
                  assert(_b <= _a);
                  return _a - _b;
                }
              
                /**
                * @dev Adds two numbers, throws on overflow.
                */
                function add(uint256 _a, uint256 _b) internal pure returns (uint256 c) {
                  c = _a + _b;
                  assert(c >= _a);
                  return c;
                }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/BasicToken.sol
              
              /**
               * @title Basic token
               * @dev Basic version of StandardToken, with no allowances.
               */
              contract BasicToken is ERC20Basic {
                using SafeMath for uint256;
              
                mapping(address => uint256) internal balances;
              
                uint256 internal totalSupply_;
              
                /**
                * @dev Total number of tokens in existence
                */
                function totalSupply() public view returns (uint256) {
                  return totalSupply_;
                }
              
                /**
                * @dev Transfer token for a specified address
                * @param _to The address to transfer to.
                * @param _value The amount to be transferred.
                */
                function transfer(address _to, uint256 _value) public returns (bool) {
                  require(_value <= balances[msg.sender]);
                  require(_to != address(0));
              
                  balances[msg.sender] = balances[msg.sender].sub(_value);
                  balances[_to] = balances[_to].add(_value);
                  emit Transfer(msg.sender, _to, _value);
                  return true;
                }
              
                /**
                * @dev Gets the balance of the specified address.
                * @param _owner The address to query the the balance of.
                * @return An uint256 representing the amount owned by the passed address.
                */
                function balanceOf(address _owner) public view returns (uint256) {
                  return balances[_owner];
                }
              
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
              
              /**
               * @title ERC20 interface
               * @dev see https://github.com/ethereum/EIPs/issues/20
               */
              contract ERC20 is ERC20Basic {
                function allowance(address _owner, address _spender)
                  public view returns (uint256);
              
                function transferFrom(address _from, address _to, uint256 _value)
                  public returns (bool);
              
                function approve(address _spender, uint256 _value) public returns (bool);
                event Approval(
                  address indexed owner,
                  address indexed spender,
                  uint256 value
                );
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol
              
              /**
               * @title Standard ERC20 token
               *
               * @dev Implementation of the basic standard token.
               * https://github.com/ethereum/EIPs/issues/20
               * Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
               */
              contract StandardToken is ERC20, BasicToken {
              
                mapping (address => mapping (address => uint256)) internal allowed;
              
              
                /**
                 * @dev Transfer tokens from one address to another
                 * @param _from address The address which you want to send tokens from
                 * @param _to address The address which you want to transfer to
                 * @param _value uint256 the amount of tokens to be transferred
                 */
                function transferFrom(
                  address _from,
                  address _to,
                  uint256 _value
                )
                  public
                  returns (bool)
                {
                  require(_value <= balances[_from]);
                  require(_value <= allowed[_from][msg.sender]);
                  require(_to != address(0));
              
                  balances[_from] = balances[_from].sub(_value);
                  balances[_to] = balances[_to].add(_value);
                  allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
                  emit Transfer(_from, _to, _value);
                  return true;
                }
              
                /**
                 * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
                 * Beware that changing an allowance with this method brings the risk that someone may use both the old
                 * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
                 * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
                 * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                 * @param _spender The address which will spend the funds.
                 * @param _value The amount of tokens to be spent.
                 */
                function approve(address _spender, uint256 _value) public returns (bool) {
                  allowed[msg.sender][_spender] = _value;
                  emit Approval(msg.sender, _spender, _value);
                  return true;
                }
              
                /**
                 * @dev Function to check the amount of tokens that an owner allowed to a spender.
                 * @param _owner address The address which owns the funds.
                 * @param _spender address The address which will spend the funds.
                 * @return A uint256 specifying the amount of tokens still available for the spender.
                 */
                function allowance(
                  address _owner,
                  address _spender
                 )
                  public
                  view
                  returns (uint256)
                {
                  return allowed[_owner][_spender];
                }
              
                /**
                 * @dev Increase the amount of tokens that an owner allowed to a spender.
                 * approve should be called when allowed[_spender] == 0. To increment
                 * allowed value is better to use this function to avoid 2 calls (and wait until
                 * the first transaction is mined)
                 * From MonolithDAO Token.sol
                 * @param _spender The address which will spend the funds.
                 * @param _addedValue The amount of tokens to increase the allowance by.
                 */
                function increaseApproval(
                  address _spender,
                  uint256 _addedValue
                )
                  public
                  returns (bool)
                {
                  allowed[msg.sender][_spender] = (
                    allowed[msg.sender][_spender].add(_addedValue));
                  emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
                  return true;
                }
              
                /**
                 * @dev Decrease the amount of tokens that an owner allowed to a spender.
                 * approve should be called when allowed[_spender] == 0. To decrement
                 * allowed value is better to use this function to avoid 2 calls (and wait until
                 * the first transaction is mined)
                 * From MonolithDAO Token.sol
                 * @param _spender The address which will spend the funds.
                 * @param _subtractedValue The amount of tokens to decrease the allowance by.
                 */
                function decreaseApproval(
                  address _spender,
                  uint256 _subtractedValue
                )
                  public
                  returns (bool)
                {
                  uint256 oldValue = allowed[msg.sender][_spender];
                  if (_subtractedValue >= oldValue) {
                    allowed[msg.sender][_spender] = 0;
                  } else {
                    allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
                  }
                  emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
                  return true;
                }
              
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/DetailedERC20.sol
              
              /**
               * @title DetailedERC20 token
               * @dev The decimals are only for visualization purposes.
               * All the operations are done using the smallest and indivisible token unit,
               * just as on Ethereum all the operations are done in wei.
               */
              contract DetailedERC20 is ERC20 {
                string public name;
                string public symbol;
                uint8 public decimals;
              
                constructor(string _name, string _symbol, uint8 _decimals) public {
                  name = _name;
                  symbol = _symbol;
                  decimals = _decimals;
                }
              }
              
              // File: openzeppelin-solidity/contracts/ownership/Ownable.sol
              
              /**
               * @title Ownable
               * @dev The Ownable contract has an owner address, and provides basic authorization control
               * functions, this simplifies the implementation of "user permissions".
               */
              contract Ownable {
                address public owner;
              
              
                event OwnershipRenounced(address indexed previousOwner);
                event OwnershipTransferred(
                  address indexed previousOwner,
                  address indexed newOwner
                );
              
              
                /**
                 * @dev The Ownable constructor sets the original `owner` of the contract to the sender
                 * account.
                 */
                constructor() public {
                  owner = msg.sender;
                }
              
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                  require(msg.sender == owner);
                  _;
                }
              
                /**
                 * @dev Allows the current owner to relinquish control of the contract.
                 * @notice Renouncing to ownership will leave the contract without an owner.
                 * It will not be possible to call the functions with the `onlyOwner`
                 * modifier anymore.
                 */
                function renounceOwnership() public onlyOwner {
                  emit OwnershipRenounced(owner);
                  owner = address(0);
                }
              
                /**
                 * @dev Allows the current owner to transfer control of the contract to a newOwner.
                 * @param _newOwner The address to transfer ownership to.
                 */
                function transferOwnership(address _newOwner) public onlyOwner {
                  _transferOwnership(_newOwner);
                }
              
                /**
                 * @dev Transfers control of the contract to a newOwner.
                 * @param _newOwner The address to transfer ownership to.
                 */
                function _transferOwnership(address _newOwner) internal {
                  require(_newOwner != address(0));
                  emit OwnershipTransferred(owner, _newOwner);
                  owner = _newOwner;
                }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/MintableToken.sol
              
              /**
               * @title Mintable token
               * @dev Simple ERC20 Token example, with mintable token creation
               * Based on code by TokenMarketNet: https://github.com/TokenMarketNet/ico/blob/master/contracts/MintableToken.sol
               */
              contract MintableToken is StandardToken, Ownable {
                event Mint(address indexed to, uint256 amount);
                event MintFinished();
              
                bool public mintingFinished = false;
              
              
                modifier canMint() {
                  require(!mintingFinished);
                  _;
                }
              
                modifier hasMintPermission() {
                  require(msg.sender == owner);
                  _;
                }
              
                /**
                 * @dev Function to mint tokens
                 * @param _to The address that will receive the minted tokens.
                 * @param _amount The amount of tokens to mint.
                 * @return A boolean that indicates if the operation was successful.
                 */
                function mint(
                  address _to,
                  uint256 _amount
                )
                  public
                  hasMintPermission
                  canMint
                  returns (bool)
                {
                  totalSupply_ = totalSupply_.add(_amount);
                  balances[_to] = balances[_to].add(_amount);
                  emit Mint(_to, _amount);
                  emit Transfer(address(0), _to, _amount);
                  return true;
                }
              
                /**
                 * @dev Function to stop minting new tokens.
                 * @return True if the operation was successful.
                 */
                function finishMinting() public onlyOwner canMint returns (bool) {
                  mintingFinished = true;
                  emit MintFinished();
                  return true;
                }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/BurnableToken.sol
              
              /**
               * @title Burnable Token
               * @dev Token that can be irreversibly burned (destroyed).
               */
              contract BurnableToken is BasicToken {
              
                event Burn(address indexed burner, uint256 value);
              
                /**
                 * @dev Burns a specific amount of tokens.
                 * @param _value The amount of token to be burned.
                 */
                function burn(uint256 _value) public {
                  _burn(msg.sender, _value);
                }
              
                function _burn(address _who, uint256 _value) internal {
                  require(_value <= balances[_who]);
                  // no need to require value <= totalSupply, since that would imply the
                  // sender's balance is greater than the totalSupply, which *should* be an assertion failure
              
                  balances[_who] = balances[_who].sub(_value);
                  totalSupply_ = totalSupply_.sub(_value);
                  emit Burn(_who, _value);
                  emit Transfer(_who, address(0), _value);
                }
              }
              
              // File: openzeppelin-solidity/contracts/lifecycle/Pausable.sol
              
              /**
               * @title Pausable
               * @dev Base contract which allows children to implement an emergency stop mechanism.
               */
              contract Pausable is Ownable {
                event Pause();
                event Unpause();
              
                bool public paused = false;
              
              
                /**
                 * @dev Modifier to make a function callable only when the contract is not paused.
                 */
                modifier whenNotPaused() {
                  require(!paused);
                  _;
                }
              
                /**
                 * @dev Modifier to make a function callable only when the contract is paused.
                 */
                modifier whenPaused() {
                  require(paused);
                  _;
                }
              
                /**
                 * @dev called by the owner to pause, triggers stopped state
                 */
                function pause() public onlyOwner whenNotPaused {
                  paused = true;
                  emit Pause();
                }
              
                /**
                 * @dev called by the owner to unpause, returns to normal state
                 */
                function unpause() public onlyOwner whenPaused {
                  paused = false;
                  emit Unpause();
                }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/PausableToken.sol
              
              /**
               * @title Pausable token
               * @dev StandardToken modified with pausable transfers.
               **/
              contract PausableToken is StandardToken, Pausable {
              
                function transfer(
                  address _to,
                  uint256 _value
                )
                  public
                  whenNotPaused
                  returns (bool)
                {
                  return super.transfer(_to, _value);
                }
              
                function transferFrom(
                  address _from,
                  address _to,
                  uint256 _value
                )
                  public
                  whenNotPaused
                  returns (bool)
                {
                  return super.transferFrom(_from, _to, _value);
                }
              
                function approve(
                  address _spender,
                  uint256 _value
                )
                  public
                  whenNotPaused
                  returns (bool)
                {
                  return super.approve(_spender, _value);
                }
              
                function increaseApproval(
                  address _spender,
                  uint _addedValue
                )
                  public
                  whenNotPaused
                  returns (bool success)
                {
                  return super.increaseApproval(_spender, _addedValue);
                }
              
                function decreaseApproval(
                  address _spender,
                  uint _subtractedValue
                )
                  public
                  whenNotPaused
                  returns (bool success)
                {
                  return super.decreaseApproval(_spender, _subtractedValue);
                }
              }
              
              // File: openzeppelin-solidity/contracts/ownership/Claimable.sol
              
              /**
               * @title Claimable
               * @dev Extension for the Ownable contract, where the ownership needs to be claimed.
               * This allows the new owner to accept the transfer.
               */
              contract Claimable is Ownable {
                address public pendingOwner;
              
                /**
                 * @dev Modifier throws if called by any account other than the pendingOwner.
                 */
                modifier onlyPendingOwner() {
                  require(msg.sender == pendingOwner);
                  _;
                }
              
                /**
                 * @dev Allows the current owner to set the pendingOwner address.
                 * @param newOwner The address to transfer ownership to.
                 */
                function transferOwnership(address newOwner) public onlyOwner {
                  pendingOwner = newOwner;
                }
              
                /**
                 * @dev Allows the pendingOwner address to finalize the transfer.
                 */
                function claimOwnership() public onlyPendingOwner {
                  emit OwnershipTransferred(owner, pendingOwner);
                  owner = pendingOwner;
                  pendingOwner = address(0);
                }
              }
              
              // File: openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol
              
              /**
               * @title SafeERC20
               * @dev Wrappers around ERC20 operations that throw on failure.
               * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
               * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
               */
              library SafeERC20 {
                function safeTransfer(
                  ERC20Basic _token,
                  address _to,
                  uint256 _value
                )
                  internal
                {
                  require(_token.transfer(_to, _value));
                }
              
                function safeTransferFrom(
                  ERC20 _token,
                  address _from,
                  address _to,
                  uint256 _value
                )
                  internal
                {
                  require(_token.transferFrom(_from, _to, _value));
                }
              
                function safeApprove(
                  ERC20 _token,
                  address _spender,
                  uint256 _value
                )
                  internal
                {
                  require(_token.approve(_spender, _value));
                }
              }
              
              // File: openzeppelin-solidity/contracts/ownership/CanReclaimToken.sol
              
              /**
               * @title Contracts that should be able to recover tokens
               * @author SylTi
               * @dev This allow a contract to recover any ERC20 token received in a contract by transferring the balance to the contract owner.
               * This will prevent any accidental loss of tokens.
               */
              contract CanReclaimToken is Ownable {
                using SafeERC20 for ERC20Basic;
              
                /**
                 * @dev Reclaim all ERC20Basic compatible tokens
                 * @param _token ERC20Basic The address of the token contract
                 */
                function reclaimToken(ERC20Basic _token) external onlyOwner {
                  uint256 balance = _token.balanceOf(this);
                  _token.safeTransfer(owner, balance);
                }
              
              }
              
              // File: contracts/utils/OwnableContract.sol
              
              // empty block is used as this contract just inherits others.
              contract OwnableContract is CanReclaimToken, Claimable { } /* solhint-disable-line no-empty-blocks */
              
              // File: contracts/token/WBTC.sol
              
              contract WBTC is StandardToken, DetailedERC20("Wrapped BTC", "WBTC", 8),
                  MintableToken, BurnableToken, PausableToken, OwnableContract {
              
                  function burn(uint value) public onlyOwner {
                      super.burn(value);
                  }
              
                  function finishMinting() public onlyOwner returns (bool) {
                      return false;
                  }
              
                  function renounceOwnership() public onlyOwner {
                      revert("renouncing ownership is blocked");
                  }
              }

              File 5 of 15: InstaAccountV2
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.7.0;
              interface AccountImplementations {
                  function getImplementation(bytes4 _sig) external view returns (address);
              }
              /**
               * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
               * instruction `delegatecall`.
               */
              contract InstaAccountV2 {
                  AccountImplementations public immutable implementations;
                  constructor(address _implementations) {
                      implementations = AccountImplementations(_implementations);
                  }
                  /**
                   * @dev Delegates the current call to `implementation`.
                   * 
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _delegate(address implementation) internal {
                      // solhint-disable-next-line no-inline-assembly
                      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 Delegates the current call to the address returned by Implementations registry.
                   * 
                   * This function does not return to its internall call site, it will return directly to the external caller.
                   */
                  function _fallback(bytes4 _sig) internal {
                      address _implementation = implementations.getImplementation(_sig);
                      require(_implementation != address(0), "InstaAccountV2: Not able to find _implementation");
                      _delegate(_implementation);
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by Implementations registry.
                   */
                  fallback () external payable {
                      _fallback(msg.sig);
                  }
                  /**
                   * @dev Fallback function that delegates calls to the address returned by Implementations registry.
                   */
                  receive () external payable {
                      if (msg.sig != 0x00000000) {
                          _fallback(msg.sig);
                      }
                  }
              }
              

              File 6 of 15: InstaImplementations
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.7.0;
              interface IndexInterface {
                  function master() external view returns (address);
              }
              contract Setup {
                  address public defaultImplementation;
                  mapping (bytes4 => address) internal sigImplementations;
                  mapping (address => bytes4[]) internal implementationSigs;
              }
              contract Implementations is Setup {
                  event LogSetDefaultImplementation(address indexed oldImplementation, address indexed newImplementation);
                  event LogAddImplementation(address indexed implementation, bytes4[] sigs);
                  event LogRemoveImplementation(address indexed implementation, bytes4[] sigs);
                  IndexInterface constant public instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723);
                  modifier isMaster() {
                      require(msg.sender == instaIndex.master(), "Implementations: not-master");
                      _;
                  }
                  function setDefaultImplementation(address _defaultImplementation) external isMaster {
                      require(_defaultImplementation != address(0), "Implementations: _defaultImplementation address not valid");
                      require(_defaultImplementation != defaultImplementation, "Implementations: _defaultImplementation cannot be same");
                      emit LogSetDefaultImplementation(defaultImplementation, _defaultImplementation);
                      defaultImplementation = _defaultImplementation;
                  }
                  function addImplementation(address _implementation, bytes4[] calldata _sigs) external isMaster {
                      require(_implementation != address(0), "Implementations: _implementation not valid.");
                      require(implementationSigs[_implementation].length == 0, "Implementations: _implementation already added.");
                      for (uint i = 0; i < _sigs.length; i++) {
                          bytes4 _sig = _sigs[i];
                          require(sigImplementations[_sig] == address(0), "Implementations: _sig already added");
                          sigImplementations[_sig] = _implementation;
                      }
                      implementationSigs[_implementation] = _sigs;
                      emit LogAddImplementation(_implementation, _sigs);
                  }
                  function removeImplementation(address _implementation) external isMaster {
                      require(_implementation != address(0), "Implementations: _implementation not valid.");
                      require(implementationSigs[_implementation].length != 0, "Implementations: _implementation not found.");
                      bytes4[] memory sigs = implementationSigs[_implementation];
                      for (uint i = 0; i < sigs.length; i++) {
                          bytes4 sig = sigs[i];
                          delete sigImplementations[sig];
                      }
                      delete implementationSigs[_implementation];
                      emit LogRemoveImplementation(_implementation, sigs);
                  }
              }
              contract InstaImplementations is Implementations {
                  function getImplementation(bytes4 _sig) external view returns (address) {
                      address _implementation = sigImplementations[_sig];
                      return _implementation == address(0) ? defaultImplementation : _implementation;
                  }
                  function getImplementationSigs(address _impl) external view returns (bytes4[] memory) {
                      return implementationSigs[_impl];
                  }
                  function getSigImplementation(bytes4 _sig) external view returns (address) {
                      return sigImplementations[_sig];
                  }
              }
              

              File 7 of 15: InstaImplementationM1
              pragma solidity ^0.7.0;
              pragma experimental ABIEncoderV2;
              import { Variables } from "./variables.sol";
              /**
               * @title InstaAccountV2.
               * @dev DeFi Smart Account Wallet.
               */
              interface ConnectorsInterface {
                  function isConnectors(string[] calldata connectorNames) external view returns (bool, address[] memory);
              }
              contract Constants is Variables {
                  // InstaIndex Address.
                  address internal immutable instaIndex;
                  // Connectors Address.
                  address public immutable connectorsM1;
                  constructor(address _instaIndex, address _connectors) {
                      connectorsM1 = _connectors;
                      instaIndex = _instaIndex;
                  }
              }
              contract InstaImplementationM1 is Constants {
                  constructor(address _instaIndex, address _connectors) Constants(_instaIndex, _connectors) {}
                  function decodeEvent(bytes memory response) internal pure returns (string memory _eventCode, bytes memory _eventParams) {
                      if (response.length > 0) {
                          (_eventCode, _eventParams) = abi.decode(response, (string, bytes));
                      }
                  }
                  event LogCast(
                      address indexed origin,
                      address indexed sender,
                      uint256 value,
                      string[] targetsNames,
                      address[] targets,
                      string[] eventNames,
                      bytes[] eventParams
                  );
                  receive() external payable {}
                   /**
                   * @dev Delegate the calls to Connector.
                   * @param _target Connector address
                   * @param _data CallData of function.
                  */
                  function spell(address _target, bytes memory _data) internal returns (bytes memory response) {
                      require(_target != address(0), "target-invalid");
                      assembly {
                          let succeeded := delegatecall(gas(), _target, add(_data, 0x20), mload(_data), 0, 0)
                          let size := returndatasize()
                          
                          response := mload(0x40)
                          mstore(0x40, add(response, and(add(add(size, 0x20), 0x1f), not(0x1f))))
                          mstore(response, size)
                          returndatacopy(add(response, 0x20), 0, size)
                          switch iszero(succeeded)
                              case 1 {
                                  // throw if delegatecall failed
                                  returndatacopy(0x00, 0x00, size)
                                  revert(0x00, size)
                              }
                      }
                  }
                  /**
                   * @dev This is the main function, Where all the different functions are called
                   * from Smart Account.
                   * @param _targetNames Array of Connector address.
                   * @param _datas Array of Calldata.
                  */
                  function cast(
                      string[] calldata _targetNames,
                      bytes[] calldata _datas,
                      address _origin
                  )
                  external
                  payable 
                  returns (bytes32) // Dummy return to fix instaIndex buildWithCast function
                  {   
                      uint256 _length = _targetNames.length;
                      require(_auth[msg.sender] || msg.sender == instaIndex, "1: permission-denied");
                      require(_length != 0, "1: length-invalid");
                      require(_length == _datas.length , "1: array-length-invalid");
                      string[] memory eventNames = new string[](_length);
                      bytes[] memory eventParams = new bytes[](_length);
                      (bool isOk, address[] memory _targets) = ConnectorsInterface(connectorsM1).isConnectors(_targetNames);
                      require(isOk, "1: not-connector");
                      for (uint i = 0; i < _length; i++) {
                          bytes memory response = spell(_targets[i], _datas[i]);
                          (eventNames[i], eventParams[i]) = decodeEvent(response);
                      }
                      emit LogCast(
                          _origin,
                          msg.sender,
                          msg.value,
                          _targetNames,
                          _targets,
                          eventNames,
                          eventParams
                      );
                  }
              }pragma solidity ^0.7.0;
              contract Variables {
                  // Auth Module(Address of Auth => bool).
                  mapping (address => bool) internal _auth;
              }

              File 8 of 15: InstaConnectorsV2
              pragma solidity ^0.7.0;
              pragma experimental ABIEncoderV2;
              /**
               * @title InstaConnectorsV2
               * @dev Registry for Connectors.
               */
              interface IndexInterface {
                  function master() external view returns (address);
              }
              interface ConnectorInterface {
                  function name() external view returns (string memory);
              }
              contract Controllers {
                  event LogController(address indexed addr, bool indexed isChief);
                  // InstaIndex Address.
                  address public immutable instaIndex;
                  constructor(address _instaIndex) {
                      instaIndex = _instaIndex;
                  }
                  // Enabled Chief(Address of Chief => bool).
                  mapping(address => bool) public chief;
                  // Enabled Connectors(Connector name => address).
                  mapping(string => address) public connectors;
                  /**
                  * @dev Throws if the sender not is Master Address from InstaIndex
                  * or Enabled Chief.
                  */
                  modifier isChief {
                      require(chief[msg.sender] || msg.sender == IndexInterface(instaIndex).master(), "not-an-chief");
                      _;
                  }
                  /**
                   * @dev Toggle a Chief. Enable if disable & vice versa
                   * @param _chiefAddress Chief Address.
                  */
                  function toggleChief(address _chiefAddress) external {
                      require(msg.sender == IndexInterface(instaIndex).master(), "toggleChief: not-master");
                      chief[_chiefAddress] = !chief[_chiefAddress];
                      emit LogController(_chiefAddress, chief[_chiefAddress]);
                  }
              }
              contract InstaConnectorsV2 is Controllers {
                  event LogConnectorAdded(
                      bytes32 indexed connectorNameHash,
                      string connectorName,
                      address indexed connector
                  );
                  event LogConnectorUpdated(
                      bytes32 indexed connectorNameHash,
                      string connectorName,
                      address indexed oldConnector,
                      address indexed newConnector
                  );
                  event LogConnectorRemoved(
                      bytes32 indexed connectorNameHash,
                      string connectorName,
                      address indexed connector
                  );
                  constructor(address _instaIndex) public Controllers(_instaIndex) {}
                  /**
                   * @dev Add Connectors
                   * @param _connectorNames Array of Connector Names.
                   * @param _connectors Array of Connector Address.
                  */
                  function addConnectors(string[] calldata _connectorNames, address[] calldata _connectors) external isChief {
                      require(_connectors.length == _connectors.length, "addConnectors: not same length");
                      for (uint i = 0; i < _connectors.length; i++) {
                          require(connectors[_connectorNames[i]] == address(0), "addConnectors: _connectorName added already");
                          require(_connectors[i] != address(0), "addConnectors: _connectors address not vaild");
                          ConnectorInterface(_connectors[i]).name(); // Checking if connector has function name()
                          connectors[_connectorNames[i]] = _connectors[i];
                          emit LogConnectorAdded(keccak256(abi.encodePacked(_connectorNames[i])), _connectorNames[i], _connectors[i]);
                      }
                  }
                  /**
                   * @dev Update Connectors
                   * @param _connectorNames Array of Connector Names.
                   * @param _connectors Array of Connector Address.
                  */
                  function updateConnectors(string[] calldata _connectorNames, address[] calldata _connectors) external isChief {
                      require(_connectorNames.length == _connectors.length, "updateConnectors: not same length");
                      for (uint i = 0; i < _connectors.length; i++) {
                          require(connectors[_connectorNames[i]] != address(0), "updateConnectors: _connectorName not added to update");
                          require(_connectors[i] != address(0), "updateConnectors: _connector address is not vaild");
                          ConnectorInterface(_connectors[i]).name(); // Checking if connector has function name()
                          emit LogConnectorUpdated(keccak256(abi.encodePacked(_connectorNames[i])), _connectorNames[i], connectors[_connectorNames[i]], _connectors[i]);
                          connectors[_connectorNames[i]] = _connectors[i];
                      }
                  }
                  /**
                   * @dev Remove Connectors
                   * @param _connectorNames Array of Connector Names.
                  */
                  function removeConnectors(string[] calldata _connectorNames) external isChief {
                      for (uint i = 0; i < _connectorNames.length; i++) {
                          require(connectors[_connectorNames[i]] != address(0), "removeConnectors: _connectorName not added to update");
                          emit LogConnectorRemoved(keccak256(abi.encodePacked(_connectorNames[i])), _connectorNames[i], connectors[_connectorNames[i]]);
                          delete connectors[_connectorNames[i]];
                      }
                  }
                  /**
                   * @dev Check if Connector addresses are enabled.
                   * @param _connectors Array of Connector Names.
                  */
                  function isConnectors(string[] calldata _connectorNames) external view returns (bool isOk, address[] memory _connectors) {
                      isOk = true;
                      uint len = _connectorNames.length;
                      _connectors = new address[](len);
                      for (uint i = 0; i < _connectors.length; i++) {
                          _connectors[i] = connectors[_connectorNames[i]];
                          if (_connectors[i] == address(0)) {
                              isOk = false;
                              break;
                          }
                      }
                  }
              }

              File 9 of 15: ConnectV2AaveV2
              pragma solidity ^0.7.0;
              /**
               * @title Aave v2.
               * @dev Lending & Borrowing.
               */
              import { TokenInterface } from "../../../common/interfaces.sol";
              import { Stores } from "../../../common/stores.sol";
              import { Helpers } from "./helpers.sol";
              import { Events } from "./events.sol";
              import { AaveInterface } from "./interface.sol";
              abstract contract AaveResolver is Events, Helpers {
                  /**
                   * @dev Deposit ETH/ERC20_Token.
                   * @notice Deposit a token to Aave v2 for lending / collaterization.
                   * @param token The address of the token to deposit.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
                   * @param amt The amount of the token to deposit. (For max: `uint256(-1)`)
                   * @param getId ID to retrieve amt.
                   * @param setId ID stores the amount of tokens deposited.
                  */
                  function deposit(
                      address token,
                      uint256 amt,
                      uint256 getId,
                      uint256 setId
                  ) external payable returns (string memory _eventName, bytes memory _eventParam) {
                      uint _amt = getUint(getId, amt);
                      AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
                      bool isEth = token == ethAddr;
                      address _token = isEth ? wethAddr : token;
                      TokenInterface tokenContract = TokenInterface(_token);
                      if (isEth) {
                          _amt = _amt == uint(-1) ? address(this).balance : _amt;
                          convertEthToWeth(isEth, tokenContract, _amt);
                      } else {
                          _amt = _amt == uint(-1) ? tokenContract.balanceOf(address(this)) : _amt;
                      }
                      approve(tokenContract, address(aave), _amt);
                      aave.deposit(_token, _amt, address(this), referralCode);
                      if (!getIsColl(_token)) {
                          aave.setUserUseReserveAsCollateral(_token, true);
                      }
                      setUint(setId, _amt);
                      _eventName = "LogDeposit(address,uint256,uint256,uint256)";
                      _eventParam = abi.encode(token, _amt, getId, setId);
                  }
                  /**
                   * @dev Withdraw ETH/ERC20_Token.
                   * @notice Withdraw deposited token from Aave v2
                   * @param token The address of the token to withdraw.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
                   * @param amt The amount of the token to withdraw. (For max: `uint256(-1)`)
                   * @param getId ID to retrieve amt.
                   * @param setId ID stores the amount of tokens withdrawn.
                  */
                  function withdraw(
                      address token,
                      uint256 amt,
                      uint256 getId,
                      uint256 setId
                  ) external payable returns (string memory _eventName, bytes memory _eventParam) {
                      uint _amt = getUint(getId, amt);
                      AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
                      bool isEth = token == ethAddr;
                      address _token = isEth ? wethAddr : token;
                      TokenInterface tokenContract = TokenInterface(_token);
                      uint initialBal = tokenContract.balanceOf(address(this));
                      aave.withdraw(_token, _amt, address(this));
                      uint finalBal = tokenContract.balanceOf(address(this));
                      _amt = sub(finalBal, initialBal);
                      convertWethToEth(isEth, tokenContract, _amt);
                      
                      setUint(setId, _amt);
                      _eventName = "LogWithdraw(address,uint256,uint256,uint256)";
                      _eventParam = abi.encode(token, _amt, getId, setId);
                  }
                  /**
                   * @dev Borrow ETH/ERC20_Token.
                   * @notice Borrow a token using Aave v2
                   * @param token The address of the token to borrow.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
                   * @param amt The amount of the token to borrow.
                   * @param rateMode The type of borrow debt. (For Stable: 1, Variable: 2)
                   * @param getId ID to retrieve amt.
                   * @param setId ID stores the amount of tokens borrowed.
                  */
                  function borrow(
                      address token,
                      uint256 amt,
                      uint256 rateMode,
                      uint256 getId,
                      uint256 setId
                  ) external payable returns (string memory _eventName, bytes memory _eventParam) {
                      uint _amt = getUint(getId, amt);
                      AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
                      bool isEth = token == ethAddr;
                      address _token = isEth ? wethAddr : token;
                      aave.borrow(_token, _amt, rateMode, referralCode, address(this));
                      convertWethToEth(isEth, TokenInterface(_token), _amt);
                      setUint(setId, _amt);
                      _eventName = "LogBorrow(address,uint256,uint256,uint256,uint256)";
                      _eventParam = abi.encode(token, _amt, rateMode, getId, setId);
                  }
                  /**
                   * @dev Payback borrowed ETH/ERC20_Token.
                   * @notice Payback debt owed.
                   * @param token The address of the token to payback.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
                   * @param amt The amount of the token to payback. (For max: `uint256(-1)`)
                   * @param rateMode The type of debt paying back. (For Stable: 1, Variable: 2)
                   * @param getId ID to retrieve amt.
                   * @param setId ID stores the amount of tokens paid back.
                  */
                  function payback(
                      address token,
                      uint256 amt,
                      uint256 rateMode,
                      uint256 getId,
                      uint256 setId
                  ) external payable returns (string memory _eventName, bytes memory _eventParam) {
                      uint _amt = getUint(getId, amt);
                      AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
                      bool isEth = token == ethAddr;
                      address _token = isEth ? wethAddr : token;
                      TokenInterface tokenContract = TokenInterface(_token);
                      _amt = _amt == uint(-1) ? getPaybackBalance(_token, rateMode) : _amt;
                      if (isEth) convertEthToWeth(isEth, tokenContract, _amt);
                      approve(tokenContract, address(aave), _amt);
                      aave.repay(_token, _amt, rateMode, address(this));
                      setUint(setId, _amt);
                      _eventName = "LogPayback(address,uint256,uint256,uint256,uint256)";
                      _eventParam = abi.encode(token, _amt, rateMode, getId, setId);
                  }
                  /**
                   * @dev Enable collateral
                   * @notice Enable an array of tokens as collateral
                   * @param tokens Array of tokens to enable collateral
                  */
                  function enableCollateral(
                      address[] calldata tokens
                  ) external payable returns (string memory _eventName, bytes memory _eventParam) {
                      uint _length = tokens.length;
                      require(_length > 0, "0-tokens-not-allowed");
                      AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
                      for (uint i = 0; i < _length; i++) {
                          address token = tokens[i];
                          if (getCollateralBalance(token) > 0 && !getIsColl(token)) {
                              aave.setUserUseReserveAsCollateral(token, true);
                          }
                      }
                      _eventName = "LogEnableCollateral(address[])";
                      _eventParam = abi.encode(tokens);
                  }
                  /**
                   * @dev Swap borrow rate mode
                   * @notice Swaps user borrow rate mode between variable and stable
                   * @param token The address of the token to swap borrow rate.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
                   * @param rateMode Desired borrow rate mode. (Stable = 1, Variable = 2)
                  */
                  function swapBorrowRateMode(
                      address token,
                      uint rateMode
                  ) external payable returns (string memory _eventName, bytes memory _eventParam) {
                      AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
                      uint currentRateMode = rateMode == 1 ? 2 : 1;
                      if (getPaybackBalance(token, currentRateMode) > 0) {
                          aave.swapBorrowRateMode(token, rateMode);
                      }
                      _eventName = "LogSwapRateMode(address,uint256)";
                      _eventParam = abi.encode(token, rateMode);
                  }
              }
              contract ConnectV2AaveV2 is AaveResolver {
                  string constant public name = "AaveV2-v1.1";
              }
              pragma solidity ^0.7.0;
              interface TokenInterface {
                  function approve(address, uint256) external;
                  function transfer(address, uint) external;
                  function transferFrom(address, address, uint) external;
                  function deposit() external payable;
                  function withdraw(uint) external;
                  function balanceOf(address) external view returns (uint);
                  function decimals() external view returns (uint);
              }
              interface MemoryInterface {
                  function getUint(uint id) external returns (uint num);
                  function setUint(uint id, uint val) external;
              }
              interface InstaMapping {
                  function cTokenMapping(address) external view returns (address);
                  function gemJoinMapping(bytes32) external view returns (address);
              }
              interface AccountInterface {
                  function enable(address) external;
                  function disable(address) external;
                  function isAuth(address) external view returns (bool);
              }
              pragma solidity ^0.7.0;
              import { MemoryInterface, InstaMapping } from "./interfaces.sol";
              abstract contract Stores {
                /**
                 * @dev Return ethereum address
                 */
                address constant internal ethAddr = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                /**
                 * @dev Return Wrapped ETH address
                 */
                address constant internal wethAddr = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
                /**
                 * @dev Return memory variable address
                 */
                MemoryInterface constant internal instaMemory = MemoryInterface(0x8a5419CfC711B2343c17a6ABf4B2bAFaBb06957F);
                /**
                 * @dev Return InstaDApp Mapping Addresses
                 */
                InstaMapping constant internal instaMapping = InstaMapping(0xe81F70Cc7C0D46e12d70efc60607F16bbD617E88);
                /**
                 * @dev Get Uint value from InstaMemory Contract.
                 */
                function getUint(uint getId, uint val) internal returns (uint returnVal) {
                  returnVal = getId == 0 ? val : instaMemory.getUint(getId);
                }
                /**
                * @dev Set Uint value in InstaMemory Contract.
                */
                function setUint(uint setId, uint val) virtual internal {
                  if (setId != 0) instaMemory.setUint(setId, val);
                }
              }
              pragma solidity ^0.7.0;
              import { DSMath } from "../../../common/math.sol";
              import { Basic } from "../../../common/basic.sol";
              import { AaveLendingPoolProviderInterface, AaveDataProviderInterface } from "./interface.sol";
              abstract contract Helpers is DSMath, Basic {
                  
                  /**
                   * @dev Aave Lending Pool Provider
                  */
                  AaveLendingPoolProviderInterface constant internal aaveProvider = AaveLendingPoolProviderInterface(0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5);
                  /**
                   * @dev Aave Protocol Data Provider
                  */
                  AaveDataProviderInterface constant internal aaveData = AaveDataProviderInterface(0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d);
                  /**
                   * @dev Aave Referral Code
                  */
                  uint16 constant internal referralCode = 3228;
                  /**
                   * @dev Checks if collateral is enabled for an asset
                   * @param token token address of the asset.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
                  */
                  function getIsColl(address token) internal view returns (bool isCol) {
                      (, , , , , , , , isCol) = aaveData.getUserReserveData(token, address(this));
                  }
                  /**
                   * @dev Get total debt balance & fee for an asset
                   * @param token token address of the debt.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
                   * @param rateMode Borrow rate mode (Stable = 1, Variable = 2)
                  */
                  function getPaybackBalance(address token, uint rateMode) internal view returns (uint) {
                      (, uint stableDebt, uint variableDebt, , , , , , ) = aaveData.getUserReserveData(token, address(this));
                      return rateMode == 1 ? stableDebt : variableDebt;
                  }
                  /**
                   * @dev Get total collateral balance for an asset
                   * @param token token address of the collateral.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
                  */
                  function getCollateralBalance(address token) internal view returns (uint bal) {
                      (bal, , , , , , , ,) = aaveData.getUserReserveData(token, address(this));
                  }
              }
              pragma solidity ^0.7.0;
              contract Events {
                  event LogDeposit(address indexed token, uint256 tokenAmt, uint256 getId, uint256 setId);
                  event LogWithdraw(address indexed token, uint256 tokenAmt, uint256 getId, uint256 setId);
                  event LogBorrow(address indexed token, uint256 tokenAmt, uint256 indexed rateMode, uint256 getId, uint256 setId);
                  event LogPayback(address indexed token, uint256 tokenAmt, uint256 indexed rateMode, uint256 getId, uint256 setId);
                  event LogEnableCollateral(address[] tokens);
                  event LogSwapRateMode(address indexed token, uint256 rateMode);
              }
              pragma solidity ^0.7.0;
              interface AaveInterface {
                  function deposit(address _asset, uint256 _amount, address _onBehalfOf, uint16 _referralCode) external;
                  function withdraw(address _asset, uint256 _amount, address _to) external;
                  function borrow(
                      address _asset,
                      uint256 _amount,
                      uint256 _interestRateMode,
                      uint16 _referralCode,
                      address _onBehalfOf
                  ) external;
                  function repay(address _asset, uint256 _amount, uint256 _rateMode, address _onBehalfOf) external;
                  function setUserUseReserveAsCollateral(address _asset, bool _useAsCollateral) external;
                  function swapBorrowRateMode(address _asset, uint256 _rateMode) external;
              }
              interface AaveLendingPoolProviderInterface {
                  function getLendingPool() external view returns (address);
              }
              interface AaveDataProviderInterface {
                  function getReserveTokensAddresses(address _asset) external view returns (
                      address aTokenAddress,
                      address stableDebtTokenAddress,
                      address variableDebtTokenAddress
                  );
                  function getUserReserveData(address _asset, address _user) external view returns (
                      uint256 currentATokenBalance,
                      uint256 currentStableDebt,
                      uint256 currentVariableDebt,
                      uint256 principalStableDebt,
                      uint256 scaledVariableDebt,
                      uint256 stableBorrowRate,
                      uint256 liquidityRate,
                      uint40 stableRateLastUpdated,
                      bool usageAsCollateralEnabled
                  );
              }
              interface AaveAddressProviderRegistryInterface {
                  function getAddressesProvidersList() external view returns (address[] memory);
              }
              interface ATokenInterface {
                  function balanceOf(address _user) external view returns(uint256);
              }pragma solidity ^0.7.0;
              import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
              contract DSMath {
                uint constant WAD = 10 ** 18;
                uint constant RAY = 10 ** 27;
                function add(uint x, uint y) internal pure returns (uint z) {
                  z = SafeMath.add(x, y);
                }
                function sub(uint x, uint y) internal virtual pure returns (uint z) {
                  z = SafeMath.sub(x, y);
                }
                function mul(uint x, uint y) internal pure returns (uint z) {
                  z = SafeMath.mul(x, y);
                }
                function div(uint x, uint y) internal pure returns (uint z) {
                  z = SafeMath.div(x, y);
                }
                function wmul(uint x, uint y) internal pure returns (uint z) {
                  z = SafeMath.add(SafeMath.mul(x, y), WAD / 2) / WAD;
                }
                function wdiv(uint x, uint y) internal pure returns (uint z) {
                  z = SafeMath.add(SafeMath.mul(x, WAD), y / 2) / y;
                }
                function rdiv(uint x, uint y) internal pure returns (uint z) {
                  z = SafeMath.add(SafeMath.mul(x, RAY), y / 2) / y;
                }
                function rmul(uint x, uint y) internal pure returns (uint z) {
                  z = SafeMath.add(SafeMath.mul(x, y), RAY / 2) / RAY;
                }
                function toInt(uint x) internal pure returns (int y) {
                  y = int(x);
                  require(y >= 0, "int-overflow");
                }
                function toRad(uint wad) internal pure returns (uint rad) {
                  rad = mul(wad, 10 ** 27);
                }
              }
              pragma solidity ^0.7.0;
              import { TokenInterface } from "./interfaces.sol";
              import { Stores } from "./stores.sol";
              import { DSMath } from "./math.sol";
              abstract contract Basic is DSMath, Stores {
                  function convert18ToDec(uint _dec, uint256 _amt) internal pure returns (uint256 amt) {
                      amt = (_amt / 10 ** (18 - _dec));
                  }
                  function convertTo18(uint _dec, uint256 _amt) internal pure returns (uint256 amt) {
                      amt = mul(_amt, 10 ** (18 - _dec));
                  }
                  function getTokenBal(TokenInterface token) internal view returns(uint _amt) {
                      _amt = address(token) == ethAddr ? address(this).balance : token.balanceOf(address(this));
                  }
                  function getTokensDec(TokenInterface buyAddr, TokenInterface sellAddr) internal view returns(uint buyDec, uint sellDec) {
                      buyDec = address(buyAddr) == ethAddr ?  18 : buyAddr.decimals();
                      sellDec = address(sellAddr) == ethAddr ?  18 : sellAddr.decimals();
                  }
                  function encodeEvent(string memory eventName, bytes memory eventParam) internal pure returns (bytes memory) {
                      return abi.encode(eventName, eventParam);
                  }
                  function approve(TokenInterface token, address spender, uint256 amount) internal {
                      try token.approve(spender, amount) {
                      } catch {
                          token.approve(spender, 0);
                          token.approve(spender, amount);
                      }
                  }
                  function changeEthAddress(address buy, address sell) internal pure returns(TokenInterface _buy, TokenInterface _sell){
                      _buy = buy == ethAddr ? TokenInterface(wethAddr) : TokenInterface(buy);
                      _sell = sell == ethAddr ? TokenInterface(wethAddr) : TokenInterface(sell);
                  }
                  function convertEthToWeth(bool isEth, TokenInterface token, uint amount) internal {
                      if(isEth) token.deposit{value: amount}();
                  }
                  function convertWethToEth(bool isEth, TokenInterface token, uint amount) internal {
                     if(isEth) {
                          approve(token, address(token), amount);
                          token.withdraw(amount);
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity >=0.6.0 <0.8.0;
              /**
               * @dev Wrappers over Solidity's arithmetic operations with added overflow
               * checks.
               *
               * Arithmetic operations in Solidity wrap on overflow. This can easily result
               * in bugs, because programmers usually assume that an overflow raises an
               * error, which is the standard behavior in high level programming languages.
               * `SafeMath` restores this intuition by reverting the transaction when an
               * operation overflows.
               *
               * Using this library instead of the unchecked operations eliminates an entire
               * class of bugs, so it's recommended to use it always.
               */
              library SafeMath {
                  /**
                   * @dev Returns the addition of two unsigned integers, with an overflow flag.
                   *
                   * _Available since v3.4._
                   */
                  function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                      uint256 c = a + b;
                      if (c < a) return (false, 0);
                      return (true, c);
                  }
                  /**
                   * @dev Returns the substraction of two unsigned integers, with an overflow flag.
                   *
                   * _Available since v3.4._
                   */
                  function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                      if (b > a) return (false, 0);
                      return (true, a - b);
                  }
                  /**
                   * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
                   *
                   * _Available since v3.4._
                   */
                  function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                      // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                      // benefit is lost if 'b' is also tested.
                      // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                      if (a == 0) return (true, 0);
                      uint256 c = a * b;
                      if (c / a != b) return (false, 0);
                      return (true, c);
                  }
                  /**
                   * @dev Returns the division of two unsigned integers, with a division by zero flag.
                   *
                   * _Available since v3.4._
                   */
                  function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                      if (b == 0) return (false, 0);
                      return (true, a / b);
                  }
                  /**
                   * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
                   *
                   * _Available since v3.4._
                   */
                  function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
                      if (b == 0) return (false, 0);
                      return (true, a % b);
                  }
                  /**
                   * @dev Returns the addition of two unsigned integers, reverting on
                   * overflow.
                   *
                   * Counterpart to Solidity's `+` operator.
                   *
                   * Requirements:
                   *
                   * - Addition cannot overflow.
                   */
                  function add(uint256 a, uint256 b) internal pure returns (uint256) {
                      uint256 c = a + b;
                      require(c >= a, "SafeMath: addition overflow");
                      return c;
                  }
                  /**
                   * @dev Returns the subtraction of two unsigned integers, reverting on
                   * overflow (when the result is negative).
                   *
                   * Counterpart to Solidity's `-` operator.
                   *
                   * Requirements:
                   *
                   * - Subtraction cannot overflow.
                   */
                  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b <= a, "SafeMath: subtraction overflow");
                      return a - b;
                  }
                  /**
                   * @dev Returns the multiplication of two unsigned integers, reverting on
                   * overflow.
                   *
                   * Counterpart to Solidity's `*` operator.
                   *
                   * Requirements:
                   *
                   * - Multiplication cannot overflow.
                   */
                  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                      if (a == 0) return 0;
                      uint256 c = a * b;
                      require(c / a == b, "SafeMath: multiplication overflow");
                      return c;
                  }
                  /**
                   * @dev Returns the integer division of two unsigned integers, reverting on
                   * division by zero. The result is rounded towards zero.
                   *
                   * Counterpart to Solidity's `/` operator. Note: this function uses a
                   * `revert` opcode (which leaves remaining gas untouched) while Solidity
                   * uses an invalid opcode to revert (consuming all remaining gas).
                   *
                   * Requirements:
                   *
                   * - The divisor cannot be zero.
                   */
                  function div(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b > 0, "SafeMath: division by zero");
                      return a / b;
                  }
                  /**
                   * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                   * reverting when dividing by zero.
                   *
                   * Counterpart to Solidity's `%` operator. This function uses a `revert`
                   * opcode (which leaves remaining gas untouched) while Solidity uses an
                   * invalid opcode to revert (consuming all remaining gas).
                   *
                   * Requirements:
                   *
                   * - The divisor cannot be zero.
                   */
                  function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                      require(b > 0, "SafeMath: modulo by zero");
                      return a % b;
                  }
                  /**
                   * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                   * overflow (when the result is negative).
                   *
                   * CAUTION: This function is deprecated because it requires allocating memory for the error
                   * message unnecessarily. For custom revert reasons use {trySub}.
                   *
                   * Counterpart to Solidity's `-` operator.
                   *
                   * Requirements:
                   *
                   * - Subtraction cannot overflow.
                   */
                  function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                      require(b <= a, errorMessage);
                      return a - b;
                  }
                  /**
                   * @dev Returns the integer division of two unsigned integers, reverting with custom message on
                   * division by zero. The result is rounded towards zero.
                   *
                   * CAUTION: This function is deprecated because it requires allocating memory for the error
                   * message unnecessarily. For custom revert reasons use {tryDiv}.
                   *
                   * Counterpart to Solidity's `/` operator. Note: this function uses a
                   * `revert` opcode (which leaves remaining gas untouched) while Solidity
                   * uses an invalid opcode to revert (consuming all remaining gas).
                   *
                   * Requirements:
                   *
                   * - The divisor cannot be zero.
                   */
                  function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                      require(b > 0, errorMessage);
                      return a / b;
                  }
                  /**
                   * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                   * reverting with custom message when dividing by zero.
                   *
                   * CAUTION: This function is deprecated because it requires allocating memory for the error
                   * message unnecessarily. For custom revert reasons use {tryMod}.
                   *
                   * Counterpart to Solidity's `%` operator. This function uses a `revert`
                   * opcode (which leaves remaining gas untouched) while Solidity uses an
                   * invalid opcode to revert (consuming all remaining gas).
                   *
                   * Requirements:
                   *
                   * - The divisor cannot be zero.
                   */
                  function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                      require(b > 0, errorMessage);
                      return a % b;
                  }
              }
              

              File 10 of 15: LendingPoolAddressesProvider
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Ownable} from '../../dependencies/openzeppelin/contracts/Ownable.sol';
              // Prettier ignore to prevent buidler flatter bug
              // prettier-ignore
              import {InitializableImmutableAdminUpgradeabilityProxy} from '../libraries/aave-upgradeability/InitializableImmutableAdminUpgradeabilityProxy.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              /**
               * @title LendingPoolAddressesProvider contract
               * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles
               * - Acting also as factory of proxies and admin of those, so with right to change its implementations
               * - Owned by the Aave Governance
               * @author Aave
               **/
              contract LendingPoolAddressesProvider is Ownable, ILendingPoolAddressesProvider {
                string private _marketId;
                mapping(bytes32 => address) private _addresses;
                bytes32 private constant LENDING_POOL = 'LENDING_POOL';
                bytes32 private constant LENDING_POOL_CONFIGURATOR = 'LENDING_POOL_CONFIGURATOR';
                bytes32 private constant POOL_ADMIN = 'POOL_ADMIN';
                bytes32 private constant EMERGENCY_ADMIN = 'EMERGENCY_ADMIN';
                bytes32 private constant LENDING_POOL_COLLATERAL_MANAGER = 'COLLATERAL_MANAGER';
                bytes32 private constant PRICE_ORACLE = 'PRICE_ORACLE';
                bytes32 private constant LENDING_RATE_ORACLE = 'LENDING_RATE_ORACLE';
                constructor(string memory marketId) public {
                  _setMarketId(marketId);
                }
                /**
                 * @dev Returns the id of the Aave market to which this contracts points to
                 * @return The market id
                 **/
                function getMarketId() external view override returns (string memory) {
                  return _marketId;
                }
                /**
                 * @dev Allows to set the market which this LendingPoolAddressesProvider represents
                 * @param marketId The market id
                 */
                function setMarketId(string memory marketId) external override onlyOwner {
                  _setMarketId(marketId);
                }
                /**
                 * @dev General function to update the implementation of a proxy registered with
                 * certain `id`. If there is no proxy registered, it will instantiate one and
                 * set as implementation the `implementationAddress`
                 * IMPORTANT Use this function carefully, only for ids that don't have an explicit
                 * setter function, in order to avoid unexpected consequences
                 * @param id The id
                 * @param implementationAddress The address of the new implementation
                 */
                function setAddressAsProxy(bytes32 id, address implementationAddress)
                  external
                  override
                  onlyOwner
                {
                  _updateImpl(id, implementationAddress);
                  emit AddressSet(id, implementationAddress, true);
                }
                /**
                 * @dev Sets an address for an id replacing the address saved in the addresses map
                 * IMPORTANT Use this function carefully, as it will do a hard replacement
                 * @param id The id
                 * @param newAddress The address to set
                 */
                function setAddress(bytes32 id, address newAddress) external override onlyOwner {
                  _addresses[id] = newAddress;
                  emit AddressSet(id, newAddress, false);
                }
                /**
                 * @dev Returns an address by id
                 * @return The address
                 */
                function getAddress(bytes32 id) public view override returns (address) {
                  return _addresses[id];
                }
                /**
                 * @dev Returns the address of the LendingPool proxy
                 * @return The LendingPool proxy address
                 **/
                function getLendingPool() external view override returns (address) {
                  return getAddress(LENDING_POOL);
                }
                /**
                 * @dev Updates the implementation of the LendingPool, or creates the proxy
                 * setting the new `pool` implementation on the first time calling it
                 * @param pool The new LendingPool implementation
                 **/
                function setLendingPoolImpl(address pool) external override onlyOwner {
                  _updateImpl(LENDING_POOL, pool);
                  emit LendingPoolUpdated(pool);
                }
                /**
                 * @dev Returns the address of the LendingPoolConfigurator proxy
                 * @return The LendingPoolConfigurator proxy address
                 **/
                function getLendingPoolConfigurator() external view override returns (address) {
                  return getAddress(LENDING_POOL_CONFIGURATOR);
                }
                /**
                 * @dev Updates the implementation of the LendingPoolConfigurator, or creates the proxy
                 * setting the new `configurator` implementation on the first time calling it
                 * @param configurator The new LendingPoolConfigurator implementation
                 **/
                function setLendingPoolConfiguratorImpl(address configurator) external override onlyOwner {
                  _updateImpl(LENDING_POOL_CONFIGURATOR, configurator);
                  emit LendingPoolConfiguratorUpdated(configurator);
                }
                /**
                 * @dev Returns the address of the LendingPoolCollateralManager. 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 LendingPoolCollateralManager
                 **/
                function getLendingPoolCollateralManager() external view override returns (address) {
                  return getAddress(LENDING_POOL_COLLATERAL_MANAGER);
                }
                /**
                 * @dev Updates the address of the LendingPoolCollateralManager
                 * @param manager The new LendingPoolCollateralManager address
                 **/
                function setLendingPoolCollateralManager(address manager) external override onlyOwner {
                  _addresses[LENDING_POOL_COLLATERAL_MANAGER] = manager;
                  emit LendingPoolCollateralManagerUpdated(manager);
                }
                /**
                 * @dev The functions below are getters/setters of addresses that are outside the context
                 * of the protocol hence the upgradable proxy pattern is not used
                 **/
                function getPoolAdmin() external view override returns (address) {
                  return getAddress(POOL_ADMIN);
                }
                function setPoolAdmin(address admin) external override onlyOwner {
                  _addresses[POOL_ADMIN] = admin;
                  emit ConfigurationAdminUpdated(admin);
                }
                function getEmergencyAdmin() external view override returns (address) {
                  return getAddress(EMERGENCY_ADMIN);
                }
                function setEmergencyAdmin(address emergencyAdmin) external override onlyOwner {
                  _addresses[EMERGENCY_ADMIN] = emergencyAdmin;
                  emit EmergencyAdminUpdated(emergencyAdmin);
                }
                function getPriceOracle() external view override returns (address) {
                  return getAddress(PRICE_ORACLE);
                }
                function setPriceOracle(address priceOracle) external override onlyOwner {
                  _addresses[PRICE_ORACLE] = priceOracle;
                  emit PriceOracleUpdated(priceOracle);
                }
                function getLendingRateOracle() external view override returns (address) {
                  return getAddress(LENDING_RATE_ORACLE);
                }
                function setLendingRateOracle(address lendingRateOracle) external override onlyOwner {
                  _addresses[LENDING_RATE_ORACLE] = lendingRateOracle;
                  emit LendingRateOracleUpdated(lendingRateOracle);
                }
                /**
                 * @dev Internal function to update the implementation of a specific proxied component of the protocol
                 * - If there is no proxy registered in the given `id`, it creates the proxy setting `newAdress`
                 *   as implementation and calls the initialize() function on the proxy
                 * - If there is already a proxy registered, it just updates the implementation to `newAddress` and
                 *   calls the initialize() function via upgradeToAndCall() in the proxy
                 * @param id The id of the proxy to be updated
                 * @param newAddress The address of the new implementation
                 **/
                function _updateImpl(bytes32 id, address newAddress) internal {
                  address payable proxyAddress = payable(_addresses[id]);
                  InitializableImmutableAdminUpgradeabilityProxy proxy =
                    InitializableImmutableAdminUpgradeabilityProxy(proxyAddress);
                  bytes memory params = abi.encodeWithSignature('initialize(address)', address(this));
                  if (proxyAddress == address(0)) {
                    proxy = new InitializableImmutableAdminUpgradeabilityProxy(address(this));
                    proxy.initialize(newAddress, params);
                    _addresses[id] = address(proxy);
                    emit ProxyCreated(id, address(proxy));
                  } else {
                    proxy.upgradeToAndCall(newAddress, params);
                  }
                }
                function _setMarketId(string memory marketId) internal {
                  _marketId = marketId;
                  emit MarketIdSet(marketId);
                }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.6.0;
              import './Context.sol';
              /**
               * @dev Contract module which provides a basic access control mechanism, where
               * there is an account (an owner) that can be granted exclusive access to
               * specific functions.
               *
               * By default, the owner account will be the one that deploys the contract. This
               * can later be changed with {transferOwnership}.
               *
               * This module is used through inheritance. It will make available the modifier
               * `onlyOwner`, which can be applied to your functions to restrict their use to
               * the owner.
               */
              contract Ownable is Context {
                address private _owner;
                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                /**
                 * @dev Initializes the contract setting the deployer as the initial owner.
                 */
                constructor() internal {
                  address msgSender = _msgSender();
                  _owner = msgSender;
                  emit OwnershipTransferred(address(0), msgSender);
                }
                /**
                 * @dev Returns the address of the current owner.
                 */
                function owner() public view returns (address) {
                  return _owner;
                }
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                  require(_owner == _msgSender(), 'Ownable: caller is not the owner');
                  _;
                }
                /**
                 * @dev Leaves the contract without owner. It will not be possible to call
                 * `onlyOwner` functions anymore. Can only be called by the current owner.
                 *
                 * NOTE: Renouncing ownership will leave the contract without an owner,
                 * thereby removing any functionality that is only available to the owner.
                 */
                function renounceOwnership() public virtual onlyOwner {
                  emit OwnershipTransferred(_owner, address(0));
                  _owner = address(0);
                }
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 * Can only be called by the current owner.
                 */
                function transferOwnership(address newOwner) public virtual onlyOwner {
                  require(newOwner != address(0), 'Ownable: new owner is the zero address');
                  emit OwnershipTransferred(_owner, newOwner);
                  _owner = newOwner;
                }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.6.12;
              /*
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with GSN meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                function _msgSender() internal virtual view returns (address payable) {
                  return msg.sender;
                }
                function _msgData() internal virtual view returns (bytes memory) {
                  this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                  return msg.data;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import './BaseImmutableAdminUpgradeabilityProxy.sol';
              import '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
              /**
               * @title InitializableAdminUpgradeabilityProxy
               * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
               */
              contract InitializableImmutableAdminUpgradeabilityProxy is
                BaseImmutableAdminUpgradeabilityProxy,
                InitializableUpgradeabilityProxy
              {
                constructor(address admin) public BaseImmutableAdminUpgradeabilityProxy(admin) {}
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                  BaseImmutableAdminUpgradeabilityProxy._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
              /**
               * @title BaseImmutableAdminUpgradeabilityProxy
               * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
               * @dev This contract combines an upgradeability proxy with an authorization
               * mechanism for administrative tasks. The admin role is stored in an immutable, which
               * helps saving transactions costs
               * 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 BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
                address immutable ADMIN;
                constructor(address admin) public {
                  ADMIN = admin;
                }
                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 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);
                }
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal virtual override {
                  require(msg.sender != ADMIN, 'Cannot call fallback function from the proxy admin');
                  super._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import './Proxy.sol';
              import '../contracts/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)
                  }
                }
              }
              // 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`.
                 */
                fallback() 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());
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                  // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                  // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                  // for accounts without code, i.e. `keccak256('')`
                  bytes32 codehash;
                  bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                  // solhint-disable-next-line no-inline-assembly
                  assembly {
                    codehash := extcodehash(account)
                  }
                  return (codehash != accountHash && codehash != 0x0);
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                  require(address(this).balance >= amount, 'Address: insufficient balance');
                  // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                  (bool success, ) = recipient.call{value: amount}('');
                  require(success, 'Address: unable to send value, recipient may have reverted');
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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);
                  }
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title LendingPoolAddressesProvider contract
               * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles
               * - Acting also as factory of proxies and admin of those, so with right to change its implementations
               * - Owned by the Aave Governance
               * @author Aave
               **/
              interface ILendingPoolAddressesProvider {
                event MarketIdSet(string newMarketId);
                event LendingPoolUpdated(address indexed newAddress);
                event ConfigurationAdminUpdated(address indexed newAddress);
                event EmergencyAdminUpdated(address indexed newAddress);
                event LendingPoolConfiguratorUpdated(address indexed newAddress);
                event LendingPoolCollateralManagerUpdated(address indexed newAddress);
                event PriceOracleUpdated(address indexed newAddress);
                event LendingRateOracleUpdated(address indexed newAddress);
                event ProxyCreated(bytes32 id, address indexed newAddress);
                event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy);
                function getMarketId() external view returns (string memory);
                function setMarketId(string calldata marketId) external;
                function setAddress(bytes32 id, address newAddress) external;
                function setAddressAsProxy(bytes32 id, address impl) external;
                function getAddress(bytes32 id) external view returns (address);
                function getLendingPool() external view returns (address);
                function setLendingPoolImpl(address pool) external;
                function getLendingPoolConfigurator() external view returns (address);
                function setLendingPoolConfiguratorImpl(address configurator) external;
                function getLendingPoolCollateralManager() external view returns (address);
                function setLendingPoolCollateralManager(address manager) external;
                function getPoolAdmin() external view returns (address);
                function setPoolAdmin(address admin) external;
                function getEmergencyAdmin() external view returns (address);
                function setEmergencyAdmin(address admin) external;
                function getPriceOracle() external view returns (address);
                function setPriceOracle(address priceOracle) external;
                function getLendingRateOracle() external view returns (address);
                function setLendingRateOracle(address lendingRateOracle) external;
              }
              

              File 11 of 15: LendingPool
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                  // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                  // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                  // for accounts without code, i.e. `keccak256('')`
                  bytes32 codehash;
                  bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                  // solhint-disable-next-line no-inline-assembly
                  assembly {
                    codehash := extcodehash(account)
                  }
                  return (codehash != accountHash && codehash != 0x0);
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                  require(address(this).balance >= amount, 'Address: insufficient balance');
                  // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                  (bool success, ) = recipient.call{value: amount}('');
                  require(success, 'Address: unable to send value, recipient may have reverted');
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {Address} from '../dependencies/openzeppelin/contracts/Address.sol';
              import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
              import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
              import {ILendingPool} from '../interfaces/ILendingPool.sol';
              import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol';
              import {DataTypes} from '../protocol/libraries/types/DataTypes.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 payable;
                using Address for address;
                using SafeERC20 for IERC20;
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                address constant MOCK_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                /**
                  @dev Fallback function, don't accept any ETH
                  **/
                receive() external payable {
                  //only contracts can send ETH to the core
                  require(msg.sender.isContract(), '22');
                }
                /**
                  @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) {
                  if (token == MOCK_ETH_ADDRESS) {
                    return user.balance; // ETH balance
                    // check if token is actually a contract
                  } else if (token.isContract()) {
                    return IERC20(token).balanceOf(user);
                  }
                  revert('INVALID_TOKEN');
                }
                /**
                 * @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[] calldata users, address[] calldata tokens)
                  external
                  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++) {
                      balances[i * tokens.length + j] = balanceOf(users[i], tokens[j]);
                    }
                  }
                  return balances;
                }
                /**
                  @dev provides balances of user wallet for all reserves available on the pool
                  */
                function getUserWalletBalances(address provider, address user)
                  external
                  view
                  returns (address[] memory, uint256[] memory)
                {
                  ILendingPool pool = ILendingPool(ILendingPoolAddressesProvider(provider).getLendingPool());
                  address[] memory reserves = pool.getReservesList();
                  address[] memory reservesWithEth = new address[](reserves.length + 1);
                  for (uint256 i = 0; i < reserves.length; i++) {
                    reservesWithEth[i] = reserves[i];
                  }
                  reservesWithEth[reserves.length] = MOCK_ETH_ADDRESS;
                  uint256[] memory balances = new uint256[](reservesWithEth.length);
                  for (uint256 j = 0; j < reserves.length; j++) {
                    DataTypes.ReserveConfigurationMap memory configuration = pool.getConfiguration(reservesWithEth[j]);
                    (bool isActive, , , ) = configuration.getFlagsMemory();
                    if (!isActive) {
                      balances[j] = 0;
                      continue;
                    }
                    balances[j] = balanceOf(user, reservesWithEth[j]);
                  }
                  balances[reserves.length] = balanceOf(user, MOCK_ETH_ADDRESS);
                  return (reservesWithEth, balances);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                /**
                 * @dev Returns the amount of tokens in existence.
                 */
                function totalSupply() external view returns (uint256);
                /**
                 * @dev Returns the amount of tokens owned by `account`.
                 */
                function balanceOf(address account) external view returns (uint256);
                /**
                 * @dev Moves `amount` tokens from the caller's account to `recipient`.
                 *
                 * Returns a boolean value indicating whether the operation succeeded.
                 *
                 * Emits a {Transfer} event.
                 */
                function transfer(address recipient, uint256 amount) external returns (bool);
                /**
                 * @dev Returns the remaining number of tokens that `spender` will be
                 * allowed to spend on behalf of `owner` through {transferFrom}. This is
                 * zero by default.
                 *
                 * This value changes when {approve} or {transferFrom} are called.
                 */
                function allowance(address owner, address spender) external view returns (uint256);
                /**
                 * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                 *
                 * Returns a boolean value indicating whether the operation succeeded.
                 *
                 * IMPORTANT: Beware that changing an allowance with this method brings the risk
                 * that someone may use both the old and the new allowance by unfortunate
                 * transaction ordering. One possible solution to mitigate this race
                 * condition is to first reduce the spender's allowance to 0 and set the
                 * desired value afterwards:
                 * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                 *
                 * Emits an {Approval} event.
                 */
                function approve(address spender, uint256 amount) external returns (bool);
                /**
                 * @dev Moves `amount` tokens from `sender` to `recipient` using the
                 * allowance mechanism. `amount` is then deducted from the caller's
                 * allowance.
                 *
                 * Returns a boolean value indicating whether the operation succeeded.
                 *
                 * Emits a {Transfer} event.
                 */
                function transferFrom(
                  address sender,
                  address recipient,
                  uint256 amount
                ) external returns (bool);
                /**
                 * @dev Emitted when `value` tokens are moved from one account (`from`) to
                 * another (`to`).
                 *
                 * Note that `value` may be zero.
                 */
                event Transfer(address indexed from, address indexed to, uint256 value);
                /**
                 * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                 * a call to {approve}. `value` is the new allowance.
                 */
                event Approval(address indexed owner, address indexed spender, uint256 value);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title LendingPoolAddressesProvider contract
               * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles
               * - Acting also as factory of proxies and admin of those, so with right to change its implementations
               * - Owned by the Aave Governance
               * @author Aave
               **/
              interface ILendingPoolAddressesProvider {
                event MarketIdSet(string newMarketId);
                event LendingPoolUpdated(address indexed newAddress);
                event ConfigurationAdminUpdated(address indexed newAddress);
                event EmergencyAdminUpdated(address indexed newAddress);
                event LendingPoolConfiguratorUpdated(address indexed newAddress);
                event LendingPoolCollateralManagerUpdated(address indexed newAddress);
                event PriceOracleUpdated(address indexed newAddress);
                event LendingRateOracleUpdated(address indexed newAddress);
                event ProxyCreated(bytes32 id, address indexed newAddress);
                event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy);
                function getMarketId() external view returns (string memory);
                function setMarketId(string calldata marketId) external;
                function setAddress(bytes32 id, address newAddress) external;
                function setAddressAsProxy(bytes32 id, address impl) external;
                function getAddress(bytes32 id) external view returns (address);
                function getLendingPool() external view returns (address);
                function setLendingPoolImpl(address pool) external;
                function getLendingPoolConfigurator() external view returns (address);
                function setLendingPoolConfiguratorImpl(address configurator) external;
                function getLendingPoolCollateralManager() external view returns (address);
                function setLendingPoolCollateralManager(address manager) external;
                function getPoolAdmin() external view returns (address);
                function setPoolAdmin(address admin) external;
                function getEmergencyAdmin() external view returns (address);
                function setEmergencyAdmin(address admin) external;
                function getPriceOracle() external view returns (address);
                function setPriceOracle(address priceOracle) external;
                function getLendingRateOracle() external view returns (address);
                function setLendingRateOracle(address lendingRateOracle) external;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {ILendingPoolAddressesProvider} from './ILendingPoolAddressesProvider.sol';
              import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
              interface ILendingPool {
                /**
                 * @dev Emitted on deposit()
                 * @param reserve The address of the underlying asset of the reserve
                 * @param user The address initiating the deposit
                 * @param onBehalfOf The beneficiary of the deposit, receiving the aTokens
                 * @param amount The amount deposited
                 * @param referral The referral code used
                 **/
                event Deposit(
                  address indexed reserve,
                  address user,
                  address indexed onBehalfOf,
                  uint256 amount,
                  uint16 indexed referral
                );
                /**
                 * @dev Emitted on withdraw()
                 * @param reserve The address of the underlyng asset being withdrawn
                 * @param user The address initiating the withdrawal, owner of aTokens
                 * @param to Address that will receive the underlying
                 * @param amount The amount to be withdrawn
                 **/
                event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
                /**
                 * @dev Emitted on borrow() and flashLoan() when debt needs to be opened
                 * @param reserve The address of the underlying asset being borrowed
                 * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just
                 * initiator of the transaction on flashLoan()
                 * @param onBehalfOf The address that will be getting the debt
                 * @param amount The amount borrowed out
                 * @param borrowRateMode The rate mode: 1 for Stable, 2 for Variable
                 * @param borrowRate The numeric rate at which the user has borrowed
                 * @param referral The referral code used
                 **/
                event Borrow(
                  address indexed reserve,
                  address user,
                  address indexed onBehalfOf,
                  uint256 amount,
                  uint256 borrowRateMode,
                  uint256 borrowRate,
                  uint16 indexed referral
                );
                /**
                 * @dev Emitted on repay()
                 * @param reserve The address of the underlying asset of the reserve
                 * @param user The beneficiary of the repayment, getting his debt reduced
                 * @param repayer The address of the user initiating the repay(), providing the funds
                 * @param amount The amount repaid
                 **/
                event Repay(
                  address indexed reserve,
                  address indexed user,
                  address indexed repayer,
                  uint256 amount
                );
                /**
                 * @dev Emitted on swapBorrowRateMode()
                 * @param reserve The address of the underlying asset of the reserve
                 * @param user The address of the user swapping his rate mode
                 * @param rateMode The rate mode that the user wants to swap to
                 **/
                event Swap(address indexed reserve, address indexed user, uint256 rateMode);
                /**
                 * @dev Emitted on setUserUseReserveAsCollateral()
                 * @param reserve The address of the underlying asset of the reserve
                 * @param user The address of the user enabling the usage as collateral
                 **/
                event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
                /**
                 * @dev Emitted on setUserUseReserveAsCollateral()
                 * @param reserve The address of the underlying asset of the reserve
                 * @param user The address of the user enabling the usage as collateral
                 **/
                event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
                /**
                 * @dev Emitted on rebalanceStableBorrowRate()
                 * @param reserve The address of the underlying asset of the reserve
                 * @param user The address of the user for which the rebalance has been executed
                 **/
                event RebalanceStableBorrowRate(address indexed reserve, address indexed user);
                /**
                 * @dev Emitted on flashLoan()
                 * @param target The address of the flash loan receiver contract
                 * @param initiator The address initiating the flash loan
                 * @param asset The address of the asset being flash borrowed
                 * @param amount The amount flash borrowed
                 * @param premium The fee flash borrowed
                 * @param referralCode The referral code used
                 **/
                event FlashLoan(
                  address indexed target,
                  address indexed initiator,
                  address indexed asset,
                  uint256 amount,
                  uint256 premium,
                  uint16 referralCode
                );
                /**
                 * @dev Emitted when the pause is triggered.
                 */
                event Paused();
                /**
                 * @dev Emitted when the pause is lifted.
                 */
                event Unpaused();
                /**
                 * @dev Emitted when a borrower is liquidated. This event is emitted by the LendingPool via
                 * LendingPoolCollateral manager using a DELEGATECALL
                 * This allows to have the events in the generated ABI for LendingPool.
                 * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                 * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                 * @param user The address of the borrower getting liquidated
                 * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                 * @param liquidatedCollateralAmount The amount of collateral received by the liiquidator
                 * @param liquidator The address of the liquidator
                 * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
                 * to receive the underlying collateral asset directly
                 **/
                event LiquidationCall(
                  address indexed collateralAsset,
                  address indexed debtAsset,
                  address indexed user,
                  uint256 debtToCover,
                  uint256 liquidatedCollateralAmount,
                  address liquidator,
                  bool receiveAToken
                );
                /**
                 * @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared
                 * in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal,
                 * the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it
                 * gets added to the LendingPool ABI
                 * @param reserve The address of the underlying asset of the reserve
                 * @param liquidityRate The new liquidity rate
                 * @param stableBorrowRate The new stable borrow rate
                 * @param variableBorrowRate The new variable borrow rate
                 * @param liquidityIndex The new liquidity index
                 * @param variableBorrowIndex The new variable borrow index
                 **/
                event ReserveDataUpdated(
                  address indexed reserve,
                  uint256 liquidityRate,
                  uint256 stableBorrowRate,
                  uint256 variableBorrowRate,
                  uint256 liquidityIndex,
                  uint256 variableBorrowIndex
                );
                /**
                 * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                 * - E.g. User deposits 100 USDC and gets in return 100 aUSDC
                 * @param asset The address of the underlying asset to deposit
                 * @param amount The amount to be deposited
                 * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
                 *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
                 *   is a different wallet
                 * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                 *   0 if the action is executed directly by the user, without any middle-man
                 **/
                function deposit(
                  address asset,
                  uint256 amount,
                  address onBehalfOf,
                  uint16 referralCode
                ) external;
                /**
                 * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
                 * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
                 * @param asset The address of the underlying asset to withdraw
                 * @param amount The underlying amount to be withdrawn
                 *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
                 * @param to Address that will receive the underlying, same as msg.sender if the user
                 *   wants to receive it on his own wallet, or a different address if the beneficiary is a
                 *   different wallet
                 * @return The final amount withdrawn
                 **/
                function withdraw(
                  address asset,
                  uint256 amount,
                  address to
                ) external returns (uint256);
                /**
                 * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
                 * already deposited enough collateral, or he was given enough allowance by a credit delegator on the
                 * corresponding debt token (StableDebtToken or VariableDebtToken)
                 * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
                 *   and 100 stable/variable debt tokens, depending on the `interestRateMode`
                 * @param asset The address of the underlying asset to borrow
                 * @param amount The amount to be borrowed
                 * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable
                 * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                 *   0 if the action is executed directly by the user, without any middle-man
                 * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself
                 * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator
                 * if he has been given credit delegation allowance
                 **/
                function borrow(
                  address asset,
                  uint256 amount,
                  uint256 interestRateMode,
                  uint16 referralCode,
                  address onBehalfOf
                ) external;
                /**
                 * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned
                 * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address
                 * @param asset The address of the borrowed underlying asset previously borrowed
                 * @param amount The amount to repay
                 * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
                 * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
                 * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
                 * user calling the function if he wants to reduce/remove his own debt, or the address of any other
                 * other borrower whose debt should be removed
                 * @return The final amount repaid
                 **/
                function repay(
                  address asset,
                  uint256 amount,
                  uint256 rateMode,
                  address onBehalfOf
                ) external returns (uint256);
                /**
                 * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa
                 * @param asset The address of the underlying asset borrowed
                 * @param rateMode The rate mode that the user wants to swap to
                 **/
                function swapBorrowRateMode(address asset, uint256 rateMode) external;
                /**
                 * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve.
                 * - Users can be rebalanced if the following conditions are satisfied:
                 *     1. Usage ratio is above 95%
                 *     2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been
                 *        borrowed at a stable rate and depositors are not earning enough
                 * @param asset The address of the underlying asset borrowed
                 * @param user The address of the user to be rebalanced
                 **/
                function rebalanceStableBorrowRate(address asset, address user) external;
                /**
                 * @dev Allows depositors to enable/disable a specific deposited asset as collateral
                 * @param asset The address of the underlying asset deposited
                 * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise
                 **/
                function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;
                /**
                 * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
                 * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
                 *   a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
                 * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                 * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                 * @param user The address of the borrower getting liquidated
                 * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                 * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
                 * to receive the underlying collateral asset directly
                 **/
                function liquidationCall(
                  address collateralAsset,
                  address debtAsset,
                  address user,
                  uint256 debtToCover,
                  bool receiveAToken
                ) external;
                /**
                 * @dev Allows smartcontracts to access the liquidity of the pool within one transaction,
                 * as long as the amount taken plus a fee is returned.
                 * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration.
                 * For further details please visit https://developers.aave.com
                 * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface
                 * @param assets The addresses of the assets being flash-borrowed
                 * @param amounts The amounts amounts being flash-borrowed
                 * @param modes Types of the debt to open if the flash loan is not returned:
                 *   0 -> Don't open any debt, just revert if funds can't be transferred from the receiver
                 *   1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
                 *   2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
                 * @param onBehalfOf The address  that will receive the debt in the case of using on `modes` 1 or 2
                 * @param params Variadic packed params to pass to the receiver as extra information
                 * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                 *   0 if the action is executed directly by the user, without any middle-man
                 **/
                function flashLoan(
                  address receiverAddress,
                  address[] calldata assets,
                  uint256[] calldata amounts,
                  uint256[] calldata modes,
                  address onBehalfOf,
                  bytes calldata params,
                  uint16 referralCode
                ) external;
                /**
                 * @dev Returns the user account data across all the reserves
                 * @param user The address of the user
                 * @return totalCollateralETH the total collateral in ETH of the user
                 * @return totalDebtETH the total debt in ETH of the user
                 * @return availableBorrowsETH the borrowing power left of the user
                 * @return currentLiquidationThreshold the liquidation threshold of the user
                 * @return ltv the loan to value of the user
                 * @return healthFactor the current health factor of the user
                 **/
                function getUserAccountData(address user)
                  external
                  view
                  returns (
                    uint256 totalCollateralETH,
                    uint256 totalDebtETH,
                    uint256 availableBorrowsETH,
                    uint256 currentLiquidationThreshold,
                    uint256 ltv,
                    uint256 healthFactor
                  );
                function initReserve(
                  address reserve,
                  address aTokenAddress,
                  address stableDebtAddress,
                  address variableDebtAddress,
                  address interestRateStrategyAddress
                ) external;
                function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress)
                  external;
                function setConfiguration(address reserve, uint256 configuration) external;
                /**
                 * @dev Returns the configuration of the reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @return The configuration of the reserve
                 **/
                function getConfiguration(address asset)
                  external
                  view
                  returns (DataTypes.ReserveConfigurationMap memory);
                /**
                 * @dev Returns the configuration of the user across all the reserves
                 * @param user The user address
                 * @return The configuration of the user
                 **/
                function getUserConfiguration(address user)
                  external
                  view
                  returns (DataTypes.UserConfigurationMap memory);
                /**
                 * @dev Returns the normalized income normalized income of the reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @return The reserve's normalized income
                 */
                function getReserveNormalizedIncome(address asset) external view returns (uint256);
                /**
                 * @dev Returns the normalized variable debt per unit of asset
                 * @param asset The address of the underlying asset of the reserve
                 * @return The reserve normalized variable debt
                 */
                function getReserveNormalizedVariableDebt(address asset) external view returns (uint256);
                /**
                 * @dev Returns the state and configuration of the reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @return The state of the reserve
                 **/
                function getReserveData(address asset) external view returns (DataTypes.ReserveData memory);
                function finalizeTransfer(
                  address asset,
                  address from,
                  address to,
                  uint256 amount,
                  uint256 balanceFromAfter,
                  uint256 balanceToBefore
                ) external;
                function getReservesList() external view returns (address[] memory);
                function getAddressesProvider() external view returns (ILendingPoolAddressesProvider);
                function setPause(bool val) external;
                function paused() external view returns (bool);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.6.12;
              import {IERC20} from './IERC20.sol';
              import {SafeMath} from './SafeMath.sol';
              import {Address} from './Address.sol';
              /**
               * @title SafeERC20
               * @dev Wrappers around ERC20 operations that throw on failure (when the token
               * contract returns false). Tokens that return no value (and instead revert or
               * throw on failure) are also supported, non-reverting calls are assumed to be
               * successful.
               * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
               * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
               */
              library SafeERC20 {
                using SafeMath for uint256;
                using Address for address;
                function safeTransfer(
                  IERC20 token,
                  address to,
                  uint256 value
                ) internal {
                  callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                }
                function safeTransferFrom(
                  IERC20 token,
                  address from,
                  address to,
                  uint256 value
                ) internal {
                  callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                }
                function safeApprove(
                  IERC20 token,
                  address spender,
                  uint256 value
                ) internal {
                  require(
                    (value == 0) || (token.allowance(address(this), spender) == 0),
                    'SafeERC20: approve from non-zero to non-zero allowance'
                  );
                  callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                }
                function callOptionalReturn(IERC20 token, bytes memory data) private {
                  require(address(token).isContract(), 'SafeERC20: call to non-contract');
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success, bytes memory returndata) = address(token).call(data);
                  require(success, 'SafeERC20: low-level call failed');
                  if (returndata.length > 0) {
                    // Return data is optional
                    // solhint-disable-next-line max-line-length
                    require(abi.decode(returndata, (bool)), 'SafeERC20: ERC20 operation did not succeed');
                  }
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Errors} from '../helpers/Errors.sol';
              import {DataTypes} from '../types/DataTypes.sol';
              /**
               * @title ReserveConfiguration library
               * @author Aave
               * @notice Implements the bitmap logic to handle the reserve configuration
               */
              library ReserveConfiguration {
                uint256 constant LTV_MASK =                   0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000; // prettier-ignore
                uint256 constant LIQUIDATION_THRESHOLD_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF; // prettier-ignore
                uint256 constant LIQUIDATION_BONUS_MASK =     0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFF; // prettier-ignore
                uint256 constant DECIMALS_MASK =              0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFF; // prettier-ignore
                uint256 constant ACTIVE_MASK =                0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFF; // prettier-ignore
                uint256 constant FROZEN_MASK =                0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF; // prettier-ignore
                uint256 constant BORROWING_MASK =             0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFF; // prettier-ignore
                uint256 constant STABLE_BORROWING_MASK =      0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFF; // prettier-ignore
                uint256 constant RESERVE_FACTOR_MASK =        0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore
                /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed
                uint256 constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16;
                uint256 constant LIQUIDATION_BONUS_START_BIT_POSITION = 32;
                uint256 constant RESERVE_DECIMALS_START_BIT_POSITION = 48;
                uint256 constant IS_ACTIVE_START_BIT_POSITION = 56;
                uint256 constant IS_FROZEN_START_BIT_POSITION = 57;
                uint256 constant BORROWING_ENABLED_START_BIT_POSITION = 58;
                uint256 constant STABLE_BORROWING_ENABLED_START_BIT_POSITION = 59;
                uint256 constant RESERVE_FACTOR_START_BIT_POSITION = 64;
                uint256 constant MAX_VALID_LTV = 65535;
                uint256 constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535;
                uint256 constant MAX_VALID_LIQUIDATION_BONUS = 65535;
                uint256 constant MAX_VALID_DECIMALS = 255;
                uint256 constant MAX_VALID_RESERVE_FACTOR = 65535;
                /**
                 * @dev Sets the Loan to Value of the reserve
                 * @param self The reserve configuration
                 * @param ltv the new ltv
                 **/
                function setLtv(DataTypes.ReserveConfigurationMap memory self, uint256 ltv) internal pure {
                  require(ltv <= MAX_VALID_LTV, Errors.RC_INVALID_LTV);
                  self.data = (self.data & LTV_MASK) | ltv;
                }
                /**
                 * @dev Gets the Loan to Value of the reserve
                 * @param self The reserve configuration
                 * @return The loan to value
                 **/
                function getLtv(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) {
                  return self.data & ~LTV_MASK;
                }
                /**
                 * @dev Sets the liquidation threshold of the reserve
                 * @param self The reserve configuration
                 * @param threshold The new liquidation threshold
                 **/
                function setLiquidationThreshold(DataTypes.ReserveConfigurationMap memory self, uint256 threshold)
                  internal
                  pure
                {
                  require(threshold <= MAX_VALID_LIQUIDATION_THRESHOLD, Errors.RC_INVALID_LIQ_THRESHOLD);
                  self.data =
                    (self.data & LIQUIDATION_THRESHOLD_MASK) |
                    (threshold << LIQUIDATION_THRESHOLD_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the liquidation threshold of the reserve
                 * @param self The reserve configuration
                 * @return The liquidation threshold
                 **/
                function getLiquidationThreshold(DataTypes.ReserveConfigurationMap storage self)
                  internal
                  view
                  returns (uint256)
                {
                  return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION;
                }
                /**
                 * @dev Sets the liquidation bonus of the reserve
                 * @param self The reserve configuration
                 * @param bonus The new liquidation bonus
                 **/
                function setLiquidationBonus(DataTypes.ReserveConfigurationMap memory self, uint256 bonus) internal pure {
                  require(bonus <= MAX_VALID_LIQUIDATION_BONUS, Errors.RC_INVALID_LIQ_BONUS);
                  self.data =
                    (self.data & LIQUIDATION_BONUS_MASK) |
                    (bonus << LIQUIDATION_BONUS_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the liquidation bonus of the reserve
                 * @param self The reserve configuration
                 * @return The liquidation bonus
                 **/
                function getLiquidationBonus(DataTypes.ReserveConfigurationMap storage self)
                  internal
                  view
                  returns (uint256)
                {
                  return (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION;
                }
                /**
                 * @dev Sets the decimals of the underlying asset of the reserve
                 * @param self The reserve configuration
                 * @param decimals The decimals
                 **/
                function setDecimals(DataTypes.ReserveConfigurationMap memory self, uint256 decimals) internal pure {
                  require(decimals <= MAX_VALID_DECIMALS, Errors.RC_INVALID_DECIMALS);
                  self.data = (self.data & DECIMALS_MASK) | (decimals << RESERVE_DECIMALS_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the decimals of the underlying asset of the reserve
                 * @param self The reserve configuration
                 * @return The decimals of the asset
                 **/
                function getDecimals(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) {
                  return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION;
                }
                /**
                 * @dev Sets the active state of the reserve
                 * @param self The reserve configuration
                 * @param active The active state
                 **/
                function setActive(DataTypes.ReserveConfigurationMap memory self, bool active) internal pure {
                  self.data =
                    (self.data & ACTIVE_MASK) |
                    (uint256(active ? 1 : 0) << IS_ACTIVE_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the active state of the reserve
                 * @param self The reserve configuration
                 * @return The active state
                 **/
                function getActive(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) {
                  return (self.data & ~ACTIVE_MASK) != 0;
                }
                /**
                 * @dev Sets the frozen state of the reserve
                 * @param self The reserve configuration
                 * @param frozen The frozen state
                 **/
                function setFrozen(DataTypes.ReserveConfigurationMap memory self, bool frozen) internal pure {
                  self.data =
                    (self.data & FROZEN_MASK) |
                    (uint256(frozen ? 1 : 0) << IS_FROZEN_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the frozen state of the reserve
                 * @param self The reserve configuration
                 * @return The frozen state
                 **/
                function getFrozen(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) {
                  return (self.data & ~FROZEN_MASK) != 0;
                }
                /**
                 * @dev Enables or disables borrowing on the reserve
                 * @param self The reserve configuration
                 * @param enabled True if the borrowing needs to be enabled, false otherwise
                 **/
                function setBorrowingEnabled(DataTypes.ReserveConfigurationMap memory self, bool enabled) internal pure {
                  self.data =
                    (self.data & BORROWING_MASK) |
                    (uint256(enabled ? 1 : 0) << BORROWING_ENABLED_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the borrowing state of the reserve
                 * @param self The reserve configuration
                 * @return The borrowing state
                 **/
                function getBorrowingEnabled(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) {
                  return (self.data & ~BORROWING_MASK) != 0;
                }
                /**
                 * @dev Enables or disables stable rate borrowing on the reserve
                 * @param self The reserve configuration
                 * @param enabled True if the stable rate borrowing needs to be enabled, false otherwise
                 **/
                function setStableRateBorrowingEnabled(DataTypes.ReserveConfigurationMap memory self, bool enabled)
                  internal
                  pure
                {
                  self.data =
                    (self.data & STABLE_BORROWING_MASK) |
                    (uint256(enabled ? 1 : 0) << STABLE_BORROWING_ENABLED_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the stable rate borrowing state of the reserve
                 * @param self The reserve configuration
                 * @return The stable rate borrowing state
                 **/
                function getStableRateBorrowingEnabled(DataTypes.ReserveConfigurationMap storage self)
                  internal
                  view
                  returns (bool)
                {
                  return (self.data & ~STABLE_BORROWING_MASK) != 0;
                }
                /**
                 * @dev Sets the reserve factor of the reserve
                 * @param self The reserve configuration
                 * @param reserveFactor The reserve factor
                 **/
                function setReserveFactor(DataTypes.ReserveConfigurationMap memory self, uint256 reserveFactor)
                  internal
                  pure
                {
                  require(reserveFactor <= MAX_VALID_RESERVE_FACTOR, Errors.RC_INVALID_RESERVE_FACTOR);
                  self.data =
                    (self.data & RESERVE_FACTOR_MASK) |
                    (reserveFactor << RESERVE_FACTOR_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the reserve factor of the reserve
                 * @param self The reserve configuration
                 * @return The reserve factor
                 **/
                function getReserveFactor(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) {
                  return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION;
                }
                /**
                 * @dev Gets the configuration flags of the reserve
                 * @param self The reserve configuration
                 * @return The state flags representing active, frozen, borrowing enabled, stableRateBorrowing enabled
                 **/
                function getFlags(DataTypes.ReserveConfigurationMap storage self)
                  internal
                  view
                  returns (
                    bool,
                    bool,
                    bool,
                    bool
                  )
                {
                  uint256 dataLocal = self.data;
                  return (
                    (dataLocal & ~ACTIVE_MASK) != 0,
                    (dataLocal & ~FROZEN_MASK) != 0,
                    (dataLocal & ~BORROWING_MASK) != 0,
                    (dataLocal & ~STABLE_BORROWING_MASK) != 0
                  );
                }
                /**
                 * @dev Gets the configuration paramters of the reserve
                 * @param self The reserve configuration
                 * @return The state params representing ltv, liquidation threshold, liquidation bonus, the reserve decimals
                 **/
                function getParams(DataTypes.ReserveConfigurationMap storage self)
                  internal
                  view
                  returns (
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256
                  )
                {
                  uint256 dataLocal = self.data;
                  return (
                    dataLocal & ~LTV_MASK,
                    (dataLocal & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION,
                    (dataLocal & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION,
                    (dataLocal & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION,
                    (dataLocal & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION
                  );
                }
                /**
                 * @dev Gets the configuration paramters of the reserve from a memory object
                 * @param self The reserve configuration
                 * @return The state params representing ltv, liquidation threshold, liquidation bonus, the reserve decimals
                 **/
                function getParamsMemory(DataTypes.ReserveConfigurationMap memory self)
                  internal
                  pure
                  returns (
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256
                  )
                {
                  return (
                    self.data & ~LTV_MASK,
                    (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION,
                    (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION,
                    (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION,
                    (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION
                  );
                }
                /**
                 * @dev Gets the configuration flags of the reserve from a memory object
                 * @param self The reserve configuration
                 * @return The state flags representing active, frozen, borrowing enabled, stableRateBorrowing enabled
                 **/
                function getFlagsMemory(DataTypes.ReserveConfigurationMap memory self)
                  internal
                  pure
                  returns (
                    bool,
                    bool,
                    bool,
                    bool
                  )
                {
                  return (
                    (self.data & ~ACTIVE_MASK) != 0,
                    (self.data & ~FROZEN_MASK) != 0,
                    (self.data & ~BORROWING_MASK) != 0,
                    (self.data & ~STABLE_BORROWING_MASK) != 0
                  );
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              library DataTypes {
                // refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties.
                struct ReserveData {
                  //stores the reserve configuration
                  ReserveConfigurationMap configuration;
                  //the liquidity index. Expressed in ray
                  uint128 liquidityIndex;
                  //variable borrow index. Expressed in ray
                  uint128 variableBorrowIndex;
                  //the current supply rate. Expressed in ray
                  uint128 currentLiquidityRate;
                  //the current variable borrow rate. Expressed in ray
                  uint128 currentVariableBorrowRate;
                  //the current stable borrow rate. Expressed in ray
                  uint128 currentStableBorrowRate;
                  uint40 lastUpdateTimestamp;
                  //tokens addresses
                  address aTokenAddress;
                  address stableDebtTokenAddress;
                  address variableDebtTokenAddress;
                  //address of the interest rate strategy
                  address interestRateStrategyAddress;
                  //the id of the reserve. Represents the position in the list of the active reserves
                  uint8 id;
                }
                struct ReserveConfigurationMap {
                  //bit 0-15: LTV
                  //bit 16-31: Liq. threshold
                  //bit 32-47: Liq. bonus
                  //bit 48-55: Decimals
                  //bit 56: Reserve is active
                  //bit 57: reserve is frozen
                  //bit 58: borrowing is enabled
                  //bit 59: stable rate borrowing enabled
                  //bit 60-63: reserved
                  //bit 64-79: reserve factor
                  uint256 data;
                }
                struct UserConfigurationMap {
                  uint256 data;
                }
                enum InterestRateMode {NONE, STABLE, VARIABLE}
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @dev Wrappers over Solidity's arithmetic operations with added overflow
               * checks.
               *
               * Arithmetic operations in Solidity wrap on overflow. This can easily result
               * in bugs, because programmers usually assume that an overflow raises an
               * error, which is the standard behavior in high level programming languages.
               * `SafeMath` restores this intuition by reverting the transaction when an
               * operation overflows.
               *
               * Using this library instead of the unchecked operations eliminates an entire
               * class of bugs, so it's recommended to use it always.
               */
              library SafeMath {
                /**
                 * @dev Returns the addition of two unsigned integers, reverting on
                 * overflow.
                 *
                 * Counterpart to Solidity's `+` operator.
                 *
                 * Requirements:
                 * - Addition cannot overflow.
                 */
                function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  require(c >= a, 'SafeMath: addition overflow');
                  return c;
                }
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting on
                 * overflow (when the result is negative).
                 *
                 * Counterpart to Solidity's `-` operator.
                 *
                 * Requirements:
                 * - Subtraction cannot overflow.
                 */
                function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  return sub(a, b, 'SafeMath: subtraction overflow');
                }
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                 * overflow (when the result is negative).
                 *
                 * Counterpart to Solidity's `-` operator.
                 *
                 * Requirements:
                 * - Subtraction cannot overflow.
                 */
                function sub(
                  uint256 a,
                  uint256 b,
                  string memory errorMessage
                ) internal pure returns (uint256) {
                  require(b <= a, errorMessage);
                  uint256 c = a - b;
                  return c;
                }
                /**
                 * @dev Returns the multiplication of two unsigned integers, reverting on
                 * overflow.
                 *
                 * Counterpart to Solidity's `*` operator.
                 *
                 * Requirements:
                 * - Multiplication cannot overflow.
                 */
                function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                  if (a == 0) {
                    return 0;
                  }
                  uint256 c = a * b;
                  require(c / a == b, 'SafeMath: multiplication overflow');
                  return c;
                }
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts on
                 * division by zero. The result is rounded towards zero.
                 *
                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                 * uses an invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  return div(a, b, 'SafeMath: division by zero');
                }
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                 * division by zero. The result is rounded towards zero.
                 *
                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                 * uses an invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function div(
                  uint256 a,
                  uint256 b,
                  string memory errorMessage
                ) internal pure returns (uint256) {
                  // Solidity only automatically asserts when dividing by 0
                  require(b > 0, errorMessage);
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  return c;
                }
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts when dividing by zero.
                 *
                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                 * invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                  return mod(a, b, 'SafeMath: modulo by zero');
                }
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts with custom message when dividing by zero.
                 *
                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                 * invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function mod(
                  uint256 a,
                  uint256 b,
                  string memory errorMessage
                ) internal pure returns (uint256) {
                  require(b != 0, errorMessage);
                  return a % b;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title Errors library
               * @author Aave
               * @notice Defines the error messages emitted by the different contracts of the Aave protocol
               * @dev Error messages prefix glossary:
               *  - VL = ValidationLogic
               *  - MATH = Math libraries
               *  - CT = Common errors between tokens (AToken, VariableDebtToken and StableDebtToken)
               *  - AT = AToken
               *  - SDT = StableDebtToken
               *  - VDT = VariableDebtToken
               *  - LP = LendingPool
               *  - LPAPR = LendingPoolAddressesProviderRegistry
               *  - LPC = LendingPoolConfiguration
               *  - RL = ReserveLogic
               *  - LPCM = LendingPoolCollateralManager
               *  - P = Pausable
               */
              library Errors {
                //common errors
                string public constant CALLER_NOT_POOL_ADMIN = '33'; // 'The caller must be the pool admin'
                string public constant BORROW_ALLOWANCE_NOT_ENOUGH = '59'; // User borrows on behalf, but allowance are too small
                //contract specific errors
                string public constant VL_INVALID_AMOUNT = '1'; // 'Amount must be greater than 0'
                string public constant VL_NO_ACTIVE_RESERVE = '2'; // 'Action requires an active reserve'
                string public constant VL_RESERVE_FROZEN = '3'; // 'Action cannot be performed because the reserve is frozen'
                string public constant VL_CURRENT_AVAILABLE_LIQUIDITY_NOT_ENOUGH = '4'; // 'The current liquidity is not enough'
                string public constant VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE = '5'; // 'User cannot withdraw more than the available balance'
                string public constant VL_TRANSFER_NOT_ALLOWED = '6'; // 'Transfer cannot be allowed.'
                string public constant VL_BORROWING_NOT_ENABLED = '7'; // 'Borrowing is not enabled'
                string public constant VL_INVALID_INTEREST_RATE_MODE_SELECTED = '8'; // 'Invalid interest rate mode selected'
                string public constant VL_COLLATERAL_BALANCE_IS_0 = '9'; // 'The collateral balance is 0'
                string public constant VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '10'; // 'Health factor is lesser than the liquidation threshold'
                string public constant VL_COLLATERAL_CANNOT_COVER_NEW_BORROW = '11'; // 'There is not enough collateral to cover a new borrow'
                string public constant VL_STABLE_BORROWING_NOT_ENABLED = '12'; // stable borrowing not enabled
                string public constant VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY = '13'; // collateral is (mostly) the same currency that is being borrowed
                string public constant VL_AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE = '14'; // 'The requested amount is greater than the max loan size in stable rate mode
                string public constant VL_NO_DEBT_OF_SELECTED_TYPE = '15'; // 'for repayment of stable debt, the user needs to have stable debt, otherwise, he needs to have variable debt'
                string public constant VL_NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '16'; // 'To repay on behalf of an user an explicit amount to repay is needed'
                string public constant VL_NO_STABLE_RATE_LOAN_IN_RESERVE = '17'; // 'User does not have a stable rate loan in progress on this reserve'
                string public constant VL_NO_VARIABLE_RATE_LOAN_IN_RESERVE = '18'; // 'User does not have a variable rate loan in progress on this reserve'
                string public constant VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0 = '19'; // 'The underlying balance needs to be greater than 0'
                string public constant VL_DEPOSIT_ALREADY_IN_USE = '20'; // 'User deposit is already being used as collateral'
                string public constant LP_NOT_ENOUGH_STABLE_BORROW_BALANCE = '21'; // 'User does not have any stable rate loan for this reserve'
                string public constant LP_INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '22'; // 'Interest rate rebalance conditions were not met'
                string public constant LP_LIQUIDATION_CALL_FAILED = '23'; // 'Liquidation call failed'
                string public constant LP_NOT_ENOUGH_LIQUIDITY_TO_BORROW = '24'; // 'There is not enough liquidity available to borrow'
                string public constant LP_REQUESTED_AMOUNT_TOO_SMALL = '25'; // 'The requested amount is too small for a FlashLoan.'
                string public constant LP_INCONSISTENT_PROTOCOL_ACTUAL_BALANCE = '26'; // 'The actual balance of the protocol is inconsistent'
                string public constant LP_CALLER_NOT_LENDING_POOL_CONFIGURATOR = '27'; // 'The caller of the function is not the lending pool configurator'
                string public constant LP_INCONSISTENT_FLASHLOAN_PARAMS = '28';
                string public constant CT_CALLER_MUST_BE_LENDING_POOL = '29'; // 'The caller of this function must be a lending pool'
                string public constant CT_CANNOT_GIVE_ALLOWANCE_TO_HIMSELF = '30'; // 'User cannot give allowance to himself'
                string public constant CT_TRANSFER_AMOUNT_NOT_GT_0 = '31'; // 'Transferred amount needs to be greater than zero'
                string public constant RL_RESERVE_ALREADY_INITIALIZED = '32'; // 'Reserve has already been initialized'
                string public constant LPC_RESERVE_LIQUIDITY_NOT_0 = '34'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_ATOKEN_POOL_ADDRESS = '35'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_STABLE_DEBT_TOKEN_POOL_ADDRESS = '36'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_VARIABLE_DEBT_TOKEN_POOL_ADDRESS = '37'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_STABLE_DEBT_TOKEN_UNDERLYING_ADDRESS = '38'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_VARIABLE_DEBT_TOKEN_UNDERLYING_ADDRESS = '39'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_ADDRESSES_PROVIDER_ID = '40'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_CONFIGURATION = '75'; // 'Invalid risk parameters for the reserve'
                string public constant LPC_CALLER_NOT_EMERGENCY_ADMIN = '76'; // 'The caller must be the emergency admin'
                string public constant LPAPR_PROVIDER_NOT_REGISTERED = '41'; // 'Provider is not registered'
                string public constant LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '42'; // 'Health factor is not below the threshold'
                string public constant LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED = '43'; // 'The collateral chosen cannot be liquidated'
                string public constant LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '44'; // 'User did not borrow the specified currency'
                string public constant LPCM_NOT_ENOUGH_LIQUIDITY_TO_LIQUIDATE = '45'; // "There isn't enough liquidity available to liquidate"
                string public constant LPCM_NO_ERRORS = '46'; // 'No errors'
                string public constant LP_INVALID_FLASHLOAN_MODE = '47'; //Invalid flashloan mode selected
                string public constant MATH_MULTIPLICATION_OVERFLOW = '48';
                string public constant MATH_ADDITION_OVERFLOW = '49';
                string public constant MATH_DIVISION_BY_ZERO = '50';
                string public constant RL_LIQUIDITY_INDEX_OVERFLOW = '51'; //  Liquidity index overflows uint128
                string public constant RL_VARIABLE_BORROW_INDEX_OVERFLOW = '52'; //  Variable borrow index overflows uint128
                string public constant RL_LIQUIDITY_RATE_OVERFLOW = '53'; //  Liquidity rate overflows uint128
                string public constant RL_VARIABLE_BORROW_RATE_OVERFLOW = '54'; //  Variable borrow rate overflows uint128
                string public constant RL_STABLE_BORROW_RATE_OVERFLOW = '55'; //  Stable borrow rate overflows uint128
                string public constant CT_INVALID_MINT_AMOUNT = '56'; //invalid amount to mint
                string public constant LP_FAILED_REPAY_WITH_COLLATERAL = '57';
                string public constant CT_INVALID_BURN_AMOUNT = '58'; //invalid amount to burn
                string public constant LP_FAILED_COLLATERAL_SWAP = '60';
                string public constant LP_INVALID_EQUAL_ASSETS_TO_SWAP = '61';
                string public constant LP_REENTRANCY_NOT_ALLOWED = '62';
                string public constant LP_CALLER_MUST_BE_AN_ATOKEN = '63';
                string public constant LP_IS_PAUSED = '64'; // 'Pool is paused'
                string public constant LP_NO_MORE_RESERVES_ALLOWED = '65';
                string public constant LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN = '66';
                string public constant RC_INVALID_LTV = '67';
                string public constant RC_INVALID_LIQ_THRESHOLD = '68';
                string public constant RC_INVALID_LIQ_BONUS = '69';
                string public constant RC_INVALID_DECIMALS = '70';
                string public constant RC_INVALID_RESERVE_FACTOR = '71';
                string public constant LPAPR_INVALID_ADDRESSES_PROVIDER_ID = '72';
                string public constant VL_INCONSISTENT_FLASHLOAN_PARAMS = '73';
                string public constant LP_INCONSISTENT_PARAMS_LENGTH = '74';
                string public constant UL_INVALID_INDEX = '77';
                string public constant LP_NOT_CONTRACT = '78';
                string public constant SDT_STABLE_DEBT_OVERFLOW = '79';
                string public constant SDT_BURN_EXCEEDS_BALANCE = '80';
                enum CollateralManagerErrors {
                  NO_ERROR,
                  NO_COLLATERAL_AVAILABLE,
                  COLLATERAL_CANNOT_BE_LIQUIDATED,
                  CURRRENCY_NOT_BORROWED,
                  HEALTH_FACTOR_ABOVE_THRESHOLD,
                  NOT_ENOUGH_LIQUIDITY,
                  NO_ACTIVE_RESERVE,
                  HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD,
                  INVALID_EQUAL_ASSETS_TO_SWAP,
                  FROZEN_RESERVE
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {SafeMath} from '../../dependencies/openzeppelin/contracts//SafeMath.sol';
              import {IERC20} from '../../dependencies/openzeppelin/contracts//IERC20.sol';
              import {IAToken} from '../../interfaces/IAToken.sol';
              import {IStableDebtToken} from '../../interfaces/IStableDebtToken.sol';
              import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol';
              import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol';
              import {ILendingPoolCollateralManager} from '../../interfaces/ILendingPoolCollateralManager.sol';
              import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
              import {GenericLogic} from '../libraries/logic/GenericLogic.sol';
              import {Helpers} from '../libraries/helpers/Helpers.sol';
              import {WadRayMath} from '../libraries/math/WadRayMath.sol';
              import {PercentageMath} from '../libraries/math/PercentageMath.sol';
              import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol';
              import {DataTypes} from '../libraries/types/DataTypes.sol';
              import {LendingPoolStorage} from './LendingPoolStorage.sol';
              /**
               * @title LendingPoolCollateralManager contract
               * @author Aave
               * @dev Implements actions involving management of collateral in the protocol, the main one being the liquidations
               * IMPORTANT This contract will run always via DELEGATECALL, through the LendingPool, so the chain of inheritance
               * is the same as the LendingPool, to have compatible storage layouts
               **/
              contract LendingPoolCollateralManager is
                ILendingPoolCollateralManager,
                VersionedInitializable,
                LendingPoolStorage
              {
                using SafeERC20 for IERC20;
                using SafeMath for uint256;
                using WadRayMath for uint256;
                using PercentageMath for uint256;
                uint256 internal constant LIQUIDATION_CLOSE_FACTOR_PERCENT = 5000;
                struct LiquidationCallLocalVars {
                  uint256 userCollateralBalance;
                  uint256 userStableDebt;
                  uint256 userVariableDebt;
                  uint256 maxLiquidatableDebt;
                  uint256 actualDebtToLiquidate;
                  uint256 liquidationRatio;
                  uint256 maxAmountCollateralToLiquidate;
                  uint256 userStableRate;
                  uint256 maxCollateralToLiquidate;
                  uint256 debtAmountNeeded;
                  uint256 healthFactor;
                  uint256 liquidatorPreviousATokenBalance;
                  IAToken collateralAtoken;
                  bool isCollateralEnabled;
                  DataTypes.InterestRateMode borrowRateMode;
                  uint256 errorCode;
                  string errorMsg;
                }
                /**
                 * @dev As thIS contract extends the VersionedInitializable contract to match the state
                 * of the LendingPool contract, the getRevision() function is needed, but the value is not
                 * important, as the initialize() function will never be called here
                 */
                function getRevision() internal pure override returns (uint256) {
                  return 0;
                }
                /**
                 * @dev Function to liquidate a position if its Health Factor drops below 1
                 * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
                 *   a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
                 * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                 * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                 * @param user The address of the borrower getting liquidated
                 * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                 * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
                 * to receive the underlying collateral asset directly
                 **/
                function liquidationCall(
                  address collateralAsset,
                  address debtAsset,
                  address user,
                  uint256 debtToCover,
                  bool receiveAToken
                ) external override returns (uint256, string memory) {
                  DataTypes.ReserveData storage collateralReserve = _reserves[collateralAsset];
                  DataTypes.ReserveData storage debtReserve = _reserves[debtAsset];
                  DataTypes.UserConfigurationMap storage userConfig = _usersConfig[user];
                  LiquidationCallLocalVars memory vars;
                  (, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
                    user,
                    _reserves,
                    userConfig,
                    _reservesList,
                    _reservesCount,
                    _addressesProvider.getPriceOracle()
                  );
                  (vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(user, debtReserve);
                  (vars.errorCode, vars.errorMsg) = ValidationLogic.validateLiquidationCall(
                    collateralReserve,
                    debtReserve,
                    userConfig,
                    vars.healthFactor,
                    vars.userStableDebt,
                    vars.userVariableDebt
                  );
                  if (Errors.CollateralManagerErrors(vars.errorCode) != Errors.CollateralManagerErrors.NO_ERROR) {
                    return (vars.errorCode, vars.errorMsg);
                  }
                  vars.collateralAtoken = IAToken(collateralReserve.aTokenAddress);
                  vars.userCollateralBalance = vars.collateralAtoken.balanceOf(user);
                  vars.maxLiquidatableDebt = vars.userStableDebt.add(vars.userVariableDebt).percentMul(
                    LIQUIDATION_CLOSE_FACTOR_PERCENT
                  );
                  vars.actualDebtToLiquidate = debtToCover > vars.maxLiquidatableDebt
                    ? vars.maxLiquidatableDebt
                    : debtToCover;
                  (
                    vars.maxCollateralToLiquidate,
                    vars.debtAmountNeeded
                  ) = _calculateAvailableCollateralToLiquidate(
                    collateralReserve,
                    debtReserve,
                    collateralAsset,
                    debtAsset,
                    vars.actualDebtToLiquidate,
                    vars.userCollateralBalance
                  );
                  // If debtAmountNeeded < actualDebtToLiquidate, there isn't enough
                  // collateral to cover the actual amount that is being liquidated, hence we liquidate
                  // a smaller amount
                  if (vars.debtAmountNeeded < vars.actualDebtToLiquidate) {
                    vars.actualDebtToLiquidate = vars.debtAmountNeeded;
                  }
                  // If the liquidator reclaims the underlying asset, we make sure there is enough available liquidity in the
                  // collateral reserve
                  if (!receiveAToken) {
                    uint256 currentAvailableCollateral =
                      IERC20(collateralAsset).balanceOf(address(vars.collateralAtoken));
                    if (currentAvailableCollateral < vars.maxCollateralToLiquidate) {
                      return (
                        uint256(Errors.CollateralManagerErrors.NOT_ENOUGH_LIQUIDITY),
                        Errors.LPCM_NOT_ENOUGH_LIQUIDITY_TO_LIQUIDATE
                      );
                    }
                  }
                  debtReserve.updateState();
                  if (vars.userVariableDebt >= vars.actualDebtToLiquidate) {
                    IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
                      user,
                      vars.actualDebtToLiquidate,
                      debtReserve.variableBorrowIndex
                    );
                  } else {
                    // If the user doesn't have variable debt, no need to try to burn variable debt tokens
                    if (vars.userVariableDebt > 0) {
                      IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
                        user,
                        vars.userVariableDebt,
                        debtReserve.variableBorrowIndex
                      );
                    }
                    IStableDebtToken(debtReserve.stableDebtTokenAddress).burn(
                      user,
                      vars.actualDebtToLiquidate.sub(vars.userVariableDebt)
                    );
                  }
                  debtReserve.updateInterestRates(
                    debtAsset,
                    debtReserve.aTokenAddress,
                    vars.actualDebtToLiquidate,
                    0
                  );
                  if (receiveAToken) {
                    vars.liquidatorPreviousATokenBalance = IERC20(vars.collateralAtoken).balanceOf(msg.sender);
                    vars.collateralAtoken.transferOnLiquidation(user, msg.sender, vars.maxCollateralToLiquidate);
                    if (vars.liquidatorPreviousATokenBalance == 0) {
                      DataTypes.UserConfigurationMap storage liquidatorConfig = _usersConfig[msg.sender];
                      liquidatorConfig.setUsingAsCollateral(collateralReserve.id, true);
                      emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender);
                    }
                  } else {
                    collateralReserve.updateState();
                    collateralReserve.updateInterestRates(
                      collateralAsset,
                      address(vars.collateralAtoken),
                      0,
                      vars.maxCollateralToLiquidate
                    );
                    // Burn the equivalent amount of aToken, sending the underlying to the liquidator
                    vars.collateralAtoken.burn(
                      user,
                      msg.sender,
                      vars.maxCollateralToLiquidate,
                      collateralReserve.liquidityIndex
                    );
                  }
                  // If the collateral being liquidated is equal to the user balance,
                  // we set the currency as not being used as collateral anymore
                  if (vars.maxCollateralToLiquidate == vars.userCollateralBalance) {
                    userConfig.setUsingAsCollateral(collateralReserve.id, false);
                    emit ReserveUsedAsCollateralDisabled(collateralAsset, user);
                  }
                  // Transfers the debt asset being repaid to the aToken, where the liquidity is kept
                  IERC20(debtAsset).safeTransferFrom(
                    msg.sender,
                    debtReserve.aTokenAddress,
                    vars.actualDebtToLiquidate
                  );
                  emit LiquidationCall(
                    collateralAsset,
                    debtAsset,
                    user,
                    vars.actualDebtToLiquidate,
                    vars.maxCollateralToLiquidate,
                    msg.sender,
                    receiveAToken
                  );
                  return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.LPCM_NO_ERRORS);
                }
                struct AvailableCollateralToLiquidateLocalVars {
                  uint256 userCompoundedBorrowBalance;
                  uint256 liquidationBonus;
                  uint256 collateralPrice;
                  uint256 debtAssetPrice;
                  uint256 maxAmountCollateralToLiquidate;
                  uint256 debtAssetDecimals;
                  uint256 collateralDecimals;
                }
                /**
                 * @dev Calculates how much of a specific collateral can be liquidated, given
                 * a certain amount of debt asset.
                 * - This function needs to be called after all the checks to validate the liquidation have been performed,
                 *   otherwise it might fail.
                 * @param collateralReserve The data of the collateral reserve
                 * @param debtReserve The data of the debt reserve
                 * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                 * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                 * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                 * @param userCollateralBalance The collateral balance for the specific `collateralAsset` of the user being liquidated
                 * @return collateralAmount: The maximum amount that is possible to liquidate given all the liquidation constraints
                 *                           (user balance, close factor)
                 *         debtAmountNeeded: The amount to repay with the liquidation
                 **/
                function _calculateAvailableCollateralToLiquidate(
                  DataTypes.ReserveData storage collateralReserve,
                  DataTypes.ReserveData storage debtReserve,
                  address collateralAsset,
                  address debtAsset,
                  uint256 debtToCover,
                  uint256 userCollateralBalance
                ) internal view returns (uint256, uint256) {
                  uint256 collateralAmount = 0;
                  uint256 debtAmountNeeded = 0;
                  IPriceOracleGetter oracle = IPriceOracleGetter(_addressesProvider.getPriceOracle());
                  AvailableCollateralToLiquidateLocalVars memory vars;
                  vars.collateralPrice = oracle.getAssetPrice(collateralAsset);
                  vars.debtAssetPrice = oracle.getAssetPrice(debtAsset);
                  (, , vars.liquidationBonus, vars.collateralDecimals, ) = collateralReserve
                    .configuration
                    .getParams();
                  vars.debtAssetDecimals = debtReserve.configuration.getDecimals();
                  // This is the maximum possible amount of the selected collateral that can be liquidated, given the
                  // max amount of liquidatable debt
                  vars.maxAmountCollateralToLiquidate = vars
                    .debtAssetPrice
                    .mul(debtToCover)
                    .mul(10**vars.collateralDecimals)
                    .percentMul(vars.liquidationBonus)
                    .div(vars.collateralPrice.mul(10**vars.debtAssetDecimals));
                  if (vars.maxAmountCollateralToLiquidate > userCollateralBalance) {
                    collateralAmount = userCollateralBalance;
                    debtAmountNeeded = vars
                      .collateralPrice
                      .mul(collateralAmount)
                      .mul(10**vars.debtAssetDecimals)
                      .div(vars.debtAssetPrice.mul(10**vars.collateralDecimals))
                      .percentDiv(vars.liquidationBonus);
                  } else {
                    collateralAmount = vars.maxAmountCollateralToLiquidate;
                    debtAmountNeeded = debtToCover;
                  }
                  return (collateralAmount, debtAmountNeeded);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
              import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
              interface IAToken is IERC20, IScaledBalanceToken {
                /**
                 * @dev Emitted after the mint action
                 * @param from The address performing the mint
                 * @param value The amount being
                 * @param index The new liquidity index of the reserve
                 **/
                event Mint(address indexed from, uint256 value, uint256 index);
                /**
                 * @dev Mints `amount` aTokens to `user`
                 * @param user The address receiving the minted tokens
                 * @param amount The amount of tokens getting minted
                 * @param index The new liquidity index of the reserve
                 * @return `true` if the the previous balance of the user was 0
                 */
                function mint(
                  address user,
                  uint256 amount,
                  uint256 index
                ) external returns (bool);
                /**
                 * @dev Emitted after aTokens are burned
                 * @param from The owner of the aTokens, getting them burned
                 * @param target The address that will receive the underlying
                 * @param value The amount being burned
                 * @param index The new liquidity index of the reserve
                 **/
                event Burn(address indexed from, address indexed target, uint256 value, uint256 index);
                /**
                 * @dev Emitted during the transfer action
                 * @param from The user whose tokens are being transferred
                 * @param to The recipient
                 * @param value The amount being transferred
                 * @param index The new liquidity index of the reserve
                 **/
                event BalanceTransfer(address indexed from, address indexed to, uint256 value, uint256 index);
                /**
                 * @dev Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying`
                 * @param user The owner of the aTokens, getting them burned
                 * @param receiverOfUnderlying The address that will receive the underlying
                 * @param amount The amount being burned
                 * @param index The new liquidity index of the reserve
                 **/
                function burn(
                  address user,
                  address receiverOfUnderlying,
                  uint256 amount,
                  uint256 index
                ) external;
                /**
                 * @dev Mints aTokens to the reserve treasury
                 * @param amount The amount of tokens getting minted
                 * @param index The new liquidity index of the reserve
                 */
                function mintToTreasury(uint256 amount, uint256 index) external;
                /**
                 * @dev Transfers aTokens in the event of a borrow being liquidated, in case the liquidators reclaims the aToken
                 * @param from The address getting liquidated, current owner of the aTokens
                 * @param to The recipient
                 * @param value The amount of tokens getting transferred
                 **/
                function transferOnLiquidation(
                  address from,
                  address to,
                  uint256 value
                ) external;
                /**
                 * @dev Transfers the underlying asset to `target`. Used by the LendingPool to transfer
                 * assets in borrow(), withdraw() and flashLoan()
                 * @param user The recipient of the aTokens
                 * @param amount The amount getting transferred
                 * @return The amount transferred
                 **/
                function transferUnderlyingTo(address user, uint256 amount) external returns (uint256);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title IStableDebtToken
               * @notice Defines the interface for the stable debt token
               * @dev It does not inherit from IERC20 to save in code size
               * @author Aave
               **/
              interface IStableDebtToken {
                /**
                 * @dev Emitted when new stable debt is minted
                 * @param user The address of the user who triggered the minting
                 * @param onBehalfOf The recipient of stable debt tokens
                 * @param amount The amount minted
                 * @param currentBalance The current balance of the user
                 * @param balanceIncrease The increase in balance since the last action of the user
                 * @param newRate The rate of the debt after the minting
                 * @param avgStableRate The new average stable rate after the minting
                 * @param newTotalSupply The new total supply of the stable debt token after the action
                 **/
                event Mint(
                  address indexed user,
                  address indexed onBehalfOf,
                  uint256 amount,
                  uint256 currentBalance,
                  uint256 balanceIncrease,
                  uint256 newRate,
                  uint256 avgStableRate,
                  uint256 newTotalSupply
                );
                /**
                 * @dev Emitted when new stable debt is burned
                 * @param user The address of the user
                 * @param amount The amount being burned
                 * @param currentBalance The current balance of the user
                 * @param balanceIncrease The the increase in balance since the last action of the user
                 * @param avgStableRate The new average stable rate after the burning
                 * @param newTotalSupply The new total supply of the stable debt token after the action
                 **/
                event Burn(
                  address indexed user,
                  uint256 amount,
                  uint256 currentBalance,
                  uint256 balanceIncrease,
                  uint256 avgStableRate,
                  uint256 newTotalSupply
                );
                /**
                 * @dev Mints debt token to the `onBehalfOf` address.
                 * - The resulting rate is the weighted average between the rate of the new debt
                 * and the rate of the previous debt
                 * @param user The address receiving the borrowed underlying, being the delegatee in case
                 * of credit delegate, or same as `onBehalfOf` otherwise
                 * @param onBehalfOf The address receiving the debt tokens
                 * @param amount The amount of debt tokens to mint
                 * @param rate The rate of the debt being minted
                 **/
                function mint(
                  address user,
                  address onBehalfOf,
                  uint256 amount,
                  uint256 rate
                ) external returns (bool);
                /**
                 * @dev Burns debt of `user`
                 * - The resulting rate is the weighted average between the rate of the new debt
                 * and the rate of the previous debt
                 * @param user The address of the user getting his debt burned
                 * @param amount The amount of debt tokens getting burned
                 **/
                function burn(address user, uint256 amount) external;
                /**
                 * @dev Returns the average rate of all the stable rate loans.
                 * @return The average stable rate
                 **/
                function getAverageStableRate() external view returns (uint256);
                /**
                 * @dev Returns the stable rate of the user debt
                 * @return The stable rate of the user
                 **/
                function getUserStableRate(address user) external view returns (uint256);
                /**
                 * @dev Returns the timestamp of the last update of the user
                 * @return The timestamp
                 **/
                function getUserLastUpdated(address user) external view returns (uint40);
                /**
                 * @dev Returns the principal, the total supply and the average stable rate
                 **/
                function getSupplyData()
                  external
                  view
                  returns (
                    uint256,
                    uint256,
                    uint256,
                    uint40
                  );
                /**
                 * @dev Returns the timestamp of the last update of the total supply
                 * @return The timestamp
                 **/
                function getTotalSupplyLastUpdated() external view returns (uint40);
                /**
                 * @dev Returns the total supply and the average stable rate
                 **/
                function getTotalSupplyAndAvgRate() external view returns (uint256, uint256);
                /**
                 * @dev Returns the principal debt balance of the user
                 * @return The debt balance of the user since the last burn/mint action
                 **/
                function principalBalanceOf(address user) external view returns (uint256);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
              /**
               * @title IVariableDebtToken
               * @author Aave
               * @notice Defines the basic interface for a variable debt token.
               **/
              interface IVariableDebtToken is IScaledBalanceToken {
                /**
                 * @dev Emitted after the mint action
                 * @param from The address performing the mint
                 * @param onBehalfOf The address of the user on which behalf minting has been performed
                 * @param value The amount to be minted
                 * @param index The last index of the reserve
                 **/
                event Mint(address indexed from, address indexed onBehalfOf, uint256 value, uint256 index);
                /**
                 * @dev Mints debt token to the `onBehalfOf` address
                 * @param user The address receiving the borrowed underlying, being the delegatee in case
                 * of credit delegate, or same as `onBehalfOf` otherwise
                 * @param onBehalfOf The address receiving the debt tokens
                 * @param amount The amount of debt being minted
                 * @param index The variable debt index of the reserve
                 * @return `true` if the the previous balance of the user is 0
                 **/
                function mint(
                  address user,
                  address onBehalfOf,
                  uint256 amount,
                  uint256 index
                ) external returns (bool);
                /**
                 * @dev Emitted when variable debt is burnt
                 * @param user The user which debt has been burned
                 * @param amount The amount of debt being burned
                 * @param index The index of the user
                 **/
                event Burn(address indexed user, uint256 amount, uint256 index);
                /**
                 * @dev Burns user variable debt
                 * @param user The user which debt is burnt
                 * @param index The variable debt index of the reserve
                 **/
                function burn(
                  address user,
                  uint256 amount,
                  uint256 index
                ) external;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @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);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title ILendingPoolCollateralManager
               * @author Aave
               * @notice Defines the actions involving management of collateral in the protocol.
               **/
              interface ILendingPoolCollateralManager {
                /**
                 * @dev Emitted when a borrower is liquidated
                 * @param collateral The address of the collateral being liquidated
                 * @param principal The address of the reserve
                 * @param user The address of the user being liquidated
                 * @param debtToCover The total amount liquidated
                 * @param liquidatedCollateralAmount The amount of collateral being liquidated
                 * @param liquidator The address of the liquidator
                 * @param receiveAToken true if the liquidator wants to receive aTokens, false otherwise
                 **/
                event LiquidationCall(
                  address indexed collateral,
                  address indexed principal,
                  address indexed user,
                  uint256 debtToCover,
                  uint256 liquidatedCollateralAmount,
                  address liquidator,
                  bool receiveAToken
                );
                /**
                 * @dev Emitted when a reserve is disabled as collateral for an user
                 * @param reserve The address of the reserve
                 * @param user The address of the user
                 **/
                event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
                /**
                 * @dev Emitted when a reserve is enabled as collateral for an user
                 * @param reserve The address of the reserve
                 * @param user The address of the user
                 **/
                event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
                /**
                 * @dev Users can invoke this function to liquidate an undercollateralized position.
                 * @param collateral The address of the collateral to liquidated
                 * @param principal The address of the principal reserve
                 * @param user The address of the borrower
                 * @param debtToCover 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 principal,
                  address user,
                  uint256 debtToCover,
                  bool receiveAToken
                ) external returns (uint256, string memory);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title VersionedInitializable
               *
               * @dev Helper contract to implement 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 pure virtual 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;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {ReserveLogic} from './ReserveLogic.sol';
              import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
              import {UserConfiguration} from '../configuration/UserConfiguration.sol';
              import {WadRayMath} from '../math/WadRayMath.sol';
              import {PercentageMath} from '../math/PercentageMath.sol';
              import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
              import {DataTypes} from '../types/DataTypes.sol';
              /**
               * @title GenericLogic library
               * @author Aave
               * @title Implements protocol-level logic to calculate and validate the state of a user
               */
              library GenericLogic {
                using ReserveLogic for DataTypes.ReserveData;
                using SafeMath for uint256;
                using WadRayMath for uint256;
                using PercentageMath for uint256;
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                using UserConfiguration for DataTypes.UserConfigurationMap;
                uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1 ether;
                struct balanceDecreaseAllowedLocalVars {
                  uint256 decimals;
                  uint256 liquidationThreshold;
                  uint256 totalCollateralInETH;
                  uint256 totalDebtInETH;
                  uint256 avgLiquidationThreshold;
                  uint256 amountToDecreaseInETH;
                  uint256 collateralBalanceAfterDecrease;
                  uint256 liquidationThresholdAfterDecrease;
                  uint256 healthFactorAfterDecrease;
                  bool reserveUsageAsCollateralEnabled;
                }
                /**
                 * @dev Checks if a specific balance decrease is allowed
                 * (i.e. doesn't bring the user borrow position health factor under HEALTH_FACTOR_LIQUIDATION_THRESHOLD)
                 * @param asset The address of the underlying asset of the reserve
                 * @param user The address of the user
                 * @param amount The amount to decrease
                 * @param reservesData The data of all the reserves
                 * @param userConfig The user configuration
                 * @param reserves The list of all the active reserves
                 * @param oracle The address of the oracle contract
                 * @return true if the decrease of the balance is allowed
                 **/
                function balanceDecreaseAllowed(
                  address asset,
                  address user,
                  uint256 amount,
                  mapping(address => DataTypes.ReserveData) storage reservesData,
                  DataTypes.UserConfigurationMap calldata userConfig,
                  mapping(uint256 => address) storage reserves,
                  uint256 reservesCount,
                  address oracle
                ) external view returns (bool) {
                  if (!userConfig.isBorrowingAny() || !userConfig.isUsingAsCollateral(reservesData[asset].id)) {
                    return true;
                  }
                  
                  balanceDecreaseAllowedLocalVars memory vars;
                  (, vars.liquidationThreshold, , vars.decimals, ) = reservesData[asset]
                    .configuration
                    .getParams();
                  if (vars.liquidationThreshold == 0) {
                    return true; 
                  }
                  (
                    vars.totalCollateralInETH,
                    vars.totalDebtInETH,
                    ,
                    vars.avgLiquidationThreshold,
                  ) = calculateUserAccountData(user, reservesData, userConfig, reserves, reservesCount, oracle);
                  if (vars.totalDebtInETH == 0) {
                    return true;
                  }
                  vars.amountToDecreaseInETH = IPriceOracleGetter(oracle).getAssetPrice(asset).mul(amount).div(
                    10**vars.decimals
                  );
                  vars.collateralBalanceAfterDecrease = vars.totalCollateralInETH.sub(vars.amountToDecreaseInETH);
                  //if there is a borrow, there can't be 0 collateral
                  if (vars.collateralBalanceAfterDecrease == 0) {
                    return false;
                  }
                  vars.liquidationThresholdAfterDecrease = vars
                    .totalCollateralInETH
                    .mul(vars.avgLiquidationThreshold)
                    .sub(vars.amountToDecreaseInETH.mul(vars.liquidationThreshold))
                    .div(vars.collateralBalanceAfterDecrease);
                  uint256 healthFactorAfterDecrease =
                    calculateHealthFactorFromBalances(
                      vars.collateralBalanceAfterDecrease,
                      vars.totalDebtInETH,
                      vars.liquidationThresholdAfterDecrease
                    );
                  return healthFactorAfterDecrease >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD;
                }
                struct CalculateUserAccountDataVars {
                  uint256 reserveUnitPrice;
                  uint256 tokenUnit;
                  uint256 compoundedLiquidityBalance;
                  uint256 compoundedBorrowBalance;
                  uint256 decimals;
                  uint256 ltv;
                  uint256 liquidationThreshold;
                  uint256 i;
                  uint256 healthFactor;
                  uint256 totalCollateralInETH;
                  uint256 totalDebtInETH;
                  uint256 avgLtv;
                  uint256 avgLiquidationThreshold;
                  uint256 reservesLength;
                  bool healthFactorBelowThreshold;
                  address currentReserveAddress;
                  bool usageAsCollateralEnabled;
                  bool userUsesReserveAsCollateral;
                }
                /**
                 * @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
                 * @param reservesData Data of all the reserves
                 * @param userConfig The configuration of the user
                 * @param reserves The list of the available reserves
                 * @param oracle The price oracle address
                 * @return The total collateral and total debt of the user in ETH, the avg ltv, liquidation threshold and the HF
                 **/
                function calculateUserAccountData(
                  address user,
                  mapping(address => DataTypes.ReserveData) storage reservesData,
                  DataTypes.UserConfigurationMap memory userConfig,
                  mapping(uint256 => address) storage reserves,
                  uint256 reservesCount,
                  address oracle
                )
                  internal
                  view
                  returns (
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256
                  )
                {
                  CalculateUserAccountDataVars memory vars;
                  if (userConfig.isEmpty()) {
                    return (0, 0, 0, 0, uint256(-1));
                  }
                  for (vars.i = 0; vars.i < reservesCount; vars.i++) {
                    if (!userConfig.isUsingAsCollateralOrBorrowing(vars.i)) {
                      continue;
                    }
                    vars.currentReserveAddress = reserves[vars.i];
                    DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress];
                    (vars.ltv, vars.liquidationThreshold, , vars.decimals, ) = currentReserve
                      .configuration
                      .getParams();
                    vars.tokenUnit = 10**vars.decimals;
                    vars.reserveUnitPrice = IPriceOracleGetter(oracle).getAssetPrice(vars.currentReserveAddress);
                    if (vars.liquidationThreshold != 0 && userConfig.isUsingAsCollateral(vars.i)) {
                      vars.compoundedLiquidityBalance = IERC20(currentReserve.aTokenAddress).balanceOf(user);
                      uint256 liquidityBalanceETH =
                        vars.reserveUnitPrice.mul(vars.compoundedLiquidityBalance).div(vars.tokenUnit);
                      vars.totalCollateralInETH = vars.totalCollateralInETH.add(liquidityBalanceETH);
                      vars.avgLtv = vars.avgLtv.add(liquidityBalanceETH.mul(vars.ltv));
                      vars.avgLiquidationThreshold = vars.avgLiquidationThreshold.add(
                        liquidityBalanceETH.mul(vars.liquidationThreshold)
                      );
                    }
                    if (userConfig.isBorrowing(vars.i)) {
                      vars.compoundedBorrowBalance = IERC20(currentReserve.stableDebtTokenAddress).balanceOf(
                        user
                      );
                      vars.compoundedBorrowBalance = vars.compoundedBorrowBalance.add(
                        IERC20(currentReserve.variableDebtTokenAddress).balanceOf(user)
                      );
                      vars.totalDebtInETH = vars.totalDebtInETH.add(
                        vars.reserveUnitPrice.mul(vars.compoundedBorrowBalance).div(vars.tokenUnit)
                      );
                    }
                  }
                  vars.avgLtv = vars.totalCollateralInETH > 0
                    ? vars.avgLtv.div(vars.totalCollateralInETH)
                    : 0;
                  vars.avgLiquidationThreshold = vars.totalCollateralInETH > 0
                    ? vars.avgLiquidationThreshold.div(vars.totalCollateralInETH)
                    : 0;
                  vars.healthFactor = calculateHealthFactorFromBalances(
                    vars.totalCollateralInETH,
                    vars.totalDebtInETH,
                    vars.avgLiquidationThreshold
                  );
                  return (
                    vars.totalCollateralInETH,
                    vars.totalDebtInETH,
                    vars.avgLtv,
                    vars.avgLiquidationThreshold,
                    vars.healthFactor
                  );
                }
                /**
                 * @dev Calculates the health factor from the corresponding balances
                 * @param totalCollateralInETH The total collateral in ETH
                 * @param totalDebtInETH The total debt in ETH
                 * @param liquidationThreshold The avg liquidation threshold
                 * @return The health factor calculated from the balances provided
                 **/
                function calculateHealthFactorFromBalances(
                  uint256 totalCollateralInETH,
                  uint256 totalDebtInETH,
                  uint256 liquidationThreshold
                ) internal pure returns (uint256) {
                  if (totalDebtInETH == 0) return uint256(-1);
                  return (totalCollateralInETH.percentMul(liquidationThreshold)).wadDiv(totalDebtInETH);
                }
                /**
                 * @dev Calculates the equivalent amount in ETH that an user can borrow, depending on the available collateral and the
                 * average Loan To Value
                 * @param totalCollateralInETH The total collateral in ETH
                 * @param totalDebtInETH The total borrow balance
                 * @param ltv The average loan to value
                 * @return the amount available to borrow in ETH for the user
                 **/
                function calculateAvailableBorrowsETH(
                  uint256 totalCollateralInETH,
                  uint256 totalDebtInETH,
                  uint256 ltv
                ) internal pure returns (uint256) {
                  
                  uint256 availableBorrowsETH = totalCollateralInETH.percentMul(ltv); 
                  if (availableBorrowsETH < totalDebtInETH) {
                    return 0;
                  }
                  availableBorrowsETH = availableBorrowsETH.sub(totalDebtInETH);
                  return availableBorrowsETH;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {DataTypes} from '../types/DataTypes.sol';
              /**
               * @title Helpers library
               * @author Aave
               */
              library Helpers {
                /**
                 * @dev Fetches the user current stable and variable debt balances
                 * @param user The user address
                 * @param reserve The reserve data object
                 * @return The stable and variable debt balance
                 **/
                function getUserCurrentDebt(address user, DataTypes.ReserveData storage reserve)
                  internal
                  view
                  returns (uint256, uint256)
                {
                  return (
                    IERC20(reserve.stableDebtTokenAddress).balanceOf(user),
                    IERC20(reserve.variableDebtTokenAddress).balanceOf(user)
                  );
                }
                function getUserCurrentDebtMemory(address user, DataTypes.ReserveData memory reserve)
                  internal
                  view
                  returns (uint256, uint256)
                {
                  return (
                    IERC20(reserve.stableDebtTokenAddress).balanceOf(user),
                    IERC20(reserve.variableDebtTokenAddress).balanceOf(user)
                  );
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Errors} from '../helpers/Errors.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 {
                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) {
                  if (a == 0 || b == 0) {
                    return 0;
                  }
                  require(a <= (type(uint256).max - halfWAD) / b, Errors.MATH_MULTIPLICATION_OVERFLOW);
                  return (a * b + halfWAD) / 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) {
                  require(b != 0, Errors.MATH_DIVISION_BY_ZERO);
                  uint256 halfB = b / 2;
                  require(a <= (type(uint256).max - halfB) / WAD, Errors.MATH_MULTIPLICATION_OVERFLOW);
                  return (a * WAD + halfB) / 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) {
                  if (a == 0 || b == 0) {
                    return 0;
                  }
                  require(a <= (type(uint256).max - halfRAY) / b, Errors.MATH_MULTIPLICATION_OVERFLOW);
                  return (a * b + halfRAY) / 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) {
                  require(b != 0, Errors.MATH_DIVISION_BY_ZERO);
                  uint256 halfB = b / 2;
                  require(a <= (type(uint256).max - halfB) / RAY, Errors.MATH_MULTIPLICATION_OVERFLOW);
                  return (a * RAY + halfB) / 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;
                  uint256 result = halfRatio + a;
                  require(result >= halfRatio, Errors.MATH_ADDITION_OVERFLOW);
                  return result / WAD_RAY_RATIO;
                }
                /**
                 * @dev Converts wad up to ray
                 * @param a Wad
                 * @return a converted in ray
                 **/
                function wadToRay(uint256 a) internal pure returns (uint256) {
                  uint256 result = a * WAD_RAY_RATIO;
                  require(result / WAD_RAY_RATIO == a, Errors.MATH_MULTIPLICATION_OVERFLOW);
                  return result;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Errors} from '../helpers/Errors.sol';
              /**
               * @title PercentageMath library
               * @author Aave
               * @notice Provides functions to perform percentage calculations
               * @dev Percentages are defined by default with 2 decimals of precision (100.00). The precision is indicated by PERCENTAGE_FACTOR
               * @dev Operations are rounded half up
               **/
              library PercentageMath {
                uint256 constant PERCENTAGE_FACTOR = 1e4; //percentage plus two decimals
                uint256 constant HALF_PERCENT = PERCENTAGE_FACTOR / 2;
                /**
                 * @dev Executes a percentage multiplication
                 * @param value The value of which the percentage needs to be calculated
                 * @param percentage The percentage of the value to be calculated
                 * @return The percentage of value
                 **/
                function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256) {
                  if (value == 0 || percentage == 0) {
                    return 0;
                  }
                  require(
                    value <= (type(uint256).max - HALF_PERCENT) / percentage,
                    Errors.MATH_MULTIPLICATION_OVERFLOW
                  );
                  return (value * percentage + HALF_PERCENT) / PERCENTAGE_FACTOR;
                }
                /**
                 * @dev Executes a percentage division
                 * @param value The value of which the percentage needs to be calculated
                 * @param percentage The percentage of the value to be calculated
                 * @return The value divided the percentage
                 **/
                function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256) {
                  require(percentage != 0, Errors.MATH_DIVISION_BY_ZERO);
                  uint256 halfPercentage = percentage / 2;
                  require(
                    value <= (type(uint256).max - halfPercentage) / PERCENTAGE_FACTOR,
                    Errors.MATH_MULTIPLICATION_OVERFLOW
                  );
                  return (value * PERCENTAGE_FACTOR + halfPercentage) / percentage;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {ReserveLogic} from './ReserveLogic.sol';
              import {GenericLogic} from './GenericLogic.sol';
              import {WadRayMath} from '../math/WadRayMath.sol';
              import {PercentageMath} from '../math/PercentageMath.sol';
              import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
              import {UserConfiguration} from '../configuration/UserConfiguration.sol';
              import {Errors} from '../helpers/Errors.sol';
              import {Helpers} from '../helpers/Helpers.sol';
              import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol';
              import {DataTypes} from '../types/DataTypes.sol';
              /**
               * @title ReserveLogic library
               * @author Aave
               * @notice Implements functions to validate the different actions of the protocol
               */
              library ValidationLogic {
                using ReserveLogic for DataTypes.ReserveData;
                using SafeMath for uint256;
                using WadRayMath for uint256;
                using PercentageMath for uint256;
                using SafeERC20 for IERC20;
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                using UserConfiguration for DataTypes.UserConfigurationMap;
                uint256 public constant REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD = 4000;
                uint256 public constant REBALANCE_UP_USAGE_RATIO_THRESHOLD = 0.95 * 1e27; //usage ratio of 95%
                /**
                 * @dev Validates a deposit action
                 * @param reserve The reserve object on which the user is depositing
                 * @param amount The amount to be deposited
                 */
                function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) external view {
                  (bool isActive, bool isFrozen, , ) = reserve.configuration.getFlags();
                  require(amount != 0, Errors.VL_INVALID_AMOUNT);
                  require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
                  require(!isFrozen, Errors.VL_RESERVE_FROZEN);
                }
                /**
                 * @dev Validates a withdraw action
                 * @param reserveAddress The address of the reserve
                 * @param amount The amount to be withdrawn
                 * @param userBalance The balance of the user
                 * @param reservesData The reserves state
                 * @param userConfig The user configuration
                 * @param reserves The addresses of the reserves
                 * @param reservesCount The number of reserves
                 * @param oracle The price oracle
                 */
                function validateWithdraw(
                  address reserveAddress,
                  uint256 amount,
                  uint256 userBalance,
                  mapping(address => DataTypes.ReserveData) storage reservesData,
                  DataTypes.UserConfigurationMap storage userConfig,
                  mapping(uint256 => address) storage reserves,
                  uint256 reservesCount,
                  address oracle
                ) external view {
                  require(amount != 0, Errors.VL_INVALID_AMOUNT);
                  require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE);
                  (bool isActive, , , ) = reservesData[reserveAddress].configuration.getFlags();
                  require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
                  require(
                    GenericLogic.balanceDecreaseAllowed(
                      reserveAddress,
                      msg.sender,
                      amount,
                      reservesData,
                      userConfig,
                      reserves,
                      reservesCount,
                      oracle
                    ),
                    Errors.VL_TRANSFER_NOT_ALLOWED
                  );
                }
                struct ValidateBorrowLocalVars {
                  uint256 currentLtv;
                  uint256 currentLiquidationThreshold;
                  uint256 amountOfCollateralNeededETH;
                  uint256 userCollateralBalanceETH;
                  uint256 userBorrowBalanceETH;
                  uint256 availableLiquidity;
                  uint256 healthFactor;
                  bool isActive;
                  bool isFrozen;
                  bool borrowingEnabled;
                  bool stableRateBorrowingEnabled;
                }
                /**
                 * @dev Validates a borrow action
                 * @param asset The address of the asset to borrow
                 * @param reserve The reserve state from which the user is borrowing
                 * @param userAddress The address of the user
                 * @param amount The amount to be borrowed
                 * @param amountInETH The amount to be borrowed, in ETH
                 * @param interestRateMode The interest rate mode at which the user is borrowing
                 * @param maxStableLoanPercent The max amount of the liquidity that can be borrowed at stable rate, in percentage
                 * @param reservesData The state of all the reserves
                 * @param userConfig The state of the user for the specific reserve
                 * @param reserves The addresses of all the active reserves
                 * @param oracle The price oracle
                 */
                function validateBorrow(
                  address asset,
                  DataTypes.ReserveData storage reserve,
                  address userAddress,
                  uint256 amount,
                  uint256 amountInETH,
                  uint256 interestRateMode,
                  uint256 maxStableLoanPercent,
                  mapping(address => DataTypes.ReserveData) storage reservesData,
                  DataTypes.UserConfigurationMap storage userConfig,
                  mapping(uint256 => address) storage reserves,
                  uint256 reservesCount,
                  address oracle
                ) external view {
                  ValidateBorrowLocalVars memory vars;
                  (vars.isActive, vars.isFrozen, vars.borrowingEnabled, vars.stableRateBorrowingEnabled) = reserve
                    .configuration
                    .getFlags();
                  require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE);
                  require(!vars.isFrozen, Errors.VL_RESERVE_FROZEN);
                  require(amount != 0, Errors.VL_INVALID_AMOUNT);
                  require(vars.borrowingEnabled, Errors.VL_BORROWING_NOT_ENABLED);
                  //validate interest rate mode
                  require(
                    uint256(DataTypes.InterestRateMode.VARIABLE) == interestRateMode ||
                      uint256(DataTypes.InterestRateMode.STABLE) == interestRateMode,
                    Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED
                  );
                  (
                    vars.userCollateralBalanceETH,
                    vars.userBorrowBalanceETH,
                    vars.currentLtv,
                    vars.currentLiquidationThreshold,
                    vars.healthFactor
                  ) = GenericLogic.calculateUserAccountData(
                    userAddress,
                    reservesData,
                    userConfig,
                    reserves,
                    reservesCount,
                    oracle
                  );
                  require(vars.userCollateralBalanceETH > 0, Errors.VL_COLLATERAL_BALANCE_IS_0);
                  require(
                    vars.healthFactor > GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                    Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
                  );
                  //add the current already borrowed amount to the amount requested to calculate the total collateral needed.
                  vars.amountOfCollateralNeededETH = vars.userBorrowBalanceETH.add(amountInETH).percentDiv(
                    vars.currentLtv
                  ); //LTV is calculated in percentage
                  require(
                    vars.amountOfCollateralNeededETH <= vars.userCollateralBalanceETH,
                    Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW
                  );
                  /**
                   * Following conditions need to be met if the user is borrowing at a stable rate:
                   * 1. Reserve must be enabled for stable rate borrowing
                   * 2. Users cannot borrow from the reserve if their collateral is (mostly) the same currency
                   *    they are borrowing, to prevent abuses.
                   * 3. Users will be able to borrow only a portion of the total available liquidity
                   **/
                  if (interestRateMode == uint256(DataTypes.InterestRateMode.STABLE)) {
                    //check if the borrow mode is stable and if stable rate borrowing is enabled on this reserve
                    require(vars.stableRateBorrowingEnabled, Errors.VL_STABLE_BORROWING_NOT_ENABLED);
                    require(
                      !userConfig.isUsingAsCollateral(reserve.id) ||
                        reserve.configuration.getLtv() == 0 ||
                        amount > IERC20(reserve.aTokenAddress).balanceOf(userAddress),
                      Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY
                    );
                    vars.availableLiquidity = IERC20(asset).balanceOf(reserve.aTokenAddress);
                    //calculate the max available loan size in stable rate mode as a percentage of the
                    //available liquidity
                    uint256 maxLoanSizeStable = vars.availableLiquidity.percentMul(maxStableLoanPercent);
                    require(amount <= maxLoanSizeStable, Errors.VL_AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE);
                  }
                }
                /**
                 * @dev Validates a repay action
                 * @param reserve The reserve state from which the user is repaying
                 * @param amountSent The amount sent for the repayment. Can be an actual value or uint(-1)
                 * @param onBehalfOf The address of the user msg.sender is repaying for
                 * @param stableDebt The borrow balance of the user
                 * @param variableDebt The borrow balance of the user
                 */
                function validateRepay(
                  DataTypes.ReserveData storage reserve,
                  uint256 amountSent,
                  DataTypes.InterestRateMode rateMode,
                  address onBehalfOf,
                  uint256 stableDebt,
                  uint256 variableDebt
                ) external view {
                  bool isActive = reserve.configuration.getActive();
                  require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
                  require(amountSent > 0, Errors.VL_INVALID_AMOUNT);
                  require(
                    (stableDebt > 0 &&
                      DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE) ||
                      (variableDebt > 0 &&
                        DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.VARIABLE),
                    Errors.VL_NO_DEBT_OF_SELECTED_TYPE
                  );
                  require(
                    amountSent != uint256(-1) || msg.sender == onBehalfOf,
                    Errors.VL_NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF
                  );
                }
                /**
                 * @dev Validates a swap of borrow rate mode.
                 * @param reserve The reserve state on which the user is swapping the rate
                 * @param userConfig The user reserves configuration
                 * @param stableDebt The stable debt of the user
                 * @param variableDebt The variable debt of the user
                 * @param currentRateMode The rate mode of the borrow
                 */
                function validateSwapRateMode(
                  DataTypes.ReserveData storage reserve,
                  DataTypes.UserConfigurationMap storage userConfig,
                  uint256 stableDebt,
                  uint256 variableDebt,
                  DataTypes.InterestRateMode currentRateMode
                ) external view {
                  (bool isActive, bool isFrozen, , bool stableRateEnabled) = reserve.configuration.getFlags();
                  require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
                  require(!isFrozen, Errors.VL_RESERVE_FROZEN);
                  if (currentRateMode == DataTypes.InterestRateMode.STABLE) {
                    require(stableDebt > 0, Errors.VL_NO_STABLE_RATE_LOAN_IN_RESERVE);
                  } else if (currentRateMode == DataTypes.InterestRateMode.VARIABLE) {
                    require(variableDebt > 0, Errors.VL_NO_VARIABLE_RATE_LOAN_IN_RESERVE);
                    /**
                     * user wants to swap to stable, before swapping we need to ensure that
                     * 1. stable borrow rate is enabled on the reserve
                     * 2. user is not trying to abuse the reserve by depositing
                     * more collateral than he is borrowing, artificially lowering
                     * the interest rate, borrowing at variable, and switching to stable
                     **/
                    require(stableRateEnabled, Errors.VL_STABLE_BORROWING_NOT_ENABLED);
                    require(
                      !userConfig.isUsingAsCollateral(reserve.id) ||
                        reserve.configuration.getLtv() == 0 ||
                        stableDebt.add(variableDebt) > IERC20(reserve.aTokenAddress).balanceOf(msg.sender),
                      Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY
                    );
                  } else {
                    revert(Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED);
                  }
                }
                /**
                 * @dev Validates a stable borrow rate rebalance action
                 * @param reserve The reserve state on which the user is getting rebalanced
                 * @param reserveAddress The address of the reserve
                 * @param stableDebtToken The stable debt token instance
                 * @param variableDebtToken The variable debt token instance
                 * @param aTokenAddress The address of the aToken contract
                 */
                function validateRebalanceStableBorrowRate(
                  DataTypes.ReserveData storage reserve,
                  address reserveAddress,
                  IERC20 stableDebtToken,
                  IERC20 variableDebtToken,
                  address aTokenAddress
                ) external view {
                  (bool isActive, , , ) = reserve.configuration.getFlags();
                  require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
                  //if the usage ratio is below 95%, no rebalances are needed
                  uint256 totalDebt =
                    stableDebtToken.totalSupply().add(variableDebtToken.totalSupply()).wadToRay();
                  uint256 availableLiquidity = IERC20(reserveAddress).balanceOf(aTokenAddress).wadToRay();
                  uint256 usageRatio = totalDebt == 0 ? 0 : totalDebt.rayDiv(availableLiquidity.add(totalDebt));
                  //if the liquidity rate is below REBALANCE_UP_THRESHOLD of the max variable APR at 95% usage,
                  //then we allow rebalancing of the stable rate positions.
                  uint256 currentLiquidityRate = reserve.currentLiquidityRate;
                  uint256 maxVariableBorrowRate =
                    IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).getMaxVariableBorrowRate();
                  require(
                    usageRatio >= REBALANCE_UP_USAGE_RATIO_THRESHOLD &&
                      currentLiquidityRate <=
                      maxVariableBorrowRate.percentMul(REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD),
                    Errors.LP_INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET
                  );
                }
                /**
                 * @dev Validates the action of setting an asset as collateral
                 * @param reserve The state of the reserve that the user is enabling or disabling as collateral
                 * @param reserveAddress The address of the reserve
                 * @param reservesData The data of all the reserves
                 * @param userConfig The state of the user for the specific reserve
                 * @param reserves The addresses of all the active reserves
                 * @param oracle The price oracle
                 */
                function validateSetUseReserveAsCollateral(
                  DataTypes.ReserveData storage reserve,
                  address reserveAddress,
                  bool useAsCollateral,
                  mapping(address => DataTypes.ReserveData) storage reservesData,
                  DataTypes.UserConfigurationMap storage userConfig,
                  mapping(uint256 => address) storage reserves,
                  uint256 reservesCount,
                  address oracle
                ) external view {
                  uint256 underlyingBalance = IERC20(reserve.aTokenAddress).balanceOf(msg.sender);
                  require(underlyingBalance > 0, Errors.VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0);
                  require(
                    useAsCollateral ||
                      GenericLogic.balanceDecreaseAllowed(
                        reserveAddress,
                        msg.sender,
                        underlyingBalance,
                        reservesData,
                        userConfig,
                        reserves,
                        reservesCount,
                        oracle
                      ),
                    Errors.VL_DEPOSIT_ALREADY_IN_USE
                  );
                }
                /**
                 * @dev Validates a flashloan action
                 * @param assets The assets being flashborrowed
                 * @param amounts The amounts for each asset being borrowed
                 **/
                function validateFlashloan(address[] memory assets, uint256[] memory amounts) internal pure {
                  require(assets.length == amounts.length, Errors.VL_INCONSISTENT_FLASHLOAN_PARAMS);
                }
                /**
                 * @dev Validates the liquidation action
                 * @param collateralReserve The reserve data of the collateral
                 * @param principalReserve The reserve data of the principal
                 * @param userConfig The user configuration
                 * @param userHealthFactor The user's health factor
                 * @param userStableDebt Total stable debt balance of the user
                 * @param userVariableDebt Total variable debt balance of the user
                 **/
                function validateLiquidationCall(
                  DataTypes.ReserveData storage collateralReserve,
                  DataTypes.ReserveData storage principalReserve,
                  DataTypes.UserConfigurationMap storage userConfig,
                  uint256 userHealthFactor,
                  uint256 userStableDebt,
                  uint256 userVariableDebt
                ) internal view returns (uint256, string memory) {
                  if (
                    !collateralReserve.configuration.getActive() || !principalReserve.configuration.getActive()
                  ) {
                    return (
                      uint256(Errors.CollateralManagerErrors.NO_ACTIVE_RESERVE),
                      Errors.VL_NO_ACTIVE_RESERVE
                    );
                  }
                  if (userHealthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) {
                    return (
                      uint256(Errors.CollateralManagerErrors.HEALTH_FACTOR_ABOVE_THRESHOLD),
                      Errors.LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD
                    );
                  }
                  bool isCollateralEnabled =
                    collateralReserve.configuration.getLiquidationThreshold() > 0 &&
                      userConfig.isUsingAsCollateral(collateralReserve.id);
                  //if collateral isn't enabled as collateral by user, it cannot be liquidated
                  if (!isCollateralEnabled) {
                    return (
                      uint256(Errors.CollateralManagerErrors.COLLATERAL_CANNOT_BE_LIQUIDATED),
                      Errors.LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED
                    );
                  }
                  if (userStableDebt == 0 && userVariableDebt == 0) {
                    return (
                      uint256(Errors.CollateralManagerErrors.CURRRENCY_NOT_BORROWED),
                      Errors.LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER
                    );
                  }
                  return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.LPCM_NO_ERRORS);
                }
                /**
                 * @dev Validates an aToken transfer
                 * @param from The user from which the aTokens are being transferred
                 * @param reservesData The state of all the reserves
                 * @param userConfig The state of the user for the specific reserve
                 * @param reserves The addresses of all the active reserves
                 * @param oracle The price oracle
                 */
                function validateTransfer(
                  address from,
                  mapping(address => DataTypes.ReserveData) storage reservesData,
                  DataTypes.UserConfigurationMap storage userConfig,
                  mapping(uint256 => address) storage reserves,
                  uint256 reservesCount,
                  address oracle
                ) internal view {
                  (, , , , uint256 healthFactor) =
                    GenericLogic.calculateUserAccountData(
                      from,
                      reservesData,
                      userConfig,
                      reserves,
                      reservesCount,
                      oracle
                    );
                  require(
                    healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                    Errors.VL_TRANSFER_NOT_ALLOWED
                  );
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol';
              import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol';
              import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              import {DataTypes} from '../libraries/types/DataTypes.sol';
              contract LendingPoolStorage {
                using ReserveLogic for DataTypes.ReserveData;
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                using UserConfiguration for DataTypes.UserConfigurationMap;
                ILendingPoolAddressesProvider internal _addressesProvider;
                mapping(address => DataTypes.ReserveData) internal _reserves;
                mapping(address => DataTypes.UserConfigurationMap) internal _usersConfig;
                // the list of the available reserves, structured as a mapping for gas savings reasons
                mapping(uint256 => address) internal _reservesList;
                uint256 internal _reservesCount;
                bool internal _paused;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              interface IScaledBalanceToken {
                /**
                 * @dev Returns the scaled balance of the user. The scaled balance is the sum of all the
                 * updated stored balance divided by the reserve's liquidity index at the moment of the update
                 * @param user The user whose balance is calculated
                 * @return The scaled balance of the user
                 **/
                function scaledBalanceOf(address user) external view returns (uint256);
                /**
                 * @dev Returns the scaled balance of the user and the scaled total supply.
                 * @param user The address of the user
                 * @return The scaled balance of the user
                 * @return The scaled balance and the scaled total supply
                 **/
                function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256);
                /**
                 * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index)
                 * @return The scaled total supply
                 **/
                function scaledTotalSupply() external view returns (uint256);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {IAToken} from '../../../interfaces/IAToken.sol';
              import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol';
              import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
              import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol';
              import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
              import {MathUtils} from '../math/MathUtils.sol';
              import {WadRayMath} from '../math/WadRayMath.sol';
              import {PercentageMath} from '../math/PercentageMath.sol';
              import {Errors} from '../helpers/Errors.sol';
              import {DataTypes} from '../types/DataTypes.sol';
              /**
               * @title ReserveLogic library
               * @author Aave
               * @notice Implements the logic to update the reserves state
               */
              library ReserveLogic {
                using SafeMath for uint256;
                using WadRayMath for uint256;
                using PercentageMath for uint256;
                using SafeERC20 for IERC20;
                /**
                 * @dev Emitted when the state of a reserve is updated
                 * @param asset The address of the underlying asset of the reserve
                 * @param liquidityRate The new liquidity rate
                 * @param stableBorrowRate The new stable borrow rate
                 * @param variableBorrowRate The new variable borrow rate
                 * @param liquidityIndex The new liquidity index
                 * @param variableBorrowIndex The new variable borrow index
                 **/
                event ReserveDataUpdated(
                  address indexed asset,
                  uint256 liquidityRate,
                  uint256 stableBorrowRate,
                  uint256 variableBorrowRate,
                  uint256 liquidityIndex,
                  uint256 variableBorrowIndex
                );
                using ReserveLogic for DataTypes.ReserveData;
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                /**
                 * @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 for each unit of asset one unit of income has been accrued
                 * @param reserve The reserve object
                 * @return the normalized income. expressed in ray
                 **/
                function getNormalizedIncome(DataTypes.ReserveData storage reserve)
                  internal
                  view
                  returns (uint256)
                {
                  uint40 timestamp = reserve.lastUpdateTimestamp;
                  //solium-disable-next-line
                  if (timestamp == uint40(block.timestamp)) {
                    //if the index was updated in the same block, no need to perform any calculation
                    return reserve.liquidityIndex;
                  }
                  uint256 cumulated =
                    MathUtils.calculateLinearInterest(reserve.currentLiquidityRate, timestamp).rayMul(
                      reserve.liquidityIndex
                    );
                  return cumulated;
                }
                /**
                 * @dev Returns the ongoing normalized variable debt for the reserve
                 * A value of 1e27 means there is no debt. As time passes, the income is accrued
                 * A value of 2*1e27 means that for each unit of debt, one unit worth of interest has been accumulated
                 * @param reserve The reserve object
                 * @return The normalized variable debt. expressed in ray
                 **/
                function getNormalizedDebt(DataTypes.ReserveData storage reserve)
                  internal
                  view
                  returns (uint256)
                {
                  uint40 timestamp = reserve.lastUpdateTimestamp;
                  //solium-disable-next-line
                  if (timestamp == uint40(block.timestamp)) {
                    //if the index was updated in the same block, no need to perform any calculation
                    return reserve.variableBorrowIndex;
                  }
                  uint256 cumulated =
                    MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp).rayMul(
                      reserve.variableBorrowIndex
                    );
                  return cumulated;
                }
                /**
                 * @dev Updates the liquidity cumulative index and the variable borrow index.
                 * @param reserve the reserve object
                 **/
                function updateState(DataTypes.ReserveData storage reserve) internal {
                  uint256 scaledVariableDebt =
                    IVariableDebtToken(reserve.variableDebtTokenAddress).scaledTotalSupply();
                  uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex;
                  uint256 previousLiquidityIndex = reserve.liquidityIndex;
                  uint40 lastUpdatedTimestamp = reserve.lastUpdateTimestamp;
                  (uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) =
                    _updateIndexes(
                      reserve,
                      scaledVariableDebt,
                      previousLiquidityIndex,
                      previousVariableBorrowIndex,
                      lastUpdatedTimestamp
                    );
                  _mintToTreasury(
                    reserve,
                    scaledVariableDebt,
                    previousVariableBorrowIndex,
                    newLiquidityIndex,
                    newVariableBorrowIndex,
                    lastUpdatedTimestamp
                  );
                }
                /**
                 * @dev Accumulates a predefined amount of asset to the reserve as a fixed, instantaneous income. Used for example to accumulate
                 * the flashloan fee to the reserve, and spread it between all the depositors
                 * @param reserve The reserve object
                 * @param totalLiquidity The total liquidity available in the reserve
                 * @param amount The amount to accomulate
                 **/
                function cumulateToLiquidityIndex(
                  DataTypes.ReserveData storage reserve,
                  uint256 totalLiquidity,
                  uint256 amount
                ) internal {
                  uint256 amountToLiquidityRatio = amount.wadToRay().rayDiv(totalLiquidity.wadToRay());
                  uint256 result = amountToLiquidityRatio.add(WadRayMath.ray());
                  result = result.rayMul(reserve.liquidityIndex);
                  require(result <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW);
                  reserve.liquidityIndex = uint128(result);
                }
                /**
                 * @dev Initializes a reserve
                 * @param reserve The reserve object
                 * @param aTokenAddress The address of the overlying atoken contract
                 * @param interestRateStrategyAddress The address of the interest rate strategy contract
                 **/
                function init(
                  DataTypes.ReserveData storage reserve,
                  address aTokenAddress,
                  address stableDebtTokenAddress,
                  address variableDebtTokenAddress,
                  address interestRateStrategyAddress
                ) external {
                  require(reserve.aTokenAddress == address(0), Errors.RL_RESERVE_ALREADY_INITIALIZED);
                  reserve.liquidityIndex = uint128(WadRayMath.ray());
                  reserve.variableBorrowIndex = uint128(WadRayMath.ray());
                  reserve.aTokenAddress = aTokenAddress;
                  reserve.stableDebtTokenAddress = stableDebtTokenAddress;
                  reserve.variableDebtTokenAddress = variableDebtTokenAddress;
                  reserve.interestRateStrategyAddress = interestRateStrategyAddress;
                }
                struct UpdateInterestRatesLocalVars {
                  address stableDebtTokenAddress;
                  uint256 availableLiquidity;
                  uint256 totalStableDebt;
                  uint256 newLiquidityRate;
                  uint256 newStableRate;
                  uint256 newVariableRate;
                  uint256 avgStableRate;
                  uint256 totalVariableDebt;
                }
                /**
                 * @dev Updates the reserve current stable borrow rate, the current variable borrow rate and the current liquidity rate
                 * @param reserve The address of the reserve to be updated
                 * @param liquidityAdded The amount of liquidity added to the protocol (deposit or repay) in the previous action
                 * @param liquidityTaken The amount of liquidity taken from the protocol (redeem or borrow)
                 **/
                function updateInterestRates(
                  DataTypes.ReserveData storage reserve,
                  address reserveAddress,
                  address aTokenAddress,
                  uint256 liquidityAdded,
                  uint256 liquidityTaken
                ) internal {
                  UpdateInterestRatesLocalVars memory vars;
                  vars.stableDebtTokenAddress = reserve.stableDebtTokenAddress;
                  (vars.totalStableDebt, vars.avgStableRate) = IStableDebtToken(vars.stableDebtTokenAddress)
                    .getTotalSupplyAndAvgRate();
                  //calculates the total variable debt locally using the scaled total supply instead
                  //of totalSupply(), as it's noticeably cheaper. Also, the index has been
                  //updated by the previous updateState() call
                  vars.totalVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress)
                    .scaledTotalSupply()
                    .rayMul(reserve.variableBorrowIndex);
                  vars.availableLiquidity = IERC20(reserveAddress).balanceOf(aTokenAddress);
                  (
                    vars.newLiquidityRate,
                    vars.newStableRate,
                    vars.newVariableRate
                  ) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates(
                    reserveAddress,
                    vars.availableLiquidity.add(liquidityAdded).sub(liquidityTaken),
                    vars.totalStableDebt,
                    vars.totalVariableDebt,
                    vars.avgStableRate,
                    reserve.configuration.getReserveFactor()
                  );
                  require(vars.newLiquidityRate <= type(uint128).max, Errors.RL_LIQUIDITY_RATE_OVERFLOW);
                  require(vars.newStableRate <= type(uint128).max, Errors.RL_STABLE_BORROW_RATE_OVERFLOW);
                  require(vars.newVariableRate <= type(uint128).max, Errors.RL_VARIABLE_BORROW_RATE_OVERFLOW);
                  reserve.currentLiquidityRate = uint128(vars.newLiquidityRate);
                  reserve.currentStableBorrowRate = uint128(vars.newStableRate);
                  reserve.currentVariableBorrowRate = uint128(vars.newVariableRate);
                  emit ReserveDataUpdated(
                    reserveAddress,
                    vars.newLiquidityRate,
                    vars.newStableRate,
                    vars.newVariableRate,
                    reserve.liquidityIndex,
                    reserve.variableBorrowIndex
                  );
                }
                struct MintToTreasuryLocalVars {
                  uint256 currentStableDebt;
                  uint256 principalStableDebt;
                  uint256 previousStableDebt;
                  uint256 currentVariableDebt;
                  uint256 previousVariableDebt;
                  uint256 avgStableRate;
                  uint256 cumulatedStableInterest;
                  uint256 totalDebtAccrued;
                  uint256 amountToMint;
                  uint256 reserveFactor;
                  uint40 stableSupplyUpdatedTimestamp;
                }
                /**
                 * @dev Mints part of the repaid interest to the reserve treasury as a function of the reserveFactor for the
                 * specific asset.
                 * @param reserve The reserve reserve to be updated
                 * @param scaledVariableDebt The current scaled total variable debt
                 * @param previousVariableBorrowIndex The variable borrow index before the last accumulation of the interest
                 * @param newLiquidityIndex The new liquidity index
                 * @param newVariableBorrowIndex The variable borrow index after the last accumulation of the interest
                 **/
                function _mintToTreasury(
                  DataTypes.ReserveData storage reserve,
                  uint256 scaledVariableDebt,
                  uint256 previousVariableBorrowIndex,
                  uint256 newLiquidityIndex,
                  uint256 newVariableBorrowIndex,
                  uint40 timestamp
                ) internal {
                  MintToTreasuryLocalVars memory vars;
                  vars.reserveFactor = reserve.configuration.getReserveFactor();
                  if (vars.reserveFactor == 0) {
                    return;
                  }
                  //fetching the principal, total stable debt and the avg stable rate
                  (
                    vars.principalStableDebt,
                    vars.currentStableDebt,
                    vars.avgStableRate,
                    vars.stableSupplyUpdatedTimestamp
                  ) = IStableDebtToken(reserve.stableDebtTokenAddress).getSupplyData();
                  //calculate the last principal variable debt
                  vars.previousVariableDebt = scaledVariableDebt.rayMul(previousVariableBorrowIndex);
                  //calculate the new total supply after accumulation of the index
                  vars.currentVariableDebt = scaledVariableDebt.rayMul(newVariableBorrowIndex);
                  //calculate the stable debt until the last timestamp update
                  vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest(
                    vars.avgStableRate,
                    vars.stableSupplyUpdatedTimestamp,
                    timestamp
                  );
                  vars.previousStableDebt = vars.principalStableDebt.rayMul(vars.cumulatedStableInterest);
                  //debt accrued is the sum of the current debt minus the sum of the debt at the last update
                  vars.totalDebtAccrued = vars
                    .currentVariableDebt
                    .add(vars.currentStableDebt)
                    .sub(vars.previousVariableDebt)
                    .sub(vars.previousStableDebt);
                  vars.amountToMint = vars.totalDebtAccrued.percentMul(vars.reserveFactor);
                  if (vars.amountToMint != 0) {
                    IAToken(reserve.aTokenAddress).mintToTreasury(vars.amountToMint, newLiquidityIndex);
                  }
                }
                /**
                 * @dev Updates the reserve indexes and the timestamp of the update
                 * @param reserve The reserve reserve to be updated
                 * @param scaledVariableDebt The scaled variable debt
                 * @param liquidityIndex The last stored liquidity index
                 * @param variableBorrowIndex The last stored variable borrow index
                 **/
                function _updateIndexes(
                  DataTypes.ReserveData storage reserve,
                  uint256 scaledVariableDebt,
                  uint256 liquidityIndex,
                  uint256 variableBorrowIndex,
                  uint40 timestamp
                ) internal returns (uint256, uint256) {
                  uint256 currentLiquidityRate = reserve.currentLiquidityRate;
                  uint256 newLiquidityIndex = liquidityIndex;
                  uint256 newVariableBorrowIndex = variableBorrowIndex;
                  //only cumulating if there is any income being produced
                  if (currentLiquidityRate > 0) {
                    uint256 cumulatedLiquidityInterest =
                      MathUtils.calculateLinearInterest(currentLiquidityRate, timestamp);
                    newLiquidityIndex = cumulatedLiquidityInterest.rayMul(liquidityIndex);
                    require(newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW);
                    reserve.liquidityIndex = uint128(newLiquidityIndex);
                    //as the liquidity rate might come only from stable rate loans, we need to ensure
                    //that there is actual variable debt before accumulating
                    if (scaledVariableDebt != 0) {
                      uint256 cumulatedVariableBorrowInterest =
                        MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp);
                      newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex);
                      require(
                        newVariableBorrowIndex <= type(uint128).max,
                        Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW
                      );
                      reserve.variableBorrowIndex = uint128(newVariableBorrowIndex);
                    }
                  }
                  //solium-disable-next-line
                  reserve.lastUpdateTimestamp = uint40(block.timestamp);
                  return (newLiquidityIndex, newVariableBorrowIndex);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Errors} from '../helpers/Errors.sol';
              import {DataTypes} from '../types/DataTypes.sol';
              /**
               * @title UserConfiguration library
               * @author Aave
               * @notice Implements the bitmap logic to handle the user configuration
               */
              library UserConfiguration {
                uint256 internal constant BORROWING_MASK =
                  0x5555555555555555555555555555555555555555555555555555555555555555;
                /**
                 * @dev Sets if the user is borrowing the reserve identified by reserveIndex
                 * @param self The configuration object
                 * @param reserveIndex The index of the reserve in the bitmap
                 * @param borrowing True if the user is borrowing the reserve, false otherwise
                 **/
                function setBorrowing(
                  DataTypes.UserConfigurationMap storage self,
                  uint256 reserveIndex,
                  bool borrowing
                ) internal {
                  require(reserveIndex < 128, Errors.UL_INVALID_INDEX);
                  self.data =
                    (self.data & ~(1 << (reserveIndex * 2))) |
                    (uint256(borrowing ? 1 : 0) << (reserveIndex * 2));
                }
                /**
                 * @dev Sets if the user is using as collateral the reserve identified by reserveIndex
                 * @param self The configuration object
                 * @param reserveIndex The index of the reserve in the bitmap
                 * @param usingAsCollateral True if the user is usin the reserve as collateral, false otherwise
                 **/
                function setUsingAsCollateral(
                  DataTypes.UserConfigurationMap storage self,
                  uint256 reserveIndex,
                  bool usingAsCollateral
                ) internal {
                  require(reserveIndex < 128, Errors.UL_INVALID_INDEX);
                  self.data =
                    (self.data & ~(1 << (reserveIndex * 2 + 1))) |
                    (uint256(usingAsCollateral ? 1 : 0) << (reserveIndex * 2 + 1));
                }
                /**
                 * @dev Used to validate if a user has been using the reserve for borrowing or as collateral
                 * @param self The configuration object
                 * @param reserveIndex The index of the reserve in the bitmap
                 * @return True if the user has been using a reserve for borrowing or as collateral, false otherwise
                 **/
                function isUsingAsCollateralOrBorrowing(DataTypes.UserConfigurationMap memory self, uint256 reserveIndex)
                  internal
                  pure
                  returns (bool)
                {
                  require(reserveIndex < 128, Errors.UL_INVALID_INDEX);
                  return (self.data >> (reserveIndex * 2)) & 3 != 0;
                }
                /**
                 * @dev Used to validate if a user has been using the reserve for borrowing
                 * @param self The configuration object
                 * @param reserveIndex The index of the reserve in the bitmap
                 * @return True if the user has been using a reserve for borrowing, false otherwise
                 **/
                function isBorrowing(DataTypes.UserConfigurationMap memory self, uint256 reserveIndex)
                  internal
                  pure
                  returns (bool)
                {
                  require(reserveIndex < 128, Errors.UL_INVALID_INDEX);
                  return (self.data >> (reserveIndex * 2)) & 1 != 0;
                }
                /**
                 * @dev Used to validate if a user has been using the reserve as collateral
                 * @param self The configuration object
                 * @param reserveIndex The index of the reserve in the bitmap
                 * @return True if the user has been using a reserve as collateral, false otherwise
                 **/
                function isUsingAsCollateral(DataTypes.UserConfigurationMap memory self, uint256 reserveIndex)
                  internal
                  pure
                  returns (bool)
                {
                  require(reserveIndex < 128, Errors.UL_INVALID_INDEX);
                  return (self.data >> (reserveIndex * 2 + 1)) & 1 != 0;
                }
                /**
                 * @dev Used to validate if a user has been borrowing from any reserve
                 * @param self The configuration object
                 * @return True if the user has been borrowing any reserve, false otherwise
                 **/
                function isBorrowingAny(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
                  return self.data & BORROWING_MASK != 0;
                }
                /**
                 * @dev Used to validate if a user has not been using any reserve
                 * @param self The configuration object
                 * @return True if the user has been borrowing any reserve, false otherwise
                 **/
                function isEmpty(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
                  return self.data == 0;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title IReserveInterestRateStrategyInterface interface
               * @dev Interface for the calculation of the interest rates
               * @author Aave
               */
              interface IReserveInterestRateStrategy {
                function baseVariableBorrowRate() external view returns (uint256);
                function getMaxVariableBorrowRate() external view returns (uint256);
                function calculateInterestRates(
                  address reserve,
                  uint256 utilizationRate,
                  uint256 totalStableDebt,
                  uint256 totalVariableDebt,
                  uint256 averageStableBorrowRate,
                  uint256 reserveFactor
                )
                  external
                  view
                  returns (
                    uint256 liquidityRate,
                    uint256 stableBorrowRate,
                    uint256 variableBorrowRate
                  );
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {WadRayMath} from './WadRayMath.sol';
              library MathUtils {
                using SafeMath for uint256;
                using WadRayMath for uint256;
                /// @dev Ignoring leap years
                uint256 internal constant SECONDS_PER_YEAR = 365 days;
                /**
                 * @dev Function to calculate the interest accumulated 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));
                  return (rate.mul(timeDifference) / SECONDS_PER_YEAR).add(WadRayMath.ray());
                }
                /**
                 * @dev Function to calculate the interest using a compounded interest rate formula
                 * To avoid expensive exponentiation, the calculation is performed using a binomial approximation:
                 *
                 *  (1+x)^n = 1+n*x+[n/2*(n-1)]*x^2+[n/6*(n-1)*(n-2)*x^3...
                 *
                 * The approximation slightly underpays liquidity providers and undercharges borrowers, with the advantage of great gas cost reductions
                 * The whitepaper contains reference to the approximation and a table showing the margin of error per different time periods
                 *
                 * @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,
                  uint256 currentTimestamp
                ) internal pure returns (uint256) {
                  //solium-disable-next-line
                  uint256 exp = currentTimestamp.sub(uint256(lastUpdateTimestamp));
                  if (exp == 0) {
                    return WadRayMath.ray();
                  }
                  uint256 expMinusOne = exp - 1;
                  uint256 expMinusTwo = exp > 2 ? exp - 2 : 0;
                  uint256 ratePerSecond = rate / SECONDS_PER_YEAR;
                  uint256 basePowerTwo = ratePerSecond.rayMul(ratePerSecond);
                  uint256 basePowerThree = basePowerTwo.rayMul(ratePerSecond);
                  uint256 secondTerm = exp.mul(expMinusOne).mul(basePowerTwo) / 2;
                  uint256 thirdTerm = exp.mul(expMinusOne).mul(expMinusTwo).mul(basePowerThree) / 6;
                  return WadRayMath.ray().add(ratePerSecond.mul(exp)).add(secondTerm).add(thirdTerm);
                }
                /**
                 * @dev Calculates the compounded interest between the timestamp of the last update and the current block timestamp
                 * @param rate The interest rate (in ray)
                 * @param lastUpdateTimestamp The timestamp from which the interest accumulation needs to be calculated
                 **/
                function calculateCompoundedInterest(uint256 rate, uint40 lastUpdateTimestamp)
                  internal
                  view
                  returns (uint256)
                {
                  return calculateCompoundedInterest(rate, lastUpdateTimestamp, block.timestamp);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {FlashLoanReceiverBase} from '../../flashloan/base/FlashLoanReceiverBase.sol';
              import {MintableERC20} from '../tokens/MintableERC20.sol';
              import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              contract MockFlashLoanReceiver is FlashLoanReceiverBase {
                using SafeERC20 for IERC20;
                ILendingPoolAddressesProvider internal _provider;
                event ExecutedWithFail(address[] _assets, uint256[] _amounts, uint256[] _premiums);
                event ExecutedWithSuccess(address[] _assets, uint256[] _amounts, uint256[] _premiums);
                bool _failExecution;
                uint256 _amountToApprove;
                bool _simulateEOA;
                constructor(ILendingPoolAddressesProvider provider) public FlashLoanReceiverBase(provider) {}
                function setFailExecutionTransfer(bool fail) public {
                  _failExecution = fail;
                }
                function setAmountToApprove(uint256 amountToApprove) public {
                  _amountToApprove = amountToApprove;
                }
                function setSimulateEOA(bool flag) public {
                  _simulateEOA = flag;
                }
                function amountToApprove() public view returns (uint256) {
                  return _amountToApprove;
                }
                function simulateEOA() public view returns (bool) {
                  return _simulateEOA;
                }
                function executeOperation(
                  address[] memory assets,
                  uint256[] memory amounts,
                  uint256[] memory premiums,
                  address initiator,
                  bytes memory params
                ) public override returns (bool) {
                  params;
                  initiator;
                  if (_failExecution) {
                    emit ExecutedWithFail(assets, amounts, premiums);
                    return !_simulateEOA;
                  }
                  for (uint256 i = 0; i < assets.length; i++) {
                    //mint to this contract the specific amount
                    MintableERC20 token = MintableERC20(assets[i]);
                    //check the contract has the specified balance
                    require(
                      amounts[i] <= IERC20(assets[i]).balanceOf(address(this)),
                      'Invalid balance for the contract'
                    );
                    uint256 amountToReturn = (_amountToApprove != 0)
                      ? _amountToApprove
                      : amounts[i].add(premiums[i]);
                    //execution does not fail - mint tokens and return them to the _destination
                    token.mint(premiums[i]);
                    IERC20(assets[i]).approve(address(LENDING_POOL), amountToReturn);
                  }
                  emit ExecutedWithSuccess(assets, amounts, premiums);
                  return true;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {IFlashLoanReceiver} from '../interfaces/IFlashLoanReceiver.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              import {ILendingPool} from '../../interfaces/ILendingPool.sol';
              abstract contract FlashLoanReceiverBase is IFlashLoanReceiver {
                using SafeERC20 for IERC20;
                using SafeMath for uint256;
                ILendingPoolAddressesProvider public immutable override ADDRESSES_PROVIDER;
                ILendingPool public immutable override LENDING_POOL;
                constructor(ILendingPoolAddressesProvider provider) public {
                  ADDRESSES_PROVIDER = provider;
                  LENDING_POOL = ILendingPool(provider.getLendingPool());
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {ERC20} from '../../dependencies/openzeppelin/contracts/ERC20.sol';
              /**
               * @title ERC20Mintable
               * @dev ERC20 minting logic
               */
              contract MintableERC20 is ERC20 {
                constructor(
                  string memory name,
                  string memory symbol,
                  uint8 decimals
                ) public ERC20(name, symbol) {
                  _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(_msgSender(), value);
                  return true;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              import {ILendingPool} from '../../interfaces/ILendingPool.sol';
              /**
               * @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[] calldata assets,
                  uint256[] calldata amounts,
                  uint256[] calldata premiums,
                  address initiator,
                  bytes calldata params
                ) external returns (bool);
                function ADDRESSES_PROVIDER() external view returns (ILendingPoolAddressesProvider);
                function LENDING_POOL() external view returns (ILendingPool);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.6.0;
              import './Context.sol';
              import './IERC20.sol';
              import './SafeMath.sol';
              import './Address.sol';
              /**
               * @dev Implementation of the {IERC20} interface.
               *
               * This implementation is agnostic to the way tokens are created. This means
               * that a supply mechanism has to be added in a derived contract using {_mint}.
               * For a generic mechanism see {ERC20PresetMinterPauser}.
               *
               * TIP: For a detailed writeup see our guide
               * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
               * to implement supply mechanisms].
               *
               * We have followed general OpenZeppelin guidelines: functions revert instead
               * of returning `false` on failure. This behavior is nonetheless conventional
               * and does not conflict with the expectations of ERC20 applications.
               *
               * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
               * This allows applications to reconstruct the allowance for all accounts just
               * by listening to said events. Other implementations of the EIP may not emit
               * these events, as it isn't required by the specification.
               *
               * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
               * functions have been added to mitigate the well-known issues around setting
               * allowances. See {IERC20-approve}.
               */
              contract ERC20 is Context, IERC20 {
                using SafeMath for uint256;
                using Address for address;
                mapping(address => uint256) private _balances;
                mapping(address => mapping(address => uint256)) private _allowances;
                uint256 private _totalSupply;
                string private _name;
                string private _symbol;
                uint8 private _decimals;
                /**
                 * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
                 * a default value of 18.
                 *
                 * To select a different value for {decimals}, use {_setupDecimals}.
                 *
                 * All three of these values are immutable: they can only be set once during
                 * construction.
                 */
                constructor(string memory name, string memory symbol) public {
                  _name = name;
                  _symbol = symbol;
                  _decimals = 18;
                }
                /**
                 * @dev Returns the name of the token.
                 */
                function name() public view returns (string memory) {
                  return _name;
                }
                /**
                 * @dev Returns the symbol of the token, usually a shorter version of the
                 * name.
                 */
                function symbol() public view returns (string memory) {
                  return _symbol;
                }
                /**
                 * @dev Returns the number of decimals used to get its user representation.
                 * For example, if `decimals` equals `2`, a balance of `505` tokens should
                 * be displayed to a user as `5,05` (`505 / 10 ** 2`).
                 *
                 * Tokens usually opt for a value of 18, imitating the relationship between
                 * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
                 * called.
                 *
                 * NOTE: This information is only used for _display_ purposes: it in
                 * no way affects any of the arithmetic of the contract, including
                 * {IERC20-balanceOf} and {IERC20-transfer}.
                 */
                function decimals() public view returns (uint8) {
                  return _decimals;
                }
                /**
                 * @dev See {IERC20-totalSupply}.
                 */
                function totalSupply() public override view returns (uint256) {
                  return _totalSupply;
                }
                /**
                 * @dev See {IERC20-balanceOf}.
                 */
                function balanceOf(address account) public override view returns (uint256) {
                  return _balances[account];
                }
                /**
                 * @dev See {IERC20-transfer}.
                 *
                 * Requirements:
                 *
                 * - `recipient` cannot be the zero address.
                 * - the caller must have a balance of at least `amount`.
                 */
                function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                  _transfer(_msgSender(), recipient, amount);
                  return true;
                }
                /**
                 * @dev See {IERC20-allowance}.
                 */
                function allowance(address owner, address spender)
                  public
                  virtual
                  override
                  view
                  returns (uint256)
                {
                  return _allowances[owner][spender];
                }
                /**
                 * @dev See {IERC20-approve}.
                 *
                 * Requirements:
                 *
                 * - `spender` cannot be the zero address.
                 */
                function approve(address spender, uint256 amount) public virtual override returns (bool) {
                  _approve(_msgSender(), spender, amount);
                  return true;
                }
                /**
                 * @dev See {IERC20-transferFrom}.
                 *
                 * Emits an {Approval} event indicating the updated allowance. This is not
                 * required by the EIP. See the note at the beginning of {ERC20};
                 *
                 * Requirements:
                 * - `sender` and `recipient` cannot be the zero address.
                 * - `sender` must have a balance of at least `amount`.
                 * - the caller must have allowance for ``sender``'s tokens of at least
                 * `amount`.
                 */
                function transferFrom(
                  address sender,
                  address recipient,
                  uint256 amount
                ) public virtual override returns (bool) {
                  _transfer(sender, recipient, amount);
                  _approve(
                    sender,
                    _msgSender(),
                    _allowances[sender][_msgSender()].sub(amount, 'ERC20: transfer amount exceeds allowance')
                  );
                  return true;
                }
                /**
                 * @dev Atomically increases the allowance granted to `spender` by the caller.
                 *
                 * This is an alternative to {approve} that can be used as a mitigation for
                 * problems described in {IERC20-approve}.
                 *
                 * Emits an {Approval} event indicating the updated allowance.
                 *
                 * Requirements:
                 *
                 * - `spender` cannot be the zero address.
                 */
                function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                  _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                  return true;
                }
                /**
                 * @dev Atomically decreases the allowance granted to `spender` by the caller.
                 *
                 * This is an alternative to {approve} that can be used as a mitigation for
                 * problems described in {IERC20-approve}.
                 *
                 * Emits an {Approval} event indicating the updated allowance.
                 *
                 * Requirements:
                 *
                 * - `spender` cannot be the zero address.
                 * - `spender` must have allowance for the caller of at least
                 * `subtractedValue`.
                 */
                function decreaseAllowance(address spender, uint256 subtractedValue)
                  public
                  virtual
                  returns (bool)
                {
                  _approve(
                    _msgSender(),
                    spender,
                    _allowances[_msgSender()][spender].sub(
                      subtractedValue,
                      'ERC20: decreased allowance below zero'
                    )
                  );
                  return true;
                }
                /**
                 * @dev Moves tokens `amount` from `sender` to `recipient`.
                 *
                 * This is internal function is equivalent to {transfer}, and can be used to
                 * e.g. implement automatic token fees, slashing mechanisms, etc.
                 *
                 * Emits a {Transfer} event.
                 *
                 * Requirements:
                 *
                 * - `sender` cannot be the zero address.
                 * - `recipient` cannot be the zero address.
                 * - `sender` must have a balance of at least `amount`.
                 */
                function _transfer(
                  address sender,
                  address recipient,
                  uint256 amount
                ) internal virtual {
                  require(sender != address(0), 'ERC20: transfer from the zero address');
                  require(recipient != address(0), 'ERC20: transfer to the zero address');
                  _beforeTokenTransfer(sender, recipient, amount);
                  _balances[sender] = _balances[sender].sub(amount, 'ERC20: transfer amount exceeds balance');
                  _balances[recipient] = _balances[recipient].add(amount);
                  emit Transfer(sender, recipient, amount);
                }
                /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                 * the total supply.
                 *
                 * Emits a {Transfer} event with `from` set to the zero address.
                 *
                 * Requirements
                 *
                 * - `to` cannot be the zero address.
                 */
                function _mint(address account, uint256 amount) internal virtual {
                  require(account != address(0), 'ERC20: mint to the zero address');
                  _beforeTokenTransfer(address(0), account, amount);
                  _totalSupply = _totalSupply.add(amount);
                  _balances[account] = _balances[account].add(amount);
                  emit Transfer(address(0), account, amount);
                }
                /**
                 * @dev Destroys `amount` tokens from `account`, reducing the
                 * total supply.
                 *
                 * Emits a {Transfer} event with `to` set to the zero address.
                 *
                 * Requirements
                 *
                 * - `account` cannot be the zero address.
                 * - `account` must have at least `amount` tokens.
                 */
                function _burn(address account, uint256 amount) internal virtual {
                  require(account != address(0), 'ERC20: burn from the zero address');
                  _beforeTokenTransfer(account, address(0), amount);
                  _balances[account] = _balances[account].sub(amount, 'ERC20: burn amount exceeds balance');
                  _totalSupply = _totalSupply.sub(amount);
                  emit Transfer(account, address(0), amount);
                }
                /**
                 * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
                 *
                 * This is internal function is equivalent to `approve`, and can be used to
                 * e.g. set automatic allowances for certain subsystems, etc.
                 *
                 * Emits an {Approval} event.
                 *
                 * Requirements:
                 *
                 * - `owner` cannot be the zero address.
                 * - `spender` cannot be the zero address.
                 */
                function _approve(
                  address owner,
                  address spender,
                  uint256 amount
                ) internal virtual {
                  require(owner != address(0), 'ERC20: approve from the zero address');
                  require(spender != address(0), 'ERC20: approve to the zero address');
                  _allowances[owner][spender] = amount;
                  emit Approval(owner, spender, amount);
                }
                /**
                 * @dev Sets {decimals} to a value other than the default one of 18.
                 *
                 * WARNING: This function should only be called from the constructor. Most
                 * applications that interact with token contracts will not expect
                 * {decimals} to ever change, and may work incorrectly if it does.
                 */
                function _setupDecimals(uint8 decimals_) internal {
                  _decimals = decimals_;
                }
                /**
                 * @dev Hook that is called before any transfer of tokens. This includes
                 * minting and burning.
                 *
                 * Calling conditions:
                 *
                 * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                 * will be to transferred to `to`.
                 * - when `from` is zero, `amount` tokens will be minted for `to`.
                 * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                 * - `from` and `to` are never both zero.
                 *
                 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                 */
                function _beforeTokenTransfer(
                  address from,
                  address to,
                  uint256 amount
                ) internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.6.12;
              /*
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with GSN meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                function _msgSender() internal virtual view returns (address payable) {
                  return msg.sender;
                }
                function _msgData() internal virtual view returns (bytes memory) {
                  this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                  return msg.data;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol';
              import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
              import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
              import {IChainlinkAggregator} from '../interfaces/IChainlinkAggregator.sol';
              import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol';
              /// @title AaveOracle
              /// @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 AaveOracle is IPriceOracleGetter, Ownable {
                using SafeERC20 for IERC20;
                event WethSet(address indexed weth);
                event AssetSourceUpdated(address indexed asset, address indexed source);
                event FallbackOracleUpdated(address indexed fallbackOracle);
                mapping(address => IChainlinkAggregator) private assetsSources;
                IPriceOracleGetter private _fallbackOracle;
                address public immutable WETH;
                /// @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,
                  address weth
                ) public {
                  _setFallbackOracle(fallbackOracle);
                  _setAssetsSources(assets, sources);
                  WETH = weth;
                  emit WethSet(weth);
                }
                /// @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
                {
                  _setAssetsSources(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 {
                  _setFallbackOracle(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 _setAssetsSources(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 _setFallbackOracle(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 == WETH) {
                    return 1 ether;
                  } else if (address(source) == address(0)) {
                    return _fallbackOracle.getAssetPrice(asset);
                  } else {
                    int256 price = IChainlinkAggregator(source).latestAnswer();
                    if (price > 0) {
                      return uint256(price);
                    } else {
                      return _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);
                }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.6.0;
              import './Context.sol';
              /**
               * @dev Contract module which provides a basic access control mechanism, where
               * there is an account (an owner) that can be granted exclusive access to
               * specific functions.
               *
               * By default, the owner account will be the one that deploys the contract. This
               * can later be changed with {transferOwnership}.
               *
               * This module is used through inheritance. It will make available the modifier
               * `onlyOwner`, which can be applied to your functions to restrict their use to
               * the owner.
               */
              contract Ownable is Context {
                address private _owner;
                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                /**
                 * @dev Initializes the contract setting the deployer as the initial owner.
                 */
                constructor() internal {
                  address msgSender = _msgSender();
                  _owner = msgSender;
                  emit OwnershipTransferred(address(0), msgSender);
                }
                /**
                 * @dev Returns the address of the current owner.
                 */
                function owner() public view returns (address) {
                  return _owner;
                }
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                  require(_owner == _msgSender(), 'Ownable: caller is not the owner');
                  _;
                }
                /**
                 * @dev Leaves the contract without owner. It will not be possible to call
                 * `onlyOwner` functions anymore. Can only be called by the current owner.
                 *
                 * NOTE: Renouncing ownership will leave the contract without an owner,
                 * thereby removing any functionality that is only available to the owner.
                 */
                function renounceOwnership() public virtual onlyOwner {
                  emit OwnershipTransferred(_owner, address(0));
                  _owner = address(0);
                }
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 * Can only be called by the current owner.
                 */
                function transferOwnership(address newOwner) public virtual onlyOwner {
                  require(newOwner != address(0), 'Ownable: new owner is the zero address');
                  emit OwnershipTransferred(_owner, newOwner);
                  _owner = newOwner;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
              import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
              import {IUiPoolDataProvider} from './interfaces/IUiPoolDataProvider.sol';
              import {ILendingPool} from '../interfaces/ILendingPool.sol';
              import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
              import {IAToken} from '../interfaces/IAToken.sol';
              import {IVariableDebtToken} from '../interfaces/IVariableDebtToken.sol';
              import {IStableDebtToken} from '../interfaces/IStableDebtToken.sol';
              import {WadRayMath} from '../protocol/libraries/math/WadRayMath.sol';
              import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol';
              import {UserConfiguration} from '../protocol/libraries/configuration/UserConfiguration.sol';
              import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
              import {
                DefaultReserveInterestRateStrategy
              } from '../protocol/lendingpool/DefaultReserveInterestRateStrategy.sol';
              contract UiPoolDataProvider is IUiPoolDataProvider {
                using WadRayMath for uint256;
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                using UserConfiguration for DataTypes.UserConfigurationMap;
                address public constant MOCK_USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96;
                function getInterestRateStrategySlopes(DefaultReserveInterestRateStrategy interestRateStrategy)
                  internal
                  view
                  returns (
                    uint256,
                    uint256,
                    uint256,
                    uint256
                  )
                {
                  return (
                    interestRateStrategy.variableRateSlope1(),
                    interestRateStrategy.variableRateSlope2(),
                    interestRateStrategy.stableRateSlope1(),
                    interestRateStrategy.stableRateSlope2()
                  );
                }
                function getReservesData(ILendingPoolAddressesProvider provider, address user)
                  external
                  view
                  override
                  returns (
                    AggregatedReserveData[] memory,
                    UserReserveData[] memory,
                    uint256
                  )
                {
                  ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
                  IPriceOracleGetter oracle = IPriceOracleGetter(provider.getPriceOracle());
                  address[] memory reserves = lendingPool.getReservesList();
                  DataTypes.UserConfigurationMap memory userConfig = lendingPool.getUserConfiguration(user);
                  AggregatedReserveData[] memory reservesData = new AggregatedReserveData[](reserves.length);
                  UserReserveData[] memory userReservesData =
                    new UserReserveData[](user != address(0) ? reserves.length : 0);
                  for (uint256 i = 0; i < reserves.length; i++) {
                    AggregatedReserveData memory reserveData = reservesData[i];
                    reserveData.underlyingAsset = reserves[i];
                    // reserve current state
                    DataTypes.ReserveData memory baseData =
                      lendingPool.getReserveData(reserveData.underlyingAsset);
                    reserveData.liquidityIndex = baseData.liquidityIndex;
                    reserveData.variableBorrowIndex = baseData.variableBorrowIndex;
                    reserveData.liquidityRate = baseData.currentLiquidityRate;
                    reserveData.variableBorrowRate = baseData.currentVariableBorrowRate;
                    reserveData.stableBorrowRate = baseData.currentStableBorrowRate;
                    reserveData.lastUpdateTimestamp = baseData.lastUpdateTimestamp;
                    reserveData.aTokenAddress = baseData.aTokenAddress;
                    reserveData.stableDebtTokenAddress = baseData.stableDebtTokenAddress;
                    reserveData.variableDebtTokenAddress = baseData.variableDebtTokenAddress;
                    reserveData.interestRateStrategyAddress = baseData.interestRateStrategyAddress;
                    reserveData.priceInEth = oracle.getAssetPrice(reserveData.underlyingAsset);
                    reserveData.availableLiquidity = IERC20Detailed(reserveData.underlyingAsset).balanceOf(
                      reserveData.aTokenAddress
                    );
                    (
                      reserveData.totalPrincipalStableDebt,
                      ,
                      reserveData.averageStableRate,
                      reserveData.stableDebtLastUpdateTimestamp
                    ) = IStableDebtToken(reserveData.stableDebtTokenAddress).getSupplyData();
                    reserveData.totalScaledVariableDebt = IVariableDebtToken(reserveData.variableDebtTokenAddress)
                      .scaledTotalSupply();
                    // reserve configuration
                    // we're getting this info from the aToken, because some of assets can be not compliant with ETC20Detailed
                    reserveData.symbol = IERC20Detailed(reserveData.aTokenAddress).symbol();
                    reserveData.name = '';
                    (
                      reserveData.baseLTVasCollateral,
                      reserveData.reserveLiquidationThreshold,
                      reserveData.reserveLiquidationBonus,
                      reserveData.decimals,
                      reserveData.reserveFactor
                    ) = baseData.configuration.getParamsMemory();
                    (
                      reserveData.isActive,
                      reserveData.isFrozen,
                      reserveData.borrowingEnabled,
                      reserveData.stableBorrowRateEnabled
                    ) = baseData.configuration.getFlagsMemory();
                    reserveData.usageAsCollateralEnabled = reserveData.baseLTVasCollateral != 0;
                    (
                      reserveData.variableRateSlope1,
                      reserveData.variableRateSlope2,
                      reserveData.stableRateSlope1,
                      reserveData.stableRateSlope2
                    ) = getInterestRateStrategySlopes(
                      DefaultReserveInterestRateStrategy(reserveData.interestRateStrategyAddress)
                    );
                    if (user != address(0)) {
                      // user reserve data
                      userReservesData[i].underlyingAsset = reserveData.underlyingAsset;
                      userReservesData[i].scaledATokenBalance = IAToken(reserveData.aTokenAddress)
                        .scaledBalanceOf(user);
                      userReservesData[i].usageAsCollateralEnabledOnUser = userConfig.isUsingAsCollateral(i);
                      if (userConfig.isBorrowing(i)) {
                        userReservesData[i].scaledVariableDebt = IVariableDebtToken(
                          reserveData
                            .variableDebtTokenAddress
                        )
                          .scaledBalanceOf(user);
                        userReservesData[i].principalStableDebt = IStableDebtToken(
                          reserveData
                            .stableDebtTokenAddress
                        )
                          .principalBalanceOf(user);
                        if (userReservesData[i].principalStableDebt != 0) {
                          userReservesData[i].stableBorrowRate = IStableDebtToken(
                            reserveData
                              .stableDebtTokenAddress
                          )
                            .getUserStableRate(user);
                          userReservesData[i].stableBorrowLastUpdateTimestamp = IStableDebtToken(
                            reserveData
                              .stableDebtTokenAddress
                          )
                            .getUserLastUpdated(user);
                        }
                      }
                    }
                  }
                  return (reservesData, userReservesData, oracle.getAssetPrice(MOCK_USD_ADDRESS));
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {IERC20} from './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);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              interface IUiPoolDataProvider {
                struct AggregatedReserveData {
                  address underlyingAsset;
                  string name;
                  string symbol;
                  uint256 decimals;
                  uint256 baseLTVasCollateral;
                  uint256 reserveLiquidationThreshold;
                  uint256 reserveLiquidationBonus;
                  uint256 reserveFactor;
                  bool usageAsCollateralEnabled;
                  bool borrowingEnabled;
                  bool stableBorrowRateEnabled;
                  bool isActive;
                  bool isFrozen;
                  // base data
                  uint128 liquidityIndex;
                  uint128 variableBorrowIndex;
                  uint128 liquidityRate;
                  uint128 variableBorrowRate;
                  uint128 stableBorrowRate;
                  uint40 lastUpdateTimestamp;
                  address aTokenAddress;
                  address stableDebtTokenAddress;
                  address variableDebtTokenAddress;
                  address interestRateStrategyAddress;
                  //
                  uint256 availableLiquidity;
                  uint256 totalPrincipalStableDebt;
                  uint256 averageStableRate;
                  uint256 stableDebtLastUpdateTimestamp;
                  uint256 totalScaledVariableDebt;
                  uint256 priceInEth;
                  uint256 variableRateSlope1;
                  uint256 variableRateSlope2;
                  uint256 stableRateSlope1;
                  uint256 stableRateSlope2;
                }
                //
                //  struct ReserveData {
                //    uint256 averageStableBorrowRate;
                //    uint256 totalLiquidity;
                //  }
                struct UserReserveData {
                  address underlyingAsset;
                  uint256 scaledATokenBalance;
                  bool usageAsCollateralEnabledOnUser;
                  uint256 stableBorrowRate;
                  uint256 scaledVariableDebt;
                  uint256 principalStableDebt;
                  uint256 stableBorrowLastUpdateTimestamp;
                }
                //
                //  struct ATokenSupplyData {
                //    string name;
                //    string symbol;
                //    uint8 decimals;
                //    uint256 totalSupply;
                //    address aTokenAddress;
                //  }
                function getReservesData(ILendingPoolAddressesProvider provider, address user)
                  external
                  view
                  returns (
                    AggregatedReserveData[] memory,
                    UserReserveData[] memory,
                    uint256
                  );
                //  function getUserReservesData(ILendingPoolAddressesProvider provider, address user)
                //    external
                //    view
                //    returns (UserReserveData[] memory);
                //
                //  function getAllATokenSupply(ILendingPoolAddressesProvider provider)
                //    external
                //    view
                //    returns (ATokenSupplyData[] memory);
                //
                //  function getATokenSupply(address[] calldata aTokens)
                //    external
                //    view
                //    returns (ATokenSupplyData[] memory);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IReserveInterestRateStrategy} from '../../interfaces/IReserveInterestRateStrategy.sol';
              import {WadRayMath} from '../libraries/math/WadRayMath.sol';
              import {PercentageMath} from '../libraries/math/PercentageMath.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              import {ILendingRateOracle} from '../../interfaces/ILendingRateOracle.sol';
              /**
               * @title DefaultReserveInterestRateStrategy contract
               * @notice Implements the calculation of the interest rates depending on the reserve state
               * @dev The model of interest rate is based on 2 slopes, one before the `OPTIMAL_UTILIZATION_RATE`
               * point of utilization and another from that one to 100%
               * - An instance of this same contract, can't be used across different Aave markets, due to the caching
               *   of the LendingPoolAddressesProvider
               * @author Aave
               **/
              contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy {
                using WadRayMath for uint256;
                using SafeMath for uint256;
                using PercentageMath 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 immutable OPTIMAL_UTILIZATION_RATE;
                /**
                 * @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 immutable EXCESS_UTILIZATION_RATE;
                ILendingPoolAddressesProvider public immutable addressesProvider;
                // Base variable borrow rate when Utilization rate = 0. Expressed in ray
                uint256 internal immutable _baseVariableBorrowRate;
                // Slope of the variable interest curve when utilization rate > 0 and <= OPTIMAL_UTILIZATION_RATE. Expressed in ray
                uint256 internal immutable _variableRateSlope1;
                // Slope of the variable interest curve when utilization rate > OPTIMAL_UTILIZATION_RATE. Expressed in ray
                uint256 internal immutable _variableRateSlope2;
                // Slope of the stable interest curve when utilization rate > 0 and <= OPTIMAL_UTILIZATION_RATE. Expressed in ray
                uint256 internal immutable _stableRateSlope1;
                // Slope of the stable interest curve when utilization rate > OPTIMAL_UTILIZATION_RATE. Expressed in ray
                uint256 internal immutable _stableRateSlope2;
                constructor(
                  ILendingPoolAddressesProvider provider,
                  uint256 optimalUtilizationRate,
                  uint256 baseVariableBorrowRate,
                  uint256 variableRateSlope1,
                  uint256 variableRateSlope2,
                  uint256 stableRateSlope1,
                  uint256 stableRateSlope2
                ) public {
                  OPTIMAL_UTILIZATION_RATE = optimalUtilizationRate;
                  EXCESS_UTILIZATION_RATE = WadRayMath.ray().sub(optimalUtilizationRate);
                  addressesProvider = provider;
                  _baseVariableBorrowRate = baseVariableBorrowRate;
                  _variableRateSlope1 = variableRateSlope1;
                  _variableRateSlope2 = variableRateSlope2;
                  _stableRateSlope1 = stableRateSlope1;
                  _stableRateSlope2 = stableRateSlope2;
                }
                function variableRateSlope1() external view returns (uint256) {
                  return _variableRateSlope1;
                }
                function variableRateSlope2() external view returns (uint256) {
                  return _variableRateSlope2;
                }
                function stableRateSlope1() external view returns (uint256) {
                  return _stableRateSlope1;
                }
                function stableRateSlope2() external view returns (uint256) {
                  return _stableRateSlope2;
                }
                function baseVariableBorrowRate() external view override returns (uint256) {
                  return _baseVariableBorrowRate;
                }
                function getMaxVariableBorrowRate() external view override returns (uint256) {
                  return _baseVariableBorrowRate.add(_variableRateSlope1).add(_variableRateSlope2);
                }
                struct CalcInterestRatesLocalVars {
                  uint256 totalDebt;
                  uint256 currentVariableBorrowRate;
                  uint256 currentStableBorrowRate;
                  uint256 currentLiquidityRate;
                  uint256 utilizationRate;
                }
                /**
                 * @dev Calculates the interest rates depending on the reserve's state and configurations
                 * @param reserve The address of the reserve
                 * @param availableLiquidity The liquidity available in the reserve
                 * @param totalStableDebt The total borrowed from the reserve a stable rate
                 * @param totalVariableDebt The total borrowed from the reserve at a variable rate
                 * @param averageStableBorrowRate The weighted average of all the stable rate loans
                 * @param reserveFactor The reserve portion of the interest that goes to the treasury of the market
                 * @return The liquidity rate, the stable borrow rate and the variable borrow rate
                 **/
                function calculateInterestRates(
                  address reserve,
                  uint256 availableLiquidity,
                  uint256 totalStableDebt,
                  uint256 totalVariableDebt,
                  uint256 averageStableBorrowRate,
                  uint256 reserveFactor
                )
                  external
                  view
                  override
                  returns (
                    uint256,
                    uint256,
                    uint256
                  )
                {
                  CalcInterestRatesLocalVars memory vars;
                  vars.totalDebt = totalStableDebt.add(totalVariableDebt);
                  vars.currentVariableBorrowRate = 0;
                  vars.currentStableBorrowRate = 0;
                  vars.currentLiquidityRate = 0;
                  uint256 utilizationRate =
                    vars.totalDebt == 0
                      ? 0
                      : vars.totalDebt.rayDiv(availableLiquidity.add(vars.totalDebt));
                  vars.currentStableBorrowRate = ILendingRateOracle(addressesProvider.getLendingRateOracle())
                    .getMarketBorrowRate(reserve);
                  if (utilizationRate > OPTIMAL_UTILIZATION_RATE) {
                    uint256 excessUtilizationRateRatio =
                      utilizationRate.sub(OPTIMAL_UTILIZATION_RATE).rayDiv(EXCESS_UTILIZATION_RATE);
                    vars.currentStableBorrowRate = vars.currentStableBorrowRate.add(_stableRateSlope1).add(
                      _stableRateSlope2.rayMul(excessUtilizationRateRatio)
                    );
                    vars.currentVariableBorrowRate = _baseVariableBorrowRate.add(_variableRateSlope1).add(
                      _variableRateSlope2.rayMul(excessUtilizationRateRatio)
                    );
                  } else {
                    vars.currentStableBorrowRate = vars.currentStableBorrowRate.add(
                      _stableRateSlope1.rayMul(utilizationRate.rayDiv(OPTIMAL_UTILIZATION_RATE))
                    );
                    vars.currentVariableBorrowRate = _baseVariableBorrowRate.add(
                      utilizationRate.rayMul(_variableRateSlope1).rayDiv(OPTIMAL_UTILIZATION_RATE)
                    );
                  }
                  vars.currentLiquidityRate = _getOverallBorrowRate(
                    totalStableDebt,
                    totalVariableDebt,
                    vars
                      .currentVariableBorrowRate,
                    averageStableBorrowRate
                  )
                    .rayMul(utilizationRate)
                    .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(reserveFactor));
                  return (
                    vars.currentLiquidityRate,
                    vars.currentStableBorrowRate,
                    vars.currentVariableBorrowRate
                  );
                }
                /**
                 * @dev Calculates the overall borrow rate as the weighted average between the total variable debt and total stable debt
                 * @param totalStableDebt The total borrowed from the reserve a stable rate
                 * @param totalVariableDebt The total borrowed from the reserve at a variable rate
                 * @param currentVariableBorrowRate The current variable borrow rate of the reserve
                 * @param currentAverageStableBorrowRate The current weighted average of all the stable rate loans
                 * @return The weighted averaged borrow rate
                 **/
                function _getOverallBorrowRate(
                  uint256 totalStableDebt,
                  uint256 totalVariableDebt,
                  uint256 currentVariableBorrowRate,
                  uint256 currentAverageStableBorrowRate
                ) internal pure returns (uint256) {
                  uint256 totalDebt = totalStableDebt.add(totalVariableDebt);
                  if (totalDebt == 0) return 0;
                  uint256 weightedVariableRate = totalVariableDebt.wadToRay().rayMul(currentVariableBorrowRate);
                  uint256 weightedStableRate = totalStableDebt.wadToRay().rayMul(currentAverageStableBorrowRate);
                  uint256 overallBorrowRate =
                    weightedVariableRate.add(weightedStableRate).rayDiv(totalDebt.wadToRay());
                  return overallBorrowRate;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @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;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {ILendingRateOracle} from '../../interfaces/ILendingRateOracle.sol';
              import {Ownable} from '../../dependencies/openzeppelin/contracts/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;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Ownable} from '../../dependencies/openzeppelin/contracts/Ownable.sol';
              import {
                ILendingPoolAddressesProviderRegistry
              } from '../../interfaces/ILendingPoolAddressesProviderRegistry.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              /**
               * @title LendingPoolAddressesProviderRegistry contract
               * @dev Main registry of LendingPoolAddressesProvider of multiple Aave protocol's markets
               * - Used for indexing purposes of Aave protocol's markets
               * - The id assigned to a LendingPoolAddressesProvider refers to the market it is connected with,
               *   for example with `0` for the Aave main market and `1` for the next created
               * @author Aave
               **/
              contract LendingPoolAddressesProviderRegistry is Ownable, ILendingPoolAddressesProviderRegistry {
                mapping(address => uint256) private _addressesProviders;
                address[] private _addressesProvidersList;
                /**
                 * @dev Returns the list of registered addresses provider
                 * @return The list of addresses provider, potentially containing address(0) elements
                 **/
                function getAddressesProvidersList() external view override returns (address[] memory) {
                  address[] memory addressesProvidersList = _addressesProvidersList;
                  uint256 maxLength = addressesProvidersList.length;
                  address[] memory activeProviders = new address[](maxLength);
                  for (uint256 i = 0; i < maxLength; i++) {
                    if (_addressesProviders[addressesProvidersList[i]] > 0) {
                      activeProviders[i] = addressesProvidersList[i];
                    }
                  }
                  return activeProviders;
                }
                /**
                 * @dev Registers an addresses provider
                 * @param provider The address of the new LendingPoolAddressesProvider
                 * @param id The id for the new LendingPoolAddressesProvider, referring to the market it belongs to
                 **/
                function registerAddressesProvider(address provider, uint256 id) external override onlyOwner {
                  require(id != 0, Errors.LPAPR_INVALID_ADDRESSES_PROVIDER_ID);
                  _addressesProviders[provider] = id;
                  _addToAddressesProvidersList(provider);
                  emit AddressesProviderRegistered(provider);
                }
                /**
                 * @dev Removes a LendingPoolAddressesProvider from the list of registered addresses provider
                 * @param provider The LendingPoolAddressesProvider address
                 **/
                function unregisterAddressesProvider(address provider) external override onlyOwner {
                  require(_addressesProviders[provider] > 0, Errors.LPAPR_PROVIDER_NOT_REGISTERED);
                  _addressesProviders[provider] = 0;
                  emit AddressesProviderUnregistered(provider);
                }
                /**
                 * @dev Returns the id on a registered LendingPoolAddressesProvider
                 * @return The id or 0 if the LendingPoolAddressesProvider is not registered
                 */
                function getAddressesProviderIdByAddress(address addressesProvider)
                  external
                  view
                  override
                  returns (uint256)
                {
                  return _addressesProviders[addressesProvider];
                }
                function _addToAddressesProvidersList(address provider) internal {
                  uint256 providersCount = _addressesProvidersList.length;
                  for (uint256 i = 0; i < providersCount; i++) {
                    if (_addressesProvidersList[i] == provider) {
                      return;
                    }
                  }
                  _addressesProvidersList.push(provider);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title LendingPoolAddressesProviderRegistry contract
               * @dev Main registry of LendingPoolAddressesProvider of multiple Aave protocol's markets
               * - Used for indexing purposes of Aave protocol's markets
               * - The id assigned to a LendingPoolAddressesProvider refers to the market it is connected with,
               *   for example with `0` for the Aave main market and `1` for the next created
               * @author Aave
               **/
              interface ILendingPoolAddressesProviderRegistry {
                event AddressesProviderRegistered(address indexed newAddress);
                event AddressesProviderUnregistered(address indexed newAddress);
                function getAddressesProvidersList() external view returns (address[] memory);
                function getAddressesProviderIdByAddress(address addressesProvider)
                  external
                  view
                  returns (uint256);
                function registerAddressesProvider(address provider, uint256 id) external;
                function unregisterAddressesProvider(address provider) external;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {ILendingPool} from '../../interfaces/ILendingPool.sol';
              import {IDelegationToken} from '../../interfaces/IDelegationToken.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              import {AToken} from './AToken.sol';
              /**
               * @title Aave AToken enabled to delegate voting power of the underlying asset to a different address
               * @dev The underlying asset needs to be compatible with the COMP delegation interface
               * @author Aave
               */
              contract DelegationAwareAToken is AToken {
                modifier onlyPoolAdmin {
                  require(
                    _msgSender() == ILendingPool(POOL).getAddressesProvider().getPoolAdmin(),
                    Errors.CALLER_NOT_POOL_ADMIN
                  );
                  _;
                }
                constructor(
                  ILendingPool pool,
                  address underlyingAssetAddress,
                  address reserveTreasury,
                  string memory tokenName,
                  string memory tokenSymbol,
                  address incentivesController
                )
                  public
                  AToken(
                    pool,
                    underlyingAssetAddress,
                    reserveTreasury,
                    tokenName,
                    tokenSymbol,
                    incentivesController
                  )
                {}
                /**
                 * @dev Delegates voting power of the underlying asset to a `delegatee` address
                 * @param delegatee The address that will receive the delegation
                 **/
                function delegateUnderlyingTo(address delegatee) external onlyPoolAdmin {
                  IDelegationToken(UNDERLYING_ASSET_ADDRESS).delegate(delegatee);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title IDelegationToken
               * @dev Implements an interface for tokens with delegation COMP/UNI compatible
               * @author Aave
               **/
              interface IDelegationToken {
                function delegate(address delegatee) external;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {ILendingPool} from '../../interfaces/ILendingPool.sol';
              import {IAToken} from '../../interfaces/IAToken.sol';
              import {WadRayMath} from '../libraries/math/WadRayMath.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
              import {IncentivizedERC20} from './IncentivizedERC20.sol';
              /**
               * @title Aave ERC20 AToken
               * @dev Implementation of the interest bearing token for the Aave protocol
               * @author Aave
               */
              contract AToken is VersionedInitializable, IncentivizedERC20, IAToken {
                using WadRayMath for uint256;
                using SafeERC20 for IERC20;
                bytes public constant EIP712_REVISION = bytes('1');
                bytes32 internal constant EIP712_DOMAIN =
                  keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');
                bytes32 public constant PERMIT_TYPEHASH =
                  keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)');
                uint256 public constant UINT_MAX_VALUE = uint256(-1);
                uint256 public constant ATOKEN_REVISION = 0x1;
                address public immutable UNDERLYING_ASSET_ADDRESS;
                address public immutable RESERVE_TREASURY_ADDRESS;
                ILendingPool public immutable POOL;
                /// @dev owner => next valid nonce to submit with permit()
                mapping(address => uint256) public _nonces;
                bytes32 public DOMAIN_SEPARATOR;
                modifier onlyLendingPool {
                  require(_msgSender() == address(POOL), Errors.CT_CALLER_MUST_BE_LENDING_POOL);
                  _;
                }
                constructor(
                  ILendingPool pool,
                  address underlyingAssetAddress,
                  address reserveTreasuryAddress,
                  string memory tokenName,
                  string memory tokenSymbol,
                  address incentivesController
                ) public IncentivizedERC20(tokenName, tokenSymbol, 18, incentivesController) {
                  POOL = pool;
                  UNDERLYING_ASSET_ADDRESS = underlyingAssetAddress;
                  RESERVE_TREASURY_ADDRESS = reserveTreasuryAddress;
                }
                function getRevision() internal pure virtual override returns (uint256) {
                  return ATOKEN_REVISION;
                }
                function initialize(
                  uint8 underlyingAssetDecimals,
                  string calldata tokenName,
                  string calldata tokenSymbol
                ) external virtual initializer {
                  uint256 chainId;
                  //solium-disable-next-line
                  assembly {
                    chainId := chainid()
                  }
                  DOMAIN_SEPARATOR = keccak256(
                    abi.encode(
                      EIP712_DOMAIN,
                      keccak256(bytes(tokenName)),
                      keccak256(EIP712_REVISION),
                      chainId,
                      address(this)
                    )
                  );
                  _setName(tokenName);
                  _setSymbol(tokenSymbol);
                  _setDecimals(underlyingAssetDecimals);
                }
                /**
                 * @dev Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying`
                 * - Only callable by the LendingPool, as extra state updates there need to be managed
                 * @param user The owner of the aTokens, getting them burned
                 * @param receiverOfUnderlying The address that will receive the underlying
                 * @param amount The amount being burned
                 * @param index The new liquidity index of the reserve
                 **/
                function burn(
                  address user,
                  address receiverOfUnderlying,
                  uint256 amount,
                  uint256 index
                ) external override onlyLendingPool {
                  uint256 amountScaled = amount.rayDiv(index);
                  require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT);
                  _burn(user, amountScaled);
                  IERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(receiverOfUnderlying, amount);
                  emit Transfer(user, address(0), amount);
                  emit Burn(user, receiverOfUnderlying, amount, index);
                }
                /**
                 * @dev Mints `amount` aTokens to `user`
                 * - Only callable by the LendingPool, as extra state updates there need to be managed
                 * @param user The address receiving the minted tokens
                 * @param amount The amount of tokens getting minted
                 * @param index The new liquidity index of the reserve
                 * @return `true` if the the previous balance of the user was 0
                 */
                function mint(
                  address user,
                  uint256 amount,
                  uint256 index
                ) external override onlyLendingPool returns (bool) {
                  uint256 previousBalance = super.balanceOf(user);
                  uint256 amountScaled = amount.rayDiv(index);
                  require(amountScaled != 0, Errors.CT_INVALID_MINT_AMOUNT);
                  _mint(user, amountScaled);
                  emit Transfer(address(0), user, amount);
                  emit Mint(user, amount, index);
                  return previousBalance == 0;
                }
                /**
                 * @dev Mints aTokens to the reserve treasury
                 * - Only callable by the LendingPool
                 * @param amount The amount of tokens getting minted
                 * @param index The new liquidity index of the reserve
                 */
                function mintToTreasury(uint256 amount, uint256 index) external override onlyLendingPool {
                  if (amount == 0) {
                    return;
                  }
                  // Compared to the normal mint, we don't check for rounding errors.
                  // The amount to mint can easily be very small since it is a fraction of the interest ccrued.
                  // In that case, the treasury will experience a (very small) loss, but it
                  // wont cause potentially valid transactions to fail.
                  _mint(RESERVE_TREASURY_ADDRESS, amount.rayDiv(index));
                  emit Transfer(address(0), RESERVE_TREASURY_ADDRESS, amount);
                  emit Mint(RESERVE_TREASURY_ADDRESS, amount, index);
                }
                /**
                 * @dev Transfers aTokens in the event of a borrow being liquidated, in case the liquidators reclaims the aToken
                 * - Only callable by the LendingPool
                 * @param from The address getting liquidated, current owner of the aTokens
                 * @param to The recipient
                 * @param value The amount of tokens getting transferred
                 **/
                function transferOnLiquidation(
                  address from,
                  address to,
                  uint256 value
                ) external override onlyLendingPool {
                  // Being a normal transfer, the Transfer() and BalanceTransfer() are emitted
                  // so no need to emit a specific event here
                  _transfer(from, to, value, false);
                  emit Transfer(from, to, value);
                }
                /**
                 * @dev Calculates the balance of the user: principal balance + interest generated by the principal
                 * @param user The user whose balance is calculated
                 * @return The balance of the user
                 **/
                function balanceOf(address user)
                  public
                  view
                  override(IncentivizedERC20, IERC20)
                  returns (uint256)
                {
                  return super.balanceOf(user).rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS));
                }
                /**
                 * @dev Returns the scaled balance of the user. The scaled balance is the sum of all the
                 * updated stored balance divided by the reserve's liquidity index at the moment of the update
                 * @param user The user whose balance is calculated
                 * @return The scaled balance of the user
                 **/
                function scaledBalanceOf(address user) external view override returns (uint256) {
                  return super.balanceOf(user);
                }
                /**
                 * @dev Returns the scaled balance of the user and the scaled total supply.
                 * @param user The address of the user
                 * @return The scaled balance of the user
                 * @return The scaled balance and the scaled total supply
                 **/
                function getScaledUserBalanceAndSupply(address user)
                  external
                  view
                  override
                  returns (uint256, uint256)
                {
                  return (super.balanceOf(user), super.totalSupply());
                }
                /**
                 * @dev calculates the total supply of the specific aToken
                 * since the balance of every single user increases over time, the total supply
                 * does that too.
                 * @return the current total supply
                 **/
                function totalSupply() public view override(IncentivizedERC20, IERC20) returns (uint256) {
                  uint256 currentSupplyScaled = super.totalSupply();
                  if (currentSupplyScaled == 0) {
                    return 0;
                  }
                  return currentSupplyScaled.rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS));
                }
                /**
                 * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index)
                 * @return the scaled total supply
                 **/
                function scaledTotalSupply() public view virtual override returns (uint256) {
                  return super.totalSupply();
                }
                /**
                 * @dev Transfers the underlying asset to `target`. Used by the LendingPool to transfer
                 * assets in borrow(), withdraw() and flashLoan()
                 * @param target The recipient of the aTokens
                 * @param amount The amount getting transferred
                 * @return The amount transferred
                 **/
                function transferUnderlyingTo(address target, uint256 amount)
                  external
                  override
                  onlyLendingPool
                  returns (uint256)
                {
                  IERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(target, amount);
                  return amount;
                }
                /**
                 * @dev implements the permit function as for
                 * https://github.com/ethereum/EIPs/blob/8a34d644aacf0f9f8f00815307fd7dd5da07655f/EIPS/eip-2612.md
                 * @param owner The owner of the funds
                 * @param spender The spender
                 * @param value The amount
                 * @param deadline The deadline timestamp, type(uint256).max for max deadline
                 * @param v Signature param
                 * @param s Signature param
                 * @param r Signature param
                 */
                function permit(
                  address owner,
                  address spender,
                  uint256 value,
                  uint256 deadline,
                  uint8 v,
                  bytes32 r,
                  bytes32 s
                ) external {
                  require(owner != address(0), 'INVALID_OWNER');
                  //solium-disable-next-line
                  require(block.timestamp <= deadline, 'INVALID_EXPIRATION');
                  uint256 currentValidNonce = _nonces[owner];
                  bytes32 digest =
                    keccak256(
                      abi.encodePacked(
                        '\\x19\\x01',
                        DOMAIN_SEPARATOR,
                        keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))
                      )
                    );
                  require(owner == ecrecover(digest, v, r, s), 'INVALID_SIGNATURE');
                  _nonces[owner] = currentValidNonce.add(1);
                  _approve(owner, spender, value);
                }
                /**
                 * @dev Transfers the aTokens between two users. Validates the transfer
                 * (ie checks for valid HF after the transfer) if required
                 * @param from The source address
                 * @param to The destination address
                 * @param amount The amount getting transferred
                 * @param validate `true` if the transfer needs to be validated
                 **/
                function _transfer(
                  address from,
                  address to,
                  uint256 amount,
                  bool validate
                ) internal {
                  uint256 index = POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS);
                  uint256 fromBalanceBefore = super.balanceOf(from).rayMul(index);
                  uint256 toBalanceBefore = super.balanceOf(to).rayMul(index);
                  super._transfer(from, to, amount.rayDiv(index));
                  if (validate) {
                    POOL.finalizeTransfer(
                      UNDERLYING_ASSET_ADDRESS,
                      from,
                      to,
                      amount,
                      fromBalanceBefore,
                      toBalanceBefore
                    );
                  }
                  emit BalanceTransfer(from, to, amount, index);
                }
                /**
                 * @dev Overrides the parent _transfer to force validated transfer() and transferFrom()
                 * @param from The source address
                 * @param to The destination address
                 * @param amount The amount getting transferred
                 **/
                function _transfer(
                  address from,
                  address to,
                  uint256 amount
                ) internal override {
                  _transfer(from, to, amount, true);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Context} from '../../dependencies/openzeppelin/contracts/Context.sol';
              import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {IERC20Detailed} from '../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
              import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol';
              /**
               * @title ERC20
               * @notice Basic ERC20 implementation
               * @author Aave, inspired by the Openzeppelin ERC20 implementation
               **/
              contract IncentivizedERC20 is Context, IERC20, IERC20Detailed {
                using SafeMath for uint256;
                IAaveIncentivesController internal immutable _incentivesController;
                mapping(address => uint256) internal _balances;
                mapping(address => mapping(address => uint256)) private _allowances;
                uint256 internal _totalSupply;
                string private _name;
                string private _symbol;
                uint8 private _decimals;
                constructor(
                  string memory name,
                  string memory symbol,
                  uint8 decimals,
                  address incentivesController
                ) public {
                  _name = name;
                  _symbol = symbol;
                  _decimals = decimals;
                  _incentivesController = IAaveIncentivesController(incentivesController);
                }
                /**
                 * @return The name of the token
                 **/
                function name() public view override returns (string memory) {
                  return _name;
                }
                /**
                 * @return The symbol of the token
                 **/
                function symbol() public view override returns (string memory) {
                  return _symbol;
                }
                /**
                 * @return The decimals of the token
                 **/
                function decimals() public view override returns (uint8) {
                  return _decimals;
                }
                /**
                 * @return The total supply of the token
                 **/
                function totalSupply() public view virtual override returns (uint256) {
                  return _totalSupply;
                }
                /**
                 * @return The balance of the token
                 **/
                function balanceOf(address account) public view virtual override returns (uint256) {
                  return _balances[account];
                }
                /**
                 * @dev Executes a transfer of tokens from _msgSender() to recipient
                 * @param recipient The recipient of the tokens
                 * @param amount The amount of tokens being transferred
                 * @return `true` if the transfer succeeds, `false` otherwise
                 **/
                function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                  _transfer(_msgSender(), recipient, amount);
                  emit Transfer(_msgSender(), recipient, amount);
                  return true;
                }
                /**
                 * @dev Returns the allowance of spender on the tokens owned by owner
                 * @param owner The owner of the tokens
                 * @param spender The user allowed to spend the owner's tokens
                 * @return The amount of owner's tokens spender is allowed to spend
                 **/
                function allowance(address owner, address spender)
                  public
                  view
                  virtual
                  override
                  returns (uint256)
                {
                  return _allowances[owner][spender];
                }
                /**
                 * @dev Allows `spender` to spend the tokens owned by _msgSender()
                 * @param spender The user allowed to spend _msgSender() tokens
                 * @return `true`
                 **/
                function approve(address spender, uint256 amount) public virtual override returns (bool) {
                  _approve(_msgSender(), spender, amount);
                  return true;
                }
                /**
                 * @dev Executes a transfer of token from sender to recipient, if _msgSender() is allowed to do so
                 * @param sender The owner of the tokens
                 * @param recipient The recipient of the tokens
                 * @param amount The amount of tokens being transferred
                 * @return `true` if the transfer succeeds, `false` otherwise
                 **/
                function transferFrom(
                  address sender,
                  address recipient,
                  uint256 amount
                ) public virtual override returns (bool) {
                  _transfer(sender, recipient, amount);
                  _approve(
                    sender,
                    _msgSender(),
                    _allowances[sender][_msgSender()].sub(amount, 'ERC20: transfer amount exceeds allowance')
                  );
                  emit Transfer(sender, recipient, amount);
                  return true;
                }
                /**
                 * @dev Increases the allowance of spender to spend _msgSender() tokens
                 * @param spender The user allowed to spend on behalf of _msgSender()
                 * @param addedValue The amount being added to the allowance
                 * @return `true`
                 **/
                function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                  _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                  return true;
                }
                /**
                 * @dev Decreases the allowance of spender to spend _msgSender() tokens
                 * @param spender The user allowed to spend on behalf of _msgSender()
                 * @param subtractedValue The amount being subtracted to the allowance
                 * @return `true`
                 **/
                function decreaseAllowance(address spender, uint256 subtractedValue)
                  public
                  virtual
                  returns (bool)
                {
                  _approve(
                    _msgSender(),
                    spender,
                    _allowances[_msgSender()][spender].sub(
                      subtractedValue,
                      'ERC20: decreased allowance below zero'
                    )
                  );
                  return true;
                }
                function _transfer(
                  address sender,
                  address recipient,
                  uint256 amount
                ) internal virtual {
                  require(sender != address(0), 'ERC20: transfer from the zero address');
                  require(recipient != address(0), 'ERC20: transfer to the zero address');
                  _beforeTokenTransfer(sender, recipient, amount);
                  uint256 oldSenderBalance = _balances[sender];
                  _balances[sender] = oldSenderBalance.sub(amount, 'ERC20: transfer amount exceeds balance');
                  uint256 oldRecipientBalance = _balances[recipient];
                  _balances[recipient] = _balances[recipient].add(amount);
                  if (address(_incentivesController) != address(0)) {
                    uint256 currentTotalSupply = _totalSupply;
                    _incentivesController.handleAction(sender, currentTotalSupply, oldSenderBalance);
                    if (sender != recipient) {
                      _incentivesController.handleAction(recipient, currentTotalSupply, oldRecipientBalance);
                    }
                  }
                }
                function _mint(address account, uint256 amount) internal virtual {
                  require(account != address(0), 'ERC20: mint to the zero address');
                  _beforeTokenTransfer(address(0), account, amount);
                  uint256 oldTotalSupply = _totalSupply;
                  _totalSupply = oldTotalSupply.add(amount);
                  uint256 oldAccountBalance = _balances[account];
                  _balances[account] = oldAccountBalance.add(amount);
                  if (address(_incentivesController) != address(0)) {
                    _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance);
                  }
                }
                function _burn(address account, uint256 amount) internal virtual {
                  require(account != address(0), 'ERC20: burn from the zero address');
                  _beforeTokenTransfer(account, address(0), amount);
                  uint256 oldTotalSupply = _totalSupply;
                  _totalSupply = oldTotalSupply.sub(amount);
                  uint256 oldAccountBalance = _balances[account];
                  _balances[account] = oldAccountBalance.sub(amount, 'ERC20: burn amount exceeds balance');
                  if (address(_incentivesController) != address(0)) {
                    _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance);
                  }
                }
                function _approve(
                  address owner,
                  address spender,
                  uint256 amount
                ) internal virtual {
                  require(owner != address(0), 'ERC20: approve from the zero address');
                  require(spender != address(0), 'ERC20: approve to the zero address');
                  _allowances[owner][spender] = amount;
                  emit Approval(owner, spender, amount);
                }
                function _setName(string memory newName) internal {
                  _name = newName;
                }
                function _setSymbol(string memory newSymbol) internal {
                  _symbol = newSymbol;
                }
                function _setDecimals(uint8 newDecimals) internal {
                  _decimals = newDecimals;
                }
                function _beforeTokenTransfer(
                  address from,
                  address to,
                  uint256 amount
                ) internal virtual {}
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              interface IAaveIncentivesController {
                function handleAction(
                  address user,
                  uint256 userBalance,
                  uint256 totalSupply
                ) external;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {AToken} from '../../protocol/tokenization/AToken.sol';
              import {LendingPool} from '../../protocol/lendingpool/LendingPool.sol';
              contract MockAToken is AToken {
                constructor(
                  LendingPool pool,
                  address underlyingAssetAddress,
                  address reserveTreasury,
                  string memory tokenName,
                  string memory tokenSymbol,
                  address incentivesController
                )
                  public
                  AToken(
                    pool,
                    underlyingAssetAddress,
                    reserveTreasury,
                    tokenName,
                    tokenSymbol,
                    incentivesController
                  )
                {}
                function getRevision() internal pure override returns (uint256) {
                  return 0x2;
                }
                function initialize(
                  uint8 _underlyingAssetDecimals,
                  string calldata _tokenName,
                  string calldata _tokenSymbol
                ) external virtual override initializer {
                  _setName(_tokenName);
                  _setSymbol(_tokenSymbol);
                  _setDecimals(_underlyingAssetDecimals);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {Address} from '../../dependencies/openzeppelin/contracts/Address.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              import {IAToken} from '../../interfaces/IAToken.sol';
              import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol';
              import {IFlashLoanReceiver} from '../../flashloan/interfaces/IFlashLoanReceiver.sol';
              import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol';
              import {IStableDebtToken} from '../../interfaces/IStableDebtToken.sol';
              import {ILendingPool} from '../../interfaces/ILendingPool.sol';
              import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
              import {Helpers} from '../libraries/helpers/Helpers.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              import {WadRayMath} from '../libraries/math/WadRayMath.sol';
              import {PercentageMath} from '../libraries/math/PercentageMath.sol';
              import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
              import {GenericLogic} from '../libraries/logic/GenericLogic.sol';
              import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol';
              import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol';
              import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol';
              import {DataTypes} from '../libraries/types/DataTypes.sol';
              import {LendingPoolStorage} from './LendingPoolStorage.sol';
              /**
               * @title LendingPool contract
               * @dev Main point of interaction with an Aave protocol's market
               * - Users can:
               *   # Deposit
               *   # Withdraw
               *   # Borrow
               *   # Repay
               *   # Swap their loans between variable and stable rate
               *   # Enable/disable their deposits as collateral rebalance stable rate borrow positions
               *   # Liquidate positions
               *   # Execute Flash Loans
               * - To be covered by a proxy contract, owned by the LendingPoolAddressesProvider of the specific market
               * - All admin functions are callable by the LendingPoolConfigurator contract defined also in the
               *   LendingPoolAddressesProvider
               * @author Aave
               **/
              contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage {
                using SafeMath for uint256;
                using WadRayMath for uint256;
                using PercentageMath for uint256;
                using SafeERC20 for IERC20;
                //main configuration parameters
                uint256 public constant MAX_STABLE_RATE_BORROW_SIZE_PERCENT = 2500;
                uint256 public constant FLASHLOAN_PREMIUM_TOTAL = 9;
                uint256 public constant MAX_NUMBER_RESERVES = 128;
                uint256 public constant LENDINGPOOL_REVISION = 0x3;
                modifier whenNotPaused() {
                  _whenNotPaused();
                  _;
                }
                modifier onlyLendingPoolConfigurator() {
                  _onlyLendingPoolConfigurator();
                  _;
                }
                function _whenNotPaused() internal view {
                  require(!_paused, Errors.LP_IS_PAUSED);
                }
                function _onlyLendingPoolConfigurator() internal view {
                  require(
                    _addressesProvider.getLendingPoolConfigurator() == msg.sender,
                    Errors.LP_CALLER_NOT_LENDING_POOL_CONFIGURATOR
                  );
                }
                function getRevision() internal pure override returns (uint256) {
                  return LENDINGPOOL_REVISION;
                }
                /**
                 * @dev Function is invoked by the proxy contract when the LendingPool contract is added to the
                 * LendingPoolAddressesProvider of the market.
                 * - Caching the address of the LendingPoolAddressesProvider in order to reduce gas consumption
                 *   on subsequent operations
                 * @param provider The address of the LendingPoolAddressesProvider
                 **/
                function initialize(ILendingPoolAddressesProvider provider) public initializer {
                  _addressesProvider = provider;
                }
                /**
                 * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                 * - E.g. User deposits 100 USDC and gets in return 100 aUSDC
                 * @param asset The address of the underlying asset to deposit
                 * @param amount The amount to be deposited
                 * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
                 *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
                 *   is a different wallet
                 * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                 *   0 if the action is executed directly by the user, without any middle-man
                 **/
                function deposit(
                  address asset,
                  uint256 amount,
                  address onBehalfOf,
                  uint16 referralCode
                ) external override whenNotPaused {
                  DataTypes.ReserveData storage reserve = _reserves[asset];
                  ValidationLogic.validateDeposit(reserve, amount);
                  address aToken = reserve.aTokenAddress;
                  reserve.updateState();
                  reserve.updateInterestRates(asset, aToken, amount, 0);
                  IERC20(asset).safeTransferFrom(msg.sender, aToken, amount);
                  bool isFirstDeposit = IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex);
                  if (isFirstDeposit) {
                    _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true);
                    emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf);
                  }
                  emit Deposit(asset, msg.sender, onBehalfOf, amount, referralCode);
                }
                /**
                 * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
                 * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
                 * @param asset The address of the underlying asset to withdraw
                 * @param amount The underlying amount to be withdrawn
                 *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
                 * @param to Address that will receive the underlying, same as msg.sender if the user
                 *   wants to receive it on his own wallet, or a different address if the beneficiary is a
                 *   different wallet
                 * @return The final amount withdrawn
                 **/
                function withdraw(
                  address asset,
                  uint256 amount,
                  address to
                ) external override whenNotPaused  returns (uint256) {
                  DataTypes.ReserveData storage reserve = _reserves[asset];
                  address aToken = reserve.aTokenAddress;
                  uint256 userBalance = IAToken(aToken).balanceOf(msg.sender);
                  uint256 amountToWithdraw = amount;
                  if (amount == type(uint256).max) {
                    amountToWithdraw = userBalance;
                  }
                  ValidationLogic.validateWithdraw(
                    asset,
                    amountToWithdraw,
                    userBalance,
                    _reserves,
                    _usersConfig[msg.sender],
                    _reservesList,
                    _reservesCount,
                    _addressesProvider.getPriceOracle()
                  );
                  reserve.updateState();
                  reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw);
                  if (amountToWithdraw == userBalance) {
                    _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, false);
                    emit ReserveUsedAsCollateralDisabled(asset, msg.sender);
                  }
                  IAToken(aToken).burn(msg.sender, to, amountToWithdraw, reserve.liquidityIndex);
                  emit Withdraw(asset, msg.sender, to, amountToWithdraw);
                  return amountToWithdraw;
                }
                /**
                 * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
                 * already deposited enough collateral, or he was given enough allowance by a credit delegator on the
                 * corresponding debt token (StableDebtToken or VariableDebtToken)
                 * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
                 *   and 100 stable/variable debt tokens, depending on the `interestRateMode`
                 * @param asset The address of the underlying asset to borrow
                 * @param amount The amount to be borrowed
                 * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable
                 * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                 *   0 if the action is executed directly by the user, without any middle-man
                 * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself
                 * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator
                 * if he has been given credit delegation allowance
                 **/
                function borrow(
                  address asset,
                  uint256 amount,
                  uint256 interestRateMode,
                  uint16 referralCode,
                  address onBehalfOf
                ) external override whenNotPaused {
                  DataTypes.ReserveData storage reserve = _reserves[asset];
                  _executeBorrow(
                    ExecuteBorrowParams(
                      asset,
                      msg.sender,
                      onBehalfOf,
                      amount,
                      interestRateMode,
                      reserve.aTokenAddress,
                      referralCode,
                      true
                    )
                  );
                }
                /**
                 * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned
                 * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address
                 * @param asset The address of the borrowed underlying asset previously borrowed
                 * @param amount The amount to repay
                 * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
                 * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
                 * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
                 * user calling the function if he wants to reduce/remove his own debt, or the address of any other
                 * other borrower whose debt should be removed
                 * @return The final amount repaid
                 **/
                function repay(
                  address asset,
                  uint256 amount,
                  uint256 rateMode,
                  address onBehalfOf
                ) external override whenNotPaused returns (uint256) {
                  DataTypes.ReserveData storage reserve = _reserves[asset];
                  (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve);
                  DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode);
                  ValidationLogic.validateRepay(
                    reserve,
                    amount,
                    interestRateMode,
                    onBehalfOf,
                    stableDebt,
                    variableDebt
                  );
                  uint256 paybackAmount =
                    interestRateMode == DataTypes.InterestRateMode.STABLE ? stableDebt : variableDebt;
                  if (amount < paybackAmount) {
                    paybackAmount = amount;
                  }
                  reserve.updateState();
                  if (interestRateMode == DataTypes.InterestRateMode.STABLE) {
                    IStableDebtToken(reserve.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount);
                  } else {
                    IVariableDebtToken(reserve.variableDebtTokenAddress).burn(
                      onBehalfOf,
                      paybackAmount,
                      reserve.variableBorrowIndex
                    );
                  }
                  address aToken = reserve.aTokenAddress;
                  reserve.updateInterestRates(asset, aToken, paybackAmount, 0);
                  if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) {
                    _usersConfig[onBehalfOf].setBorrowing(reserve.id, false);
                  }
                  IERC20(asset).safeTransferFrom(msg.sender, aToken, paybackAmount);
                  emit Repay(asset, onBehalfOf, msg.sender, paybackAmount);
                  return paybackAmount;
                }
                /**
                 * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa
                 * @param asset The address of the underlying asset borrowed
                 * @param rateMode The rate mode that the user wants to swap to
                 **/
                function swapBorrowRateMode(address asset, uint256 rateMode) external override whenNotPaused {
                  DataTypes.ReserveData storage reserve = _reserves[asset];
                  (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve);
                  DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode);
                  ValidationLogic.validateSwapRateMode(
                    reserve,
                    _usersConfig[msg.sender],
                    stableDebt,
                    variableDebt,
                    interestRateMode
                  );
                  reserve.updateState();
                  if (interestRateMode == DataTypes.InterestRateMode.STABLE) {
                    IStableDebtToken(reserve.stableDebtTokenAddress).burn(msg.sender, stableDebt);
                    IVariableDebtToken(reserve.variableDebtTokenAddress).mint(
                      msg.sender,
                      msg.sender,
                      stableDebt,
                      reserve.variableBorrowIndex
                    );
                  } else {
                    IVariableDebtToken(reserve.variableDebtTokenAddress).burn(
                      msg.sender,
                      variableDebt,
                      reserve.variableBorrowIndex
                    );
                    IStableDebtToken(reserve.stableDebtTokenAddress).mint(
                      msg.sender,
                      msg.sender,
                      variableDebt,
                      reserve.currentStableBorrowRate
                    );
                  }
                  reserve.updateInterestRates(asset, reserve.aTokenAddress, 0, 0);
                  emit Swap(asset, msg.sender, rateMode);
                }
                /**
                 * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve.
                 * - Users can be rebalanced if the following conditions are satisfied:
                 *     1. Usage ratio is above 95%
                 *     2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been
                 *        borrowed at a stable rate and depositors are not earning enough
                 * @param asset The address of the underlying asset borrowed
                 * @param user The address of the user to be rebalanced
                 **/
                function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused {
                  DataTypes.ReserveData storage reserve = _reserves[asset];
                  IERC20 stableDebtToken = IERC20(reserve.stableDebtTokenAddress);
                  IERC20 variableDebtToken = IERC20(reserve.variableDebtTokenAddress);
                  address aTokenAddress = reserve.aTokenAddress;
                  uint256 stableDebt = IERC20(stableDebtToken).balanceOf(user);
                  ValidationLogic.validateRebalanceStableBorrowRate(
                    reserve,
                    asset,
                    stableDebtToken,
                    variableDebtToken,
                    aTokenAddress
                  );
                  reserve.updateState();
                  IStableDebtToken(address(stableDebtToken)).burn(user, stableDebt);
                  IStableDebtToken(address(stableDebtToken)).mint(
                    user,
                    user,
                    stableDebt,
                    reserve.currentStableBorrowRate
                  );
                  reserve.updateInterestRates(asset, aTokenAddress, 0, 0);
                  emit RebalanceStableBorrowRate(asset, user);
                }
                /**
                 * @dev Allows depositors to enable/disable a specific deposited asset as collateral
                 * @param asset The address of the underlying asset deposited
                 * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise
                 **/
                function setUserUseReserveAsCollateral(address asset, bool useAsCollateral)
                  external
                  override
                  whenNotPaused
                {
                  DataTypes.ReserveData storage reserve = _reserves[asset];
                  ValidationLogic.validateSetUseReserveAsCollateral(
                    reserve,
                    asset,
                    useAsCollateral,
                    _reserves,
                    _usersConfig[msg.sender],
                    _reservesList,
                    _reservesCount,
                    _addressesProvider.getPriceOracle()
                  );
                  _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral);
                  if (useAsCollateral) {
                    emit ReserveUsedAsCollateralEnabled(asset, msg.sender);
                  } else {
                    emit ReserveUsedAsCollateralDisabled(asset, msg.sender);
                  }
                }
                /**
                 * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
                 * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
                 *   a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
                 * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                 * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                 * @param user The address of the borrower getting liquidated
                 * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                 * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
                 * to receive the underlying collateral asset directly
                 **/
                function liquidationCall(
                  address collateralAsset,
                  address debtAsset,
                  address user,
                  uint256 debtToCover,
                  bool receiveAToken
                ) external override whenNotPaused {
                  address collateralManager = _addressesProvider.getLendingPoolCollateralManager();
                  //solium-disable-next-line
                  (bool success, bytes memory result) =
                    collateralManager.delegatecall(
                      abi.encodeWithSignature(
                        'liquidationCall(address,address,address,uint256,bool)',
                        collateralAsset,
                        debtAsset,
                        user,
                        debtToCover,
                        receiveAToken
                      )
                    );
                  require(success, Errors.LP_LIQUIDATION_CALL_FAILED);
                  (uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string));
                  require(returnCode == 0, string(abi.encodePacked(returnMessage)));
                }
                struct FlashLoanLocalVars {
                  IFlashLoanReceiver receiver;
                  address oracle;
                  uint256 i;
                  address currentAsset;
                  address currentATokenAddress;
                  uint256 currentAmount;
                  uint256 currentPremium;
                  uint256 currentAmountPlusPremium;
                  address debtToken;
                }
                /**
                 * @dev Allows smartcontracts to access the liquidity of the pool within one transaction,
                 * as long as the amount taken plus a fee is returned.
                 * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration.
                 * For further details please visit https://developers.aave.com
                 * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface
                 * @param assets The addresses of the assets being flash-borrowed
                 * @param amounts The amounts amounts being flash-borrowed
                 * @param modes Types of the debt to open if the flash loan is not returned:
                 *   0 -> Don't open any debt, just revert if funds can't be transferred from the receiver
                 *   1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
                 *   2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
                 * @param onBehalfOf The address  that will receive the debt in the case of using on `modes` 1 or 2
                 * @param params Variadic packed params to pass to the receiver as extra information
                 * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                 *   0 if the action is executed directly by the user, without any middle-man
                 **/
                function flashLoan(
                  address receiverAddress,
                  address[] calldata assets,
                  uint256[] calldata amounts,
                  uint256[] calldata modes,
                  address onBehalfOf,
                  bytes calldata params,
                  uint16 referralCode
                ) external override whenNotPaused {
                  FlashLoanLocalVars memory vars;
                  ValidationLogic.validateFlashloan(assets, amounts);
                  address[] memory aTokenAddresses = new address[](assets.length);
                  uint256[] memory premiums = new uint256[](assets.length);
                  vars.receiver = IFlashLoanReceiver(receiverAddress);
                  for (vars.i = 0; vars.i < assets.length; vars.i++) {
                    aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress;
                    premiums[vars.i] = amounts[vars.i].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000);
                    IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]);
                  }
                  require(
                    vars.receiver.executeOperation(assets, amounts, premiums, msg.sender, params),
                    Errors.LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN
                  );
                  for (vars.i = 0; vars.i < assets.length; vars.i++) {
                    vars.currentAsset = assets[vars.i];
                    vars.currentAmount = amounts[vars.i];
                    vars.currentPremium = premiums[vars.i];
                    vars.currentATokenAddress = aTokenAddresses[vars.i];
                    vars.currentAmountPlusPremium = vars.currentAmount.add(vars.currentPremium);
                    if (DataTypes.InterestRateMode(modes[vars.i]) == DataTypes.InterestRateMode.NONE) {
                      _reserves[vars.currentAsset].updateState();
                      _reserves[vars.currentAsset].cumulateToLiquidityIndex(
                        IERC20(vars.currentATokenAddress).totalSupply(),
                        vars.currentPremium
                      );
                      _reserves[vars.currentAsset].updateInterestRates(
                        vars.currentAsset,
                        vars.currentATokenAddress,
                        vars.currentAmountPlusPremium,
                        0
                      );
                      IERC20(vars.currentAsset).safeTransferFrom(
                        receiverAddress,
                        vars.currentATokenAddress,
                        vars.currentAmountPlusPremium
                      );
                    } else {
                      // If the user chose to not return the funds, the system checks if there is enough collateral and
                      // eventually opens a debt position
                      _executeBorrow(
                        ExecuteBorrowParams(
                          vars.currentAsset,
                          msg.sender,
                          onBehalfOf,
                          vars.currentAmount,
                          modes[vars.i],
                          vars.currentATokenAddress,
                          referralCode,
                          false
                        )
                      );
                    }
                    emit FlashLoan(
                      receiverAddress,
                      msg.sender,
                      vars.currentAsset,
                      vars.currentAmount,
                      vars.currentPremium,
                      referralCode
                    );
                  }
                }
                /**
                 * @dev Returns the state and configuration of the reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @return The state of the reserve
                 **/
                function getReserveData(address asset)
                  external
                  view
                  override
                  returns (DataTypes.ReserveData memory)
                {
                  return _reserves[asset];
                }
                /**
                 * @dev Returns the user account data across all the reserves
                 * @param user The address of the user
                 * @return totalCollateralETH the total collateral in ETH of the user
                 * @return totalDebtETH the total debt in ETH of the user
                 * @return availableBorrowsETH the borrowing power left of the user
                 * @return currentLiquidationThreshold the liquidation threshold of the user
                 * @return ltv the loan to value of the user
                 * @return healthFactor the current health factor of the user
                 **/
                function getUserAccountData(address user)
                  external
                  view
                  override
                  returns (
                    uint256 totalCollateralETH,
                    uint256 totalDebtETH,
                    uint256 availableBorrowsETH,
                    uint256 currentLiquidationThreshold,
                    uint256 ltv,
                    uint256 healthFactor
                  )
                {
                  (
                    totalCollateralETH,
                    totalDebtETH,
                    ltv,
                    currentLiquidationThreshold,
                    healthFactor
                  ) = GenericLogic.calculateUserAccountData(
                    user,
                    _reserves,
                    _usersConfig[user],
                    _reservesList,
                    _reservesCount,
                    _addressesProvider.getPriceOracle()
                  );
                  availableBorrowsETH = GenericLogic.calculateAvailableBorrowsETH(
                    totalCollateralETH,
                    totalDebtETH,
                    ltv
                  );
                }
                /**
                 * @dev Returns the configuration of the reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @return The configuration of the reserve
                 **/
                function getConfiguration(address asset)
                  external
                  view
                  override
                  returns (DataTypes.ReserveConfigurationMap memory)
                {
                  return _reserves[asset].configuration;
                }
                /**
                 * @dev Returns the configuration of the user across all the reserves
                 * @param user The user address
                 * @return The configuration of the user
                 **/
                function getUserConfiguration(address user)
                  external
                  view
                  override
                  returns (DataTypes.UserConfigurationMap memory)
                {
                  return _usersConfig[user];
                }
                /**
                 * @dev Returns the normalized income per unit of asset
                 * @param asset The address of the underlying asset of the reserve
                 * @return The reserve's normalized income
                 */
                function getReserveNormalizedIncome(address asset)
                  external
                  view
                  virtual
                  override
                  returns (uint256)
                {
                  return _reserves[asset].getNormalizedIncome();
                }
                /**
                 * @dev Returns the normalized variable debt per unit of asset
                 * @param asset The address of the underlying asset of the reserve
                 * @return The reserve normalized variable debt
                 */
                function getReserveNormalizedVariableDebt(address asset)
                  external
                  view
                  override
                  returns (uint256)
                {
                  return _reserves[asset].getNormalizedDebt();
                }
                /**
                 * @dev Returns if the LendingPool is paused
                 */
                function paused() external view override returns (bool) {
                  return _paused;
                }
                /**
                 * @dev Returns the list of the initialized reserves
                 **/
                function getReservesList() external view override returns (address[] memory) {
                  address[] memory _activeReserves = new address[](_reservesCount);
                  for (uint256 i = 0; i < _reservesCount; i++) {
                    _activeReserves[i] = _reservesList[i];
                  }
                  return _activeReserves;
                }
                /**
                 * @dev Returns the cached LendingPoolAddressesProvider connected to this contract
                 **/
                function getAddressesProvider() external view override returns (ILendingPoolAddressesProvider) {
                  return _addressesProvider;
                }
                /**
                 * @dev Validates and finalizes an aToken transfer
                 * - Only callable by the overlying aToken of the `asset`
                 * @param asset The address of the underlying asset of the aToken
                 * @param from The user from which the aTokens are transferred
                 * @param to The user receiving the aTokens
                 * @param amount The amount being transferred/withdrawn
                 * @param balanceFromBefore The aToken balance of the `from` user before the transfer
                 * @param balanceToBefore The aToken balance of the `to` user before the transfer
                 */
                function finalizeTransfer(
                  address asset,
                  address from,
                  address to,
                  uint256 amount,
                  uint256 balanceFromBefore,
                  uint256 balanceToBefore
                ) external override whenNotPaused {
                  require(msg.sender == _reserves[asset].aTokenAddress, Errors.LP_CALLER_MUST_BE_AN_ATOKEN);
                  ValidationLogic.validateTransfer(
                    from,
                    _reserves,
                    _usersConfig[from],
                    _reservesList,
                    _reservesCount,
                    _addressesProvider.getPriceOracle()
                  );
                  uint256 reserveId = _reserves[asset].id;
                  if (from != to) {
                    if (balanceFromBefore.sub(amount) == 0) {
                      DataTypes.UserConfigurationMap storage fromConfig = _usersConfig[from];
                      fromConfig.setUsingAsCollateral(reserveId, false);
                      emit ReserveUsedAsCollateralDisabled(asset, from);
                    }
                    if (balanceToBefore == 0 && amount != 0) {
                      DataTypes.UserConfigurationMap storage toConfig = _usersConfig[to];
                      toConfig.setUsingAsCollateral(reserveId, true);
                      emit ReserveUsedAsCollateralEnabled(asset, to);
                    }
                  }
                }
                /**
                 * @dev Initializes a reserve, activating it, assigning an aToken and debt tokens and an
                 * interest rate strategy
                 * - Only callable by the LendingPoolConfigurator contract
                 * @param asset The address of the underlying asset of the reserve
                 * @param aTokenAddress The address of the aToken that will be assigned to the reserve
                 * @param stableDebtAddress The address of the StableDebtToken that will be assigned to the reserve
                 * @param aTokenAddress The address of the VariableDebtToken that will be assigned to the reserve
                 * @param interestRateStrategyAddress The address of the interest rate strategy contract
                 **/
                function initReserve(
                  address asset,
                  address aTokenAddress,
                  address stableDebtAddress,
                  address variableDebtAddress,
                  address interestRateStrategyAddress
                ) external override onlyLendingPoolConfigurator {
                  require(Address.isContract(asset), Errors.LP_NOT_CONTRACT);
                  _reserves[asset].init(
                    aTokenAddress,
                    stableDebtAddress,
                    variableDebtAddress,
                    interestRateStrategyAddress
                  );
                  _addReserveToList(asset);
                }
                /**
                 * @dev Updates the address of the interest rate strategy contract
                 * - Only callable by the LendingPoolConfigurator contract
                 * @param asset The address of the underlying asset of the reserve
                 * @param rateStrategyAddress The address of the interest rate strategy contract
                 **/
                function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress)
                  external
                  override
                  onlyLendingPoolConfigurator
                {
                  _reserves[asset].interestRateStrategyAddress = rateStrategyAddress;
                }
                /**
                 * @dev Sets the configuration bitmap of the reserve as a whole
                 * - Only callable by the LendingPoolConfigurator contract
                 * @param asset The address of the underlying asset of the reserve
                 * @param configuration The new configuration bitmap
                 **/
                function setConfiguration(address asset, uint256 configuration)
                  external
                  override
                  onlyLendingPoolConfigurator
                {
                  _reserves[asset].configuration.data = configuration;
                }
                /**
                 * @dev Set the _pause state of a reserve
                 * - Only callable by the LendingPoolConfigurator contract
                 * @param val `true` to pause the reserve, `false` to un-pause it
                 */
                function setPause(bool val) external override onlyLendingPoolConfigurator {
                  _paused = val;
                  if (_paused) {
                    emit Paused();
                  } else {
                    emit Unpaused();
                  }
                }
                struct ExecuteBorrowParams {
                  address asset;
                  address user;
                  address onBehalfOf;
                  uint256 amount;
                  uint256 interestRateMode;
                  address aTokenAddress;
                  uint16 referralCode;
                  bool releaseUnderlying;
                }
                function _executeBorrow(ExecuteBorrowParams memory vars) internal {
                  DataTypes.ReserveData storage reserve = _reserves[vars.asset];
                  DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf];
                  address oracle = _addressesProvider.getPriceOracle();
                  uint256 amountInETH =
                    IPriceOracleGetter(oracle).getAssetPrice(vars.asset).mul(vars.amount).div(
                      10**reserve.configuration.getDecimals()
                    );
                  ValidationLogic.validateBorrow(
                    vars.asset,
                    reserve,
                    vars.onBehalfOf,
                    vars.amount,
                    amountInETH,
                    vars.interestRateMode,
                    MAX_STABLE_RATE_BORROW_SIZE_PERCENT,
                    _reserves,
                    userConfig,
                    _reservesList,
                    _reservesCount,
                    oracle
                  );
                  reserve.updateState();
                  uint256 currentStableRate = 0;
                  bool isFirstBorrowing = false;
                  if (DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE) {
                    currentStableRate = reserve.currentStableBorrowRate;
                    isFirstBorrowing = IStableDebtToken(reserve.stableDebtTokenAddress).mint(
                      vars.user,
                      vars.onBehalfOf,
                      vars.amount,
                      currentStableRate
                    );
                  } else {
                    isFirstBorrowing = IVariableDebtToken(reserve.variableDebtTokenAddress).mint(
                      vars.user,
                      vars.onBehalfOf,
                      vars.amount,
                      reserve.variableBorrowIndex
                    );
                  }
                  if (isFirstBorrowing) {
                    userConfig.setBorrowing(reserve.id, true);
                  }
                  reserve.updateInterestRates(
                    vars.asset,
                    vars.aTokenAddress,
                    0,
                    vars.releaseUnderlying ? vars.amount : 0
                  );
                  if (vars.releaseUnderlying) {
                    IAToken(vars.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount);
                  }
                  emit Borrow(
                    vars.asset,
                    vars.user,
                    vars.onBehalfOf,
                    vars.amount,
                    vars.interestRateMode,
                    DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE
                      ? currentStableRate
                      : reserve.currentVariableBorrowRate,
                    vars.referralCode
                  );
                }
                function _addReserveToList(address asset) internal {
                  uint256 reservesCount = _reservesCount;
                  require(reservesCount < MAX_NUMBER_RESERVES, Errors.LP_NO_MORE_RESERVES_ALLOWED);
                  bool reserveAlreadyAdded = _reserves[asset].id != 0 || _reservesList[0] == asset;
                  if (!reserveAlreadyAdded) {
                    _reserves[asset].id = uint8(reservesCount);
                    _reservesList[reservesCount] = asset;
                    _reservesCount = reservesCount + 1;
                  }
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol';
              import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
              import {IWETH} from './interfaces/IWETH.sol';
              import {IWETHGateway} from './interfaces/IWETHGateway.sol';
              import {ILendingPool} from '../interfaces/ILendingPool.sol';
              import {IAToken} from '../interfaces/IAToken.sol';
              import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol';
              import {UserConfiguration} from '../protocol/libraries/configuration/UserConfiguration.sol';
              import {Helpers} from '../protocol/libraries/helpers/Helpers.sol';
              import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
              contract WETHGateway is IWETHGateway, Ownable {
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                using UserConfiguration for DataTypes.UserConfigurationMap;
                IWETH internal immutable WETH;
                ILendingPool internal immutable POOL;
                IAToken internal immutable aWETH;
                /**
                 * @dev Sets the WETH address and the LendingPoolAddressesProvider address. Infinite approves lending pool.
                 * @param weth Address of the Wrapped Ether contract
                 * @param pool Address of the LendingPool contract
                 **/
                constructor(address weth, address pool) public {
                  ILendingPool poolInstance = ILendingPool(pool);
                  WETH = IWETH(weth);
                  POOL = poolInstance;
                  aWETH = IAToken(poolInstance.getReserveData(weth).aTokenAddress);
                  IWETH(weth).approve(pool, uint256(-1));
                }
                /**
                 * @dev deposits WETH into the reserve, using native ETH. A corresponding amount of the overlying asset (aTokens)
                 * is minted.
                 * @param onBehalfOf address of the user who will receive the aTokens representing the deposit
                 * @param referralCode integrators are assigned a referral code and can potentially receive rewards.
                 **/
                function depositETH(address onBehalfOf, uint16 referralCode) external payable override {
                  WETH.deposit{value: msg.value}();
                  POOL.deposit(address(WETH), msg.value, onBehalfOf, referralCode);
                }
                /**
                 * @dev withdraws the WETH _reserves of msg.sender.
                 * @param amount amount of aWETH to withdraw and receive native ETH
                 * @param to address of the user who will receive native ETH
                 */
                function withdrawETH(uint256 amount, address to) external override {
                  uint256 userBalance = aWETH.balanceOf(msg.sender);
                  uint256 amountToWithdraw = amount;
                  // if amount is equal to uint(-1), the user wants to redeem everything
                  if (amount == type(uint256).max) {
                    amountToWithdraw = userBalance;
                  }
                  aWETH.transferFrom(msg.sender, address(this), amountToWithdraw);
                  POOL.withdraw(address(WETH), amountToWithdraw, address(this));
                  WETH.withdraw(amountToWithdraw);
                  _safeTransferETH(to, amountToWithdraw);
                }
                /**
                 * @dev repays a borrow on the WETH reserve, for the specified amount (or for the whole amount, if uint256(-1) is specified).
                 * @param amount the amount to repay, or uint256(-1) if the user wants to repay everything
                 * @param rateMode the rate mode to repay
                 * @param onBehalfOf the address for which msg.sender is repaying
                 */
                function repayETH(
                  uint256 amount,
                  uint256 rateMode,
                  address onBehalfOf
                ) external payable override {
                  (uint256 stableDebt, uint256 variableDebt) =
                    Helpers.getUserCurrentDebtMemory(onBehalfOf, POOL.getReserveData(address(WETH)));
                  uint256 paybackAmount =
                    DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE
                      ? stableDebt
                      : variableDebt;
                  if (amount < paybackAmount) {
                    paybackAmount = amount;
                  }
                  require(msg.value >= paybackAmount, 'msg.value is less than repayment amount');
                  WETH.deposit{value: paybackAmount}();
                  POOL.repay(address(WETH), msg.value, rateMode, onBehalfOf);
                  // refund remaining dust eth
                  if (msg.value > paybackAmount) _safeTransferETH(msg.sender, msg.value - paybackAmount);
                }
                /**
                 * @dev borrow WETH, unwraps to ETH and send both the ETH and DebtTokens to msg.sender, via `approveDelegation` and onBehalf argument in `LendingPool.borrow`.
                 * @param amount the amount of ETH to borrow
                 * @param interesRateMode the interest rate mode
                 * @param referralCode integrators are assigned a referral code and can potentially receive rewards
                 */
                function borrowETH(
                  uint256 amount,
                  uint256 interesRateMode,
                  uint16 referralCode
                ) external override {
                  POOL.borrow(address(WETH), amount, interesRateMode, referralCode, msg.sender);
                  WETH.withdraw(amount);
                  _safeTransferETH(msg.sender, amount);
                }
                /**
                 * @dev transfer ETH to an address, revert if it fails.
                 * @param to recipient of the transfer
                 * @param value the amount to send
                 */
                function _safeTransferETH(address to, uint256 value) internal {
                  (bool success, ) = to.call{value: value}(new bytes(0));
                  require(success, 'ETH_TRANSFER_FAILED');
                }
                /**
                 * @dev transfer ERC20 from the utility contract, for ERC20 recovery in case of stuck tokens due
                 * direct transfers to the contract address.
                 * @param token token to transfer
                 * @param to recipient of the transfer
                 * @param amount amount to send
                 */
                function emergencyTokenTransfer(
                  address token,
                  address to,
                  uint256 amount
                ) external onlyOwner {
                  IERC20(token).transfer(to, amount);
                }
                /**
                 * @dev transfer native Ether from the utility contract, for native Ether recovery in case of stuck Ether
                 * due selfdestructs or transfer ether to pre-computated contract address before deployment.
                 * @param to recipient of the transfer
                 * @param amount amount to send
                 */
                function emergencyEtherTransfer(address to, uint256 amount) external onlyOwner {
                  _safeTransferETH(to, amount);
                }
                /**
                 * @dev Get WETH address used by WETHGateway
                 */
                function getWETHAddress() external view returns (address) {
                  return address(WETH);
                }
                /**
                 * @dev Get aWETH address used by WETHGateway
                 */
                function getAWETHAddress() external view returns (address) {
                  return address(aWETH);
                }
                /**
                 * @dev Get LendingPool address used by WETHGateway
                 */
                function getLendingPoolAddress() external view returns (address) {
                  return address(POOL);
                }
                /**
                 * @dev Only WETH contract is allowed to transfer ETH here. Prevent other addresses to send Ether to this contract.
                 */
                receive() external payable {
                  require(msg.sender == address(WETH), 'Receive not allowed');
                }
                /**
                 * @dev Revert fallback calls
                 */
                fallback() external payable {
                  revert('Fallback not allowed');
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              interface IWETH {
                function deposit() external payable;
                function withdraw(uint256) external;
                function approve(address guy, uint256 wad) external returns (bool);
                function transferFrom(
                  address src,
                  address dst,
                  uint256 wad
                ) external returns (bool);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              interface IWETHGateway {
                function depositETH(address onBehalfOf, uint16 referralCode) external payable;
                function withdrawETH(uint256 amount, address onBehalfOf) external;
                function repayETH(
                  uint256 amount,
                  uint256 rateMode,
                  address onBehalfOf
                ) external payable;
                function borrowETH(
                  uint256 amount,
                  uint256 interesRateMode,
                  uint16 referralCode
                ) external;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
              import {
                InitializableImmutableAdminUpgradeabilityProxy
              } from '../libraries/aave-upgradeability/InitializableImmutableAdminUpgradeabilityProxy.sol';
              import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              import {ILendingPool} from '../../interfaces/ILendingPool.sol';
              import {ITokenConfiguration} from '../../interfaces/ITokenConfiguration.sol';
              import {IERC20Detailed} from '../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              import {PercentageMath} from '../libraries/math/PercentageMath.sol';
              import {DataTypes} from '../libraries/types/DataTypes.sol';
              /**
               * @title LendingPoolConfigurator contract
               * @author Aave
               * @dev Implements the configuration methods for the Aave protocol
               **/
              contract LendingPoolConfigurator is VersionedInitializable {
                using SafeMath for uint256;
                using PercentageMath for uint256;
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                /**
                 * @dev Emitted when a reserve is initialized.
                 * @param asset The address of the underlying asset of the reserve
                 * @param aToken The address of the associated aToken contract
                 * @param stableDebtToken The address of the associated stable rate debt token
                 * @param variableDebtToken The address of the associated variable rate debt token
                 * @param interestRateStrategyAddress The address of the interest rate strategy for the reserve
                 **/
                event ReserveInitialized(
                  address indexed asset,
                  address indexed aToken,
                  address stableDebtToken,
                  address variableDebtToken,
                  address interestRateStrategyAddress
                );
                /**
                 * @dev Emitted when borrowing is enabled on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @param stableRateEnabled True if stable rate borrowing is enabled, false otherwise
                 **/
                event BorrowingEnabledOnReserve(address indexed asset, bool stableRateEnabled);
                /**
                 * @dev Emitted when borrowing is disabled on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                event BorrowingDisabledOnReserve(address indexed asset);
                /**
                 * @dev Emitted when the collateralization risk parameters for the specified asset are updated.
                 * @param asset The address of the underlying asset of the reserve
                 * @param ltv The loan to value of the asset when used as collateral
                 * @param liquidationThreshold The threshold at which loans using this asset as collateral will be considered undercollateralized
                 * @param liquidationBonus The bonus liquidators receive to liquidate this asset
                 **/
                event CollateralConfigurationChanged(
                  address indexed asset,
                  uint256 ltv,
                  uint256 liquidationThreshold,
                  uint256 liquidationBonus
                );
                /**
                 * @dev Emitted when stable rate borrowing is enabled on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                event StableRateEnabledOnReserve(address indexed asset);
                /**
                 * @dev Emitted when stable rate borrowing is disabled on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                event StableRateDisabledOnReserve(address indexed asset);
                /**
                 * @dev Emitted when a reserve is activated
                 * @param asset The address of the underlying asset of the reserve
                 **/
                event ReserveActivated(address indexed asset);
                /**
                 * @dev Emitted when a reserve is deactivated
                 * @param asset The address of the underlying asset of the reserve
                 **/
                event ReserveDeactivated(address indexed asset);
                /**
                 * @dev Emitted when a reserve is frozen
                 * @param asset The address of the underlying asset of the reserve
                 **/
                event ReserveFrozen(address indexed asset);
                /**
                 * @dev Emitted when a reserve is unfrozen
                 * @param asset The address of the underlying asset of the reserve
                 **/
                event ReserveUnfrozen(address indexed asset);
                /**
                 * @dev Emitted when a reserve factor is updated
                 * @param asset The address of the underlying asset of the reserve
                 * @param factor The new reserve factor
                 **/
                event ReserveFactorChanged(address indexed asset, uint256 factor);
                /**
                 * @dev Emitted when the reserve decimals are updated
                 * @param asset The address of the underlying asset of the reserve
                 * @param decimals The new decimals
                 **/
                event ReserveDecimalsChanged(address indexed asset, uint256 decimals);
                /**
                 * @dev Emitted when a reserve interest strategy contract is updated
                 * @param asset The address of the underlying asset of the reserve
                 * @param strategy The new address of the interest strategy contract
                 **/
                event ReserveInterestRateStrategyChanged(address indexed asset, address strategy);
                /**
                 * @dev Emitted when an aToken implementation is upgraded
                 * @param asset The address of the underlying asset of the reserve
                 * @param proxy The aToken proxy address
                 * @param implementation The new aToken implementation
                 **/
                event ATokenUpgraded(
                  address indexed asset,
                  address indexed proxy,
                  address indexed implementation
                );
                /**
                 * @dev Emitted when the implementation of a stable debt token is upgraded
                 * @param asset The address of the underlying asset of the reserve
                 * @param proxy The stable debt token proxy address
                 * @param implementation The new aToken implementation
                 **/
                event StableDebtTokenUpgraded(
                  address indexed asset,
                  address indexed proxy,
                  address indexed implementation
                );
                /**
                 * @dev Emitted when the implementation of a variable debt token is upgraded
                 * @param asset The address of the underlying asset of the reserve
                 * @param proxy The variable debt token proxy address
                 * @param implementation The new aToken implementation
                 **/
                event VariableDebtTokenUpgraded(
                  address indexed asset,
                  address indexed proxy,
                  address indexed implementation
                );
                ILendingPoolAddressesProvider internal addressesProvider;
                ILendingPool internal pool;
                modifier onlyPoolAdmin {
                  require(addressesProvider.getPoolAdmin() == msg.sender, Errors.CALLER_NOT_POOL_ADMIN);
                  _;
                }
                modifier onlyEmergencyAdmin {
                  require(
                    addressesProvider.getEmergencyAdmin() == msg.sender,
                    Errors.LPC_CALLER_NOT_EMERGENCY_ADMIN
                  );
                  _;
                }
                uint256 internal constant CONFIGURATOR_REVISION = 0x1;
                function getRevision() internal pure override returns (uint256) {
                  return CONFIGURATOR_REVISION;
                }
                function initialize(ILendingPoolAddressesProvider provider) public initializer {
                  addressesProvider = provider;
                  pool = ILendingPool(addressesProvider.getLendingPool());
                }
                /**
                 * @dev Initializes a reserve
                 * @param aTokenImpl  The address of the aToken contract implementation
                 * @param stableDebtTokenImpl The address of the stable debt token contract
                 * @param variableDebtTokenImpl The address of the variable debt token contract
                 * @param underlyingAssetDecimals The decimals of the reserve underlying asset
                 * @param interestRateStrategyAddress The address of the interest rate strategy contract for this reserve
                 **/
                function initReserve(
                  address aTokenImpl,
                  address stableDebtTokenImpl,
                  address variableDebtTokenImpl,
                  uint8 underlyingAssetDecimals,
                  address interestRateStrategyAddress
                ) public onlyPoolAdmin {
                  address asset = ITokenConfiguration(aTokenImpl).UNDERLYING_ASSET_ADDRESS();
                  require(
                    address(pool) == ITokenConfiguration(aTokenImpl).POOL(),
                    Errors.LPC_INVALID_ATOKEN_POOL_ADDRESS
                  );
                  require(
                    address(pool) == ITokenConfiguration(stableDebtTokenImpl).POOL(),
                    Errors.LPC_INVALID_STABLE_DEBT_TOKEN_POOL_ADDRESS
                  );
                  require(
                    address(pool) == ITokenConfiguration(variableDebtTokenImpl).POOL(),
                    Errors.LPC_INVALID_VARIABLE_DEBT_TOKEN_POOL_ADDRESS
                  );
                  require(
                    asset == ITokenConfiguration(stableDebtTokenImpl).UNDERLYING_ASSET_ADDRESS(),
                    Errors.LPC_INVALID_STABLE_DEBT_TOKEN_UNDERLYING_ADDRESS
                  );
                  require(
                    asset == ITokenConfiguration(variableDebtTokenImpl).UNDERLYING_ASSET_ADDRESS(),
                    Errors.LPC_INVALID_VARIABLE_DEBT_TOKEN_UNDERLYING_ADDRESS
                  );
                  address aTokenProxyAddress = _initTokenWithProxy(aTokenImpl, underlyingAssetDecimals);
                  address stableDebtTokenProxyAddress =
                    _initTokenWithProxy(stableDebtTokenImpl, underlyingAssetDecimals);
                  address variableDebtTokenProxyAddress =
                    _initTokenWithProxy(variableDebtTokenImpl, underlyingAssetDecimals);
                  pool.initReserve(
                    asset,
                    aTokenProxyAddress,
                    stableDebtTokenProxyAddress,
                    variableDebtTokenProxyAddress,
                    interestRateStrategyAddress
                  );
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setDecimals(underlyingAssetDecimals);
                  currentConfig.setActive(true);
                  currentConfig.setFrozen(false);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit ReserveInitialized(
                    asset,
                    aTokenProxyAddress,
                    stableDebtTokenProxyAddress,
                    variableDebtTokenProxyAddress,
                    interestRateStrategyAddress
                  );
                }
                /**
                 * @dev Updates the aToken implementation for the reserve
                 * @param asset The address of the underlying asset of the reserve to be updated
                 * @param implementation The address of the new aToken implementation
                 **/
                function updateAToken(address asset, address implementation) external onlyPoolAdmin {
                  DataTypes.ReserveData memory reserveData = pool.getReserveData(asset);
                  _upgradeTokenImplementation(asset, reserveData.aTokenAddress, implementation);
                  emit ATokenUpgraded(asset, reserveData.aTokenAddress, implementation);
                }
                /**
                 * @dev Updates the stable debt token implementation for the reserve
                 * @param asset The address of the underlying asset of the reserve to be updated
                 * @param implementation The address of the new aToken implementation
                 **/
                function updateStableDebtToken(address asset, address implementation) external onlyPoolAdmin {
                  DataTypes.ReserveData memory reserveData = pool.getReserveData(asset);
                  _upgradeTokenImplementation(asset, reserveData.stableDebtTokenAddress, implementation);
                  emit StableDebtTokenUpgraded(asset, reserveData.stableDebtTokenAddress, implementation);
                }
                /**
                 * @dev Updates the variable debt token implementation for the asset
                 * @param asset The address of the underlying asset of the reserve to be updated
                 * @param implementation The address of the new aToken implementation
                 **/
                function updateVariableDebtToken(address asset, address implementation) external onlyPoolAdmin {
                  DataTypes.ReserveData memory reserveData = pool.getReserveData(asset);
                  _upgradeTokenImplementation(asset, reserveData.variableDebtTokenAddress, implementation);
                  emit VariableDebtTokenUpgraded(asset, reserveData.variableDebtTokenAddress, implementation);
                }
                /**
                 * @dev Enables borrowing on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @param stableBorrowRateEnabled True if stable borrow rate needs to be enabled by default on this reserve
                 **/
                function enableBorrowingOnReserve(address asset, bool stableBorrowRateEnabled)
                  external
                  onlyPoolAdmin
                {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setBorrowingEnabled(true);
                  currentConfig.setStableRateBorrowingEnabled(stableBorrowRateEnabled);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit BorrowingEnabledOnReserve(asset, stableBorrowRateEnabled);
                }
                /**
                 * @dev Disables borrowing on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                function disableBorrowingOnReserve(address asset) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setBorrowingEnabled(false);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit BorrowingDisabledOnReserve(asset);
                }
                /**
                 * @dev Configures the reserve collateralization parameters
                 * all the values are expressed in percentages with two decimals of precision. A valid value is 10000, which means 100.00%
                 * @param asset The address of the underlying asset of the reserve
                 * @param ltv The loan to value of the asset when used as collateral
                 * @param liquidationThreshold The threshold at which loans using this asset as collateral will be considered undercollateralized
                 * @param liquidationBonus The bonus liquidators receive to liquidate this asset. The values is always above 100%. A value of 105%
                 * means the liquidator will receive a 5% bonus
                 **/
                function configureReserveAsCollateral(
                  address asset,
                  uint256 ltv,
                  uint256 liquidationThreshold,
                  uint256 liquidationBonus
                ) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  //validation of the parameters: the LTV can
                  //only be lower or equal than the liquidation threshold
                  //(otherwise a loan against the asset would cause instantaneous liquidation)
                  require(ltv <= liquidationThreshold, Errors.LPC_INVALID_CONFIGURATION);
                  if (liquidationThreshold != 0) {
                    //liquidation bonus must be bigger than 100.00%, otherwise the liquidator would receive less
                    //collateral than needed to cover the debt
                    require(
                      liquidationBonus > PercentageMath.PERCENTAGE_FACTOR,
                      Errors.LPC_INVALID_CONFIGURATION
                    );
                    //if threshold * bonus is less than PERCENTAGE_FACTOR, it's guaranteed that at the moment
                    //a loan is taken there is enough collateral available to cover the liquidation bonus
                    require(
                      liquidationThreshold.percentMul(liquidationBonus) <= PercentageMath.PERCENTAGE_FACTOR,
                      Errors.LPC_INVALID_CONFIGURATION
                    );
                  } else {
                    require(liquidationBonus == 0, Errors.LPC_INVALID_CONFIGURATION);
                    //if the liquidation threshold is being set to 0,
                    // the reserve is being disabled as collateral. To do so,
                    //we need to ensure no liquidity is deposited
                    _checkNoLiquidity(asset);
                  }
                  currentConfig.setLtv(ltv);
                  currentConfig.setLiquidationThreshold(liquidationThreshold);
                  currentConfig.setLiquidationBonus(liquidationBonus);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit CollateralConfigurationChanged(asset, ltv, liquidationThreshold, liquidationBonus);
                }
                /**
                 * @dev Enable stable rate borrowing on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                function enableReserveStableRate(address asset) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setStableRateBorrowingEnabled(true);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit StableRateEnabledOnReserve(asset);
                }
                /**
                 * @dev Disable stable rate borrowing on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                function disableReserveStableRate(address asset) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setStableRateBorrowingEnabled(false);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit StableRateDisabledOnReserve(asset);
                }
                /**
                 * @dev Activates a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                function activateReserve(address asset) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setActive(true);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit ReserveActivated(asset);
                }
                /**
                 * @dev Deactivates a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                function deactivateReserve(address asset) external onlyPoolAdmin {
                  _checkNoLiquidity(asset);
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setActive(false);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit ReserveDeactivated(asset);
                }
                /**
                 * @dev Freezes a reserve. A frozen reserve doesn't allow any new deposit, borrow or rate swap
                 *  but allows repayments, liquidations, rate rebalances and withdrawals
                 * @param asset The address of the underlying asset of the reserve
                 **/
                function freezeReserve(address asset) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setFrozen(true);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit ReserveFrozen(asset);
                }
                /**
                 * @dev Unfreezes a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                function unfreezeReserve(address asset) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setFrozen(false);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit ReserveUnfrozen(asset);
                }
                /**
                 * @dev Updates the reserve factor of a reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @param reserveFactor The new reserve factor of the reserve
                 **/
                function setReserveFactor(address asset, uint256 reserveFactor) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setReserveFactor(reserveFactor);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit ReserveFactorChanged(asset, reserveFactor);
                }
                /**
                 * @dev Sets the interest rate strategy of a reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @param rateStrategyAddress The new address of the interest strategy contract
                 **/
                function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress)
                  external
                  onlyPoolAdmin
                {
                  pool.setReserveInterestRateStrategyAddress(asset, rateStrategyAddress);
                  emit ReserveInterestRateStrategyChanged(asset, rateStrategyAddress);
                }
                /**
                 * @dev pauses or unpauses all the actions of the protocol, including aToken transfers
                 * @param val true if protocol needs to be paused, false otherwise
                 **/
                function setPoolPause(bool val) external onlyEmergencyAdmin {
                  pool.setPause(val);
                }
                function _initTokenWithProxy(address implementation, uint8 decimals) internal returns (address) {
                  InitializableImmutableAdminUpgradeabilityProxy proxy =
                    new InitializableImmutableAdminUpgradeabilityProxy(address(this));
                  bytes memory params =
                    abi.encodeWithSignature(
                      'initialize(uint8,string,string)',
                      decimals,
                      IERC20Detailed(implementation).name(),
                      IERC20Detailed(implementation).symbol()
                    );
                  proxy.initialize(implementation, params);
                  return address(proxy);
                }
                function _upgradeTokenImplementation(
                  address asset,
                  address proxyAddress,
                  address implementation
                ) internal {
                  InitializableImmutableAdminUpgradeabilityProxy proxy =
                    InitializableImmutableAdminUpgradeabilityProxy(payable(proxyAddress));
                  DataTypes.ReserveConfigurationMap memory configuration = pool.getConfiguration(asset);
                  (, , , uint256 decimals, ) = configuration.getParamsMemory();
                  bytes memory params =
                    abi.encodeWithSignature(
                      'initialize(uint8,string,string)',
                      uint8(decimals),
                      IERC20Detailed(implementation).name(),
                      IERC20Detailed(implementation).symbol()
                    );
                  proxy.upgradeToAndCall(implementation, params);
                }
                function _checkNoLiquidity(address asset) internal view {
                  DataTypes.ReserveData memory reserveData = pool.getReserveData(asset);
                  uint256 availableLiquidity = IERC20Detailed(asset).balanceOf(reserveData.aTokenAddress);
                  require(
                    availableLiquidity == 0 && reserveData.currentLiquidityRate == 0,
                    Errors.LPC_RESERVE_LIQUIDITY_NOT_0
                  );
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import './BaseImmutableAdminUpgradeabilityProxy.sol';
              import '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
              /**
               * @title InitializableAdminUpgradeabilityProxy
               * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
               */
              contract InitializableImmutableAdminUpgradeabilityProxy is
                BaseImmutableAdminUpgradeabilityProxy,
                InitializableUpgradeabilityProxy
              {
                constructor(address admin) public BaseImmutableAdminUpgradeabilityProxy(admin) {}
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                  BaseImmutableAdminUpgradeabilityProxy._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity ^0.6.12;
              /**
               * @title ITokenConfiguration
               * @author Aave
               * @dev Common interface between aTokens and debt tokens to fetch the
               * token configuration
               **/
              interface ITokenConfiguration {
                function UNDERLYING_ASSET_ADDRESS() external view returns (address);
                function POOL() external view returns (address);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
              /**
               * @title BaseImmutableAdminUpgradeabilityProxy
               * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
               * @dev This contract combines an upgradeability proxy with an authorization
               * mechanism for administrative tasks. The admin role is stored in an immutable, which
               * helps saving transactions costs
               * 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 BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
                address immutable ADMIN;
                constructor(address admin) public {
                  ADMIN = admin;
                }
                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 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);
                }
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal virtual override {
                  require(msg.sender != ADMIN, 'Cannot call fallback function from the proxy admin');
                  super._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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);
                  }
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import './Proxy.sol';
              import '../contracts/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)
                  }
                }
              }
              // 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`.
                 */
                fallback() 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());
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
              import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
              import {ILendingPool} from '../interfaces/ILendingPool.sol';
              import {IStableDebtToken} from '../interfaces/IStableDebtToken.sol';
              import {IVariableDebtToken} from '../interfaces/IVariableDebtToken.sol';
              import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol';
              import {UserConfiguration} from '../protocol/libraries/configuration/UserConfiguration.sol';
              import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
              contract AaveProtocolDataProvider {
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                using UserConfiguration for DataTypes.UserConfigurationMap;
                address constant MKR = 0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2;
                address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                struct TokenData {
                  string symbol;
                  address tokenAddress;
                }
                ILendingPoolAddressesProvider public immutable ADDRESSES_PROVIDER;
                constructor(ILendingPoolAddressesProvider addressesProvider) public {
                  ADDRESSES_PROVIDER = addressesProvider;
                }
                function getAllReservesTokens() external view returns (TokenData[] memory) {
                  ILendingPool pool = ILendingPool(ADDRESSES_PROVIDER.getLendingPool());
                  address[] memory reserves = pool.getReservesList();
                  TokenData[] memory reservesTokens = new TokenData[](reserves.length);
                  for (uint256 i = 0; i < reserves.length; i++) {
                    if (reserves[i] == MKR) {
                      reservesTokens[i] = TokenData({symbol: 'MKR', tokenAddress: reserves[i]});
                      continue;
                    }
                    if (reserves[i] == ETH) {
                      reservesTokens[i] = TokenData({symbol: 'ETH', tokenAddress: reserves[i]});
                      continue;
                    }
                    reservesTokens[i] = TokenData({
                      symbol: IERC20Detailed(reserves[i]).symbol(),
                      tokenAddress: reserves[i]
                    });
                  }
                  return reservesTokens;
                }
                function getAllATokens() external view returns (TokenData[] memory) {
                  ILendingPool pool = ILendingPool(ADDRESSES_PROVIDER.getLendingPool());
                  address[] memory reserves = pool.getReservesList();
                  TokenData[] memory aTokens = new TokenData[](reserves.length);
                  for (uint256 i = 0; i < reserves.length; i++) {
                    DataTypes.ReserveData memory reserveData = pool.getReserveData(reserves[i]);
                    aTokens[i] = TokenData({
                      symbol: IERC20Detailed(reserveData.aTokenAddress).symbol(),
                      tokenAddress: reserveData.aTokenAddress
                    });
                  }
                  return aTokens;
                }
                function getReserveConfigurationData(address asset)
                  external
                  view
                  returns (
                    uint256 decimals,
                    uint256 ltv,
                    uint256 liquidationThreshold,
                    uint256 liquidationBonus,
                    uint256 reserveFactor,
                    bool usageAsCollateralEnabled,
                    bool borrowingEnabled,
                    bool stableBorrowRateEnabled,
                    bool isActive,
                    bool isFrozen
                  )
                {
                  DataTypes.ReserveConfigurationMap memory configuration =
                    ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getConfiguration(asset);
                  (ltv, liquidationThreshold, liquidationBonus, decimals, reserveFactor) = configuration
                    .getParamsMemory();
                  (isActive, isFrozen, borrowingEnabled, stableBorrowRateEnabled) = configuration
                    .getFlagsMemory();
                  usageAsCollateralEnabled = liquidationThreshold > 0;
                }
                function getReserveData(address asset)
                  external
                  view
                  returns (
                    uint256 availableLiquidity,
                    uint256 totalStableDebt,
                    uint256 totalVariableDebt,
                    uint256 liquidityRate,
                    uint256 variableBorrowRate,
                    uint256 stableBorrowRate,
                    uint256 averageStableBorrowRate,
                    uint256 liquidityIndex,
                    uint256 variableBorrowIndex,
                    uint40 lastUpdateTimestamp
                  )
                {
                  DataTypes.ReserveData memory reserve =
                    ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getReserveData(asset);
                  return (
                    IERC20Detailed(asset).balanceOf(reserve.aTokenAddress),
                    IERC20Detailed(reserve.stableDebtTokenAddress).totalSupply(),
                    IERC20Detailed(reserve.variableDebtTokenAddress).totalSupply(),
                    reserve.currentLiquidityRate,
                    reserve.currentVariableBorrowRate,
                    reserve.currentStableBorrowRate,
                    IStableDebtToken(reserve.stableDebtTokenAddress).getAverageStableRate(),
                    reserve.liquidityIndex,
                    reserve.variableBorrowIndex,
                    reserve.lastUpdateTimestamp
                  );
                }
                function getUserReserveData(address asset, address user)
                  external
                  view
                  returns (
                    uint256 currentATokenBalance,
                    uint256 currentStableDebt,
                    uint256 currentVariableDebt,
                    uint256 principalStableDebt,
                    uint256 scaledVariableDebt,
                    uint256 stableBorrowRate,
                    uint256 liquidityRate,
                    uint40 stableRateLastUpdated,
                    bool usageAsCollateralEnabled
                  )
                {
                  DataTypes.ReserveData memory reserve =
                    ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getReserveData(asset);
                  DataTypes.UserConfigurationMap memory userConfig =
                    ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getUserConfiguration(user);
                  currentATokenBalance = IERC20Detailed(reserve.aTokenAddress).balanceOf(user);
                  currentVariableDebt = IERC20Detailed(reserve.variableDebtTokenAddress).balanceOf(user);
                  currentStableDebt = IERC20Detailed(reserve.stableDebtTokenAddress).balanceOf(user);
                  principalStableDebt = IStableDebtToken(reserve.stableDebtTokenAddress).principalBalanceOf(user);
                  scaledVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress).scaledBalanceOf(user);
                  liquidityRate = reserve.currentLiquidityRate;
                  stableBorrowRate = IStableDebtToken(reserve.stableDebtTokenAddress).getUserStableRate(user);
                  stableRateLastUpdated = IStableDebtToken(reserve.stableDebtTokenAddress).getUserLastUpdated(
                    user
                  );
                  usageAsCollateralEnabled = userConfig.isUsingAsCollateral(reserve.id);
                }
                function getReserveTokensAddresses(address asset)
                  external
                  view
                  returns (
                    address aTokenAddress,
                    address stableDebtTokenAddress,
                    address variableDebtTokenAddress
                  )
                {
                  DataTypes.ReserveData memory reserve =
                    ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getReserveData(asset);
                  return (
                    reserve.aTokenAddress,
                    reserve.stableDebtTokenAddress,
                    reserve.variableDebtTokenAddress
                  );
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol';
              import {WadRayMath} from '../libraries/math/WadRayMath.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              import {DebtTokenBase} from './base/DebtTokenBase.sol';
              /**
               * @title VariableDebtToken
               * @notice Implements a variable debt token to track the borrowing positions of users
               * at variable rate mode
               * @author Aave
               **/
              contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
                using WadRayMath for uint256;
                uint256 public constant DEBT_TOKEN_REVISION = 0x1;
                constructor(
                  address pool,
                  address underlyingAsset,
                  string memory name,
                  string memory symbol,
                  address incentivesController
                ) public DebtTokenBase(pool, underlyingAsset, name, symbol, incentivesController) {}
                /**
                 * @dev Gets the revision of the stable debt token implementation
                 * @return The debt token implementation revision
                 **/
                function getRevision() internal pure virtual override returns (uint256) {
                  return DEBT_TOKEN_REVISION;
                }
                /**
                 * @dev Calculates the accumulated debt balance of the user
                 * @return The debt balance of the user
                 **/
                function balanceOf(address user) public view virtual override returns (uint256) {
                  uint256 scaledBalance = super.balanceOf(user);
                  if (scaledBalance == 0) {
                    return 0;
                  }
                  return scaledBalance.rayMul(POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET_ADDRESS));
                }
                /**
                 * @dev Mints debt token to the `onBehalfOf` address
                 * -  Only callable by the LendingPool
                 * @param user The address receiving the borrowed underlying, being the delegatee in case
                 * of credit delegate, or same as `onBehalfOf` otherwise
                 * @param onBehalfOf The address receiving the debt tokens
                 * @param amount The amount of debt being minted
                 * @param index The variable debt index of the reserve
                 * @return `true` if the the previous balance of the user is 0
                 **/
                function mint(
                  address user,
                  address onBehalfOf,
                  uint256 amount,
                  uint256 index
                ) external override onlyLendingPool returns (bool) {
                  if (user != onBehalfOf) {
                    _decreaseBorrowAllowance(onBehalfOf, user, amount);
                  }
                  uint256 previousBalance = super.balanceOf(onBehalfOf);
                  uint256 amountScaled = amount.rayDiv(index);
                  require(amountScaled != 0, Errors.CT_INVALID_MINT_AMOUNT);
                  _mint(onBehalfOf, amountScaled);
                  emit Transfer(address(0), onBehalfOf, amount);
                  emit Mint(user, onBehalfOf, amount, index);
                  return previousBalance == 0;
                }
                /**
                 * @dev Burns user variable debt
                 * - Only callable by the LendingPool
                 * @param user The user whose debt is getting burned
                 * @param amount The amount getting burned
                 * @param index The variable debt index of the reserve
                 **/
                function burn(
                  address user,
                  uint256 amount,
                  uint256 index
                ) external override onlyLendingPool {
                  uint256 amountScaled = amount.rayDiv(index);
                  require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT);
                  _burn(user, amountScaled);
                  emit Transfer(user, address(0), amount);
                  emit Burn(user, amount, index);
                }
                /**
                 * @dev Returns the principal debt balance of the user from
                 * @return The debt balance of the user since the last burn/mint action
                 **/
                function scaledBalanceOf(address user) public view virtual override returns (uint256) {
                  return super.balanceOf(user);
                }
                /**
                 * @dev Returns the total supply of the variable debt token. Represents the total debt accrued by the users
                 * @return The total supply
                 **/
                function totalSupply() public view virtual override returns (uint256) {
                  return
                    super.totalSupply().rayMul(POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET_ADDRESS));
                }
                /**
                 * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index)
                 * @return the scaled total supply
                 **/
                function scaledTotalSupply() public view virtual override returns (uint256) {
                  return super.totalSupply();
                }
                /**
                 * @dev Returns the principal balance of the user and principal total supply.
                 * @param user The address of the user
                 * @return The principal balance of the user
                 * @return The principal total supply
                 **/
                function getScaledUserBalanceAndSupply(address user)
                  external
                  view
                  override
                  returns (uint256, uint256)
                {
                  return (super.balanceOf(user), super.totalSupply());
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {ILendingPool} from '../../../interfaces/ILendingPool.sol';
              import {ICreditDelegationToken} from '../../../interfaces/ICreditDelegationToken.sol';
              import {
                VersionedInitializable
              } from '../../libraries/aave-upgradeability/VersionedInitializable.sol';
              import {IncentivizedERC20} from '../IncentivizedERC20.sol';
              import {Errors} from '../../libraries/helpers/Errors.sol';
              /**
               * @title DebtTokenBase
               * @notice Base contract for different types of debt tokens, like StableDebtToken or VariableDebtToken
               * @author Aave
               */
              abstract contract DebtTokenBase is
                IncentivizedERC20,
                VersionedInitializable,
                ICreditDelegationToken
              {
                address public immutable UNDERLYING_ASSET_ADDRESS;
                ILendingPool public immutable POOL;
                mapping(address => mapping(address => uint256)) internal _borrowAllowances;
                /**
                 * @dev Only lending pool can call functions marked by this modifier
                 **/
                modifier onlyLendingPool {
                  require(_msgSender() == address(POOL), Errors.CT_CALLER_MUST_BE_LENDING_POOL);
                  _;
                }
                /**
                 * @dev The metadata of the token will be set on the proxy, that the reason of
                 * passing "NULL" and 0 as metadata
                 */
                constructor(
                  address pool,
                  address underlyingAssetAddress,
                  string memory name,
                  string memory symbol,
                  address incentivesController
                ) public IncentivizedERC20(name, symbol, 18, incentivesController) {
                  POOL = ILendingPool(pool);
                  UNDERLYING_ASSET_ADDRESS = underlyingAssetAddress;
                }
                /**
                 * @dev Initializes the debt token.
                 * @param name The name of the token
                 * @param symbol The symbol of the token
                 * @param decimals The decimals of the token
                 */
                function initialize(
                  uint8 decimals,
                  string memory name,
                  string memory symbol
                ) public initializer {
                  _setName(name);
                  _setSymbol(symbol);
                  _setDecimals(decimals);
                }
                /**
                 * @dev delegates borrowing power to a user on the specific debt token
                 * @param delegatee the address receiving the delegated borrowing power
                 * @param amount the maximum amount being delegated. Delegation will still
                 * respect the liquidation constraints (even if delegated, a delegatee cannot
                 * force a delegator HF to go below 1)
                 **/
                function approveDelegation(address delegatee, uint256 amount) external override {
                  _borrowAllowances[_msgSender()][delegatee] = amount;
                  emit BorrowAllowanceDelegated(_msgSender(), delegatee, UNDERLYING_ASSET_ADDRESS, amount);
                }
                /**
                 * @dev returns the borrow allowance of the user
                 * @param fromUser The user to giving allowance
                 * @param toUser The user to give allowance to
                 * @return the current allowance of toUser
                 **/
                function borrowAllowance(address fromUser, address toUser)
                  external
                  view
                  override
                  returns (uint256)
                {
                  return _borrowAllowances[fromUser][toUser];
                }
                /**
                 * @dev Being non transferrable, the debt token does not implement any of the
                 * standard ERC20 functions for transfer and allowance.
                 **/
                function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                  recipient;
                  amount;
                  revert('TRANSFER_NOT_SUPPORTED');
                }
                function allowance(address owner, address spender)
                  public
                  view
                  virtual
                  override
                  returns (uint256)
                {
                  owner;
                  spender;
                  revert('ALLOWANCE_NOT_SUPPORTED');
                }
                function approve(address spender, uint256 amount) public virtual override returns (bool) {
                  spender;
                  amount;
                  revert('APPROVAL_NOT_SUPPORTED');
                }
                function transferFrom(
                  address sender,
                  address recipient,
                  uint256 amount
                ) public virtual override returns (bool) {
                  sender;
                  recipient;
                  amount;
                  revert('TRANSFER_NOT_SUPPORTED');
                }
                function increaseAllowance(address spender, uint256 addedValue)
                  public
                  virtual
                  override
                  returns (bool)
                {
                  spender;
                  addedValue;
                  revert('ALLOWANCE_NOT_SUPPORTED');
                }
                function decreaseAllowance(address spender, uint256 subtractedValue)
                  public
                  virtual
                  override
                  returns (bool)
                {
                  spender;
                  subtractedValue;
                  revert('ALLOWANCE_NOT_SUPPORTED');
                }
                function _decreaseBorrowAllowance(
                  address delegator,
                  address delegatee,
                  uint256 amount
                ) internal {
                  uint256 newAllowance =
                    _borrowAllowances[delegator][delegatee].sub(amount, Errors.BORROW_ALLOWANCE_NOT_ENOUGH);
                  _borrowAllowances[delegator][delegatee] = newAllowance;
                  emit BorrowAllowanceDelegated(delegator, delegatee, UNDERLYING_ASSET_ADDRESS, newAllowance);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              interface ICreditDelegationToken {
                event BorrowAllowanceDelegated(
                  address indexed fromUser,
                  address indexed toUser,
                  address asset,
                  uint256 amount
                );
                /**
                 * @dev delegates borrowing power to a user on the specific debt token
                 * @param delegatee the address receiving the delegated borrowing power
                 * @param amount the maximum amount being delegated. Delegation will still
                 * respect the liquidation constraints (even if delegated, a delegatee cannot
                 * force a delegator HF to go below 1)
                 **/
                function approveDelegation(address delegatee, uint256 amount) external;
                /**
                 * @dev returns the borrow allowance of the user
                 * @param fromUser The user to giving allowance
                 * @param toUser The user to give allowance to
                 * @return the current allowance of toUser
                 **/
                function borrowAllowance(address fromUser, address toUser) external view returns (uint256);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {VariableDebtToken} from '../../protocol/tokenization/VariableDebtToken.sol';
              contract MockVariableDebtToken is VariableDebtToken {
                constructor(
                  address _pool,
                  address _underlyingAssetAddress,
                  string memory _tokenName,
                  string memory _tokenSymbol,
                  address incentivesController
                )
                  public
                  VariableDebtToken(
                    _pool,
                    _underlyingAssetAddress,
                    _tokenName,
                    _tokenSymbol,
                    incentivesController
                  )
                {}
                function getRevision() internal pure override returns (uint256) {
                  return 0x2;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
              interface IExchangeAdapter {
                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);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {ERC20} from '../../dependencies/openzeppelin/contracts/ERC20.sol';
              /**
               * @title ERC20Mintable
               * @dev ERC20 minting logic
               */
              contract MintableDelegationERC20 is ERC20 {
                address public delegatee;
                constructor(
                  string memory name,
                  string memory symbol,
                  uint8 decimals
                ) public ERC20(name, symbol) {
                  _setupDecimals(decimals);
                }
                /**
                 * @dev Function to mint tokensp
                 * @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;
                }
                function delegate(address delegateeAddress) external {
                  delegatee = delegateeAddress;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {DebtTokenBase} from './base/DebtTokenBase.sol';
              import {MathUtils} from '../libraries/math/MathUtils.sol';
              import {WadRayMath} from '../libraries/math/WadRayMath.sol';
              import {IStableDebtToken} from '../../interfaces/IStableDebtToken.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              /**
               * @title StableDebtToken
               * @notice Implements a stable debt token to track the borrowing positions of users
               * at stable rate mode
               * @author Aave
               **/
              contract StableDebtToken is IStableDebtToken, DebtTokenBase {
                using WadRayMath for uint256;
                uint256 public constant DEBT_TOKEN_REVISION = 0x1;
                uint256 internal _avgStableRate;
                mapping(address => uint40) internal _timestamps;
                mapping(address => uint256) internal _usersStableRate;
                uint40 internal _totalSupplyTimestamp;
                constructor(
                  address pool,
                  address underlyingAsset,
                  string memory name,
                  string memory symbol,
                  address incentivesController
                ) public DebtTokenBase(pool, underlyingAsset, name, symbol, incentivesController) {}
                /**
                 * @dev Gets the revision of the stable debt token implementation
                 * @return The debt token implementation revision
                 **/
                function getRevision() internal pure virtual override returns (uint256) {
                  return DEBT_TOKEN_REVISION;
                }
                /**
                 * @dev Returns the average stable rate across all the stable rate debt
                 * @return the average stable rate
                 **/
                function getAverageStableRate() external view virtual override returns (uint256) {
                  return _avgStableRate;
                }
                /**
                 * @dev Returns the timestamp of the last user action
                 * @return The last update timestamp
                 **/
                function getUserLastUpdated(address user) external view virtual override returns (uint40) {
                  return _timestamps[user];
                }
                /**
                 * @dev Returns the stable rate of the user
                 * @param user The address of the user
                 * @return The stable rate of user
                 **/
                function getUserStableRate(address user) external view virtual override returns (uint256) {
                  return _usersStableRate[user];
                }
                /**
                 * @dev Calculates the current user debt balance
                 * @return The accumulated debt of the user
                 **/
                function balanceOf(address account) public view virtual override returns (uint256) {
                  uint256 accountBalance = super.balanceOf(account);
                  uint256 stableRate = _usersStableRate[account];
                  if (accountBalance == 0) {
                    return 0;
                  }
                  uint256 cumulatedInterest =
                    MathUtils.calculateCompoundedInterest(stableRate, _timestamps[account]);
                  return accountBalance.rayMul(cumulatedInterest);
                }
                struct MintLocalVars {
                  uint256 previousSupply;
                  uint256 nextSupply;
                  uint256 amountInRay;
                  uint256 newStableRate;
                  uint256 currentAvgStableRate;
                }
                /**
                 * @dev Mints debt token to the `onBehalfOf` address.
                 * -  Only callable by the LendingPool
                 * - The resulting rate is the weighted average between the rate of the new debt
                 * and the rate of the previous debt
                 * @param user The address receiving the borrowed underlying, being the delegatee in case
                 * of credit delegate, or same as `onBehalfOf` otherwise
                 * @param onBehalfOf The address receiving the debt tokens
                 * @param amount The amount of debt tokens to mint
                 * @param rate The rate of the debt being minted
                 **/
                function mint(
                  address user,
                  address onBehalfOf,
                  uint256 amount,
                  uint256 rate
                ) external override onlyLendingPool returns (bool) {
                  MintLocalVars memory vars;
                  if (user != onBehalfOf) {
                    _decreaseBorrowAllowance(onBehalfOf, user, amount);
                  }
                  (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(onBehalfOf);
                  vars.previousSupply = totalSupply();
                  vars.currentAvgStableRate = _avgStableRate;
                  vars.nextSupply = _totalSupply = vars.previousSupply.add(amount);
                  vars.amountInRay = amount.wadToRay();
                  vars.newStableRate = _usersStableRate[onBehalfOf]
                    .rayMul(currentBalance.wadToRay())
                    .add(vars.amountInRay.rayMul(rate))
                    .rayDiv(currentBalance.add(amount).wadToRay());
                  require(vars.newStableRate <= type(uint128).max, Errors.SDT_STABLE_DEBT_OVERFLOW);
                  _usersStableRate[onBehalfOf] = vars.newStableRate;
                  //solium-disable-next-line
                  _totalSupplyTimestamp = _timestamps[onBehalfOf] = uint40(block.timestamp);
                  // Calculates the updated average stable rate
                  vars.currentAvgStableRate = _avgStableRate = vars
                    .currentAvgStableRate
                    .rayMul(vars.previousSupply.wadToRay())
                    .add(rate.rayMul(vars.amountInRay))
                    .rayDiv(vars.nextSupply.wadToRay());
                  _mint(onBehalfOf, amount.add(balanceIncrease), vars.previousSupply);
                  emit Transfer(address(0), onBehalfOf, amount);
                  emit Mint(
                    user,
                    onBehalfOf,
                    amount,
                    currentBalance,
                    balanceIncrease,
                    vars.newStableRate,
                    vars.currentAvgStableRate,
                    vars.nextSupply
                  );
                  return currentBalance == 0;
                }
                /**
                 * @dev Burns debt of `user`
                 * @param user The address of the user getting his debt burned
                 * @param amount The amount of debt tokens getting burned
                 **/
                function burn(address user, uint256 amount) external override onlyLendingPool {
                  (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(user);
                  uint256 previousSupply = totalSupply();
                  uint256 newAvgStableRate = 0;
                  uint256 nextSupply = 0;
                  uint256 userStableRate = _usersStableRate[user];
                  // Since the total supply and each single user debt accrue separately,
                  // there might be accumulation errors so that the last borrower repaying
                  // mght actually try to repay more than the available debt supply.
                  // In this case we simply set the total supply and the avg stable rate to 0
                  if (previousSupply <= amount) {
                    _avgStableRate = 0;
                    _totalSupply = 0;
                  } else {
                    nextSupply = _totalSupply = previousSupply.sub(amount);
                    uint256 firstTerm = _avgStableRate.rayMul(previousSupply.wadToRay());
                    uint256 secondTerm = userStableRate.rayMul(amount.wadToRay());
                    // For the same reason described above, when the last user is repaying it might
                    // happen that user rate * user balance > avg rate * total supply. In that case,
                    // we simply set the avg rate to 0
                    if (secondTerm >= firstTerm) {
                      newAvgStableRate = _avgStableRate = _totalSupply = 0;
                    } else {
                      newAvgStableRate = _avgStableRate = firstTerm.sub(secondTerm).rayDiv(nextSupply.wadToRay());
                    }
                  }
                  if (amount == currentBalance) {
                    _usersStableRate[user] = 0;
                    _timestamps[user] = 0;
                  } else {
                    //solium-disable-next-line
                    _timestamps[user] = uint40(block.timestamp);
                  }
                  //solium-disable-next-line
                  _totalSupplyTimestamp = uint40(block.timestamp);
                  if (balanceIncrease > amount) {
                    uint256 amountToMint = balanceIncrease.sub(amount);
                    _mint(user, amountToMint, previousSupply);
                    emit Mint(
                      user,
                      user,
                      amountToMint,
                      currentBalance,
                      balanceIncrease,
                      userStableRate,
                      newAvgStableRate,
                      nextSupply
                    );
                  } else {
                    uint256 amountToBurn = amount.sub(balanceIncrease);
                    _burn(user, amountToBurn, previousSupply);
                    emit Burn(user, amountToBurn, currentBalance, balanceIncrease, newAvgStableRate, nextSupply);
                  }
                  emit Transfer(user, address(0), amount);
                }
                /**
                 * @dev Calculates the increase in balance since the last user interaction
                 * @param user The address of the user for which the interest is being accumulated
                 * @return The previous principal balance, the new principal balance and the balance increase
                 **/
                function _calculateBalanceIncrease(address user)
                  internal
                  view
                  returns (
                    uint256,
                    uint256,
                    uint256
                  )
                {
                  uint256 previousPrincipalBalance = super.balanceOf(user);
                  if (previousPrincipalBalance == 0) {
                    return (0, 0, 0);
                  }
                  // Calculation of the accrued interest since the last accumulation
                  uint256 balanceIncrease = balanceOf(user).sub(previousPrincipalBalance);
                  return (
                    previousPrincipalBalance,
                    previousPrincipalBalance.add(balanceIncrease),
                    balanceIncrease
                  );
                }
                /**
                 * @dev Returns the principal and total supply, the average borrow rate and the last supply update timestamp
                 **/
                function getSupplyData()
                  public
                  view
                  override
                  returns (
                    uint256,
                    uint256,
                    uint256,
                    uint40
                  )
                {
                  uint256 avgRate = _avgStableRate;
                  return (super.totalSupply(), _calcTotalSupply(avgRate), avgRate, _totalSupplyTimestamp);
                }
                /**
                 * @dev Returns the the total supply and the average stable rate
                 **/
                function getTotalSupplyAndAvgRate() public view override returns (uint256, uint256) {
                  uint256 avgRate = _avgStableRate;
                  return (_calcTotalSupply(avgRate), avgRate);
                }
                /**
                 * @dev Returns the total supply
                 **/
                function totalSupply() public view override returns (uint256) {
                  return _calcTotalSupply(_avgStableRate);
                }
                /**
                 * @dev Returns the timestamp at which the total supply was updated
                 **/
                function getTotalSupplyLastUpdated() public view override returns (uint40) {
                  return _totalSupplyTimestamp;
                }
                /**
                 * @dev Returns the principal debt balance of the user from
                 * @param user The user's address
                 * @return The debt balance of the user since the last burn/mint action
                 **/
                function principalBalanceOf(address user) external view virtual override returns (uint256) {
                  return super.balanceOf(user);
                }
                /**
                 * @dev Calculates the total supply
                 * @param avgRate The average rate at which the total supply increases
                 * @return The debt balance of the user since the last burn/mint action
                 **/
                function _calcTotalSupply(uint256 avgRate) internal view virtual returns (uint256) {
                  uint256 principalSupply = super.totalSupply();
                  if (principalSupply == 0) {
                    return 0;
                  }
                  uint256 cumulatedInterest =
                    MathUtils.calculateCompoundedInterest(avgRate, _totalSupplyTimestamp);
                  return principalSupply.rayMul(cumulatedInterest);
                }
                /**
                 * @dev Mints stable debt tokens to an user
                 * @param account The account receiving the debt tokens
                 * @param amount The amount being minted
                 * @param oldTotalSupply the total supply before the minting event
                 **/
                function _mint(
                  address account,
                  uint256 amount,
                  uint256 oldTotalSupply
                ) internal {
                  uint256 oldAccountBalance = _balances[account];
                  _balances[account] = oldAccountBalance.add(amount);
                  if (address(_incentivesController) != address(0)) {
                    _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance);
                  }
                }
                /**
                 * @dev Burns stable debt tokens of an user
                 * @param account The user getting his debt burned
                 * @param amount The amount being burned
                 * @param oldTotalSupply The total supply before the burning event
                 **/
                function _burn(
                  address account,
                  uint256 amount,
                  uint256 oldTotalSupply
                ) internal {
                  uint256 oldAccountBalance = _balances[account];
                  _balances[account] = oldAccountBalance.sub(amount, Errors.SDT_BURN_EXCEEDS_BALANCE);
                  if (address(_incentivesController) != address(0)) {
                    _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance);
                  }
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {StableDebtToken} from '../../protocol/tokenization/StableDebtToken.sol';
              contract MockStableDebtToken is StableDebtToken {
                constructor(
                  address _pool,
                  address _underlyingAssetAddress,
                  string memory _tokenName,
                  string memory _tokenSymbol,
                  address incentivesController
                )
                  public
                  StableDebtToken(_pool, _underlyingAssetAddress, _tokenName, _tokenSymbol, incentivesController)
                {}
                function getRevision() internal pure override returns (uint256) {
                  return 0x2;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {StableDebtToken} from '../protocol/tokenization/StableDebtToken.sol';
              import {VariableDebtToken} from '../protocol/tokenization/VariableDebtToken.sol';
              import {LendingRateOracle} from '../mocks/oracle/LendingRateOracle.sol';
              import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol';
              import {StringLib} from './StringLib.sol';
              contract StableAndVariableTokensHelper is Ownable {
                address payable private pool;
                address private addressesProvider;
                event deployedContracts(address stableToken, address variableToken);
                constructor(address payable _pool, address _addressesProvider) public {
                  pool = _pool;
                  addressesProvider = _addressesProvider;
                }
                function initDeployment(
                  address[] calldata tokens,
                  string[] calldata symbols,
                  address incentivesController
                ) external onlyOwner {
                  require(tokens.length == symbols.length, 'Arrays not same length');
                  require(pool != address(0), 'Pool can not be zero address');
                  for (uint256 i = 0; i < tokens.length; i++) {
                    emit deployedContracts(
                      address(
                        new StableDebtToken(
                          pool,
                          tokens[i],
                          StringLib.concat('Aave stable debt bearing ', symbols[i]),
                          StringLib.concat('stableDebt', symbols[i]),
                          incentivesController
                        )
                      ),
                      address(
                        new VariableDebtToken(
                          pool,
                          tokens[i],
                          StringLib.concat('Aave variable debt bearing ', symbols[i]),
                          StringLib.concat('variableDebt', symbols[i]),
                          incentivesController
                        )
                      )
                    );
                  }
                }
                function setOracleBorrowRates(
                  address[] calldata assets,
                  uint256[] calldata rates,
                  address oracle
                ) external onlyOwner {
                  require(assets.length == rates.length, 'Arrays not same length');
                  for (uint256 i = 0; i < assets.length; i++) {
                    // LendingRateOracle owner must be this contract
                    LendingRateOracle(oracle).setMarketBorrowRate(assets[i], rates[i]);
                  }
                }
                function setOracleOwnership(address oracle, address admin) external onlyOwner {
                  require(admin != address(0), 'owner can not be zero');
                  require(LendingRateOracle(oracle).owner() == address(this), 'helper is not owner');
                  LendingRateOracle(oracle).transferOwnership(admin);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              library StringLib {
                function concat(string memory a, string memory b) internal pure returns (string memory) {
                  return string(abi.encodePacked(a, b));
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {LendingPool} from '../protocol/lendingpool/LendingPool.sol';
              import {
                LendingPoolAddressesProvider
              } from '../protocol/configuration/LendingPoolAddressesProvider.sol';
              import {LendingPoolConfigurator} from '../protocol/lendingpool/LendingPoolConfigurator.sol';
              import {AToken} from '../protocol/tokenization/AToken.sol';
              import {
                DefaultReserveInterestRateStrategy
              } from '../protocol/lendingpool/DefaultReserveInterestRateStrategy.sol';
              import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol';
              import {StringLib} from './StringLib.sol';
              contract ATokensAndRatesHelper is Ownable {
                address payable private pool;
                address private addressesProvider;
                address private poolConfigurator;
                event deployedContracts(address aToken, address strategy);
                constructor(
                  address payable _pool,
                  address _addressesProvider,
                  address _poolConfigurator
                ) public {
                  pool = _pool;
                  addressesProvider = _addressesProvider;
                  poolConfigurator = _poolConfigurator;
                }
                function initDeployment(
                  address[] calldata assets,
                  string[] calldata symbols,
                  uint256[6][] calldata rates,
                  address treasuryAddress,
                  address incentivesController
                ) external onlyOwner {
                  require(assets.length == symbols.length, 't Arrays not same length');
                  require(rates.length == symbols.length, 'r Arrays not same length');
                  for (uint256 i = 0; i < assets.length; i++) {
                    emit deployedContracts(
                      address(
                        new AToken(
                          LendingPool(pool),
                          assets[i],
                          treasuryAddress,
                          StringLib.concat('Aave interest bearing ', symbols[i]),
                          StringLib.concat('a', symbols[i]),
                          incentivesController
                        )
                      ),
                      address(
                        new DefaultReserveInterestRateStrategy(
                          LendingPoolAddressesProvider(addressesProvider),
                          rates[i][0],
                          rates[i][1],
                          rates[i][2],
                          rates[i][3],
                          rates[i][4],
                          rates[i][5]
                        )
                      )
                    );
                  }
                }
                function initReserve(
                  address[] calldata stables,
                  address[] calldata variables,
                  address[] calldata aTokens,
                  address[] calldata strategies,
                  uint8[] calldata reserveDecimals
                ) external onlyOwner {
                  require(variables.length == stables.length);
                  require(aTokens.length == stables.length);
                  require(strategies.length == stables.length);
                  require(reserveDecimals.length == stables.length);
                  for (uint256 i = 0; i < stables.length; i++) {
                    LendingPoolConfigurator(poolConfigurator).initReserve(
                      aTokens[i],
                      stables[i],
                      variables[i],
                      reserveDecimals[i],
                      strategies[i]
                    );
                  }
                }
                function configureReserves(
                  address[] calldata assets,
                  uint256[] calldata baseLTVs,
                  uint256[] calldata liquidationThresholds,
                  uint256[] calldata liquidationBonuses,
                  uint256[] calldata reserveFactors,
                  bool[] calldata stableBorrowingEnabled
                ) external onlyOwner {
                  require(baseLTVs.length == assets.length);
                  require(liquidationThresholds.length == assets.length);
                  require(liquidationBonuses.length == assets.length);
                  require(stableBorrowingEnabled.length == assets.length);
                  require(reserveFactors.length == assets.length);
                  LendingPoolConfigurator configurator = LendingPoolConfigurator(poolConfigurator);
                  for (uint256 i = 0; i < assets.length; i++) {
                    configurator.configureReserveAsCollateral(
                      assets[i],
                      baseLTVs[i],
                      liquidationThresholds[i],
                      liquidationBonuses[i]
                    );
                    configurator.enableBorrowingOnReserve(
                      assets[i],
                      stableBorrowingEnabled[i]
                    );
                    configurator.setReserveFactor(assets[i], reserveFactors[i]);
                  }
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Ownable} from '../../dependencies/openzeppelin/contracts/Ownable.sol';
              // Prettier ignore to prevent buidler flatter bug
              // prettier-ignore
              import {InitializableImmutableAdminUpgradeabilityProxy} from '../libraries/aave-upgradeability/InitializableImmutableAdminUpgradeabilityProxy.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              /**
               * @title LendingPoolAddressesProvider contract
               * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles
               * - Acting also as factory of proxies and admin of those, so with right to change its implementations
               * - Owned by the Aave Governance
               * @author Aave
               **/
              contract LendingPoolAddressesProvider is Ownable, ILendingPoolAddressesProvider {
                string private _marketId;
                mapping(bytes32 => address) private _addresses;
                bytes32 private constant LENDING_POOL = 'LENDING_POOL';
                bytes32 private constant LENDING_POOL_CONFIGURATOR = 'LENDING_POOL_CONFIGURATOR';
                bytes32 private constant POOL_ADMIN = 'POOL_ADMIN';
                bytes32 private constant EMERGENCY_ADMIN = 'EMERGENCY_ADMIN';
                bytes32 private constant LENDING_POOL_COLLATERAL_MANAGER = 'COLLATERAL_MANAGER';
                bytes32 private constant PRICE_ORACLE = 'PRICE_ORACLE';
                bytes32 private constant LENDING_RATE_ORACLE = 'LENDING_RATE_ORACLE';
                constructor(string memory marketId) public {
                  _setMarketId(marketId);
                }
                /**
                 * @dev Returns the id of the Aave market to which this contracts points to
                 * @return The market id
                 **/
                function getMarketId() external view override returns (string memory) {
                  return _marketId;
                }
                /**
                 * @dev Allows to set the market which this LendingPoolAddressesProvider represents
                 * @param marketId The market id
                 */
                function setMarketId(string memory marketId) external override onlyOwner {
                  _setMarketId(marketId);
                }
                /**
                 * @dev General function to update the implementation of a proxy registered with
                 * certain `id`. If there is no proxy registered, it will instantiate one and
                 * set as implementation the `implementationAddress`
                 * IMPORTANT Use this function carefully, only for ids that don't have an explicit
                 * setter function, in order to avoid unexpected consequences
                 * @param id The id
                 * @param implementationAddress The address of the new implementation
                 */
                function setAddressAsProxy(bytes32 id, address implementationAddress)
                  external
                  override
                  onlyOwner
                {
                  _updateImpl(id, implementationAddress);
                  emit AddressSet(id, implementationAddress, true);
                }
                /**
                 * @dev Sets an address for an id replacing the address saved in the addresses map
                 * IMPORTANT Use this function carefully, as it will do a hard replacement
                 * @param id The id
                 * @param newAddress The address to set
                 */
                function setAddress(bytes32 id, address newAddress) external override onlyOwner {
                  _addresses[id] = newAddress;
                  emit AddressSet(id, newAddress, false);
                }
                /**
                 * @dev Returns an address by id
                 * @return The address
                 */
                function getAddress(bytes32 id) public view override returns (address) {
                  return _addresses[id];
                }
                /**
                 * @dev Returns the address of the LendingPool proxy
                 * @return The LendingPool proxy address
                 **/
                function getLendingPool() external view override returns (address) {
                  return getAddress(LENDING_POOL);
                }
                /**
                 * @dev Updates the implementation of the LendingPool, or creates the proxy
                 * setting the new `pool` implementation on the first time calling it
                 * @param pool The new LendingPool implementation
                 **/
                function setLendingPoolImpl(address pool) external override onlyOwner {
                  _updateImpl(LENDING_POOL, pool);
                  emit LendingPoolUpdated(pool);
                }
                /**
                 * @dev Returns the address of the LendingPoolConfigurator proxy
                 * @return The LendingPoolConfigurator proxy address
                 **/
                function getLendingPoolConfigurator() external view override returns (address) {
                  return getAddress(LENDING_POOL_CONFIGURATOR);
                }
                /**
                 * @dev Updates the implementation of the LendingPoolConfigurator, or creates the proxy
                 * setting the new `configurator` implementation on the first time calling it
                 * @param configurator The new LendingPoolConfigurator implementation
                 **/
                function setLendingPoolConfiguratorImpl(address configurator) external override onlyOwner {
                  _updateImpl(LENDING_POOL_CONFIGURATOR, configurator);
                  emit LendingPoolConfiguratorUpdated(configurator);
                }
                /**
                 * @dev Returns the address of the LendingPoolCollateralManager. 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 LendingPoolCollateralManager
                 **/
                function getLendingPoolCollateralManager() external view override returns (address) {
                  return getAddress(LENDING_POOL_COLLATERAL_MANAGER);
                }
                /**
                 * @dev Updates the address of the LendingPoolCollateralManager
                 * @param manager The new LendingPoolCollateralManager address
                 **/
                function setLendingPoolCollateralManager(address manager) external override onlyOwner {
                  _addresses[LENDING_POOL_COLLATERAL_MANAGER] = manager;
                  emit LendingPoolCollateralManagerUpdated(manager);
                }
                /**
                 * @dev The functions below are getters/setters of addresses that are outside the context
                 * of the protocol hence the upgradable proxy pattern is not used
                 **/
                function getPoolAdmin() external view override returns (address) {
                  return getAddress(POOL_ADMIN);
                }
                function setPoolAdmin(address admin) external override onlyOwner {
                  _addresses[POOL_ADMIN] = admin;
                  emit ConfigurationAdminUpdated(admin);
                }
                function getEmergencyAdmin() external view override returns (address) {
                  return getAddress(EMERGENCY_ADMIN);
                }
                function setEmergencyAdmin(address emergencyAdmin) external override onlyOwner {
                  _addresses[EMERGENCY_ADMIN] = emergencyAdmin;
                  emit EmergencyAdminUpdated(emergencyAdmin);
                }
                function getPriceOracle() external view override returns (address) {
                  return getAddress(PRICE_ORACLE);
                }
                function setPriceOracle(address priceOracle) external override onlyOwner {
                  _addresses[PRICE_ORACLE] = priceOracle;
                  emit PriceOracleUpdated(priceOracle);
                }
                function getLendingRateOracle() external view override returns (address) {
                  return getAddress(LENDING_RATE_ORACLE);
                }
                function setLendingRateOracle(address lendingRateOracle) external override onlyOwner {
                  _addresses[LENDING_RATE_ORACLE] = lendingRateOracle;
                  emit LendingRateOracleUpdated(lendingRateOracle);
                }
                /**
                 * @dev Internal function to update the implementation of a specific proxied component of the protocol
                 * - If there is no proxy registered in the given `id`, it creates the proxy setting `newAdress`
                 *   as implementation and calls the initialize() function on the proxy
                 * - If there is already a proxy registered, it just updates the implementation to `newAddress` and
                 *   calls the initialize() function via upgradeToAndCall() in the proxy
                 * @param id The id of the proxy to be updated
                 * @param newAddress The address of the new implementation
                 **/
                function _updateImpl(bytes32 id, address newAddress) internal {
                  address payable proxyAddress = payable(_addresses[id]);
                  InitializableImmutableAdminUpgradeabilityProxy proxy =
                    InitializableImmutableAdminUpgradeabilityProxy(proxyAddress);
                  bytes memory params = abi.encodeWithSignature('initialize(address)', address(this));
                  if (proxyAddress == address(0)) {
                    proxy = new InitializableImmutableAdminUpgradeabilityProxy(address(this));
                    proxy.initialize(newAddress, params);
                    _addresses[id] = address(proxy);
                    emit ProxyCreated(id, address(proxy));
                  } else {
                    proxy.upgradeToAndCall(newAddress, params);
                  }
                }
                function _setMarketId(string memory marketId) internal {
                  _marketId = marketId;
                  emit MarketIdSet(marketId);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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);
                  }
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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 virtual override {
                  require(msg.sender != _admin(), 'Cannot call fallback function from the proxy admin');
                  super._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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();
                }
              }
              

              File 12 of 15: ValidationLogic
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                  // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                  // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                  // for accounts without code, i.e. `keccak256('')`
                  bytes32 codehash;
                  bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                  // solhint-disable-next-line no-inline-assembly
                  assembly {
                    codehash := extcodehash(account)
                  }
                  return (codehash != accountHash && codehash != 0x0);
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                  require(address(this).balance >= amount, 'Address: insufficient balance');
                  // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                  (bool success, ) = recipient.call{value: amount}('');
                  require(success, 'Address: unable to send value, recipient may have reverted');
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {Address} from '../dependencies/openzeppelin/contracts/Address.sol';
              import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
              import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
              import {ILendingPool} from '../interfaces/ILendingPool.sol';
              import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol';
              import {DataTypes} from '../protocol/libraries/types/DataTypes.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 payable;
                using Address for address;
                using SafeERC20 for IERC20;
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                address constant MOCK_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                /**
                  @dev Fallback function, don't accept any ETH
                  **/
                receive() external payable {
                  //only contracts can send ETH to the core
                  require(msg.sender.isContract(), '22');
                }
                /**
                  @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) {
                  if (token == MOCK_ETH_ADDRESS) {
                    return user.balance; // ETH balance
                    // check if token is actually a contract
                  } else if (token.isContract()) {
                    return IERC20(token).balanceOf(user);
                  }
                  revert('INVALID_TOKEN');
                }
                /**
                 * @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[] calldata users, address[] calldata tokens)
                  external
                  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++) {
                      balances[i * tokens.length + j] = balanceOf(users[i], tokens[j]);
                    }
                  }
                  return balances;
                }
                /**
                  @dev provides balances of user wallet for all reserves available on the pool
                  */
                function getUserWalletBalances(address provider, address user)
                  external
                  view
                  returns (address[] memory, uint256[] memory)
                {
                  ILendingPool pool = ILendingPool(ILendingPoolAddressesProvider(provider).getLendingPool());
                  address[] memory reserves = pool.getReservesList();
                  address[] memory reservesWithEth = new address[](reserves.length + 1);
                  for (uint256 i = 0; i < reserves.length; i++) {
                    reservesWithEth[i] = reserves[i];
                  }
                  reservesWithEth[reserves.length] = MOCK_ETH_ADDRESS;
                  uint256[] memory balances = new uint256[](reservesWithEth.length);
                  for (uint256 j = 0; j < reserves.length; j++) {
                    DataTypes.ReserveConfigurationMap memory configuration = pool.getConfiguration(reservesWithEth[j]);
                    (bool isActive, , , ) = configuration.getFlagsMemory();
                    if (!isActive) {
                      balances[j] = 0;
                      continue;
                    }
                    balances[j] = balanceOf(user, reservesWithEth[j]);
                  }
                  balances[reserves.length] = balanceOf(user, MOCK_ETH_ADDRESS);
                  return (reservesWithEth, balances);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                /**
                 * @dev Returns the amount of tokens in existence.
                 */
                function totalSupply() external view returns (uint256);
                /**
                 * @dev Returns the amount of tokens owned by `account`.
                 */
                function balanceOf(address account) external view returns (uint256);
                /**
                 * @dev Moves `amount` tokens from the caller's account to `recipient`.
                 *
                 * Returns a boolean value indicating whether the operation succeeded.
                 *
                 * Emits a {Transfer} event.
                 */
                function transfer(address recipient, uint256 amount) external returns (bool);
                /**
                 * @dev Returns the remaining number of tokens that `spender` will be
                 * allowed to spend on behalf of `owner` through {transferFrom}. This is
                 * zero by default.
                 *
                 * This value changes when {approve} or {transferFrom} are called.
                 */
                function allowance(address owner, address spender) external view returns (uint256);
                /**
                 * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
                 *
                 * Returns a boolean value indicating whether the operation succeeded.
                 *
                 * IMPORTANT: Beware that changing an allowance with this method brings the risk
                 * that someone may use both the old and the new allowance by unfortunate
                 * transaction ordering. One possible solution to mitigate this race
                 * condition is to first reduce the spender's allowance to 0 and set the
                 * desired value afterwards:
                 * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
                 *
                 * Emits an {Approval} event.
                 */
                function approve(address spender, uint256 amount) external returns (bool);
                /**
                 * @dev Moves `amount` tokens from `sender` to `recipient` using the
                 * allowance mechanism. `amount` is then deducted from the caller's
                 * allowance.
                 *
                 * Returns a boolean value indicating whether the operation succeeded.
                 *
                 * Emits a {Transfer} event.
                 */
                function transferFrom(
                  address sender,
                  address recipient,
                  uint256 amount
                ) external returns (bool);
                /**
                 * @dev Emitted when `value` tokens are moved from one account (`from`) to
                 * another (`to`).
                 *
                 * Note that `value` may be zero.
                 */
                event Transfer(address indexed from, address indexed to, uint256 value);
                /**
                 * @dev Emitted when the allowance of a `spender` for an `owner` is set by
                 * a call to {approve}. `value` is the new allowance.
                 */
                event Approval(address indexed owner, address indexed spender, uint256 value);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title LendingPoolAddressesProvider contract
               * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles
               * - Acting also as factory of proxies and admin of those, so with right to change its implementations
               * - Owned by the Aave Governance
               * @author Aave
               **/
              interface ILendingPoolAddressesProvider {
                event MarketIdSet(string newMarketId);
                event LendingPoolUpdated(address indexed newAddress);
                event ConfigurationAdminUpdated(address indexed newAddress);
                event EmergencyAdminUpdated(address indexed newAddress);
                event LendingPoolConfiguratorUpdated(address indexed newAddress);
                event LendingPoolCollateralManagerUpdated(address indexed newAddress);
                event PriceOracleUpdated(address indexed newAddress);
                event LendingRateOracleUpdated(address indexed newAddress);
                event ProxyCreated(bytes32 id, address indexed newAddress);
                event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy);
                function getMarketId() external view returns (string memory);
                function setMarketId(string calldata marketId) external;
                function setAddress(bytes32 id, address newAddress) external;
                function setAddressAsProxy(bytes32 id, address impl) external;
                function getAddress(bytes32 id) external view returns (address);
                function getLendingPool() external view returns (address);
                function setLendingPoolImpl(address pool) external;
                function getLendingPoolConfigurator() external view returns (address);
                function setLendingPoolConfiguratorImpl(address configurator) external;
                function getLendingPoolCollateralManager() external view returns (address);
                function setLendingPoolCollateralManager(address manager) external;
                function getPoolAdmin() external view returns (address);
                function setPoolAdmin(address admin) external;
                function getEmergencyAdmin() external view returns (address);
                function setEmergencyAdmin(address admin) external;
                function getPriceOracle() external view returns (address);
                function setPriceOracle(address priceOracle) external;
                function getLendingRateOracle() external view returns (address);
                function setLendingRateOracle(address lendingRateOracle) external;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {ILendingPoolAddressesProvider} from './ILendingPoolAddressesProvider.sol';
              import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
              interface ILendingPool {
                /**
                 * @dev Emitted on deposit()
                 * @param reserve The address of the underlying asset of the reserve
                 * @param user The address initiating the deposit
                 * @param onBehalfOf The beneficiary of the deposit, receiving the aTokens
                 * @param amount The amount deposited
                 * @param referral The referral code used
                 **/
                event Deposit(
                  address indexed reserve,
                  address user,
                  address indexed onBehalfOf,
                  uint256 amount,
                  uint16 indexed referral
                );
                /**
                 * @dev Emitted on withdraw()
                 * @param reserve The address of the underlyng asset being withdrawn
                 * @param user The address initiating the withdrawal, owner of aTokens
                 * @param to Address that will receive the underlying
                 * @param amount The amount to be withdrawn
                 **/
                event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
                /**
                 * @dev Emitted on borrow() and flashLoan() when debt needs to be opened
                 * @param reserve The address of the underlying asset being borrowed
                 * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just
                 * initiator of the transaction on flashLoan()
                 * @param onBehalfOf The address that will be getting the debt
                 * @param amount The amount borrowed out
                 * @param borrowRateMode The rate mode: 1 for Stable, 2 for Variable
                 * @param borrowRate The numeric rate at which the user has borrowed
                 * @param referral The referral code used
                 **/
                event Borrow(
                  address indexed reserve,
                  address user,
                  address indexed onBehalfOf,
                  uint256 amount,
                  uint256 borrowRateMode,
                  uint256 borrowRate,
                  uint16 indexed referral
                );
                /**
                 * @dev Emitted on repay()
                 * @param reserve The address of the underlying asset of the reserve
                 * @param user The beneficiary of the repayment, getting his debt reduced
                 * @param repayer The address of the user initiating the repay(), providing the funds
                 * @param amount The amount repaid
                 **/
                event Repay(
                  address indexed reserve,
                  address indexed user,
                  address indexed repayer,
                  uint256 amount
                );
                /**
                 * @dev Emitted on swapBorrowRateMode()
                 * @param reserve The address of the underlying asset of the reserve
                 * @param user The address of the user swapping his rate mode
                 * @param rateMode The rate mode that the user wants to swap to
                 **/
                event Swap(address indexed reserve, address indexed user, uint256 rateMode);
                /**
                 * @dev Emitted on setUserUseReserveAsCollateral()
                 * @param reserve The address of the underlying asset of the reserve
                 * @param user The address of the user enabling the usage as collateral
                 **/
                event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
                /**
                 * @dev Emitted on setUserUseReserveAsCollateral()
                 * @param reserve The address of the underlying asset of the reserve
                 * @param user The address of the user enabling the usage as collateral
                 **/
                event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
                /**
                 * @dev Emitted on rebalanceStableBorrowRate()
                 * @param reserve The address of the underlying asset of the reserve
                 * @param user The address of the user for which the rebalance has been executed
                 **/
                event RebalanceStableBorrowRate(address indexed reserve, address indexed user);
                /**
                 * @dev Emitted on flashLoan()
                 * @param target The address of the flash loan receiver contract
                 * @param initiator The address initiating the flash loan
                 * @param asset The address of the asset being flash borrowed
                 * @param amount The amount flash borrowed
                 * @param premium The fee flash borrowed
                 * @param referralCode The referral code used
                 **/
                event FlashLoan(
                  address indexed target,
                  address indexed initiator,
                  address indexed asset,
                  uint256 amount,
                  uint256 premium,
                  uint16 referralCode
                );
                /**
                 * @dev Emitted when the pause is triggered.
                 */
                event Paused();
                /**
                 * @dev Emitted when the pause is lifted.
                 */
                event Unpaused();
                /**
                 * @dev Emitted when a borrower is liquidated. This event is emitted by the LendingPool via
                 * LendingPoolCollateral manager using a DELEGATECALL
                 * This allows to have the events in the generated ABI for LendingPool.
                 * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                 * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                 * @param user The address of the borrower getting liquidated
                 * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                 * @param liquidatedCollateralAmount The amount of collateral received by the liiquidator
                 * @param liquidator The address of the liquidator
                 * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
                 * to receive the underlying collateral asset directly
                 **/
                event LiquidationCall(
                  address indexed collateralAsset,
                  address indexed debtAsset,
                  address indexed user,
                  uint256 debtToCover,
                  uint256 liquidatedCollateralAmount,
                  address liquidator,
                  bool receiveAToken
                );
                /**
                 * @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared
                 * in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal,
                 * the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it
                 * gets added to the LendingPool ABI
                 * @param reserve The address of the underlying asset of the reserve
                 * @param liquidityRate The new liquidity rate
                 * @param stableBorrowRate The new stable borrow rate
                 * @param variableBorrowRate The new variable borrow rate
                 * @param liquidityIndex The new liquidity index
                 * @param variableBorrowIndex The new variable borrow index
                 **/
                event ReserveDataUpdated(
                  address indexed reserve,
                  uint256 liquidityRate,
                  uint256 stableBorrowRate,
                  uint256 variableBorrowRate,
                  uint256 liquidityIndex,
                  uint256 variableBorrowIndex
                );
                /**
                 * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                 * - E.g. User deposits 100 USDC and gets in return 100 aUSDC
                 * @param asset The address of the underlying asset to deposit
                 * @param amount The amount to be deposited
                 * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
                 *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
                 *   is a different wallet
                 * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                 *   0 if the action is executed directly by the user, without any middle-man
                 **/
                function deposit(
                  address asset,
                  uint256 amount,
                  address onBehalfOf,
                  uint16 referralCode
                ) external;
                /**
                 * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
                 * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
                 * @param asset The address of the underlying asset to withdraw
                 * @param amount The underlying amount to be withdrawn
                 *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
                 * @param to Address that will receive the underlying, same as msg.sender if the user
                 *   wants to receive it on his own wallet, or a different address if the beneficiary is a
                 *   different wallet
                 * @return The final amount withdrawn
                 **/
                function withdraw(
                  address asset,
                  uint256 amount,
                  address to
                ) external returns (uint256);
                /**
                 * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
                 * already deposited enough collateral, or he was given enough allowance by a credit delegator on the
                 * corresponding debt token (StableDebtToken or VariableDebtToken)
                 * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
                 *   and 100 stable/variable debt tokens, depending on the `interestRateMode`
                 * @param asset The address of the underlying asset to borrow
                 * @param amount The amount to be borrowed
                 * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable
                 * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                 *   0 if the action is executed directly by the user, without any middle-man
                 * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself
                 * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator
                 * if he has been given credit delegation allowance
                 **/
                function borrow(
                  address asset,
                  uint256 amount,
                  uint256 interestRateMode,
                  uint16 referralCode,
                  address onBehalfOf
                ) external;
                /**
                 * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned
                 * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address
                 * @param asset The address of the borrowed underlying asset previously borrowed
                 * @param amount The amount to repay
                 * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
                 * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
                 * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
                 * user calling the function if he wants to reduce/remove his own debt, or the address of any other
                 * other borrower whose debt should be removed
                 * @return The final amount repaid
                 **/
                function repay(
                  address asset,
                  uint256 amount,
                  uint256 rateMode,
                  address onBehalfOf
                ) external returns (uint256);
                /**
                 * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa
                 * @param asset The address of the underlying asset borrowed
                 * @param rateMode The rate mode that the user wants to swap to
                 **/
                function swapBorrowRateMode(address asset, uint256 rateMode) external;
                /**
                 * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve.
                 * - Users can be rebalanced if the following conditions are satisfied:
                 *     1. Usage ratio is above 95%
                 *     2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been
                 *        borrowed at a stable rate and depositors are not earning enough
                 * @param asset The address of the underlying asset borrowed
                 * @param user The address of the user to be rebalanced
                 **/
                function rebalanceStableBorrowRate(address asset, address user) external;
                /**
                 * @dev Allows depositors to enable/disable a specific deposited asset as collateral
                 * @param asset The address of the underlying asset deposited
                 * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise
                 **/
                function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external;
                /**
                 * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
                 * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
                 *   a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
                 * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                 * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                 * @param user The address of the borrower getting liquidated
                 * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                 * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
                 * to receive the underlying collateral asset directly
                 **/
                function liquidationCall(
                  address collateralAsset,
                  address debtAsset,
                  address user,
                  uint256 debtToCover,
                  bool receiveAToken
                ) external;
                /**
                 * @dev Allows smartcontracts to access the liquidity of the pool within one transaction,
                 * as long as the amount taken plus a fee is returned.
                 * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration.
                 * For further details please visit https://developers.aave.com
                 * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface
                 * @param assets The addresses of the assets being flash-borrowed
                 * @param amounts The amounts amounts being flash-borrowed
                 * @param modes Types of the debt to open if the flash loan is not returned:
                 *   0 -> Don't open any debt, just revert if funds can't be transferred from the receiver
                 *   1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
                 *   2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
                 * @param onBehalfOf The address  that will receive the debt in the case of using on `modes` 1 or 2
                 * @param params Variadic packed params to pass to the receiver as extra information
                 * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                 *   0 if the action is executed directly by the user, without any middle-man
                 **/
                function flashLoan(
                  address receiverAddress,
                  address[] calldata assets,
                  uint256[] calldata amounts,
                  uint256[] calldata modes,
                  address onBehalfOf,
                  bytes calldata params,
                  uint16 referralCode
                ) external;
                /**
                 * @dev Returns the user account data across all the reserves
                 * @param user The address of the user
                 * @return totalCollateralETH the total collateral in ETH of the user
                 * @return totalDebtETH the total debt in ETH of the user
                 * @return availableBorrowsETH the borrowing power left of the user
                 * @return currentLiquidationThreshold the liquidation threshold of the user
                 * @return ltv the loan to value of the user
                 * @return healthFactor the current health factor of the user
                 **/
                function getUserAccountData(address user)
                  external
                  view
                  returns (
                    uint256 totalCollateralETH,
                    uint256 totalDebtETH,
                    uint256 availableBorrowsETH,
                    uint256 currentLiquidationThreshold,
                    uint256 ltv,
                    uint256 healthFactor
                  );
                function initReserve(
                  address reserve,
                  address aTokenAddress,
                  address stableDebtAddress,
                  address variableDebtAddress,
                  address interestRateStrategyAddress
                ) external;
                function setReserveInterestRateStrategyAddress(address reserve, address rateStrategyAddress)
                  external;
                function setConfiguration(address reserve, uint256 configuration) external;
                /**
                 * @dev Returns the configuration of the reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @return The configuration of the reserve
                 **/
                function getConfiguration(address asset)
                  external
                  view
                  returns (DataTypes.ReserveConfigurationMap memory);
                /**
                 * @dev Returns the configuration of the user across all the reserves
                 * @param user The user address
                 * @return The configuration of the user
                 **/
                function getUserConfiguration(address user)
                  external
                  view
                  returns (DataTypes.UserConfigurationMap memory);
                /**
                 * @dev Returns the normalized income normalized income of the reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @return The reserve's normalized income
                 */
                function getReserveNormalizedIncome(address asset) external view returns (uint256);
                /**
                 * @dev Returns the normalized variable debt per unit of asset
                 * @param asset The address of the underlying asset of the reserve
                 * @return The reserve normalized variable debt
                 */
                function getReserveNormalizedVariableDebt(address asset) external view returns (uint256);
                /**
                 * @dev Returns the state and configuration of the reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @return The state of the reserve
                 **/
                function getReserveData(address asset) external view returns (DataTypes.ReserveData memory);
                function finalizeTransfer(
                  address asset,
                  address from,
                  address to,
                  uint256 amount,
                  uint256 balanceFromAfter,
                  uint256 balanceToBefore
                ) external;
                function getReservesList() external view returns (address[] memory);
                function getAddressesProvider() external view returns (ILendingPoolAddressesProvider);
                function setPause(bool val) external;
                function paused() external view returns (bool);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.6.12;
              import {IERC20} from './IERC20.sol';
              import {SafeMath} from './SafeMath.sol';
              import {Address} from './Address.sol';
              /**
               * @title SafeERC20
               * @dev Wrappers around ERC20 operations that throw on failure (when the token
               * contract returns false). Tokens that return no value (and instead revert or
               * throw on failure) are also supported, non-reverting calls are assumed to be
               * successful.
               * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
               * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
               */
              library SafeERC20 {
                using SafeMath for uint256;
                using Address for address;
                function safeTransfer(
                  IERC20 token,
                  address to,
                  uint256 value
                ) internal {
                  callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                }
                function safeTransferFrom(
                  IERC20 token,
                  address from,
                  address to,
                  uint256 value
                ) internal {
                  callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                }
                function safeApprove(
                  IERC20 token,
                  address spender,
                  uint256 value
                ) internal {
                  require(
                    (value == 0) || (token.allowance(address(this), spender) == 0),
                    'SafeERC20: approve from non-zero to non-zero allowance'
                  );
                  callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                }
                function callOptionalReturn(IERC20 token, bytes memory data) private {
                  require(address(token).isContract(), 'SafeERC20: call to non-contract');
                  // solhint-disable-next-line avoid-low-level-calls
                  (bool success, bytes memory returndata) = address(token).call(data);
                  require(success, 'SafeERC20: low-level call failed');
                  if (returndata.length > 0) {
                    // Return data is optional
                    // solhint-disable-next-line max-line-length
                    require(abi.decode(returndata, (bool)), 'SafeERC20: ERC20 operation did not succeed');
                  }
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Errors} from '../helpers/Errors.sol';
              import {DataTypes} from '../types/DataTypes.sol';
              /**
               * @title ReserveConfiguration library
               * @author Aave
               * @notice Implements the bitmap logic to handle the reserve configuration
               */
              library ReserveConfiguration {
                uint256 constant LTV_MASK =                   0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000; // prettier-ignore
                uint256 constant LIQUIDATION_THRESHOLD_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF; // prettier-ignore
                uint256 constant LIQUIDATION_BONUS_MASK =     0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFF; // prettier-ignore
                uint256 constant DECIMALS_MASK =              0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFF; // prettier-ignore
                uint256 constant ACTIVE_MASK =                0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFF; // prettier-ignore
                uint256 constant FROZEN_MASK =                0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF; // prettier-ignore
                uint256 constant BORROWING_MASK =             0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFF; // prettier-ignore
                uint256 constant STABLE_BORROWING_MASK =      0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFF; // prettier-ignore
                uint256 constant RESERVE_FACTOR_MASK =        0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore
                /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed
                uint256 constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16;
                uint256 constant LIQUIDATION_BONUS_START_BIT_POSITION = 32;
                uint256 constant RESERVE_DECIMALS_START_BIT_POSITION = 48;
                uint256 constant IS_ACTIVE_START_BIT_POSITION = 56;
                uint256 constant IS_FROZEN_START_BIT_POSITION = 57;
                uint256 constant BORROWING_ENABLED_START_BIT_POSITION = 58;
                uint256 constant STABLE_BORROWING_ENABLED_START_BIT_POSITION = 59;
                uint256 constant RESERVE_FACTOR_START_BIT_POSITION = 64;
                uint256 constant MAX_VALID_LTV = 65535;
                uint256 constant MAX_VALID_LIQUIDATION_THRESHOLD = 65535;
                uint256 constant MAX_VALID_LIQUIDATION_BONUS = 65535;
                uint256 constant MAX_VALID_DECIMALS = 255;
                uint256 constant MAX_VALID_RESERVE_FACTOR = 65535;
                /**
                 * @dev Sets the Loan to Value of the reserve
                 * @param self The reserve configuration
                 * @param ltv the new ltv
                 **/
                function setLtv(DataTypes.ReserveConfigurationMap memory self, uint256 ltv) internal pure {
                  require(ltv <= MAX_VALID_LTV, Errors.RC_INVALID_LTV);
                  self.data = (self.data & LTV_MASK) | ltv;
                }
                /**
                 * @dev Gets the Loan to Value of the reserve
                 * @param self The reserve configuration
                 * @return The loan to value
                 **/
                function getLtv(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) {
                  return self.data & ~LTV_MASK;
                }
                /**
                 * @dev Sets the liquidation threshold of the reserve
                 * @param self The reserve configuration
                 * @param threshold The new liquidation threshold
                 **/
                function setLiquidationThreshold(DataTypes.ReserveConfigurationMap memory self, uint256 threshold)
                  internal
                  pure
                {
                  require(threshold <= MAX_VALID_LIQUIDATION_THRESHOLD, Errors.RC_INVALID_LIQ_THRESHOLD);
                  self.data =
                    (self.data & LIQUIDATION_THRESHOLD_MASK) |
                    (threshold << LIQUIDATION_THRESHOLD_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the liquidation threshold of the reserve
                 * @param self The reserve configuration
                 * @return The liquidation threshold
                 **/
                function getLiquidationThreshold(DataTypes.ReserveConfigurationMap storage self)
                  internal
                  view
                  returns (uint256)
                {
                  return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION;
                }
                /**
                 * @dev Sets the liquidation bonus of the reserve
                 * @param self The reserve configuration
                 * @param bonus The new liquidation bonus
                 **/
                function setLiquidationBonus(DataTypes.ReserveConfigurationMap memory self, uint256 bonus) internal pure {
                  require(bonus <= MAX_VALID_LIQUIDATION_BONUS, Errors.RC_INVALID_LIQ_BONUS);
                  self.data =
                    (self.data & LIQUIDATION_BONUS_MASK) |
                    (bonus << LIQUIDATION_BONUS_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the liquidation bonus of the reserve
                 * @param self The reserve configuration
                 * @return The liquidation bonus
                 **/
                function getLiquidationBonus(DataTypes.ReserveConfigurationMap storage self)
                  internal
                  view
                  returns (uint256)
                {
                  return (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION;
                }
                /**
                 * @dev Sets the decimals of the underlying asset of the reserve
                 * @param self The reserve configuration
                 * @param decimals The decimals
                 **/
                function setDecimals(DataTypes.ReserveConfigurationMap memory self, uint256 decimals) internal pure {
                  require(decimals <= MAX_VALID_DECIMALS, Errors.RC_INVALID_DECIMALS);
                  self.data = (self.data & DECIMALS_MASK) | (decimals << RESERVE_DECIMALS_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the decimals of the underlying asset of the reserve
                 * @param self The reserve configuration
                 * @return The decimals of the asset
                 **/
                function getDecimals(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) {
                  return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION;
                }
                /**
                 * @dev Sets the active state of the reserve
                 * @param self The reserve configuration
                 * @param active The active state
                 **/
                function setActive(DataTypes.ReserveConfigurationMap memory self, bool active) internal pure {
                  self.data =
                    (self.data & ACTIVE_MASK) |
                    (uint256(active ? 1 : 0) << IS_ACTIVE_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the active state of the reserve
                 * @param self The reserve configuration
                 * @return The active state
                 **/
                function getActive(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) {
                  return (self.data & ~ACTIVE_MASK) != 0;
                }
                /**
                 * @dev Sets the frozen state of the reserve
                 * @param self The reserve configuration
                 * @param frozen The frozen state
                 **/
                function setFrozen(DataTypes.ReserveConfigurationMap memory self, bool frozen) internal pure {
                  self.data =
                    (self.data & FROZEN_MASK) |
                    (uint256(frozen ? 1 : 0) << IS_FROZEN_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the frozen state of the reserve
                 * @param self The reserve configuration
                 * @return The frozen state
                 **/
                function getFrozen(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) {
                  return (self.data & ~FROZEN_MASK) != 0;
                }
                /**
                 * @dev Enables or disables borrowing on the reserve
                 * @param self The reserve configuration
                 * @param enabled True if the borrowing needs to be enabled, false otherwise
                 **/
                function setBorrowingEnabled(DataTypes.ReserveConfigurationMap memory self, bool enabled) internal pure {
                  self.data =
                    (self.data & BORROWING_MASK) |
                    (uint256(enabled ? 1 : 0) << BORROWING_ENABLED_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the borrowing state of the reserve
                 * @param self The reserve configuration
                 * @return The borrowing state
                 **/
                function getBorrowingEnabled(DataTypes.ReserveConfigurationMap storage self) internal view returns (bool) {
                  return (self.data & ~BORROWING_MASK) != 0;
                }
                /**
                 * @dev Enables or disables stable rate borrowing on the reserve
                 * @param self The reserve configuration
                 * @param enabled True if the stable rate borrowing needs to be enabled, false otherwise
                 **/
                function setStableRateBorrowingEnabled(DataTypes.ReserveConfigurationMap memory self, bool enabled)
                  internal
                  pure
                {
                  self.data =
                    (self.data & STABLE_BORROWING_MASK) |
                    (uint256(enabled ? 1 : 0) << STABLE_BORROWING_ENABLED_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the stable rate borrowing state of the reserve
                 * @param self The reserve configuration
                 * @return The stable rate borrowing state
                 **/
                function getStableRateBorrowingEnabled(DataTypes.ReserveConfigurationMap storage self)
                  internal
                  view
                  returns (bool)
                {
                  return (self.data & ~STABLE_BORROWING_MASK) != 0;
                }
                /**
                 * @dev Sets the reserve factor of the reserve
                 * @param self The reserve configuration
                 * @param reserveFactor The reserve factor
                 **/
                function setReserveFactor(DataTypes.ReserveConfigurationMap memory self, uint256 reserveFactor)
                  internal
                  pure
                {
                  require(reserveFactor <= MAX_VALID_RESERVE_FACTOR, Errors.RC_INVALID_RESERVE_FACTOR);
                  self.data =
                    (self.data & RESERVE_FACTOR_MASK) |
                    (reserveFactor << RESERVE_FACTOR_START_BIT_POSITION);
                }
                /**
                 * @dev Gets the reserve factor of the reserve
                 * @param self The reserve configuration
                 * @return The reserve factor
                 **/
                function getReserveFactor(DataTypes.ReserveConfigurationMap storage self) internal view returns (uint256) {
                  return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION;
                }
                /**
                 * @dev Gets the configuration flags of the reserve
                 * @param self The reserve configuration
                 * @return The state flags representing active, frozen, borrowing enabled, stableRateBorrowing enabled
                 **/
                function getFlags(DataTypes.ReserveConfigurationMap storage self)
                  internal
                  view
                  returns (
                    bool,
                    bool,
                    bool,
                    bool
                  )
                {
                  uint256 dataLocal = self.data;
                  return (
                    (dataLocal & ~ACTIVE_MASK) != 0,
                    (dataLocal & ~FROZEN_MASK) != 0,
                    (dataLocal & ~BORROWING_MASK) != 0,
                    (dataLocal & ~STABLE_BORROWING_MASK) != 0
                  );
                }
                /**
                 * @dev Gets the configuration paramters of the reserve
                 * @param self The reserve configuration
                 * @return The state params representing ltv, liquidation threshold, liquidation bonus, the reserve decimals
                 **/
                function getParams(DataTypes.ReserveConfigurationMap storage self)
                  internal
                  view
                  returns (
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256
                  )
                {
                  uint256 dataLocal = self.data;
                  return (
                    dataLocal & ~LTV_MASK,
                    (dataLocal & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION,
                    (dataLocal & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION,
                    (dataLocal & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION,
                    (dataLocal & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION
                  );
                }
                /**
                 * @dev Gets the configuration paramters of the reserve from a memory object
                 * @param self The reserve configuration
                 * @return The state params representing ltv, liquidation threshold, liquidation bonus, the reserve decimals
                 **/
                function getParamsMemory(DataTypes.ReserveConfigurationMap memory self)
                  internal
                  pure
                  returns (
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256
                  )
                {
                  return (
                    self.data & ~LTV_MASK,
                    (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION,
                    (self.data & ~LIQUIDATION_BONUS_MASK) >> LIQUIDATION_BONUS_START_BIT_POSITION,
                    (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION,
                    (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION
                  );
                }
                /**
                 * @dev Gets the configuration flags of the reserve from a memory object
                 * @param self The reserve configuration
                 * @return The state flags representing active, frozen, borrowing enabled, stableRateBorrowing enabled
                 **/
                function getFlagsMemory(DataTypes.ReserveConfigurationMap memory self)
                  internal
                  pure
                  returns (
                    bool,
                    bool,
                    bool,
                    bool
                  )
                {
                  return (
                    (self.data & ~ACTIVE_MASK) != 0,
                    (self.data & ~FROZEN_MASK) != 0,
                    (self.data & ~BORROWING_MASK) != 0,
                    (self.data & ~STABLE_BORROWING_MASK) != 0
                  );
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              library DataTypes {
                // refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties.
                struct ReserveData {
                  //stores the reserve configuration
                  ReserveConfigurationMap configuration;
                  //the liquidity index. Expressed in ray
                  uint128 liquidityIndex;
                  //variable borrow index. Expressed in ray
                  uint128 variableBorrowIndex;
                  //the current supply rate. Expressed in ray
                  uint128 currentLiquidityRate;
                  //the current variable borrow rate. Expressed in ray
                  uint128 currentVariableBorrowRate;
                  //the current stable borrow rate. Expressed in ray
                  uint128 currentStableBorrowRate;
                  uint40 lastUpdateTimestamp;
                  //tokens addresses
                  address aTokenAddress;
                  address stableDebtTokenAddress;
                  address variableDebtTokenAddress;
                  //address of the interest rate strategy
                  address interestRateStrategyAddress;
                  //the id of the reserve. Represents the position in the list of the active reserves
                  uint8 id;
                }
                struct ReserveConfigurationMap {
                  //bit 0-15: LTV
                  //bit 16-31: Liq. threshold
                  //bit 32-47: Liq. bonus
                  //bit 48-55: Decimals
                  //bit 56: Reserve is active
                  //bit 57: reserve is frozen
                  //bit 58: borrowing is enabled
                  //bit 59: stable rate borrowing enabled
                  //bit 60-63: reserved
                  //bit 64-79: reserve factor
                  uint256 data;
                }
                struct UserConfigurationMap {
                  uint256 data;
                }
                enum InterestRateMode {NONE, STABLE, VARIABLE}
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @dev Wrappers over Solidity's arithmetic operations with added overflow
               * checks.
               *
               * Arithmetic operations in Solidity wrap on overflow. This can easily result
               * in bugs, because programmers usually assume that an overflow raises an
               * error, which is the standard behavior in high level programming languages.
               * `SafeMath` restores this intuition by reverting the transaction when an
               * operation overflows.
               *
               * Using this library instead of the unchecked operations eliminates an entire
               * class of bugs, so it's recommended to use it always.
               */
              library SafeMath {
                /**
                 * @dev Returns the addition of two unsigned integers, reverting on
                 * overflow.
                 *
                 * Counterpart to Solidity's `+` operator.
                 *
                 * Requirements:
                 * - Addition cannot overflow.
                 */
                function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  require(c >= a, 'SafeMath: addition overflow');
                  return c;
                }
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting on
                 * overflow (when the result is negative).
                 *
                 * Counterpart to Solidity's `-` operator.
                 *
                 * Requirements:
                 * - Subtraction cannot overflow.
                 */
                function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  return sub(a, b, 'SafeMath: subtraction overflow');
                }
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                 * overflow (when the result is negative).
                 *
                 * Counterpart to Solidity's `-` operator.
                 *
                 * Requirements:
                 * - Subtraction cannot overflow.
                 */
                function sub(
                  uint256 a,
                  uint256 b,
                  string memory errorMessage
                ) internal pure returns (uint256) {
                  require(b <= a, errorMessage);
                  uint256 c = a - b;
                  return c;
                }
                /**
                 * @dev Returns the multiplication of two unsigned integers, reverting on
                 * overflow.
                 *
                 * Counterpart to Solidity's `*` operator.
                 *
                 * Requirements:
                 * - Multiplication cannot overflow.
                 */
                function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                  if (a == 0) {
                    return 0;
                  }
                  uint256 c = a * b;
                  require(c / a == b, 'SafeMath: multiplication overflow');
                  return c;
                }
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts on
                 * division by zero. The result is rounded towards zero.
                 *
                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                 * uses an invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  return div(a, b, 'SafeMath: division by zero');
                }
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                 * division by zero. The result is rounded towards zero.
                 *
                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                 * uses an invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function div(
                  uint256 a,
                  uint256 b,
                  string memory errorMessage
                ) internal pure returns (uint256) {
                  // Solidity only automatically asserts when dividing by 0
                  require(b > 0, errorMessage);
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  return c;
                }
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts when dividing by zero.
                 *
                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                 * invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                  return mod(a, b, 'SafeMath: modulo by zero');
                }
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts with custom message when dividing by zero.
                 *
                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                 * invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function mod(
                  uint256 a,
                  uint256 b,
                  string memory errorMessage
                ) internal pure returns (uint256) {
                  require(b != 0, errorMessage);
                  return a % b;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title Errors library
               * @author Aave
               * @notice Defines the error messages emitted by the different contracts of the Aave protocol
               * @dev Error messages prefix glossary:
               *  - VL = ValidationLogic
               *  - MATH = Math libraries
               *  - CT = Common errors between tokens (AToken, VariableDebtToken and StableDebtToken)
               *  - AT = AToken
               *  - SDT = StableDebtToken
               *  - VDT = VariableDebtToken
               *  - LP = LendingPool
               *  - LPAPR = LendingPoolAddressesProviderRegistry
               *  - LPC = LendingPoolConfiguration
               *  - RL = ReserveLogic
               *  - LPCM = LendingPoolCollateralManager
               *  - P = Pausable
               */
              library Errors {
                //common errors
                string public constant CALLER_NOT_POOL_ADMIN = '33'; // 'The caller must be the pool admin'
                string public constant BORROW_ALLOWANCE_NOT_ENOUGH = '59'; // User borrows on behalf, but allowance are too small
                //contract specific errors
                string public constant VL_INVALID_AMOUNT = '1'; // 'Amount must be greater than 0'
                string public constant VL_NO_ACTIVE_RESERVE = '2'; // 'Action requires an active reserve'
                string public constant VL_RESERVE_FROZEN = '3'; // 'Action cannot be performed because the reserve is frozen'
                string public constant VL_CURRENT_AVAILABLE_LIQUIDITY_NOT_ENOUGH = '4'; // 'The current liquidity is not enough'
                string public constant VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE = '5'; // 'User cannot withdraw more than the available balance'
                string public constant VL_TRANSFER_NOT_ALLOWED = '6'; // 'Transfer cannot be allowed.'
                string public constant VL_BORROWING_NOT_ENABLED = '7'; // 'Borrowing is not enabled'
                string public constant VL_INVALID_INTEREST_RATE_MODE_SELECTED = '8'; // 'Invalid interest rate mode selected'
                string public constant VL_COLLATERAL_BALANCE_IS_0 = '9'; // 'The collateral balance is 0'
                string public constant VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '10'; // 'Health factor is lesser than the liquidation threshold'
                string public constant VL_COLLATERAL_CANNOT_COVER_NEW_BORROW = '11'; // 'There is not enough collateral to cover a new borrow'
                string public constant VL_STABLE_BORROWING_NOT_ENABLED = '12'; // stable borrowing not enabled
                string public constant VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY = '13'; // collateral is (mostly) the same currency that is being borrowed
                string public constant VL_AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE = '14'; // 'The requested amount is greater than the max loan size in stable rate mode
                string public constant VL_NO_DEBT_OF_SELECTED_TYPE = '15'; // 'for repayment of stable debt, the user needs to have stable debt, otherwise, he needs to have variable debt'
                string public constant VL_NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '16'; // 'To repay on behalf of an user an explicit amount to repay is needed'
                string public constant VL_NO_STABLE_RATE_LOAN_IN_RESERVE = '17'; // 'User does not have a stable rate loan in progress on this reserve'
                string public constant VL_NO_VARIABLE_RATE_LOAN_IN_RESERVE = '18'; // 'User does not have a variable rate loan in progress on this reserve'
                string public constant VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0 = '19'; // 'The underlying balance needs to be greater than 0'
                string public constant VL_DEPOSIT_ALREADY_IN_USE = '20'; // 'User deposit is already being used as collateral'
                string public constant LP_NOT_ENOUGH_STABLE_BORROW_BALANCE = '21'; // 'User does not have any stable rate loan for this reserve'
                string public constant LP_INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '22'; // 'Interest rate rebalance conditions were not met'
                string public constant LP_LIQUIDATION_CALL_FAILED = '23'; // 'Liquidation call failed'
                string public constant LP_NOT_ENOUGH_LIQUIDITY_TO_BORROW = '24'; // 'There is not enough liquidity available to borrow'
                string public constant LP_REQUESTED_AMOUNT_TOO_SMALL = '25'; // 'The requested amount is too small for a FlashLoan.'
                string public constant LP_INCONSISTENT_PROTOCOL_ACTUAL_BALANCE = '26'; // 'The actual balance of the protocol is inconsistent'
                string public constant LP_CALLER_NOT_LENDING_POOL_CONFIGURATOR = '27'; // 'The caller of the function is not the lending pool configurator'
                string public constant LP_INCONSISTENT_FLASHLOAN_PARAMS = '28';
                string public constant CT_CALLER_MUST_BE_LENDING_POOL = '29'; // 'The caller of this function must be a lending pool'
                string public constant CT_CANNOT_GIVE_ALLOWANCE_TO_HIMSELF = '30'; // 'User cannot give allowance to himself'
                string public constant CT_TRANSFER_AMOUNT_NOT_GT_0 = '31'; // 'Transferred amount needs to be greater than zero'
                string public constant RL_RESERVE_ALREADY_INITIALIZED = '32'; // 'Reserve has already been initialized'
                string public constant LPC_RESERVE_LIQUIDITY_NOT_0 = '34'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_ATOKEN_POOL_ADDRESS = '35'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_STABLE_DEBT_TOKEN_POOL_ADDRESS = '36'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_VARIABLE_DEBT_TOKEN_POOL_ADDRESS = '37'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_STABLE_DEBT_TOKEN_UNDERLYING_ADDRESS = '38'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_VARIABLE_DEBT_TOKEN_UNDERLYING_ADDRESS = '39'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_ADDRESSES_PROVIDER_ID = '40'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_CONFIGURATION = '75'; // 'Invalid risk parameters for the reserve'
                string public constant LPC_CALLER_NOT_EMERGENCY_ADMIN = '76'; // 'The caller must be the emergency admin'
                string public constant LPAPR_PROVIDER_NOT_REGISTERED = '41'; // 'Provider is not registered'
                string public constant LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '42'; // 'Health factor is not below the threshold'
                string public constant LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED = '43'; // 'The collateral chosen cannot be liquidated'
                string public constant LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '44'; // 'User did not borrow the specified currency'
                string public constant LPCM_NOT_ENOUGH_LIQUIDITY_TO_LIQUIDATE = '45'; // "There isn't enough liquidity available to liquidate"
                string public constant LPCM_NO_ERRORS = '46'; // 'No errors'
                string public constant LP_INVALID_FLASHLOAN_MODE = '47'; //Invalid flashloan mode selected
                string public constant MATH_MULTIPLICATION_OVERFLOW = '48';
                string public constant MATH_ADDITION_OVERFLOW = '49';
                string public constant MATH_DIVISION_BY_ZERO = '50';
                string public constant RL_LIQUIDITY_INDEX_OVERFLOW = '51'; //  Liquidity index overflows uint128
                string public constant RL_VARIABLE_BORROW_INDEX_OVERFLOW = '52'; //  Variable borrow index overflows uint128
                string public constant RL_LIQUIDITY_RATE_OVERFLOW = '53'; //  Liquidity rate overflows uint128
                string public constant RL_VARIABLE_BORROW_RATE_OVERFLOW = '54'; //  Variable borrow rate overflows uint128
                string public constant RL_STABLE_BORROW_RATE_OVERFLOW = '55'; //  Stable borrow rate overflows uint128
                string public constant CT_INVALID_MINT_AMOUNT = '56'; //invalid amount to mint
                string public constant LP_FAILED_REPAY_WITH_COLLATERAL = '57';
                string public constant CT_INVALID_BURN_AMOUNT = '58'; //invalid amount to burn
                string public constant LP_FAILED_COLLATERAL_SWAP = '60';
                string public constant LP_INVALID_EQUAL_ASSETS_TO_SWAP = '61';
                string public constant LP_REENTRANCY_NOT_ALLOWED = '62';
                string public constant LP_CALLER_MUST_BE_AN_ATOKEN = '63';
                string public constant LP_IS_PAUSED = '64'; // 'Pool is paused'
                string public constant LP_NO_MORE_RESERVES_ALLOWED = '65';
                string public constant LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN = '66';
                string public constant RC_INVALID_LTV = '67';
                string public constant RC_INVALID_LIQ_THRESHOLD = '68';
                string public constant RC_INVALID_LIQ_BONUS = '69';
                string public constant RC_INVALID_DECIMALS = '70';
                string public constant RC_INVALID_RESERVE_FACTOR = '71';
                string public constant LPAPR_INVALID_ADDRESSES_PROVIDER_ID = '72';
                string public constant VL_INCONSISTENT_FLASHLOAN_PARAMS = '73';
                string public constant LP_INCONSISTENT_PARAMS_LENGTH = '74';
                string public constant UL_INVALID_INDEX = '77';
                string public constant LP_NOT_CONTRACT = '78';
                string public constant SDT_STABLE_DEBT_OVERFLOW = '79';
                string public constant SDT_BURN_EXCEEDS_BALANCE = '80';
                enum CollateralManagerErrors {
                  NO_ERROR,
                  NO_COLLATERAL_AVAILABLE,
                  COLLATERAL_CANNOT_BE_LIQUIDATED,
                  CURRRENCY_NOT_BORROWED,
                  HEALTH_FACTOR_ABOVE_THRESHOLD,
                  NOT_ENOUGH_LIQUIDITY,
                  NO_ACTIVE_RESERVE,
                  HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD,
                  INVALID_EQUAL_ASSETS_TO_SWAP,
                  FROZEN_RESERVE
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {SafeMath} from '../../dependencies/openzeppelin/contracts//SafeMath.sol';
              import {IERC20} from '../../dependencies/openzeppelin/contracts//IERC20.sol';
              import {IAToken} from '../../interfaces/IAToken.sol';
              import {IStableDebtToken} from '../../interfaces/IStableDebtToken.sol';
              import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol';
              import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol';
              import {ILendingPoolCollateralManager} from '../../interfaces/ILendingPoolCollateralManager.sol';
              import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
              import {GenericLogic} from '../libraries/logic/GenericLogic.sol';
              import {Helpers} from '../libraries/helpers/Helpers.sol';
              import {WadRayMath} from '../libraries/math/WadRayMath.sol';
              import {PercentageMath} from '../libraries/math/PercentageMath.sol';
              import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol';
              import {DataTypes} from '../libraries/types/DataTypes.sol';
              import {LendingPoolStorage} from './LendingPoolStorage.sol';
              /**
               * @title LendingPoolCollateralManager contract
               * @author Aave
               * @dev Implements actions involving management of collateral in the protocol, the main one being the liquidations
               * IMPORTANT This contract will run always via DELEGATECALL, through the LendingPool, so the chain of inheritance
               * is the same as the LendingPool, to have compatible storage layouts
               **/
              contract LendingPoolCollateralManager is
                ILendingPoolCollateralManager,
                VersionedInitializable,
                LendingPoolStorage
              {
                using SafeERC20 for IERC20;
                using SafeMath for uint256;
                using WadRayMath for uint256;
                using PercentageMath for uint256;
                uint256 internal constant LIQUIDATION_CLOSE_FACTOR_PERCENT = 5000;
                struct LiquidationCallLocalVars {
                  uint256 userCollateralBalance;
                  uint256 userStableDebt;
                  uint256 userVariableDebt;
                  uint256 maxLiquidatableDebt;
                  uint256 actualDebtToLiquidate;
                  uint256 liquidationRatio;
                  uint256 maxAmountCollateralToLiquidate;
                  uint256 userStableRate;
                  uint256 maxCollateralToLiquidate;
                  uint256 debtAmountNeeded;
                  uint256 healthFactor;
                  uint256 liquidatorPreviousATokenBalance;
                  IAToken collateralAtoken;
                  bool isCollateralEnabled;
                  DataTypes.InterestRateMode borrowRateMode;
                  uint256 errorCode;
                  string errorMsg;
                }
                /**
                 * @dev As thIS contract extends the VersionedInitializable contract to match the state
                 * of the LendingPool contract, the getRevision() function is needed, but the value is not
                 * important, as the initialize() function will never be called here
                 */
                function getRevision() internal pure override returns (uint256) {
                  return 0;
                }
                /**
                 * @dev Function to liquidate a position if its Health Factor drops below 1
                 * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
                 *   a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
                 * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                 * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                 * @param user The address of the borrower getting liquidated
                 * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                 * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
                 * to receive the underlying collateral asset directly
                 **/
                function liquidationCall(
                  address collateralAsset,
                  address debtAsset,
                  address user,
                  uint256 debtToCover,
                  bool receiveAToken
                ) external override returns (uint256, string memory) {
                  DataTypes.ReserveData storage collateralReserve = _reserves[collateralAsset];
                  DataTypes.ReserveData storage debtReserve = _reserves[debtAsset];
                  DataTypes.UserConfigurationMap storage userConfig = _usersConfig[user];
                  LiquidationCallLocalVars memory vars;
                  (, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
                    user,
                    _reserves,
                    userConfig,
                    _reservesList,
                    _reservesCount,
                    _addressesProvider.getPriceOracle()
                  );
                  (vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(user, debtReserve);
                  (vars.errorCode, vars.errorMsg) = ValidationLogic.validateLiquidationCall(
                    collateralReserve,
                    debtReserve,
                    userConfig,
                    vars.healthFactor,
                    vars.userStableDebt,
                    vars.userVariableDebt
                  );
                  if (Errors.CollateralManagerErrors(vars.errorCode) != Errors.CollateralManagerErrors.NO_ERROR) {
                    return (vars.errorCode, vars.errorMsg);
                  }
                  vars.collateralAtoken = IAToken(collateralReserve.aTokenAddress);
                  vars.userCollateralBalance = vars.collateralAtoken.balanceOf(user);
                  vars.maxLiquidatableDebt = vars.userStableDebt.add(vars.userVariableDebt).percentMul(
                    LIQUIDATION_CLOSE_FACTOR_PERCENT
                  );
                  vars.actualDebtToLiquidate = debtToCover > vars.maxLiquidatableDebt
                    ? vars.maxLiquidatableDebt
                    : debtToCover;
                  (
                    vars.maxCollateralToLiquidate,
                    vars.debtAmountNeeded
                  ) = _calculateAvailableCollateralToLiquidate(
                    collateralReserve,
                    debtReserve,
                    collateralAsset,
                    debtAsset,
                    vars.actualDebtToLiquidate,
                    vars.userCollateralBalance
                  );
                  // If debtAmountNeeded < actualDebtToLiquidate, there isn't enough
                  // collateral to cover the actual amount that is being liquidated, hence we liquidate
                  // a smaller amount
                  if (vars.debtAmountNeeded < vars.actualDebtToLiquidate) {
                    vars.actualDebtToLiquidate = vars.debtAmountNeeded;
                  }
                  // If the liquidator reclaims the underlying asset, we make sure there is enough available liquidity in the
                  // collateral reserve
                  if (!receiveAToken) {
                    uint256 currentAvailableCollateral =
                      IERC20(collateralAsset).balanceOf(address(vars.collateralAtoken));
                    if (currentAvailableCollateral < vars.maxCollateralToLiquidate) {
                      return (
                        uint256(Errors.CollateralManagerErrors.NOT_ENOUGH_LIQUIDITY),
                        Errors.LPCM_NOT_ENOUGH_LIQUIDITY_TO_LIQUIDATE
                      );
                    }
                  }
                  debtReserve.updateState();
                  if (vars.userVariableDebt >= vars.actualDebtToLiquidate) {
                    IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
                      user,
                      vars.actualDebtToLiquidate,
                      debtReserve.variableBorrowIndex
                    );
                  } else {
                    // If the user doesn't have variable debt, no need to try to burn variable debt tokens
                    if (vars.userVariableDebt > 0) {
                      IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
                        user,
                        vars.userVariableDebt,
                        debtReserve.variableBorrowIndex
                      );
                    }
                    IStableDebtToken(debtReserve.stableDebtTokenAddress).burn(
                      user,
                      vars.actualDebtToLiquidate.sub(vars.userVariableDebt)
                    );
                  }
                  debtReserve.updateInterestRates(
                    debtAsset,
                    debtReserve.aTokenAddress,
                    vars.actualDebtToLiquidate,
                    0
                  );
                  if (receiveAToken) {
                    vars.liquidatorPreviousATokenBalance = IERC20(vars.collateralAtoken).balanceOf(msg.sender);
                    vars.collateralAtoken.transferOnLiquidation(user, msg.sender, vars.maxCollateralToLiquidate);
                    if (vars.liquidatorPreviousATokenBalance == 0) {
                      DataTypes.UserConfigurationMap storage liquidatorConfig = _usersConfig[msg.sender];
                      liquidatorConfig.setUsingAsCollateral(collateralReserve.id, true);
                      emit ReserveUsedAsCollateralEnabled(collateralAsset, msg.sender);
                    }
                  } else {
                    collateralReserve.updateState();
                    collateralReserve.updateInterestRates(
                      collateralAsset,
                      address(vars.collateralAtoken),
                      0,
                      vars.maxCollateralToLiquidate
                    );
                    // Burn the equivalent amount of aToken, sending the underlying to the liquidator
                    vars.collateralAtoken.burn(
                      user,
                      msg.sender,
                      vars.maxCollateralToLiquidate,
                      collateralReserve.liquidityIndex
                    );
                  }
                  // If the collateral being liquidated is equal to the user balance,
                  // we set the currency as not being used as collateral anymore
                  if (vars.maxCollateralToLiquidate == vars.userCollateralBalance) {
                    userConfig.setUsingAsCollateral(collateralReserve.id, false);
                    emit ReserveUsedAsCollateralDisabled(collateralAsset, user);
                  }
                  // Transfers the debt asset being repaid to the aToken, where the liquidity is kept
                  IERC20(debtAsset).safeTransferFrom(
                    msg.sender,
                    debtReserve.aTokenAddress,
                    vars.actualDebtToLiquidate
                  );
                  emit LiquidationCall(
                    collateralAsset,
                    debtAsset,
                    user,
                    vars.actualDebtToLiquidate,
                    vars.maxCollateralToLiquidate,
                    msg.sender,
                    receiveAToken
                  );
                  return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.LPCM_NO_ERRORS);
                }
                struct AvailableCollateralToLiquidateLocalVars {
                  uint256 userCompoundedBorrowBalance;
                  uint256 liquidationBonus;
                  uint256 collateralPrice;
                  uint256 debtAssetPrice;
                  uint256 maxAmountCollateralToLiquidate;
                  uint256 debtAssetDecimals;
                  uint256 collateralDecimals;
                }
                /**
                 * @dev Calculates how much of a specific collateral can be liquidated, given
                 * a certain amount of debt asset.
                 * - This function needs to be called after all the checks to validate the liquidation have been performed,
                 *   otherwise it might fail.
                 * @param collateralReserve The data of the collateral reserve
                 * @param debtReserve The data of the debt reserve
                 * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                 * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                 * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                 * @param userCollateralBalance The collateral balance for the specific `collateralAsset` of the user being liquidated
                 * @return collateralAmount: The maximum amount that is possible to liquidate given all the liquidation constraints
                 *                           (user balance, close factor)
                 *         debtAmountNeeded: The amount to repay with the liquidation
                 **/
                function _calculateAvailableCollateralToLiquidate(
                  DataTypes.ReserveData storage collateralReserve,
                  DataTypes.ReserveData storage debtReserve,
                  address collateralAsset,
                  address debtAsset,
                  uint256 debtToCover,
                  uint256 userCollateralBalance
                ) internal view returns (uint256, uint256) {
                  uint256 collateralAmount = 0;
                  uint256 debtAmountNeeded = 0;
                  IPriceOracleGetter oracle = IPriceOracleGetter(_addressesProvider.getPriceOracle());
                  AvailableCollateralToLiquidateLocalVars memory vars;
                  vars.collateralPrice = oracle.getAssetPrice(collateralAsset);
                  vars.debtAssetPrice = oracle.getAssetPrice(debtAsset);
                  (, , vars.liquidationBonus, vars.collateralDecimals, ) = collateralReserve
                    .configuration
                    .getParams();
                  vars.debtAssetDecimals = debtReserve.configuration.getDecimals();
                  // This is the maximum possible amount of the selected collateral that can be liquidated, given the
                  // max amount of liquidatable debt
                  vars.maxAmountCollateralToLiquidate = vars
                    .debtAssetPrice
                    .mul(debtToCover)
                    .mul(10**vars.collateralDecimals)
                    .percentMul(vars.liquidationBonus)
                    .div(vars.collateralPrice.mul(10**vars.debtAssetDecimals));
                  if (vars.maxAmountCollateralToLiquidate > userCollateralBalance) {
                    collateralAmount = userCollateralBalance;
                    debtAmountNeeded = vars
                      .collateralPrice
                      .mul(collateralAmount)
                      .mul(10**vars.debtAssetDecimals)
                      .div(vars.debtAssetPrice.mul(10**vars.collateralDecimals))
                      .percentDiv(vars.liquidationBonus);
                  } else {
                    collateralAmount = vars.maxAmountCollateralToLiquidate;
                    debtAmountNeeded = debtToCover;
                  }
                  return (collateralAmount, debtAmountNeeded);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
              import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
              interface IAToken is IERC20, IScaledBalanceToken {
                /**
                 * @dev Emitted after the mint action
                 * @param from The address performing the mint
                 * @param value The amount being
                 * @param index The new liquidity index of the reserve
                 **/
                event Mint(address indexed from, uint256 value, uint256 index);
                /**
                 * @dev Mints `amount` aTokens to `user`
                 * @param user The address receiving the minted tokens
                 * @param amount The amount of tokens getting minted
                 * @param index The new liquidity index of the reserve
                 * @return `true` if the the previous balance of the user was 0
                 */
                function mint(
                  address user,
                  uint256 amount,
                  uint256 index
                ) external returns (bool);
                /**
                 * @dev Emitted after aTokens are burned
                 * @param from The owner of the aTokens, getting them burned
                 * @param target The address that will receive the underlying
                 * @param value The amount being burned
                 * @param index The new liquidity index of the reserve
                 **/
                event Burn(address indexed from, address indexed target, uint256 value, uint256 index);
                /**
                 * @dev Emitted during the transfer action
                 * @param from The user whose tokens are being transferred
                 * @param to The recipient
                 * @param value The amount being transferred
                 * @param index The new liquidity index of the reserve
                 **/
                event BalanceTransfer(address indexed from, address indexed to, uint256 value, uint256 index);
                /**
                 * @dev Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying`
                 * @param user The owner of the aTokens, getting them burned
                 * @param receiverOfUnderlying The address that will receive the underlying
                 * @param amount The amount being burned
                 * @param index The new liquidity index of the reserve
                 **/
                function burn(
                  address user,
                  address receiverOfUnderlying,
                  uint256 amount,
                  uint256 index
                ) external;
                /**
                 * @dev Mints aTokens to the reserve treasury
                 * @param amount The amount of tokens getting minted
                 * @param index The new liquidity index of the reserve
                 */
                function mintToTreasury(uint256 amount, uint256 index) external;
                /**
                 * @dev Transfers aTokens in the event of a borrow being liquidated, in case the liquidators reclaims the aToken
                 * @param from The address getting liquidated, current owner of the aTokens
                 * @param to The recipient
                 * @param value The amount of tokens getting transferred
                 **/
                function transferOnLiquidation(
                  address from,
                  address to,
                  uint256 value
                ) external;
                /**
                 * @dev Transfers the underlying asset to `target`. Used by the LendingPool to transfer
                 * assets in borrow(), withdraw() and flashLoan()
                 * @param user The recipient of the aTokens
                 * @param amount The amount getting transferred
                 * @return The amount transferred
                 **/
                function transferUnderlyingTo(address user, uint256 amount) external returns (uint256);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title IStableDebtToken
               * @notice Defines the interface for the stable debt token
               * @dev It does not inherit from IERC20 to save in code size
               * @author Aave
               **/
              interface IStableDebtToken {
                /**
                 * @dev Emitted when new stable debt is minted
                 * @param user The address of the user who triggered the minting
                 * @param onBehalfOf The recipient of stable debt tokens
                 * @param amount The amount minted
                 * @param currentBalance The current balance of the user
                 * @param balanceIncrease The increase in balance since the last action of the user
                 * @param newRate The rate of the debt after the minting
                 * @param avgStableRate The new average stable rate after the minting
                 * @param newTotalSupply The new total supply of the stable debt token after the action
                 **/
                event Mint(
                  address indexed user,
                  address indexed onBehalfOf,
                  uint256 amount,
                  uint256 currentBalance,
                  uint256 balanceIncrease,
                  uint256 newRate,
                  uint256 avgStableRate,
                  uint256 newTotalSupply
                );
                /**
                 * @dev Emitted when new stable debt is burned
                 * @param user The address of the user
                 * @param amount The amount being burned
                 * @param currentBalance The current balance of the user
                 * @param balanceIncrease The the increase in balance since the last action of the user
                 * @param avgStableRate The new average stable rate after the burning
                 * @param newTotalSupply The new total supply of the stable debt token after the action
                 **/
                event Burn(
                  address indexed user,
                  uint256 amount,
                  uint256 currentBalance,
                  uint256 balanceIncrease,
                  uint256 avgStableRate,
                  uint256 newTotalSupply
                );
                /**
                 * @dev Mints debt token to the `onBehalfOf` address.
                 * - The resulting rate is the weighted average between the rate of the new debt
                 * and the rate of the previous debt
                 * @param user The address receiving the borrowed underlying, being the delegatee in case
                 * of credit delegate, or same as `onBehalfOf` otherwise
                 * @param onBehalfOf The address receiving the debt tokens
                 * @param amount The amount of debt tokens to mint
                 * @param rate The rate of the debt being minted
                 **/
                function mint(
                  address user,
                  address onBehalfOf,
                  uint256 amount,
                  uint256 rate
                ) external returns (bool);
                /**
                 * @dev Burns debt of `user`
                 * - The resulting rate is the weighted average between the rate of the new debt
                 * and the rate of the previous debt
                 * @param user The address of the user getting his debt burned
                 * @param amount The amount of debt tokens getting burned
                 **/
                function burn(address user, uint256 amount) external;
                /**
                 * @dev Returns the average rate of all the stable rate loans.
                 * @return The average stable rate
                 **/
                function getAverageStableRate() external view returns (uint256);
                /**
                 * @dev Returns the stable rate of the user debt
                 * @return The stable rate of the user
                 **/
                function getUserStableRate(address user) external view returns (uint256);
                /**
                 * @dev Returns the timestamp of the last update of the user
                 * @return The timestamp
                 **/
                function getUserLastUpdated(address user) external view returns (uint40);
                /**
                 * @dev Returns the principal, the total supply and the average stable rate
                 **/
                function getSupplyData()
                  external
                  view
                  returns (
                    uint256,
                    uint256,
                    uint256,
                    uint40
                  );
                /**
                 * @dev Returns the timestamp of the last update of the total supply
                 * @return The timestamp
                 **/
                function getTotalSupplyLastUpdated() external view returns (uint40);
                /**
                 * @dev Returns the total supply and the average stable rate
                 **/
                function getTotalSupplyAndAvgRate() external view returns (uint256, uint256);
                /**
                 * @dev Returns the principal debt balance of the user
                 * @return The debt balance of the user since the last burn/mint action
                 **/
                function principalBalanceOf(address user) external view returns (uint256);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
              /**
               * @title IVariableDebtToken
               * @author Aave
               * @notice Defines the basic interface for a variable debt token.
               **/
              interface IVariableDebtToken is IScaledBalanceToken {
                /**
                 * @dev Emitted after the mint action
                 * @param from The address performing the mint
                 * @param onBehalfOf The address of the user on which behalf minting has been performed
                 * @param value The amount to be minted
                 * @param index The last index of the reserve
                 **/
                event Mint(address indexed from, address indexed onBehalfOf, uint256 value, uint256 index);
                /**
                 * @dev Mints debt token to the `onBehalfOf` address
                 * @param user The address receiving the borrowed underlying, being the delegatee in case
                 * of credit delegate, or same as `onBehalfOf` otherwise
                 * @param onBehalfOf The address receiving the debt tokens
                 * @param amount The amount of debt being minted
                 * @param index The variable debt index of the reserve
                 * @return `true` if the the previous balance of the user is 0
                 **/
                function mint(
                  address user,
                  address onBehalfOf,
                  uint256 amount,
                  uint256 index
                ) external returns (bool);
                /**
                 * @dev Emitted when variable debt is burnt
                 * @param user The user which debt has been burned
                 * @param amount The amount of debt being burned
                 * @param index The index of the user
                 **/
                event Burn(address indexed user, uint256 amount, uint256 index);
                /**
                 * @dev Burns user variable debt
                 * @param user The user which debt is burnt
                 * @param index The variable debt index of the reserve
                 **/
                function burn(
                  address user,
                  uint256 amount,
                  uint256 index
                ) external;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @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);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title ILendingPoolCollateralManager
               * @author Aave
               * @notice Defines the actions involving management of collateral in the protocol.
               **/
              interface ILendingPoolCollateralManager {
                /**
                 * @dev Emitted when a borrower is liquidated
                 * @param collateral The address of the collateral being liquidated
                 * @param principal The address of the reserve
                 * @param user The address of the user being liquidated
                 * @param debtToCover The total amount liquidated
                 * @param liquidatedCollateralAmount The amount of collateral being liquidated
                 * @param liquidator The address of the liquidator
                 * @param receiveAToken true if the liquidator wants to receive aTokens, false otherwise
                 **/
                event LiquidationCall(
                  address indexed collateral,
                  address indexed principal,
                  address indexed user,
                  uint256 debtToCover,
                  uint256 liquidatedCollateralAmount,
                  address liquidator,
                  bool receiveAToken
                );
                /**
                 * @dev Emitted when a reserve is disabled as collateral for an user
                 * @param reserve The address of the reserve
                 * @param user The address of the user
                 **/
                event ReserveUsedAsCollateralDisabled(address indexed reserve, address indexed user);
                /**
                 * @dev Emitted when a reserve is enabled as collateral for an user
                 * @param reserve The address of the reserve
                 * @param user The address of the user
                 **/
                event ReserveUsedAsCollateralEnabled(address indexed reserve, address indexed user);
                /**
                 * @dev Users can invoke this function to liquidate an undercollateralized position.
                 * @param collateral The address of the collateral to liquidated
                 * @param principal The address of the principal reserve
                 * @param user The address of the borrower
                 * @param debtToCover 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 principal,
                  address user,
                  uint256 debtToCover,
                  bool receiveAToken
                ) external returns (uint256, string memory);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title VersionedInitializable
               *
               * @dev Helper contract to implement 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 pure virtual 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;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {ReserveLogic} from './ReserveLogic.sol';
              import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
              import {UserConfiguration} from '../configuration/UserConfiguration.sol';
              import {WadRayMath} from '../math/WadRayMath.sol';
              import {PercentageMath} from '../math/PercentageMath.sol';
              import {IPriceOracleGetter} from '../../../interfaces/IPriceOracleGetter.sol';
              import {DataTypes} from '../types/DataTypes.sol';
              /**
               * @title GenericLogic library
               * @author Aave
               * @title Implements protocol-level logic to calculate and validate the state of a user
               */
              library GenericLogic {
                using ReserveLogic for DataTypes.ReserveData;
                using SafeMath for uint256;
                using WadRayMath for uint256;
                using PercentageMath for uint256;
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                using UserConfiguration for DataTypes.UserConfigurationMap;
                uint256 public constant HEALTH_FACTOR_LIQUIDATION_THRESHOLD = 1 ether;
                struct balanceDecreaseAllowedLocalVars {
                  uint256 decimals;
                  uint256 liquidationThreshold;
                  uint256 totalCollateralInETH;
                  uint256 totalDebtInETH;
                  uint256 avgLiquidationThreshold;
                  uint256 amountToDecreaseInETH;
                  uint256 collateralBalanceAfterDecrease;
                  uint256 liquidationThresholdAfterDecrease;
                  uint256 healthFactorAfterDecrease;
                  bool reserveUsageAsCollateralEnabled;
                }
                /**
                 * @dev Checks if a specific balance decrease is allowed
                 * (i.e. doesn't bring the user borrow position health factor under HEALTH_FACTOR_LIQUIDATION_THRESHOLD)
                 * @param asset The address of the underlying asset of the reserve
                 * @param user The address of the user
                 * @param amount The amount to decrease
                 * @param reservesData The data of all the reserves
                 * @param userConfig The user configuration
                 * @param reserves The list of all the active reserves
                 * @param oracle The address of the oracle contract
                 * @return true if the decrease of the balance is allowed
                 **/
                function balanceDecreaseAllowed(
                  address asset,
                  address user,
                  uint256 amount,
                  mapping(address => DataTypes.ReserveData) storage reservesData,
                  DataTypes.UserConfigurationMap calldata userConfig,
                  mapping(uint256 => address) storage reserves,
                  uint256 reservesCount,
                  address oracle
                ) external view returns (bool) {
                  if (!userConfig.isBorrowingAny() || !userConfig.isUsingAsCollateral(reservesData[asset].id)) {
                    return true;
                  }
                  
                  balanceDecreaseAllowedLocalVars memory vars;
                  (, vars.liquidationThreshold, , vars.decimals, ) = reservesData[asset]
                    .configuration
                    .getParams();
                  if (vars.liquidationThreshold == 0) {
                    return true; 
                  }
                  (
                    vars.totalCollateralInETH,
                    vars.totalDebtInETH,
                    ,
                    vars.avgLiquidationThreshold,
                  ) = calculateUserAccountData(user, reservesData, userConfig, reserves, reservesCount, oracle);
                  if (vars.totalDebtInETH == 0) {
                    return true;
                  }
                  vars.amountToDecreaseInETH = IPriceOracleGetter(oracle).getAssetPrice(asset).mul(amount).div(
                    10**vars.decimals
                  );
                  vars.collateralBalanceAfterDecrease = vars.totalCollateralInETH.sub(vars.amountToDecreaseInETH);
                  //if there is a borrow, there can't be 0 collateral
                  if (vars.collateralBalanceAfterDecrease == 0) {
                    return false;
                  }
                  vars.liquidationThresholdAfterDecrease = vars
                    .totalCollateralInETH
                    .mul(vars.avgLiquidationThreshold)
                    .sub(vars.amountToDecreaseInETH.mul(vars.liquidationThreshold))
                    .div(vars.collateralBalanceAfterDecrease);
                  uint256 healthFactorAfterDecrease =
                    calculateHealthFactorFromBalances(
                      vars.collateralBalanceAfterDecrease,
                      vars.totalDebtInETH,
                      vars.liquidationThresholdAfterDecrease
                    );
                  return healthFactorAfterDecrease >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD;
                }
                struct CalculateUserAccountDataVars {
                  uint256 reserveUnitPrice;
                  uint256 tokenUnit;
                  uint256 compoundedLiquidityBalance;
                  uint256 compoundedBorrowBalance;
                  uint256 decimals;
                  uint256 ltv;
                  uint256 liquidationThreshold;
                  uint256 i;
                  uint256 healthFactor;
                  uint256 totalCollateralInETH;
                  uint256 totalDebtInETH;
                  uint256 avgLtv;
                  uint256 avgLiquidationThreshold;
                  uint256 reservesLength;
                  bool healthFactorBelowThreshold;
                  address currentReserveAddress;
                  bool usageAsCollateralEnabled;
                  bool userUsesReserveAsCollateral;
                }
                /**
                 * @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
                 * @param reservesData Data of all the reserves
                 * @param userConfig The configuration of the user
                 * @param reserves The list of the available reserves
                 * @param oracle The price oracle address
                 * @return The total collateral and total debt of the user in ETH, the avg ltv, liquidation threshold and the HF
                 **/
                function calculateUserAccountData(
                  address user,
                  mapping(address => DataTypes.ReserveData) storage reservesData,
                  DataTypes.UserConfigurationMap memory userConfig,
                  mapping(uint256 => address) storage reserves,
                  uint256 reservesCount,
                  address oracle
                )
                  internal
                  view
                  returns (
                    uint256,
                    uint256,
                    uint256,
                    uint256,
                    uint256
                  )
                {
                  CalculateUserAccountDataVars memory vars;
                  if (userConfig.isEmpty()) {
                    return (0, 0, 0, 0, uint256(-1));
                  }
                  for (vars.i = 0; vars.i < reservesCount; vars.i++) {
                    if (!userConfig.isUsingAsCollateralOrBorrowing(vars.i)) {
                      continue;
                    }
                    vars.currentReserveAddress = reserves[vars.i];
                    DataTypes.ReserveData storage currentReserve = reservesData[vars.currentReserveAddress];
                    (vars.ltv, vars.liquidationThreshold, , vars.decimals, ) = currentReserve
                      .configuration
                      .getParams();
                    vars.tokenUnit = 10**vars.decimals;
                    vars.reserveUnitPrice = IPriceOracleGetter(oracle).getAssetPrice(vars.currentReserveAddress);
                    if (vars.liquidationThreshold != 0 && userConfig.isUsingAsCollateral(vars.i)) {
                      vars.compoundedLiquidityBalance = IERC20(currentReserve.aTokenAddress).balanceOf(user);
                      uint256 liquidityBalanceETH =
                        vars.reserveUnitPrice.mul(vars.compoundedLiquidityBalance).div(vars.tokenUnit);
                      vars.totalCollateralInETH = vars.totalCollateralInETH.add(liquidityBalanceETH);
                      vars.avgLtv = vars.avgLtv.add(liquidityBalanceETH.mul(vars.ltv));
                      vars.avgLiquidationThreshold = vars.avgLiquidationThreshold.add(
                        liquidityBalanceETH.mul(vars.liquidationThreshold)
                      );
                    }
                    if (userConfig.isBorrowing(vars.i)) {
                      vars.compoundedBorrowBalance = IERC20(currentReserve.stableDebtTokenAddress).balanceOf(
                        user
                      );
                      vars.compoundedBorrowBalance = vars.compoundedBorrowBalance.add(
                        IERC20(currentReserve.variableDebtTokenAddress).balanceOf(user)
                      );
                      vars.totalDebtInETH = vars.totalDebtInETH.add(
                        vars.reserveUnitPrice.mul(vars.compoundedBorrowBalance).div(vars.tokenUnit)
                      );
                    }
                  }
                  vars.avgLtv = vars.totalCollateralInETH > 0
                    ? vars.avgLtv.div(vars.totalCollateralInETH)
                    : 0;
                  vars.avgLiquidationThreshold = vars.totalCollateralInETH > 0
                    ? vars.avgLiquidationThreshold.div(vars.totalCollateralInETH)
                    : 0;
                  vars.healthFactor = calculateHealthFactorFromBalances(
                    vars.totalCollateralInETH,
                    vars.totalDebtInETH,
                    vars.avgLiquidationThreshold
                  );
                  return (
                    vars.totalCollateralInETH,
                    vars.totalDebtInETH,
                    vars.avgLtv,
                    vars.avgLiquidationThreshold,
                    vars.healthFactor
                  );
                }
                /**
                 * @dev Calculates the health factor from the corresponding balances
                 * @param totalCollateralInETH The total collateral in ETH
                 * @param totalDebtInETH The total debt in ETH
                 * @param liquidationThreshold The avg liquidation threshold
                 * @return The health factor calculated from the balances provided
                 **/
                function calculateHealthFactorFromBalances(
                  uint256 totalCollateralInETH,
                  uint256 totalDebtInETH,
                  uint256 liquidationThreshold
                ) internal pure returns (uint256) {
                  if (totalDebtInETH == 0) return uint256(-1);
                  return (totalCollateralInETH.percentMul(liquidationThreshold)).wadDiv(totalDebtInETH);
                }
                /**
                 * @dev Calculates the equivalent amount in ETH that an user can borrow, depending on the available collateral and the
                 * average Loan To Value
                 * @param totalCollateralInETH The total collateral in ETH
                 * @param totalDebtInETH The total borrow balance
                 * @param ltv The average loan to value
                 * @return the amount available to borrow in ETH for the user
                 **/
                function calculateAvailableBorrowsETH(
                  uint256 totalCollateralInETH,
                  uint256 totalDebtInETH,
                  uint256 ltv
                ) internal pure returns (uint256) {
                  
                  uint256 availableBorrowsETH = totalCollateralInETH.percentMul(ltv); 
                  if (availableBorrowsETH < totalDebtInETH) {
                    return 0;
                  }
                  availableBorrowsETH = availableBorrowsETH.sub(totalDebtInETH);
                  return availableBorrowsETH;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {DataTypes} from '../types/DataTypes.sol';
              /**
               * @title Helpers library
               * @author Aave
               */
              library Helpers {
                /**
                 * @dev Fetches the user current stable and variable debt balances
                 * @param user The user address
                 * @param reserve The reserve data object
                 * @return The stable and variable debt balance
                 **/
                function getUserCurrentDebt(address user, DataTypes.ReserveData storage reserve)
                  internal
                  view
                  returns (uint256, uint256)
                {
                  return (
                    IERC20(reserve.stableDebtTokenAddress).balanceOf(user),
                    IERC20(reserve.variableDebtTokenAddress).balanceOf(user)
                  );
                }
                function getUserCurrentDebtMemory(address user, DataTypes.ReserveData memory reserve)
                  internal
                  view
                  returns (uint256, uint256)
                {
                  return (
                    IERC20(reserve.stableDebtTokenAddress).balanceOf(user),
                    IERC20(reserve.variableDebtTokenAddress).balanceOf(user)
                  );
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Errors} from '../helpers/Errors.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 {
                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) {
                  if (a == 0 || b == 0) {
                    return 0;
                  }
                  require(a <= (type(uint256).max - halfWAD) / b, Errors.MATH_MULTIPLICATION_OVERFLOW);
                  return (a * b + halfWAD) / 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) {
                  require(b != 0, Errors.MATH_DIVISION_BY_ZERO);
                  uint256 halfB = b / 2;
                  require(a <= (type(uint256).max - halfB) / WAD, Errors.MATH_MULTIPLICATION_OVERFLOW);
                  return (a * WAD + halfB) / 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) {
                  if (a == 0 || b == 0) {
                    return 0;
                  }
                  require(a <= (type(uint256).max - halfRAY) / b, Errors.MATH_MULTIPLICATION_OVERFLOW);
                  return (a * b + halfRAY) / 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) {
                  require(b != 0, Errors.MATH_DIVISION_BY_ZERO);
                  uint256 halfB = b / 2;
                  require(a <= (type(uint256).max - halfB) / RAY, Errors.MATH_MULTIPLICATION_OVERFLOW);
                  return (a * RAY + halfB) / 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;
                  uint256 result = halfRatio + a;
                  require(result >= halfRatio, Errors.MATH_ADDITION_OVERFLOW);
                  return result / WAD_RAY_RATIO;
                }
                /**
                 * @dev Converts wad up to ray
                 * @param a Wad
                 * @return a converted in ray
                 **/
                function wadToRay(uint256 a) internal pure returns (uint256) {
                  uint256 result = a * WAD_RAY_RATIO;
                  require(result / WAD_RAY_RATIO == a, Errors.MATH_MULTIPLICATION_OVERFLOW);
                  return result;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Errors} from '../helpers/Errors.sol';
              /**
               * @title PercentageMath library
               * @author Aave
               * @notice Provides functions to perform percentage calculations
               * @dev Percentages are defined by default with 2 decimals of precision (100.00). The precision is indicated by PERCENTAGE_FACTOR
               * @dev Operations are rounded half up
               **/
              library PercentageMath {
                uint256 constant PERCENTAGE_FACTOR = 1e4; //percentage plus two decimals
                uint256 constant HALF_PERCENT = PERCENTAGE_FACTOR / 2;
                /**
                 * @dev Executes a percentage multiplication
                 * @param value The value of which the percentage needs to be calculated
                 * @param percentage The percentage of the value to be calculated
                 * @return The percentage of value
                 **/
                function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256) {
                  if (value == 0 || percentage == 0) {
                    return 0;
                  }
                  require(
                    value <= (type(uint256).max - HALF_PERCENT) / percentage,
                    Errors.MATH_MULTIPLICATION_OVERFLOW
                  );
                  return (value * percentage + HALF_PERCENT) / PERCENTAGE_FACTOR;
                }
                /**
                 * @dev Executes a percentage division
                 * @param value The value of which the percentage needs to be calculated
                 * @param percentage The percentage of the value to be calculated
                 * @return The value divided the percentage
                 **/
                function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256) {
                  require(percentage != 0, Errors.MATH_DIVISION_BY_ZERO);
                  uint256 halfPercentage = percentage / 2;
                  require(
                    value <= (type(uint256).max - halfPercentage) / PERCENTAGE_FACTOR,
                    Errors.MATH_MULTIPLICATION_OVERFLOW
                  );
                  return (value * PERCENTAGE_FACTOR + halfPercentage) / percentage;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {ReserveLogic} from './ReserveLogic.sol';
              import {GenericLogic} from './GenericLogic.sol';
              import {WadRayMath} from '../math/WadRayMath.sol';
              import {PercentageMath} from '../math/PercentageMath.sol';
              import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
              import {UserConfiguration} from '../configuration/UserConfiguration.sol';
              import {Errors} from '../helpers/Errors.sol';
              import {Helpers} from '../helpers/Helpers.sol';
              import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol';
              import {DataTypes} from '../types/DataTypes.sol';
              /**
               * @title ReserveLogic library
               * @author Aave
               * @notice Implements functions to validate the different actions of the protocol
               */
              library ValidationLogic {
                using ReserveLogic for DataTypes.ReserveData;
                using SafeMath for uint256;
                using WadRayMath for uint256;
                using PercentageMath for uint256;
                using SafeERC20 for IERC20;
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                using UserConfiguration for DataTypes.UserConfigurationMap;
                uint256 public constant REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD = 4000;
                uint256 public constant REBALANCE_UP_USAGE_RATIO_THRESHOLD = 0.95 * 1e27; //usage ratio of 95%
                /**
                 * @dev Validates a deposit action
                 * @param reserve The reserve object on which the user is depositing
                 * @param amount The amount to be deposited
                 */
                function validateDeposit(DataTypes.ReserveData storage reserve, uint256 amount) external view {
                  (bool isActive, bool isFrozen, , ) = reserve.configuration.getFlags();
                  require(amount != 0, Errors.VL_INVALID_AMOUNT);
                  require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
                  require(!isFrozen, Errors.VL_RESERVE_FROZEN);
                }
                /**
                 * @dev Validates a withdraw action
                 * @param reserveAddress The address of the reserve
                 * @param amount The amount to be withdrawn
                 * @param userBalance The balance of the user
                 * @param reservesData The reserves state
                 * @param userConfig The user configuration
                 * @param reserves The addresses of the reserves
                 * @param reservesCount The number of reserves
                 * @param oracle The price oracle
                 */
                function validateWithdraw(
                  address reserveAddress,
                  uint256 amount,
                  uint256 userBalance,
                  mapping(address => DataTypes.ReserveData) storage reservesData,
                  DataTypes.UserConfigurationMap storage userConfig,
                  mapping(uint256 => address) storage reserves,
                  uint256 reservesCount,
                  address oracle
                ) external view {
                  require(amount != 0, Errors.VL_INVALID_AMOUNT);
                  require(amount <= userBalance, Errors.VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE);
                  (bool isActive, , , ) = reservesData[reserveAddress].configuration.getFlags();
                  require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
                  require(
                    GenericLogic.balanceDecreaseAllowed(
                      reserveAddress,
                      msg.sender,
                      amount,
                      reservesData,
                      userConfig,
                      reserves,
                      reservesCount,
                      oracle
                    ),
                    Errors.VL_TRANSFER_NOT_ALLOWED
                  );
                }
                struct ValidateBorrowLocalVars {
                  uint256 currentLtv;
                  uint256 currentLiquidationThreshold;
                  uint256 amountOfCollateralNeededETH;
                  uint256 userCollateralBalanceETH;
                  uint256 userBorrowBalanceETH;
                  uint256 availableLiquidity;
                  uint256 healthFactor;
                  bool isActive;
                  bool isFrozen;
                  bool borrowingEnabled;
                  bool stableRateBorrowingEnabled;
                }
                /**
                 * @dev Validates a borrow action
                 * @param asset The address of the asset to borrow
                 * @param reserve The reserve state from which the user is borrowing
                 * @param userAddress The address of the user
                 * @param amount The amount to be borrowed
                 * @param amountInETH The amount to be borrowed, in ETH
                 * @param interestRateMode The interest rate mode at which the user is borrowing
                 * @param maxStableLoanPercent The max amount of the liquidity that can be borrowed at stable rate, in percentage
                 * @param reservesData The state of all the reserves
                 * @param userConfig The state of the user for the specific reserve
                 * @param reserves The addresses of all the active reserves
                 * @param oracle The price oracle
                 */
                function validateBorrow(
                  address asset,
                  DataTypes.ReserveData storage reserve,
                  address userAddress,
                  uint256 amount,
                  uint256 amountInETH,
                  uint256 interestRateMode,
                  uint256 maxStableLoanPercent,
                  mapping(address => DataTypes.ReserveData) storage reservesData,
                  DataTypes.UserConfigurationMap storage userConfig,
                  mapping(uint256 => address) storage reserves,
                  uint256 reservesCount,
                  address oracle
                ) external view {
                  ValidateBorrowLocalVars memory vars;
                  (vars.isActive, vars.isFrozen, vars.borrowingEnabled, vars.stableRateBorrowingEnabled) = reserve
                    .configuration
                    .getFlags();
                  require(vars.isActive, Errors.VL_NO_ACTIVE_RESERVE);
                  require(!vars.isFrozen, Errors.VL_RESERVE_FROZEN);
                  require(amount != 0, Errors.VL_INVALID_AMOUNT);
                  require(vars.borrowingEnabled, Errors.VL_BORROWING_NOT_ENABLED);
                  //validate interest rate mode
                  require(
                    uint256(DataTypes.InterestRateMode.VARIABLE) == interestRateMode ||
                      uint256(DataTypes.InterestRateMode.STABLE) == interestRateMode,
                    Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED
                  );
                  (
                    vars.userCollateralBalanceETH,
                    vars.userBorrowBalanceETH,
                    vars.currentLtv,
                    vars.currentLiquidationThreshold,
                    vars.healthFactor
                  ) = GenericLogic.calculateUserAccountData(
                    userAddress,
                    reservesData,
                    userConfig,
                    reserves,
                    reservesCount,
                    oracle
                  );
                  require(vars.userCollateralBalanceETH > 0, Errors.VL_COLLATERAL_BALANCE_IS_0);
                  require(
                    vars.healthFactor > GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                    Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
                  );
                  //add the current already borrowed amount to the amount requested to calculate the total collateral needed.
                  vars.amountOfCollateralNeededETH = vars.userBorrowBalanceETH.add(amountInETH).percentDiv(
                    vars.currentLtv
                  ); //LTV is calculated in percentage
                  require(
                    vars.amountOfCollateralNeededETH <= vars.userCollateralBalanceETH,
                    Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW
                  );
                  /**
                   * Following conditions need to be met if the user is borrowing at a stable rate:
                   * 1. Reserve must be enabled for stable rate borrowing
                   * 2. Users cannot borrow from the reserve if their collateral is (mostly) the same currency
                   *    they are borrowing, to prevent abuses.
                   * 3. Users will be able to borrow only a portion of the total available liquidity
                   **/
                  if (interestRateMode == uint256(DataTypes.InterestRateMode.STABLE)) {
                    //check if the borrow mode is stable and if stable rate borrowing is enabled on this reserve
                    require(vars.stableRateBorrowingEnabled, Errors.VL_STABLE_BORROWING_NOT_ENABLED);
                    require(
                      !userConfig.isUsingAsCollateral(reserve.id) ||
                        reserve.configuration.getLtv() == 0 ||
                        amount > IERC20(reserve.aTokenAddress).balanceOf(userAddress),
                      Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY
                    );
                    vars.availableLiquidity = IERC20(asset).balanceOf(reserve.aTokenAddress);
                    //calculate the max available loan size in stable rate mode as a percentage of the
                    //available liquidity
                    uint256 maxLoanSizeStable = vars.availableLiquidity.percentMul(maxStableLoanPercent);
                    require(amount <= maxLoanSizeStable, Errors.VL_AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE);
                  }
                }
                /**
                 * @dev Validates a repay action
                 * @param reserve The reserve state from which the user is repaying
                 * @param amountSent The amount sent for the repayment. Can be an actual value or uint(-1)
                 * @param onBehalfOf The address of the user msg.sender is repaying for
                 * @param stableDebt The borrow balance of the user
                 * @param variableDebt The borrow balance of the user
                 */
                function validateRepay(
                  DataTypes.ReserveData storage reserve,
                  uint256 amountSent,
                  DataTypes.InterestRateMode rateMode,
                  address onBehalfOf,
                  uint256 stableDebt,
                  uint256 variableDebt
                ) external view {
                  bool isActive = reserve.configuration.getActive();
                  require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
                  require(amountSent > 0, Errors.VL_INVALID_AMOUNT);
                  require(
                    (stableDebt > 0 &&
                      DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE) ||
                      (variableDebt > 0 &&
                        DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.VARIABLE),
                    Errors.VL_NO_DEBT_OF_SELECTED_TYPE
                  );
                  require(
                    amountSent != uint256(-1) || msg.sender == onBehalfOf,
                    Errors.VL_NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF
                  );
                }
                /**
                 * @dev Validates a swap of borrow rate mode.
                 * @param reserve The reserve state on which the user is swapping the rate
                 * @param userConfig The user reserves configuration
                 * @param stableDebt The stable debt of the user
                 * @param variableDebt The variable debt of the user
                 * @param currentRateMode The rate mode of the borrow
                 */
                function validateSwapRateMode(
                  DataTypes.ReserveData storage reserve,
                  DataTypes.UserConfigurationMap storage userConfig,
                  uint256 stableDebt,
                  uint256 variableDebt,
                  DataTypes.InterestRateMode currentRateMode
                ) external view {
                  (bool isActive, bool isFrozen, , bool stableRateEnabled) = reserve.configuration.getFlags();
                  require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
                  require(!isFrozen, Errors.VL_RESERVE_FROZEN);
                  if (currentRateMode == DataTypes.InterestRateMode.STABLE) {
                    require(stableDebt > 0, Errors.VL_NO_STABLE_RATE_LOAN_IN_RESERVE);
                  } else if (currentRateMode == DataTypes.InterestRateMode.VARIABLE) {
                    require(variableDebt > 0, Errors.VL_NO_VARIABLE_RATE_LOAN_IN_RESERVE);
                    /**
                     * user wants to swap to stable, before swapping we need to ensure that
                     * 1. stable borrow rate is enabled on the reserve
                     * 2. user is not trying to abuse the reserve by depositing
                     * more collateral than he is borrowing, artificially lowering
                     * the interest rate, borrowing at variable, and switching to stable
                     **/
                    require(stableRateEnabled, Errors.VL_STABLE_BORROWING_NOT_ENABLED);
                    require(
                      !userConfig.isUsingAsCollateral(reserve.id) ||
                        reserve.configuration.getLtv() == 0 ||
                        stableDebt.add(variableDebt) > IERC20(reserve.aTokenAddress).balanceOf(msg.sender),
                      Errors.VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY
                    );
                  } else {
                    revert(Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED);
                  }
                }
                /**
                 * @dev Validates a stable borrow rate rebalance action
                 * @param reserve The reserve state on which the user is getting rebalanced
                 * @param reserveAddress The address of the reserve
                 * @param stableDebtToken The stable debt token instance
                 * @param variableDebtToken The variable debt token instance
                 * @param aTokenAddress The address of the aToken contract
                 */
                function validateRebalanceStableBorrowRate(
                  DataTypes.ReserveData storage reserve,
                  address reserveAddress,
                  IERC20 stableDebtToken,
                  IERC20 variableDebtToken,
                  address aTokenAddress
                ) external view {
                  (bool isActive, , , ) = reserve.configuration.getFlags();
                  require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
                  //if the usage ratio is below 95%, no rebalances are needed
                  uint256 totalDebt =
                    stableDebtToken.totalSupply().add(variableDebtToken.totalSupply()).wadToRay();
                  uint256 availableLiquidity = IERC20(reserveAddress).balanceOf(aTokenAddress).wadToRay();
                  uint256 usageRatio = totalDebt == 0 ? 0 : totalDebt.rayDiv(availableLiquidity.add(totalDebt));
                  //if the liquidity rate is below REBALANCE_UP_THRESHOLD of the max variable APR at 95% usage,
                  //then we allow rebalancing of the stable rate positions.
                  uint256 currentLiquidityRate = reserve.currentLiquidityRate;
                  uint256 maxVariableBorrowRate =
                    IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).getMaxVariableBorrowRate();
                  require(
                    usageRatio >= REBALANCE_UP_USAGE_RATIO_THRESHOLD &&
                      currentLiquidityRate <=
                      maxVariableBorrowRate.percentMul(REBALANCE_UP_LIQUIDITY_RATE_THRESHOLD),
                    Errors.LP_INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET
                  );
                }
                /**
                 * @dev Validates the action of setting an asset as collateral
                 * @param reserve The state of the reserve that the user is enabling or disabling as collateral
                 * @param reserveAddress The address of the reserve
                 * @param reservesData The data of all the reserves
                 * @param userConfig The state of the user for the specific reserve
                 * @param reserves The addresses of all the active reserves
                 * @param oracle The price oracle
                 */
                function validateSetUseReserveAsCollateral(
                  DataTypes.ReserveData storage reserve,
                  address reserveAddress,
                  bool useAsCollateral,
                  mapping(address => DataTypes.ReserveData) storage reservesData,
                  DataTypes.UserConfigurationMap storage userConfig,
                  mapping(uint256 => address) storage reserves,
                  uint256 reservesCount,
                  address oracle
                ) external view {
                  uint256 underlyingBalance = IERC20(reserve.aTokenAddress).balanceOf(msg.sender);
                  require(underlyingBalance > 0, Errors.VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0);
                  require(
                    useAsCollateral ||
                      GenericLogic.balanceDecreaseAllowed(
                        reserveAddress,
                        msg.sender,
                        underlyingBalance,
                        reservesData,
                        userConfig,
                        reserves,
                        reservesCount,
                        oracle
                      ),
                    Errors.VL_DEPOSIT_ALREADY_IN_USE
                  );
                }
                /**
                 * @dev Validates a flashloan action
                 * @param assets The assets being flashborrowed
                 * @param amounts The amounts for each asset being borrowed
                 **/
                function validateFlashloan(address[] memory assets, uint256[] memory amounts) internal pure {
                  require(assets.length == amounts.length, Errors.VL_INCONSISTENT_FLASHLOAN_PARAMS);
                }
                /**
                 * @dev Validates the liquidation action
                 * @param collateralReserve The reserve data of the collateral
                 * @param principalReserve The reserve data of the principal
                 * @param userConfig The user configuration
                 * @param userHealthFactor The user's health factor
                 * @param userStableDebt Total stable debt balance of the user
                 * @param userVariableDebt Total variable debt balance of the user
                 **/
                function validateLiquidationCall(
                  DataTypes.ReserveData storage collateralReserve,
                  DataTypes.ReserveData storage principalReserve,
                  DataTypes.UserConfigurationMap storage userConfig,
                  uint256 userHealthFactor,
                  uint256 userStableDebt,
                  uint256 userVariableDebt
                ) internal view returns (uint256, string memory) {
                  if (
                    !collateralReserve.configuration.getActive() || !principalReserve.configuration.getActive()
                  ) {
                    return (
                      uint256(Errors.CollateralManagerErrors.NO_ACTIVE_RESERVE),
                      Errors.VL_NO_ACTIVE_RESERVE
                    );
                  }
                  if (userHealthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) {
                    return (
                      uint256(Errors.CollateralManagerErrors.HEALTH_FACTOR_ABOVE_THRESHOLD),
                      Errors.LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD
                    );
                  }
                  bool isCollateralEnabled =
                    collateralReserve.configuration.getLiquidationThreshold() > 0 &&
                      userConfig.isUsingAsCollateral(collateralReserve.id);
                  //if collateral isn't enabled as collateral by user, it cannot be liquidated
                  if (!isCollateralEnabled) {
                    return (
                      uint256(Errors.CollateralManagerErrors.COLLATERAL_CANNOT_BE_LIQUIDATED),
                      Errors.LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED
                    );
                  }
                  if (userStableDebt == 0 && userVariableDebt == 0) {
                    return (
                      uint256(Errors.CollateralManagerErrors.CURRRENCY_NOT_BORROWED),
                      Errors.LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER
                    );
                  }
                  return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.LPCM_NO_ERRORS);
                }
                /**
                 * @dev Validates an aToken transfer
                 * @param from The user from which the aTokens are being transferred
                 * @param reservesData The state of all the reserves
                 * @param userConfig The state of the user for the specific reserve
                 * @param reserves The addresses of all the active reserves
                 * @param oracle The price oracle
                 */
                function validateTransfer(
                  address from,
                  mapping(address => DataTypes.ReserveData) storage reservesData,
                  DataTypes.UserConfigurationMap storage userConfig,
                  mapping(uint256 => address) storage reserves,
                  uint256 reservesCount,
                  address oracle
                ) internal view {
                  (, , , , uint256 healthFactor) =
                    GenericLogic.calculateUserAccountData(
                      from,
                      reservesData,
                      userConfig,
                      reserves,
                      reservesCount,
                      oracle
                    );
                  require(
                    healthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
                    Errors.VL_TRANSFER_NOT_ALLOWED
                  );
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol';
              import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol';
              import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              import {DataTypes} from '../libraries/types/DataTypes.sol';
              contract LendingPoolStorage {
                using ReserveLogic for DataTypes.ReserveData;
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                using UserConfiguration for DataTypes.UserConfigurationMap;
                ILendingPoolAddressesProvider internal _addressesProvider;
                mapping(address => DataTypes.ReserveData) internal _reserves;
                mapping(address => DataTypes.UserConfigurationMap) internal _usersConfig;
                // the list of the available reserves, structured as a mapping for gas savings reasons
                mapping(uint256 => address) internal _reservesList;
                uint256 internal _reservesCount;
                bool internal _paused;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              interface IScaledBalanceToken {
                /**
                 * @dev Returns the scaled balance of the user. The scaled balance is the sum of all the
                 * updated stored balance divided by the reserve's liquidity index at the moment of the update
                 * @param user The user whose balance is calculated
                 * @return The scaled balance of the user
                 **/
                function scaledBalanceOf(address user) external view returns (uint256);
                /**
                 * @dev Returns the scaled balance of the user and the scaled total supply.
                 * @param user The address of the user
                 * @return The scaled balance of the user
                 * @return The scaled balance and the scaled total supply
                 **/
                function getScaledUserBalanceAndSupply(address user) external view returns (uint256, uint256);
                /**
                 * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index)
                 * @return The scaled total supply
                 **/
                function scaledTotalSupply() external view returns (uint256);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IERC20} from '../../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {SafeERC20} from '../../../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {IAToken} from '../../../interfaces/IAToken.sol';
              import {IStableDebtToken} from '../../../interfaces/IStableDebtToken.sol';
              import {IVariableDebtToken} from '../../../interfaces/IVariableDebtToken.sol';
              import {IReserveInterestRateStrategy} from '../../../interfaces/IReserveInterestRateStrategy.sol';
              import {ReserveConfiguration} from '../configuration/ReserveConfiguration.sol';
              import {MathUtils} from '../math/MathUtils.sol';
              import {WadRayMath} from '../math/WadRayMath.sol';
              import {PercentageMath} from '../math/PercentageMath.sol';
              import {Errors} from '../helpers/Errors.sol';
              import {DataTypes} from '../types/DataTypes.sol';
              /**
               * @title ReserveLogic library
               * @author Aave
               * @notice Implements the logic to update the reserves state
               */
              library ReserveLogic {
                using SafeMath for uint256;
                using WadRayMath for uint256;
                using PercentageMath for uint256;
                using SafeERC20 for IERC20;
                /**
                 * @dev Emitted when the state of a reserve is updated
                 * @param asset The address of the underlying asset of the reserve
                 * @param liquidityRate The new liquidity rate
                 * @param stableBorrowRate The new stable borrow rate
                 * @param variableBorrowRate The new variable borrow rate
                 * @param liquidityIndex The new liquidity index
                 * @param variableBorrowIndex The new variable borrow index
                 **/
                event ReserveDataUpdated(
                  address indexed asset,
                  uint256 liquidityRate,
                  uint256 stableBorrowRate,
                  uint256 variableBorrowRate,
                  uint256 liquidityIndex,
                  uint256 variableBorrowIndex
                );
                using ReserveLogic for DataTypes.ReserveData;
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                /**
                 * @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 for each unit of asset one unit of income has been accrued
                 * @param reserve The reserve object
                 * @return the normalized income. expressed in ray
                 **/
                function getNormalizedIncome(DataTypes.ReserveData storage reserve)
                  internal
                  view
                  returns (uint256)
                {
                  uint40 timestamp = reserve.lastUpdateTimestamp;
                  //solium-disable-next-line
                  if (timestamp == uint40(block.timestamp)) {
                    //if the index was updated in the same block, no need to perform any calculation
                    return reserve.liquidityIndex;
                  }
                  uint256 cumulated =
                    MathUtils.calculateLinearInterest(reserve.currentLiquidityRate, timestamp).rayMul(
                      reserve.liquidityIndex
                    );
                  return cumulated;
                }
                /**
                 * @dev Returns the ongoing normalized variable debt for the reserve
                 * A value of 1e27 means there is no debt. As time passes, the income is accrued
                 * A value of 2*1e27 means that for each unit of debt, one unit worth of interest has been accumulated
                 * @param reserve The reserve object
                 * @return The normalized variable debt. expressed in ray
                 **/
                function getNormalizedDebt(DataTypes.ReserveData storage reserve)
                  internal
                  view
                  returns (uint256)
                {
                  uint40 timestamp = reserve.lastUpdateTimestamp;
                  //solium-disable-next-line
                  if (timestamp == uint40(block.timestamp)) {
                    //if the index was updated in the same block, no need to perform any calculation
                    return reserve.variableBorrowIndex;
                  }
                  uint256 cumulated =
                    MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp).rayMul(
                      reserve.variableBorrowIndex
                    );
                  return cumulated;
                }
                /**
                 * @dev Updates the liquidity cumulative index and the variable borrow index.
                 * @param reserve the reserve object
                 **/
                function updateState(DataTypes.ReserveData storage reserve) internal {
                  uint256 scaledVariableDebt =
                    IVariableDebtToken(reserve.variableDebtTokenAddress).scaledTotalSupply();
                  uint256 previousVariableBorrowIndex = reserve.variableBorrowIndex;
                  uint256 previousLiquidityIndex = reserve.liquidityIndex;
                  uint40 lastUpdatedTimestamp = reserve.lastUpdateTimestamp;
                  (uint256 newLiquidityIndex, uint256 newVariableBorrowIndex) =
                    _updateIndexes(
                      reserve,
                      scaledVariableDebt,
                      previousLiquidityIndex,
                      previousVariableBorrowIndex,
                      lastUpdatedTimestamp
                    );
                  _mintToTreasury(
                    reserve,
                    scaledVariableDebt,
                    previousVariableBorrowIndex,
                    newLiquidityIndex,
                    newVariableBorrowIndex,
                    lastUpdatedTimestamp
                  );
                }
                /**
                 * @dev Accumulates a predefined amount of asset to the reserve as a fixed, instantaneous income. Used for example to accumulate
                 * the flashloan fee to the reserve, and spread it between all the depositors
                 * @param reserve The reserve object
                 * @param totalLiquidity The total liquidity available in the reserve
                 * @param amount The amount to accomulate
                 **/
                function cumulateToLiquidityIndex(
                  DataTypes.ReserveData storage reserve,
                  uint256 totalLiquidity,
                  uint256 amount
                ) internal {
                  uint256 amountToLiquidityRatio = amount.wadToRay().rayDiv(totalLiquidity.wadToRay());
                  uint256 result = amountToLiquidityRatio.add(WadRayMath.ray());
                  result = result.rayMul(reserve.liquidityIndex);
                  require(result <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW);
                  reserve.liquidityIndex = uint128(result);
                }
                /**
                 * @dev Initializes a reserve
                 * @param reserve The reserve object
                 * @param aTokenAddress The address of the overlying atoken contract
                 * @param interestRateStrategyAddress The address of the interest rate strategy contract
                 **/
                function init(
                  DataTypes.ReserveData storage reserve,
                  address aTokenAddress,
                  address stableDebtTokenAddress,
                  address variableDebtTokenAddress,
                  address interestRateStrategyAddress
                ) external {
                  require(reserve.aTokenAddress == address(0), Errors.RL_RESERVE_ALREADY_INITIALIZED);
                  reserve.liquidityIndex = uint128(WadRayMath.ray());
                  reserve.variableBorrowIndex = uint128(WadRayMath.ray());
                  reserve.aTokenAddress = aTokenAddress;
                  reserve.stableDebtTokenAddress = stableDebtTokenAddress;
                  reserve.variableDebtTokenAddress = variableDebtTokenAddress;
                  reserve.interestRateStrategyAddress = interestRateStrategyAddress;
                }
                struct UpdateInterestRatesLocalVars {
                  address stableDebtTokenAddress;
                  uint256 availableLiquidity;
                  uint256 totalStableDebt;
                  uint256 newLiquidityRate;
                  uint256 newStableRate;
                  uint256 newVariableRate;
                  uint256 avgStableRate;
                  uint256 totalVariableDebt;
                }
                /**
                 * @dev Updates the reserve current stable borrow rate, the current variable borrow rate and the current liquidity rate
                 * @param reserve The address of the reserve to be updated
                 * @param liquidityAdded The amount of liquidity added to the protocol (deposit or repay) in the previous action
                 * @param liquidityTaken The amount of liquidity taken from the protocol (redeem or borrow)
                 **/
                function updateInterestRates(
                  DataTypes.ReserveData storage reserve,
                  address reserveAddress,
                  address aTokenAddress,
                  uint256 liquidityAdded,
                  uint256 liquidityTaken
                ) internal {
                  UpdateInterestRatesLocalVars memory vars;
                  vars.stableDebtTokenAddress = reserve.stableDebtTokenAddress;
                  (vars.totalStableDebt, vars.avgStableRate) = IStableDebtToken(vars.stableDebtTokenAddress)
                    .getTotalSupplyAndAvgRate();
                  //calculates the total variable debt locally using the scaled total supply instead
                  //of totalSupply(), as it's noticeably cheaper. Also, the index has been
                  //updated by the previous updateState() call
                  vars.totalVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress)
                    .scaledTotalSupply()
                    .rayMul(reserve.variableBorrowIndex);
                  vars.availableLiquidity = IERC20(reserveAddress).balanceOf(aTokenAddress);
                  (
                    vars.newLiquidityRate,
                    vars.newStableRate,
                    vars.newVariableRate
                  ) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates(
                    reserveAddress,
                    vars.availableLiquidity.add(liquidityAdded).sub(liquidityTaken),
                    vars.totalStableDebt,
                    vars.totalVariableDebt,
                    vars.avgStableRate,
                    reserve.configuration.getReserveFactor()
                  );
                  require(vars.newLiquidityRate <= type(uint128).max, Errors.RL_LIQUIDITY_RATE_OVERFLOW);
                  require(vars.newStableRate <= type(uint128).max, Errors.RL_STABLE_BORROW_RATE_OVERFLOW);
                  require(vars.newVariableRate <= type(uint128).max, Errors.RL_VARIABLE_BORROW_RATE_OVERFLOW);
                  reserve.currentLiquidityRate = uint128(vars.newLiquidityRate);
                  reserve.currentStableBorrowRate = uint128(vars.newStableRate);
                  reserve.currentVariableBorrowRate = uint128(vars.newVariableRate);
                  emit ReserveDataUpdated(
                    reserveAddress,
                    vars.newLiquidityRate,
                    vars.newStableRate,
                    vars.newVariableRate,
                    reserve.liquidityIndex,
                    reserve.variableBorrowIndex
                  );
                }
                struct MintToTreasuryLocalVars {
                  uint256 currentStableDebt;
                  uint256 principalStableDebt;
                  uint256 previousStableDebt;
                  uint256 currentVariableDebt;
                  uint256 previousVariableDebt;
                  uint256 avgStableRate;
                  uint256 cumulatedStableInterest;
                  uint256 totalDebtAccrued;
                  uint256 amountToMint;
                  uint256 reserveFactor;
                  uint40 stableSupplyUpdatedTimestamp;
                }
                /**
                 * @dev Mints part of the repaid interest to the reserve treasury as a function of the reserveFactor for the
                 * specific asset.
                 * @param reserve The reserve reserve to be updated
                 * @param scaledVariableDebt The current scaled total variable debt
                 * @param previousVariableBorrowIndex The variable borrow index before the last accumulation of the interest
                 * @param newLiquidityIndex The new liquidity index
                 * @param newVariableBorrowIndex The variable borrow index after the last accumulation of the interest
                 **/
                function _mintToTreasury(
                  DataTypes.ReserveData storage reserve,
                  uint256 scaledVariableDebt,
                  uint256 previousVariableBorrowIndex,
                  uint256 newLiquidityIndex,
                  uint256 newVariableBorrowIndex,
                  uint40 timestamp
                ) internal {
                  MintToTreasuryLocalVars memory vars;
                  vars.reserveFactor = reserve.configuration.getReserveFactor();
                  if (vars.reserveFactor == 0) {
                    return;
                  }
                  //fetching the principal, total stable debt and the avg stable rate
                  (
                    vars.principalStableDebt,
                    vars.currentStableDebt,
                    vars.avgStableRate,
                    vars.stableSupplyUpdatedTimestamp
                  ) = IStableDebtToken(reserve.stableDebtTokenAddress).getSupplyData();
                  //calculate the last principal variable debt
                  vars.previousVariableDebt = scaledVariableDebt.rayMul(previousVariableBorrowIndex);
                  //calculate the new total supply after accumulation of the index
                  vars.currentVariableDebt = scaledVariableDebt.rayMul(newVariableBorrowIndex);
                  //calculate the stable debt until the last timestamp update
                  vars.cumulatedStableInterest = MathUtils.calculateCompoundedInterest(
                    vars.avgStableRate,
                    vars.stableSupplyUpdatedTimestamp,
                    timestamp
                  );
                  vars.previousStableDebt = vars.principalStableDebt.rayMul(vars.cumulatedStableInterest);
                  //debt accrued is the sum of the current debt minus the sum of the debt at the last update
                  vars.totalDebtAccrued = vars
                    .currentVariableDebt
                    .add(vars.currentStableDebt)
                    .sub(vars.previousVariableDebt)
                    .sub(vars.previousStableDebt);
                  vars.amountToMint = vars.totalDebtAccrued.percentMul(vars.reserveFactor);
                  if (vars.amountToMint != 0) {
                    IAToken(reserve.aTokenAddress).mintToTreasury(vars.amountToMint, newLiquidityIndex);
                  }
                }
                /**
                 * @dev Updates the reserve indexes and the timestamp of the update
                 * @param reserve The reserve reserve to be updated
                 * @param scaledVariableDebt The scaled variable debt
                 * @param liquidityIndex The last stored liquidity index
                 * @param variableBorrowIndex The last stored variable borrow index
                 **/
                function _updateIndexes(
                  DataTypes.ReserveData storage reserve,
                  uint256 scaledVariableDebt,
                  uint256 liquidityIndex,
                  uint256 variableBorrowIndex,
                  uint40 timestamp
                ) internal returns (uint256, uint256) {
                  uint256 currentLiquidityRate = reserve.currentLiquidityRate;
                  uint256 newLiquidityIndex = liquidityIndex;
                  uint256 newVariableBorrowIndex = variableBorrowIndex;
                  //only cumulating if there is any income being produced
                  if (currentLiquidityRate > 0) {
                    uint256 cumulatedLiquidityInterest =
                      MathUtils.calculateLinearInterest(currentLiquidityRate, timestamp);
                    newLiquidityIndex = cumulatedLiquidityInterest.rayMul(liquidityIndex);
                    require(newLiquidityIndex <= type(uint128).max, Errors.RL_LIQUIDITY_INDEX_OVERFLOW);
                    reserve.liquidityIndex = uint128(newLiquidityIndex);
                    //as the liquidity rate might come only from stable rate loans, we need to ensure
                    //that there is actual variable debt before accumulating
                    if (scaledVariableDebt != 0) {
                      uint256 cumulatedVariableBorrowInterest =
                        MathUtils.calculateCompoundedInterest(reserve.currentVariableBorrowRate, timestamp);
                      newVariableBorrowIndex = cumulatedVariableBorrowInterest.rayMul(variableBorrowIndex);
                      require(
                        newVariableBorrowIndex <= type(uint128).max,
                        Errors.RL_VARIABLE_BORROW_INDEX_OVERFLOW
                      );
                      reserve.variableBorrowIndex = uint128(newVariableBorrowIndex);
                    }
                  }
                  //solium-disable-next-line
                  reserve.lastUpdateTimestamp = uint40(block.timestamp);
                  return (newLiquidityIndex, newVariableBorrowIndex);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Errors} from '../helpers/Errors.sol';
              import {DataTypes} from '../types/DataTypes.sol';
              /**
               * @title UserConfiguration library
               * @author Aave
               * @notice Implements the bitmap logic to handle the user configuration
               */
              library UserConfiguration {
                uint256 internal constant BORROWING_MASK =
                  0x5555555555555555555555555555555555555555555555555555555555555555;
                /**
                 * @dev Sets if the user is borrowing the reserve identified by reserveIndex
                 * @param self The configuration object
                 * @param reserveIndex The index of the reserve in the bitmap
                 * @param borrowing True if the user is borrowing the reserve, false otherwise
                 **/
                function setBorrowing(
                  DataTypes.UserConfigurationMap storage self,
                  uint256 reserveIndex,
                  bool borrowing
                ) internal {
                  require(reserveIndex < 128, Errors.UL_INVALID_INDEX);
                  self.data =
                    (self.data & ~(1 << (reserveIndex * 2))) |
                    (uint256(borrowing ? 1 : 0) << (reserveIndex * 2));
                }
                /**
                 * @dev Sets if the user is using as collateral the reserve identified by reserveIndex
                 * @param self The configuration object
                 * @param reserveIndex The index of the reserve in the bitmap
                 * @param usingAsCollateral True if the user is usin the reserve as collateral, false otherwise
                 **/
                function setUsingAsCollateral(
                  DataTypes.UserConfigurationMap storage self,
                  uint256 reserveIndex,
                  bool usingAsCollateral
                ) internal {
                  require(reserveIndex < 128, Errors.UL_INVALID_INDEX);
                  self.data =
                    (self.data & ~(1 << (reserveIndex * 2 + 1))) |
                    (uint256(usingAsCollateral ? 1 : 0) << (reserveIndex * 2 + 1));
                }
                /**
                 * @dev Used to validate if a user has been using the reserve for borrowing or as collateral
                 * @param self The configuration object
                 * @param reserveIndex The index of the reserve in the bitmap
                 * @return True if the user has been using a reserve for borrowing or as collateral, false otherwise
                 **/
                function isUsingAsCollateralOrBorrowing(DataTypes.UserConfigurationMap memory self, uint256 reserveIndex)
                  internal
                  pure
                  returns (bool)
                {
                  require(reserveIndex < 128, Errors.UL_INVALID_INDEX);
                  return (self.data >> (reserveIndex * 2)) & 3 != 0;
                }
                /**
                 * @dev Used to validate if a user has been using the reserve for borrowing
                 * @param self The configuration object
                 * @param reserveIndex The index of the reserve in the bitmap
                 * @return True if the user has been using a reserve for borrowing, false otherwise
                 **/
                function isBorrowing(DataTypes.UserConfigurationMap memory self, uint256 reserveIndex)
                  internal
                  pure
                  returns (bool)
                {
                  require(reserveIndex < 128, Errors.UL_INVALID_INDEX);
                  return (self.data >> (reserveIndex * 2)) & 1 != 0;
                }
                /**
                 * @dev Used to validate if a user has been using the reserve as collateral
                 * @param self The configuration object
                 * @param reserveIndex The index of the reserve in the bitmap
                 * @return True if the user has been using a reserve as collateral, false otherwise
                 **/
                function isUsingAsCollateral(DataTypes.UserConfigurationMap memory self, uint256 reserveIndex)
                  internal
                  pure
                  returns (bool)
                {
                  require(reserveIndex < 128, Errors.UL_INVALID_INDEX);
                  return (self.data >> (reserveIndex * 2 + 1)) & 1 != 0;
                }
                /**
                 * @dev Used to validate if a user has been borrowing from any reserve
                 * @param self The configuration object
                 * @return True if the user has been borrowing any reserve, false otherwise
                 **/
                function isBorrowingAny(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
                  return self.data & BORROWING_MASK != 0;
                }
                /**
                 * @dev Used to validate if a user has not been using any reserve
                 * @param self The configuration object
                 * @return True if the user has been borrowing any reserve, false otherwise
                 **/
                function isEmpty(DataTypes.UserConfigurationMap memory self) internal pure returns (bool) {
                  return self.data == 0;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title IReserveInterestRateStrategyInterface interface
               * @dev Interface for the calculation of the interest rates
               * @author Aave
               */
              interface IReserveInterestRateStrategy {
                function baseVariableBorrowRate() external view returns (uint256);
                function getMaxVariableBorrowRate() external view returns (uint256);
                function calculateInterestRates(
                  address reserve,
                  uint256 utilizationRate,
                  uint256 totalStableDebt,
                  uint256 totalVariableDebt,
                  uint256 averageStableBorrowRate,
                  uint256 reserveFactor
                )
                  external
                  view
                  returns (
                    uint256 liquidityRate,
                    uint256 stableBorrowRate,
                    uint256 variableBorrowRate
                  );
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {SafeMath} from '../../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {WadRayMath} from './WadRayMath.sol';
              library MathUtils {
                using SafeMath for uint256;
                using WadRayMath for uint256;
                /// @dev Ignoring leap years
                uint256 internal constant SECONDS_PER_YEAR = 365 days;
                /**
                 * @dev Function to calculate the interest accumulated 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));
                  return (rate.mul(timeDifference) / SECONDS_PER_YEAR).add(WadRayMath.ray());
                }
                /**
                 * @dev Function to calculate the interest using a compounded interest rate formula
                 * To avoid expensive exponentiation, the calculation is performed using a binomial approximation:
                 *
                 *  (1+x)^n = 1+n*x+[n/2*(n-1)]*x^2+[n/6*(n-1)*(n-2)*x^3...
                 *
                 * The approximation slightly underpays liquidity providers and undercharges borrowers, with the advantage of great gas cost reductions
                 * The whitepaper contains reference to the approximation and a table showing the margin of error per different time periods
                 *
                 * @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,
                  uint256 currentTimestamp
                ) internal pure returns (uint256) {
                  //solium-disable-next-line
                  uint256 exp = currentTimestamp.sub(uint256(lastUpdateTimestamp));
                  if (exp == 0) {
                    return WadRayMath.ray();
                  }
                  uint256 expMinusOne = exp - 1;
                  uint256 expMinusTwo = exp > 2 ? exp - 2 : 0;
                  uint256 ratePerSecond = rate / SECONDS_PER_YEAR;
                  uint256 basePowerTwo = ratePerSecond.rayMul(ratePerSecond);
                  uint256 basePowerThree = basePowerTwo.rayMul(ratePerSecond);
                  uint256 secondTerm = exp.mul(expMinusOne).mul(basePowerTwo) / 2;
                  uint256 thirdTerm = exp.mul(expMinusOne).mul(expMinusTwo).mul(basePowerThree) / 6;
                  return WadRayMath.ray().add(ratePerSecond.mul(exp)).add(secondTerm).add(thirdTerm);
                }
                /**
                 * @dev Calculates the compounded interest between the timestamp of the last update and the current block timestamp
                 * @param rate The interest rate (in ray)
                 * @param lastUpdateTimestamp The timestamp from which the interest accumulation needs to be calculated
                 **/
                function calculateCompoundedInterest(uint256 rate, uint40 lastUpdateTimestamp)
                  internal
                  view
                  returns (uint256)
                {
                  return calculateCompoundedInterest(rate, lastUpdateTimestamp, block.timestamp);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {FlashLoanReceiverBase} from '../../flashloan/base/FlashLoanReceiverBase.sol';
              import {MintableERC20} from '../tokens/MintableERC20.sol';
              import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              contract MockFlashLoanReceiver is FlashLoanReceiverBase {
                using SafeERC20 for IERC20;
                ILendingPoolAddressesProvider internal _provider;
                event ExecutedWithFail(address[] _assets, uint256[] _amounts, uint256[] _premiums);
                event ExecutedWithSuccess(address[] _assets, uint256[] _amounts, uint256[] _premiums);
                bool _failExecution;
                uint256 _amountToApprove;
                bool _simulateEOA;
                constructor(ILendingPoolAddressesProvider provider) public FlashLoanReceiverBase(provider) {}
                function setFailExecutionTransfer(bool fail) public {
                  _failExecution = fail;
                }
                function setAmountToApprove(uint256 amountToApprove) public {
                  _amountToApprove = amountToApprove;
                }
                function setSimulateEOA(bool flag) public {
                  _simulateEOA = flag;
                }
                function amountToApprove() public view returns (uint256) {
                  return _amountToApprove;
                }
                function simulateEOA() public view returns (bool) {
                  return _simulateEOA;
                }
                function executeOperation(
                  address[] memory assets,
                  uint256[] memory amounts,
                  uint256[] memory premiums,
                  address initiator,
                  bytes memory params
                ) public override returns (bool) {
                  params;
                  initiator;
                  if (_failExecution) {
                    emit ExecutedWithFail(assets, amounts, premiums);
                    return !_simulateEOA;
                  }
                  for (uint256 i = 0; i < assets.length; i++) {
                    //mint to this contract the specific amount
                    MintableERC20 token = MintableERC20(assets[i]);
                    //check the contract has the specified balance
                    require(
                      amounts[i] <= IERC20(assets[i]).balanceOf(address(this)),
                      'Invalid balance for the contract'
                    );
                    uint256 amountToReturn = (_amountToApprove != 0)
                      ? _amountToApprove
                      : amounts[i].add(premiums[i]);
                    //execution does not fail - mint tokens and return them to the _destination
                    token.mint(premiums[i]);
                    IERC20(assets[i]).approve(address(LENDING_POOL), amountToReturn);
                  }
                  emit ExecutedWithSuccess(assets, amounts, premiums);
                  return true;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {IFlashLoanReceiver} from '../interfaces/IFlashLoanReceiver.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              import {ILendingPool} from '../../interfaces/ILendingPool.sol';
              abstract contract FlashLoanReceiverBase is IFlashLoanReceiver {
                using SafeERC20 for IERC20;
                using SafeMath for uint256;
                ILendingPoolAddressesProvider public immutable override ADDRESSES_PROVIDER;
                ILendingPool public immutable override LENDING_POOL;
                constructor(ILendingPoolAddressesProvider provider) public {
                  ADDRESSES_PROVIDER = provider;
                  LENDING_POOL = ILendingPool(provider.getLendingPool());
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {ERC20} from '../../dependencies/openzeppelin/contracts/ERC20.sol';
              /**
               * @title ERC20Mintable
               * @dev ERC20 minting logic
               */
              contract MintableERC20 is ERC20 {
                constructor(
                  string memory name,
                  string memory symbol,
                  uint8 decimals
                ) public ERC20(name, symbol) {
                  _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(_msgSender(), value);
                  return true;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              import {ILendingPool} from '../../interfaces/ILendingPool.sol';
              /**
               * @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[] calldata assets,
                  uint256[] calldata amounts,
                  uint256[] calldata premiums,
                  address initiator,
                  bytes calldata params
                ) external returns (bool);
                function ADDRESSES_PROVIDER() external view returns (ILendingPoolAddressesProvider);
                function LENDING_POOL() external view returns (ILendingPool);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.6.0;
              import './Context.sol';
              import './IERC20.sol';
              import './SafeMath.sol';
              import './Address.sol';
              /**
               * @dev Implementation of the {IERC20} interface.
               *
               * This implementation is agnostic to the way tokens are created. This means
               * that a supply mechanism has to be added in a derived contract using {_mint}.
               * For a generic mechanism see {ERC20PresetMinterPauser}.
               *
               * TIP: For a detailed writeup see our guide
               * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
               * to implement supply mechanisms].
               *
               * We have followed general OpenZeppelin guidelines: functions revert instead
               * of returning `false` on failure. This behavior is nonetheless conventional
               * and does not conflict with the expectations of ERC20 applications.
               *
               * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
               * This allows applications to reconstruct the allowance for all accounts just
               * by listening to said events. Other implementations of the EIP may not emit
               * these events, as it isn't required by the specification.
               *
               * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
               * functions have been added to mitigate the well-known issues around setting
               * allowances. See {IERC20-approve}.
               */
              contract ERC20 is Context, IERC20 {
                using SafeMath for uint256;
                using Address for address;
                mapping(address => uint256) private _balances;
                mapping(address => mapping(address => uint256)) private _allowances;
                uint256 private _totalSupply;
                string private _name;
                string private _symbol;
                uint8 private _decimals;
                /**
                 * @dev Sets the values for {name} and {symbol}, initializes {decimals} with
                 * a default value of 18.
                 *
                 * To select a different value for {decimals}, use {_setupDecimals}.
                 *
                 * All three of these values are immutable: they can only be set once during
                 * construction.
                 */
                constructor(string memory name, string memory symbol) public {
                  _name = name;
                  _symbol = symbol;
                  _decimals = 18;
                }
                /**
                 * @dev Returns the name of the token.
                 */
                function name() public view returns (string memory) {
                  return _name;
                }
                /**
                 * @dev Returns the symbol of the token, usually a shorter version of the
                 * name.
                 */
                function symbol() public view returns (string memory) {
                  return _symbol;
                }
                /**
                 * @dev Returns the number of decimals used to get its user representation.
                 * For example, if `decimals` equals `2`, a balance of `505` tokens should
                 * be displayed to a user as `5,05` (`505 / 10 ** 2`).
                 *
                 * Tokens usually opt for a value of 18, imitating the relationship between
                 * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
                 * called.
                 *
                 * NOTE: This information is only used for _display_ purposes: it in
                 * no way affects any of the arithmetic of the contract, including
                 * {IERC20-balanceOf} and {IERC20-transfer}.
                 */
                function decimals() public view returns (uint8) {
                  return _decimals;
                }
                /**
                 * @dev See {IERC20-totalSupply}.
                 */
                function totalSupply() public override view returns (uint256) {
                  return _totalSupply;
                }
                /**
                 * @dev See {IERC20-balanceOf}.
                 */
                function balanceOf(address account) public override view returns (uint256) {
                  return _balances[account];
                }
                /**
                 * @dev See {IERC20-transfer}.
                 *
                 * Requirements:
                 *
                 * - `recipient` cannot be the zero address.
                 * - the caller must have a balance of at least `amount`.
                 */
                function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                  _transfer(_msgSender(), recipient, amount);
                  return true;
                }
                /**
                 * @dev See {IERC20-allowance}.
                 */
                function allowance(address owner, address spender)
                  public
                  virtual
                  override
                  view
                  returns (uint256)
                {
                  return _allowances[owner][spender];
                }
                /**
                 * @dev See {IERC20-approve}.
                 *
                 * Requirements:
                 *
                 * - `spender` cannot be the zero address.
                 */
                function approve(address spender, uint256 amount) public virtual override returns (bool) {
                  _approve(_msgSender(), spender, amount);
                  return true;
                }
                /**
                 * @dev See {IERC20-transferFrom}.
                 *
                 * Emits an {Approval} event indicating the updated allowance. This is not
                 * required by the EIP. See the note at the beginning of {ERC20};
                 *
                 * Requirements:
                 * - `sender` and `recipient` cannot be the zero address.
                 * - `sender` must have a balance of at least `amount`.
                 * - the caller must have allowance for ``sender``'s tokens of at least
                 * `amount`.
                 */
                function transferFrom(
                  address sender,
                  address recipient,
                  uint256 amount
                ) public virtual override returns (bool) {
                  _transfer(sender, recipient, amount);
                  _approve(
                    sender,
                    _msgSender(),
                    _allowances[sender][_msgSender()].sub(amount, 'ERC20: transfer amount exceeds allowance')
                  );
                  return true;
                }
                /**
                 * @dev Atomically increases the allowance granted to `spender` by the caller.
                 *
                 * This is an alternative to {approve} that can be used as a mitigation for
                 * problems described in {IERC20-approve}.
                 *
                 * Emits an {Approval} event indicating the updated allowance.
                 *
                 * Requirements:
                 *
                 * - `spender` cannot be the zero address.
                 */
                function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                  _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                  return true;
                }
                /**
                 * @dev Atomically decreases the allowance granted to `spender` by the caller.
                 *
                 * This is an alternative to {approve} that can be used as a mitigation for
                 * problems described in {IERC20-approve}.
                 *
                 * Emits an {Approval} event indicating the updated allowance.
                 *
                 * Requirements:
                 *
                 * - `spender` cannot be the zero address.
                 * - `spender` must have allowance for the caller of at least
                 * `subtractedValue`.
                 */
                function decreaseAllowance(address spender, uint256 subtractedValue)
                  public
                  virtual
                  returns (bool)
                {
                  _approve(
                    _msgSender(),
                    spender,
                    _allowances[_msgSender()][spender].sub(
                      subtractedValue,
                      'ERC20: decreased allowance below zero'
                    )
                  );
                  return true;
                }
                /**
                 * @dev Moves tokens `amount` from `sender` to `recipient`.
                 *
                 * This is internal function is equivalent to {transfer}, and can be used to
                 * e.g. implement automatic token fees, slashing mechanisms, etc.
                 *
                 * Emits a {Transfer} event.
                 *
                 * Requirements:
                 *
                 * - `sender` cannot be the zero address.
                 * - `recipient` cannot be the zero address.
                 * - `sender` must have a balance of at least `amount`.
                 */
                function _transfer(
                  address sender,
                  address recipient,
                  uint256 amount
                ) internal virtual {
                  require(sender != address(0), 'ERC20: transfer from the zero address');
                  require(recipient != address(0), 'ERC20: transfer to the zero address');
                  _beforeTokenTransfer(sender, recipient, amount);
                  _balances[sender] = _balances[sender].sub(amount, 'ERC20: transfer amount exceeds balance');
                  _balances[recipient] = _balances[recipient].add(amount);
                  emit Transfer(sender, recipient, amount);
                }
                /** @dev Creates `amount` tokens and assigns them to `account`, increasing
                 * the total supply.
                 *
                 * Emits a {Transfer} event with `from` set to the zero address.
                 *
                 * Requirements
                 *
                 * - `to` cannot be the zero address.
                 */
                function _mint(address account, uint256 amount) internal virtual {
                  require(account != address(0), 'ERC20: mint to the zero address');
                  _beforeTokenTransfer(address(0), account, amount);
                  _totalSupply = _totalSupply.add(amount);
                  _balances[account] = _balances[account].add(amount);
                  emit Transfer(address(0), account, amount);
                }
                /**
                 * @dev Destroys `amount` tokens from `account`, reducing the
                 * total supply.
                 *
                 * Emits a {Transfer} event with `to` set to the zero address.
                 *
                 * Requirements
                 *
                 * - `account` cannot be the zero address.
                 * - `account` must have at least `amount` tokens.
                 */
                function _burn(address account, uint256 amount) internal virtual {
                  require(account != address(0), 'ERC20: burn from the zero address');
                  _beforeTokenTransfer(account, address(0), amount);
                  _balances[account] = _balances[account].sub(amount, 'ERC20: burn amount exceeds balance');
                  _totalSupply = _totalSupply.sub(amount);
                  emit Transfer(account, address(0), amount);
                }
                /**
                 * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
                 *
                 * This is internal function is equivalent to `approve`, and can be used to
                 * e.g. set automatic allowances for certain subsystems, etc.
                 *
                 * Emits an {Approval} event.
                 *
                 * Requirements:
                 *
                 * - `owner` cannot be the zero address.
                 * - `spender` cannot be the zero address.
                 */
                function _approve(
                  address owner,
                  address spender,
                  uint256 amount
                ) internal virtual {
                  require(owner != address(0), 'ERC20: approve from the zero address');
                  require(spender != address(0), 'ERC20: approve to the zero address');
                  _allowances[owner][spender] = amount;
                  emit Approval(owner, spender, amount);
                }
                /**
                 * @dev Sets {decimals} to a value other than the default one of 18.
                 *
                 * WARNING: This function should only be called from the constructor. Most
                 * applications that interact with token contracts will not expect
                 * {decimals} to ever change, and may work incorrectly if it does.
                 */
                function _setupDecimals(uint8 decimals_) internal {
                  _decimals = decimals_;
                }
                /**
                 * @dev Hook that is called before any transfer of tokens. This includes
                 * minting and burning.
                 *
                 * Calling conditions:
                 *
                 * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
                 * will be to transferred to `to`.
                 * - when `from` is zero, `amount` tokens will be minted for `to`.
                 * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
                 * - `from` and `to` are never both zero.
                 *
                 * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
                 */
                function _beforeTokenTransfer(
                  address from,
                  address to,
                  uint256 amount
                ) internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.6.12;
              /*
               * @dev Provides information about the current execution context, including the
               * sender of the transaction and its data. While these are generally available
               * via msg.sender and msg.data, they should not be accessed in such a direct
               * manner, since when dealing with GSN meta-transactions the account sending and
               * paying for execution may not be the actual sender (as far as an application
               * is concerned).
               *
               * This contract is only required for intermediate, library-like contracts.
               */
              abstract contract Context {
                function _msgSender() internal virtual view returns (address payable) {
                  return msg.sender;
                }
                function _msgData() internal virtual view returns (bytes memory) {
                  this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
                  return msg.data;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol';
              import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
              import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
              import {IChainlinkAggregator} from '../interfaces/IChainlinkAggregator.sol';
              import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol';
              /// @title AaveOracle
              /// @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 AaveOracle is IPriceOracleGetter, Ownable {
                using SafeERC20 for IERC20;
                event WethSet(address indexed weth);
                event AssetSourceUpdated(address indexed asset, address indexed source);
                event FallbackOracleUpdated(address indexed fallbackOracle);
                mapping(address => IChainlinkAggregator) private assetsSources;
                IPriceOracleGetter private _fallbackOracle;
                address public immutable WETH;
                /// @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,
                  address weth
                ) public {
                  _setFallbackOracle(fallbackOracle);
                  _setAssetsSources(assets, sources);
                  WETH = weth;
                  emit WethSet(weth);
                }
                /// @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
                {
                  _setAssetsSources(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 {
                  _setFallbackOracle(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 _setAssetsSources(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 _setFallbackOracle(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 == WETH) {
                    return 1 ether;
                  } else if (address(source) == address(0)) {
                    return _fallbackOracle.getAssetPrice(asset);
                  } else {
                    int256 price = IChainlinkAggregator(source).latestAnswer();
                    if (price > 0) {
                      return uint256(price);
                    } else {
                      return _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);
                }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.6.0;
              import './Context.sol';
              /**
               * @dev Contract module which provides a basic access control mechanism, where
               * there is an account (an owner) that can be granted exclusive access to
               * specific functions.
               *
               * By default, the owner account will be the one that deploys the contract. This
               * can later be changed with {transferOwnership}.
               *
               * This module is used through inheritance. It will make available the modifier
               * `onlyOwner`, which can be applied to your functions to restrict their use to
               * the owner.
               */
              contract Ownable is Context {
                address private _owner;
                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                /**
                 * @dev Initializes the contract setting the deployer as the initial owner.
                 */
                constructor() internal {
                  address msgSender = _msgSender();
                  _owner = msgSender;
                  emit OwnershipTransferred(address(0), msgSender);
                }
                /**
                 * @dev Returns the address of the current owner.
                 */
                function owner() public view returns (address) {
                  return _owner;
                }
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                  require(_owner == _msgSender(), 'Ownable: caller is not the owner');
                  _;
                }
                /**
                 * @dev Leaves the contract without owner. It will not be possible to call
                 * `onlyOwner` functions anymore. Can only be called by the current owner.
                 *
                 * NOTE: Renouncing ownership will leave the contract without an owner,
                 * thereby removing any functionality that is only available to the owner.
                 */
                function renounceOwnership() public virtual onlyOwner {
                  emit OwnershipTransferred(_owner, address(0));
                  _owner = address(0);
                }
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 * Can only be called by the current owner.
                 */
                function transferOwnership(address newOwner) public virtual onlyOwner {
                  require(newOwner != address(0), 'Ownable: new owner is the zero address');
                  emit OwnershipTransferred(_owner, newOwner);
                  _owner = newOwner;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
              import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
              import {IUiPoolDataProvider} from './interfaces/IUiPoolDataProvider.sol';
              import {ILendingPool} from '../interfaces/ILendingPool.sol';
              import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
              import {IAToken} from '../interfaces/IAToken.sol';
              import {IVariableDebtToken} from '../interfaces/IVariableDebtToken.sol';
              import {IStableDebtToken} from '../interfaces/IStableDebtToken.sol';
              import {WadRayMath} from '../protocol/libraries/math/WadRayMath.sol';
              import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol';
              import {UserConfiguration} from '../protocol/libraries/configuration/UserConfiguration.sol';
              import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
              import {
                DefaultReserveInterestRateStrategy
              } from '../protocol/lendingpool/DefaultReserveInterestRateStrategy.sol';
              contract UiPoolDataProvider is IUiPoolDataProvider {
                using WadRayMath for uint256;
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                using UserConfiguration for DataTypes.UserConfigurationMap;
                address public constant MOCK_USD_ADDRESS = 0x10F7Fc1F91Ba351f9C629c5947AD69bD03C05b96;
                function getInterestRateStrategySlopes(DefaultReserveInterestRateStrategy interestRateStrategy)
                  internal
                  view
                  returns (
                    uint256,
                    uint256,
                    uint256,
                    uint256
                  )
                {
                  return (
                    interestRateStrategy.variableRateSlope1(),
                    interestRateStrategy.variableRateSlope2(),
                    interestRateStrategy.stableRateSlope1(),
                    interestRateStrategy.stableRateSlope2()
                  );
                }
                function getReservesData(ILendingPoolAddressesProvider provider, address user)
                  external
                  view
                  override
                  returns (
                    AggregatedReserveData[] memory,
                    UserReserveData[] memory,
                    uint256
                  )
                {
                  ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
                  IPriceOracleGetter oracle = IPriceOracleGetter(provider.getPriceOracle());
                  address[] memory reserves = lendingPool.getReservesList();
                  DataTypes.UserConfigurationMap memory userConfig = lendingPool.getUserConfiguration(user);
                  AggregatedReserveData[] memory reservesData = new AggregatedReserveData[](reserves.length);
                  UserReserveData[] memory userReservesData =
                    new UserReserveData[](user != address(0) ? reserves.length : 0);
                  for (uint256 i = 0; i < reserves.length; i++) {
                    AggregatedReserveData memory reserveData = reservesData[i];
                    reserveData.underlyingAsset = reserves[i];
                    // reserve current state
                    DataTypes.ReserveData memory baseData =
                      lendingPool.getReserveData(reserveData.underlyingAsset);
                    reserveData.liquidityIndex = baseData.liquidityIndex;
                    reserveData.variableBorrowIndex = baseData.variableBorrowIndex;
                    reserveData.liquidityRate = baseData.currentLiquidityRate;
                    reserveData.variableBorrowRate = baseData.currentVariableBorrowRate;
                    reserveData.stableBorrowRate = baseData.currentStableBorrowRate;
                    reserveData.lastUpdateTimestamp = baseData.lastUpdateTimestamp;
                    reserveData.aTokenAddress = baseData.aTokenAddress;
                    reserveData.stableDebtTokenAddress = baseData.stableDebtTokenAddress;
                    reserveData.variableDebtTokenAddress = baseData.variableDebtTokenAddress;
                    reserveData.interestRateStrategyAddress = baseData.interestRateStrategyAddress;
                    reserveData.priceInEth = oracle.getAssetPrice(reserveData.underlyingAsset);
                    reserveData.availableLiquidity = IERC20Detailed(reserveData.underlyingAsset).balanceOf(
                      reserveData.aTokenAddress
                    );
                    (
                      reserveData.totalPrincipalStableDebt,
                      ,
                      reserveData.averageStableRate,
                      reserveData.stableDebtLastUpdateTimestamp
                    ) = IStableDebtToken(reserveData.stableDebtTokenAddress).getSupplyData();
                    reserveData.totalScaledVariableDebt = IVariableDebtToken(reserveData.variableDebtTokenAddress)
                      .scaledTotalSupply();
                    // reserve configuration
                    // we're getting this info from the aToken, because some of assets can be not compliant with ETC20Detailed
                    reserveData.symbol = IERC20Detailed(reserveData.aTokenAddress).symbol();
                    reserveData.name = '';
                    (
                      reserveData.baseLTVasCollateral,
                      reserveData.reserveLiquidationThreshold,
                      reserveData.reserveLiquidationBonus,
                      reserveData.decimals,
                      reserveData.reserveFactor
                    ) = baseData.configuration.getParamsMemory();
                    (
                      reserveData.isActive,
                      reserveData.isFrozen,
                      reserveData.borrowingEnabled,
                      reserveData.stableBorrowRateEnabled
                    ) = baseData.configuration.getFlagsMemory();
                    reserveData.usageAsCollateralEnabled = reserveData.baseLTVasCollateral != 0;
                    (
                      reserveData.variableRateSlope1,
                      reserveData.variableRateSlope2,
                      reserveData.stableRateSlope1,
                      reserveData.stableRateSlope2
                    ) = getInterestRateStrategySlopes(
                      DefaultReserveInterestRateStrategy(reserveData.interestRateStrategyAddress)
                    );
                    if (user != address(0)) {
                      // user reserve data
                      userReservesData[i].underlyingAsset = reserveData.underlyingAsset;
                      userReservesData[i].scaledATokenBalance = IAToken(reserveData.aTokenAddress)
                        .scaledBalanceOf(user);
                      userReservesData[i].usageAsCollateralEnabledOnUser = userConfig.isUsingAsCollateral(i);
                      if (userConfig.isBorrowing(i)) {
                        userReservesData[i].scaledVariableDebt = IVariableDebtToken(
                          reserveData
                            .variableDebtTokenAddress
                        )
                          .scaledBalanceOf(user);
                        userReservesData[i].principalStableDebt = IStableDebtToken(
                          reserveData
                            .stableDebtTokenAddress
                        )
                          .principalBalanceOf(user);
                        if (userReservesData[i].principalStableDebt != 0) {
                          userReservesData[i].stableBorrowRate = IStableDebtToken(
                            reserveData
                              .stableDebtTokenAddress
                          )
                            .getUserStableRate(user);
                          userReservesData[i].stableBorrowLastUpdateTimestamp = IStableDebtToken(
                            reserveData
                              .stableDebtTokenAddress
                          )
                            .getUserLastUpdated(user);
                        }
                      }
                    }
                  }
                  return (reservesData, userReservesData, oracle.getAssetPrice(MOCK_USD_ADDRESS));
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {IERC20} from './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);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              interface IUiPoolDataProvider {
                struct AggregatedReserveData {
                  address underlyingAsset;
                  string name;
                  string symbol;
                  uint256 decimals;
                  uint256 baseLTVasCollateral;
                  uint256 reserveLiquidationThreshold;
                  uint256 reserveLiquidationBonus;
                  uint256 reserveFactor;
                  bool usageAsCollateralEnabled;
                  bool borrowingEnabled;
                  bool stableBorrowRateEnabled;
                  bool isActive;
                  bool isFrozen;
                  // base data
                  uint128 liquidityIndex;
                  uint128 variableBorrowIndex;
                  uint128 liquidityRate;
                  uint128 variableBorrowRate;
                  uint128 stableBorrowRate;
                  uint40 lastUpdateTimestamp;
                  address aTokenAddress;
                  address stableDebtTokenAddress;
                  address variableDebtTokenAddress;
                  address interestRateStrategyAddress;
                  //
                  uint256 availableLiquidity;
                  uint256 totalPrincipalStableDebt;
                  uint256 averageStableRate;
                  uint256 stableDebtLastUpdateTimestamp;
                  uint256 totalScaledVariableDebt;
                  uint256 priceInEth;
                  uint256 variableRateSlope1;
                  uint256 variableRateSlope2;
                  uint256 stableRateSlope1;
                  uint256 stableRateSlope2;
                }
                //
                //  struct ReserveData {
                //    uint256 averageStableBorrowRate;
                //    uint256 totalLiquidity;
                //  }
                struct UserReserveData {
                  address underlyingAsset;
                  uint256 scaledATokenBalance;
                  bool usageAsCollateralEnabledOnUser;
                  uint256 stableBorrowRate;
                  uint256 scaledVariableDebt;
                  uint256 principalStableDebt;
                  uint256 stableBorrowLastUpdateTimestamp;
                }
                //
                //  struct ATokenSupplyData {
                //    string name;
                //    string symbol;
                //    uint8 decimals;
                //    uint256 totalSupply;
                //    address aTokenAddress;
                //  }
                function getReservesData(ILendingPoolAddressesProvider provider, address user)
                  external
                  view
                  returns (
                    AggregatedReserveData[] memory,
                    UserReserveData[] memory,
                    uint256
                  );
                //  function getUserReservesData(ILendingPoolAddressesProvider provider, address user)
                //    external
                //    view
                //    returns (UserReserveData[] memory);
                //
                //  function getAllATokenSupply(ILendingPoolAddressesProvider provider)
                //    external
                //    view
                //    returns (ATokenSupplyData[] memory);
                //
                //  function getATokenSupply(address[] calldata aTokens)
                //    external
                //    view
                //    returns (ATokenSupplyData[] memory);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IReserveInterestRateStrategy} from '../../interfaces/IReserveInterestRateStrategy.sol';
              import {WadRayMath} from '../libraries/math/WadRayMath.sol';
              import {PercentageMath} from '../libraries/math/PercentageMath.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              import {ILendingRateOracle} from '../../interfaces/ILendingRateOracle.sol';
              /**
               * @title DefaultReserveInterestRateStrategy contract
               * @notice Implements the calculation of the interest rates depending on the reserve state
               * @dev The model of interest rate is based on 2 slopes, one before the `OPTIMAL_UTILIZATION_RATE`
               * point of utilization and another from that one to 100%
               * - An instance of this same contract, can't be used across different Aave markets, due to the caching
               *   of the LendingPoolAddressesProvider
               * @author Aave
               **/
              contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy {
                using WadRayMath for uint256;
                using SafeMath for uint256;
                using PercentageMath 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 immutable OPTIMAL_UTILIZATION_RATE;
                /**
                 * @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 immutable EXCESS_UTILIZATION_RATE;
                ILendingPoolAddressesProvider public immutable addressesProvider;
                // Base variable borrow rate when Utilization rate = 0. Expressed in ray
                uint256 internal immutable _baseVariableBorrowRate;
                // Slope of the variable interest curve when utilization rate > 0 and <= OPTIMAL_UTILIZATION_RATE. Expressed in ray
                uint256 internal immutable _variableRateSlope1;
                // Slope of the variable interest curve when utilization rate > OPTIMAL_UTILIZATION_RATE. Expressed in ray
                uint256 internal immutable _variableRateSlope2;
                // Slope of the stable interest curve when utilization rate > 0 and <= OPTIMAL_UTILIZATION_RATE. Expressed in ray
                uint256 internal immutable _stableRateSlope1;
                // Slope of the stable interest curve when utilization rate > OPTIMAL_UTILIZATION_RATE. Expressed in ray
                uint256 internal immutable _stableRateSlope2;
                constructor(
                  ILendingPoolAddressesProvider provider,
                  uint256 optimalUtilizationRate,
                  uint256 baseVariableBorrowRate,
                  uint256 variableRateSlope1,
                  uint256 variableRateSlope2,
                  uint256 stableRateSlope1,
                  uint256 stableRateSlope2
                ) public {
                  OPTIMAL_UTILIZATION_RATE = optimalUtilizationRate;
                  EXCESS_UTILIZATION_RATE = WadRayMath.ray().sub(optimalUtilizationRate);
                  addressesProvider = provider;
                  _baseVariableBorrowRate = baseVariableBorrowRate;
                  _variableRateSlope1 = variableRateSlope1;
                  _variableRateSlope2 = variableRateSlope2;
                  _stableRateSlope1 = stableRateSlope1;
                  _stableRateSlope2 = stableRateSlope2;
                }
                function variableRateSlope1() external view returns (uint256) {
                  return _variableRateSlope1;
                }
                function variableRateSlope2() external view returns (uint256) {
                  return _variableRateSlope2;
                }
                function stableRateSlope1() external view returns (uint256) {
                  return _stableRateSlope1;
                }
                function stableRateSlope2() external view returns (uint256) {
                  return _stableRateSlope2;
                }
                function baseVariableBorrowRate() external view override returns (uint256) {
                  return _baseVariableBorrowRate;
                }
                function getMaxVariableBorrowRate() external view override returns (uint256) {
                  return _baseVariableBorrowRate.add(_variableRateSlope1).add(_variableRateSlope2);
                }
                struct CalcInterestRatesLocalVars {
                  uint256 totalDebt;
                  uint256 currentVariableBorrowRate;
                  uint256 currentStableBorrowRate;
                  uint256 currentLiquidityRate;
                  uint256 utilizationRate;
                }
                /**
                 * @dev Calculates the interest rates depending on the reserve's state and configurations
                 * @param reserve The address of the reserve
                 * @param availableLiquidity The liquidity available in the reserve
                 * @param totalStableDebt The total borrowed from the reserve a stable rate
                 * @param totalVariableDebt The total borrowed from the reserve at a variable rate
                 * @param averageStableBorrowRate The weighted average of all the stable rate loans
                 * @param reserveFactor The reserve portion of the interest that goes to the treasury of the market
                 * @return The liquidity rate, the stable borrow rate and the variable borrow rate
                 **/
                function calculateInterestRates(
                  address reserve,
                  uint256 availableLiquidity,
                  uint256 totalStableDebt,
                  uint256 totalVariableDebt,
                  uint256 averageStableBorrowRate,
                  uint256 reserveFactor
                )
                  external
                  view
                  override
                  returns (
                    uint256,
                    uint256,
                    uint256
                  )
                {
                  CalcInterestRatesLocalVars memory vars;
                  vars.totalDebt = totalStableDebt.add(totalVariableDebt);
                  vars.currentVariableBorrowRate = 0;
                  vars.currentStableBorrowRate = 0;
                  vars.currentLiquidityRate = 0;
                  uint256 utilizationRate =
                    vars.totalDebt == 0
                      ? 0
                      : vars.totalDebt.rayDiv(availableLiquidity.add(vars.totalDebt));
                  vars.currentStableBorrowRate = ILendingRateOracle(addressesProvider.getLendingRateOracle())
                    .getMarketBorrowRate(reserve);
                  if (utilizationRate > OPTIMAL_UTILIZATION_RATE) {
                    uint256 excessUtilizationRateRatio =
                      utilizationRate.sub(OPTIMAL_UTILIZATION_RATE).rayDiv(EXCESS_UTILIZATION_RATE);
                    vars.currentStableBorrowRate = vars.currentStableBorrowRate.add(_stableRateSlope1).add(
                      _stableRateSlope2.rayMul(excessUtilizationRateRatio)
                    );
                    vars.currentVariableBorrowRate = _baseVariableBorrowRate.add(_variableRateSlope1).add(
                      _variableRateSlope2.rayMul(excessUtilizationRateRatio)
                    );
                  } else {
                    vars.currentStableBorrowRate = vars.currentStableBorrowRate.add(
                      _stableRateSlope1.rayMul(utilizationRate.rayDiv(OPTIMAL_UTILIZATION_RATE))
                    );
                    vars.currentVariableBorrowRate = _baseVariableBorrowRate.add(
                      utilizationRate.rayMul(_variableRateSlope1).rayDiv(OPTIMAL_UTILIZATION_RATE)
                    );
                  }
                  vars.currentLiquidityRate = _getOverallBorrowRate(
                    totalStableDebt,
                    totalVariableDebt,
                    vars
                      .currentVariableBorrowRate,
                    averageStableBorrowRate
                  )
                    .rayMul(utilizationRate)
                    .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(reserveFactor));
                  return (
                    vars.currentLiquidityRate,
                    vars.currentStableBorrowRate,
                    vars.currentVariableBorrowRate
                  );
                }
                /**
                 * @dev Calculates the overall borrow rate as the weighted average between the total variable debt and total stable debt
                 * @param totalStableDebt The total borrowed from the reserve a stable rate
                 * @param totalVariableDebt The total borrowed from the reserve at a variable rate
                 * @param currentVariableBorrowRate The current variable borrow rate of the reserve
                 * @param currentAverageStableBorrowRate The current weighted average of all the stable rate loans
                 * @return The weighted averaged borrow rate
                 **/
                function _getOverallBorrowRate(
                  uint256 totalStableDebt,
                  uint256 totalVariableDebt,
                  uint256 currentVariableBorrowRate,
                  uint256 currentAverageStableBorrowRate
                ) internal pure returns (uint256) {
                  uint256 totalDebt = totalStableDebt.add(totalVariableDebt);
                  if (totalDebt == 0) return 0;
                  uint256 weightedVariableRate = totalVariableDebt.wadToRay().rayMul(currentVariableBorrowRate);
                  uint256 weightedStableRate = totalStableDebt.wadToRay().rayMul(currentAverageStableBorrowRate);
                  uint256 overallBorrowRate =
                    weightedVariableRate.add(weightedStableRate).rayDiv(totalDebt.wadToRay());
                  return overallBorrowRate;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @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;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {ILendingRateOracle} from '../../interfaces/ILendingRateOracle.sol';
              import {Ownable} from '../../dependencies/openzeppelin/contracts/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;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Ownable} from '../../dependencies/openzeppelin/contracts/Ownable.sol';
              import {
                ILendingPoolAddressesProviderRegistry
              } from '../../interfaces/ILendingPoolAddressesProviderRegistry.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              /**
               * @title LendingPoolAddressesProviderRegistry contract
               * @dev Main registry of LendingPoolAddressesProvider of multiple Aave protocol's markets
               * - Used for indexing purposes of Aave protocol's markets
               * - The id assigned to a LendingPoolAddressesProvider refers to the market it is connected with,
               *   for example with `0` for the Aave main market and `1` for the next created
               * @author Aave
               **/
              contract LendingPoolAddressesProviderRegistry is Ownable, ILendingPoolAddressesProviderRegistry {
                mapping(address => uint256) private _addressesProviders;
                address[] private _addressesProvidersList;
                /**
                 * @dev Returns the list of registered addresses provider
                 * @return The list of addresses provider, potentially containing address(0) elements
                 **/
                function getAddressesProvidersList() external view override returns (address[] memory) {
                  address[] memory addressesProvidersList = _addressesProvidersList;
                  uint256 maxLength = addressesProvidersList.length;
                  address[] memory activeProviders = new address[](maxLength);
                  for (uint256 i = 0; i < maxLength; i++) {
                    if (_addressesProviders[addressesProvidersList[i]] > 0) {
                      activeProviders[i] = addressesProvidersList[i];
                    }
                  }
                  return activeProviders;
                }
                /**
                 * @dev Registers an addresses provider
                 * @param provider The address of the new LendingPoolAddressesProvider
                 * @param id The id for the new LendingPoolAddressesProvider, referring to the market it belongs to
                 **/
                function registerAddressesProvider(address provider, uint256 id) external override onlyOwner {
                  require(id != 0, Errors.LPAPR_INVALID_ADDRESSES_PROVIDER_ID);
                  _addressesProviders[provider] = id;
                  _addToAddressesProvidersList(provider);
                  emit AddressesProviderRegistered(provider);
                }
                /**
                 * @dev Removes a LendingPoolAddressesProvider from the list of registered addresses provider
                 * @param provider The LendingPoolAddressesProvider address
                 **/
                function unregisterAddressesProvider(address provider) external override onlyOwner {
                  require(_addressesProviders[provider] > 0, Errors.LPAPR_PROVIDER_NOT_REGISTERED);
                  _addressesProviders[provider] = 0;
                  emit AddressesProviderUnregistered(provider);
                }
                /**
                 * @dev Returns the id on a registered LendingPoolAddressesProvider
                 * @return The id or 0 if the LendingPoolAddressesProvider is not registered
                 */
                function getAddressesProviderIdByAddress(address addressesProvider)
                  external
                  view
                  override
                  returns (uint256)
                {
                  return _addressesProviders[addressesProvider];
                }
                function _addToAddressesProvidersList(address provider) internal {
                  uint256 providersCount = _addressesProvidersList.length;
                  for (uint256 i = 0; i < providersCount; i++) {
                    if (_addressesProvidersList[i] == provider) {
                      return;
                    }
                  }
                  _addressesProvidersList.push(provider);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title LendingPoolAddressesProviderRegistry contract
               * @dev Main registry of LendingPoolAddressesProvider of multiple Aave protocol's markets
               * - Used for indexing purposes of Aave protocol's markets
               * - The id assigned to a LendingPoolAddressesProvider refers to the market it is connected with,
               *   for example with `0` for the Aave main market and `1` for the next created
               * @author Aave
               **/
              interface ILendingPoolAddressesProviderRegistry {
                event AddressesProviderRegistered(address indexed newAddress);
                event AddressesProviderUnregistered(address indexed newAddress);
                function getAddressesProvidersList() external view returns (address[] memory);
                function getAddressesProviderIdByAddress(address addressesProvider)
                  external
                  view
                  returns (uint256);
                function registerAddressesProvider(address provider, uint256 id) external;
                function unregisterAddressesProvider(address provider) external;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {ILendingPool} from '../../interfaces/ILendingPool.sol';
              import {IDelegationToken} from '../../interfaces/IDelegationToken.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              import {AToken} from './AToken.sol';
              /**
               * @title Aave AToken enabled to delegate voting power of the underlying asset to a different address
               * @dev The underlying asset needs to be compatible with the COMP delegation interface
               * @author Aave
               */
              contract DelegationAwareAToken is AToken {
                modifier onlyPoolAdmin {
                  require(
                    _msgSender() == ILendingPool(POOL).getAddressesProvider().getPoolAdmin(),
                    Errors.CALLER_NOT_POOL_ADMIN
                  );
                  _;
                }
                constructor(
                  ILendingPool pool,
                  address underlyingAssetAddress,
                  address reserveTreasury,
                  string memory tokenName,
                  string memory tokenSymbol,
                  address incentivesController
                )
                  public
                  AToken(
                    pool,
                    underlyingAssetAddress,
                    reserveTreasury,
                    tokenName,
                    tokenSymbol,
                    incentivesController
                  )
                {}
                /**
                 * @dev Delegates voting power of the underlying asset to a `delegatee` address
                 * @param delegatee The address that will receive the delegation
                 **/
                function delegateUnderlyingTo(address delegatee) external onlyPoolAdmin {
                  IDelegationToken(UNDERLYING_ASSET_ADDRESS).delegate(delegatee);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title IDelegationToken
               * @dev Implements an interface for tokens with delegation COMP/UNI compatible
               * @author Aave
               **/
              interface IDelegationToken {
                function delegate(address delegatee) external;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {ILendingPool} from '../../interfaces/ILendingPool.sol';
              import {IAToken} from '../../interfaces/IAToken.sol';
              import {WadRayMath} from '../libraries/math/WadRayMath.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
              import {IncentivizedERC20} from './IncentivizedERC20.sol';
              /**
               * @title Aave ERC20 AToken
               * @dev Implementation of the interest bearing token for the Aave protocol
               * @author Aave
               */
              contract AToken is VersionedInitializable, IncentivizedERC20, IAToken {
                using WadRayMath for uint256;
                using SafeERC20 for IERC20;
                bytes public constant EIP712_REVISION = bytes('1');
                bytes32 internal constant EIP712_DOMAIN =
                  keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');
                bytes32 public constant PERMIT_TYPEHASH =
                  keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)');
                uint256 public constant UINT_MAX_VALUE = uint256(-1);
                uint256 public constant ATOKEN_REVISION = 0x1;
                address public immutable UNDERLYING_ASSET_ADDRESS;
                address public immutable RESERVE_TREASURY_ADDRESS;
                ILendingPool public immutable POOL;
                /// @dev owner => next valid nonce to submit with permit()
                mapping(address => uint256) public _nonces;
                bytes32 public DOMAIN_SEPARATOR;
                modifier onlyLendingPool {
                  require(_msgSender() == address(POOL), Errors.CT_CALLER_MUST_BE_LENDING_POOL);
                  _;
                }
                constructor(
                  ILendingPool pool,
                  address underlyingAssetAddress,
                  address reserveTreasuryAddress,
                  string memory tokenName,
                  string memory tokenSymbol,
                  address incentivesController
                ) public IncentivizedERC20(tokenName, tokenSymbol, 18, incentivesController) {
                  POOL = pool;
                  UNDERLYING_ASSET_ADDRESS = underlyingAssetAddress;
                  RESERVE_TREASURY_ADDRESS = reserveTreasuryAddress;
                }
                function getRevision() internal pure virtual override returns (uint256) {
                  return ATOKEN_REVISION;
                }
                function initialize(
                  uint8 underlyingAssetDecimals,
                  string calldata tokenName,
                  string calldata tokenSymbol
                ) external virtual initializer {
                  uint256 chainId;
                  //solium-disable-next-line
                  assembly {
                    chainId := chainid()
                  }
                  DOMAIN_SEPARATOR = keccak256(
                    abi.encode(
                      EIP712_DOMAIN,
                      keccak256(bytes(tokenName)),
                      keccak256(EIP712_REVISION),
                      chainId,
                      address(this)
                    )
                  );
                  _setName(tokenName);
                  _setSymbol(tokenSymbol);
                  _setDecimals(underlyingAssetDecimals);
                }
                /**
                 * @dev Burns aTokens from `user` and sends the equivalent amount of underlying to `receiverOfUnderlying`
                 * - Only callable by the LendingPool, as extra state updates there need to be managed
                 * @param user The owner of the aTokens, getting them burned
                 * @param receiverOfUnderlying The address that will receive the underlying
                 * @param amount The amount being burned
                 * @param index The new liquidity index of the reserve
                 **/
                function burn(
                  address user,
                  address receiverOfUnderlying,
                  uint256 amount,
                  uint256 index
                ) external override onlyLendingPool {
                  uint256 amountScaled = amount.rayDiv(index);
                  require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT);
                  _burn(user, amountScaled);
                  IERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(receiverOfUnderlying, amount);
                  emit Transfer(user, address(0), amount);
                  emit Burn(user, receiverOfUnderlying, amount, index);
                }
                /**
                 * @dev Mints `amount` aTokens to `user`
                 * - Only callable by the LendingPool, as extra state updates there need to be managed
                 * @param user The address receiving the minted tokens
                 * @param amount The amount of tokens getting minted
                 * @param index The new liquidity index of the reserve
                 * @return `true` if the the previous balance of the user was 0
                 */
                function mint(
                  address user,
                  uint256 amount,
                  uint256 index
                ) external override onlyLendingPool returns (bool) {
                  uint256 previousBalance = super.balanceOf(user);
                  uint256 amountScaled = amount.rayDiv(index);
                  require(amountScaled != 0, Errors.CT_INVALID_MINT_AMOUNT);
                  _mint(user, amountScaled);
                  emit Transfer(address(0), user, amount);
                  emit Mint(user, amount, index);
                  return previousBalance == 0;
                }
                /**
                 * @dev Mints aTokens to the reserve treasury
                 * - Only callable by the LendingPool
                 * @param amount The amount of tokens getting minted
                 * @param index The new liquidity index of the reserve
                 */
                function mintToTreasury(uint256 amount, uint256 index) external override onlyLendingPool {
                  if (amount == 0) {
                    return;
                  }
                  // Compared to the normal mint, we don't check for rounding errors.
                  // The amount to mint can easily be very small since it is a fraction of the interest ccrued.
                  // In that case, the treasury will experience a (very small) loss, but it
                  // wont cause potentially valid transactions to fail.
                  _mint(RESERVE_TREASURY_ADDRESS, amount.rayDiv(index));
                  emit Transfer(address(0), RESERVE_TREASURY_ADDRESS, amount);
                  emit Mint(RESERVE_TREASURY_ADDRESS, amount, index);
                }
                /**
                 * @dev Transfers aTokens in the event of a borrow being liquidated, in case the liquidators reclaims the aToken
                 * - Only callable by the LendingPool
                 * @param from The address getting liquidated, current owner of the aTokens
                 * @param to The recipient
                 * @param value The amount of tokens getting transferred
                 **/
                function transferOnLiquidation(
                  address from,
                  address to,
                  uint256 value
                ) external override onlyLendingPool {
                  // Being a normal transfer, the Transfer() and BalanceTransfer() are emitted
                  // so no need to emit a specific event here
                  _transfer(from, to, value, false);
                  emit Transfer(from, to, value);
                }
                /**
                 * @dev Calculates the balance of the user: principal balance + interest generated by the principal
                 * @param user The user whose balance is calculated
                 * @return The balance of the user
                 **/
                function balanceOf(address user)
                  public
                  view
                  override(IncentivizedERC20, IERC20)
                  returns (uint256)
                {
                  return super.balanceOf(user).rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS));
                }
                /**
                 * @dev Returns the scaled balance of the user. The scaled balance is the sum of all the
                 * updated stored balance divided by the reserve's liquidity index at the moment of the update
                 * @param user The user whose balance is calculated
                 * @return The scaled balance of the user
                 **/
                function scaledBalanceOf(address user) external view override returns (uint256) {
                  return super.balanceOf(user);
                }
                /**
                 * @dev Returns the scaled balance of the user and the scaled total supply.
                 * @param user The address of the user
                 * @return The scaled balance of the user
                 * @return The scaled balance and the scaled total supply
                 **/
                function getScaledUserBalanceAndSupply(address user)
                  external
                  view
                  override
                  returns (uint256, uint256)
                {
                  return (super.balanceOf(user), super.totalSupply());
                }
                /**
                 * @dev calculates the total supply of the specific aToken
                 * since the balance of every single user increases over time, the total supply
                 * does that too.
                 * @return the current total supply
                 **/
                function totalSupply() public view override(IncentivizedERC20, IERC20) returns (uint256) {
                  uint256 currentSupplyScaled = super.totalSupply();
                  if (currentSupplyScaled == 0) {
                    return 0;
                  }
                  return currentSupplyScaled.rayMul(POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS));
                }
                /**
                 * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index)
                 * @return the scaled total supply
                 **/
                function scaledTotalSupply() public view virtual override returns (uint256) {
                  return super.totalSupply();
                }
                /**
                 * @dev Transfers the underlying asset to `target`. Used by the LendingPool to transfer
                 * assets in borrow(), withdraw() and flashLoan()
                 * @param target The recipient of the aTokens
                 * @param amount The amount getting transferred
                 * @return The amount transferred
                 **/
                function transferUnderlyingTo(address target, uint256 amount)
                  external
                  override
                  onlyLendingPool
                  returns (uint256)
                {
                  IERC20(UNDERLYING_ASSET_ADDRESS).safeTransfer(target, amount);
                  return amount;
                }
                /**
                 * @dev implements the permit function as for
                 * https://github.com/ethereum/EIPs/blob/8a34d644aacf0f9f8f00815307fd7dd5da07655f/EIPS/eip-2612.md
                 * @param owner The owner of the funds
                 * @param spender The spender
                 * @param value The amount
                 * @param deadline The deadline timestamp, type(uint256).max for max deadline
                 * @param v Signature param
                 * @param s Signature param
                 * @param r Signature param
                 */
                function permit(
                  address owner,
                  address spender,
                  uint256 value,
                  uint256 deadline,
                  uint8 v,
                  bytes32 r,
                  bytes32 s
                ) external {
                  require(owner != address(0), 'INVALID_OWNER');
                  //solium-disable-next-line
                  require(block.timestamp <= deadline, 'INVALID_EXPIRATION');
                  uint256 currentValidNonce = _nonces[owner];
                  bytes32 digest =
                    keccak256(
                      abi.encodePacked(
                        '\\x19\\x01',
                        DOMAIN_SEPARATOR,
                        keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, currentValidNonce, deadline))
                      )
                    );
                  require(owner == ecrecover(digest, v, r, s), 'INVALID_SIGNATURE');
                  _nonces[owner] = currentValidNonce.add(1);
                  _approve(owner, spender, value);
                }
                /**
                 * @dev Transfers the aTokens between two users. Validates the transfer
                 * (ie checks for valid HF after the transfer) if required
                 * @param from The source address
                 * @param to The destination address
                 * @param amount The amount getting transferred
                 * @param validate `true` if the transfer needs to be validated
                 **/
                function _transfer(
                  address from,
                  address to,
                  uint256 amount,
                  bool validate
                ) internal {
                  uint256 index = POOL.getReserveNormalizedIncome(UNDERLYING_ASSET_ADDRESS);
                  uint256 fromBalanceBefore = super.balanceOf(from).rayMul(index);
                  uint256 toBalanceBefore = super.balanceOf(to).rayMul(index);
                  super._transfer(from, to, amount.rayDiv(index));
                  if (validate) {
                    POOL.finalizeTransfer(
                      UNDERLYING_ASSET_ADDRESS,
                      from,
                      to,
                      amount,
                      fromBalanceBefore,
                      toBalanceBefore
                    );
                  }
                  emit BalanceTransfer(from, to, amount, index);
                }
                /**
                 * @dev Overrides the parent _transfer to force validated transfer() and transferFrom()
                 * @param from The source address
                 * @param to The destination address
                 * @param amount The amount getting transferred
                 **/
                function _transfer(
                  address from,
                  address to,
                  uint256 amount
                ) internal override {
                  _transfer(from, to, amount, true);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Context} from '../../dependencies/openzeppelin/contracts/Context.sol';
              import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {IERC20Detailed} from '../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
              import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol';
              /**
               * @title ERC20
               * @notice Basic ERC20 implementation
               * @author Aave, inspired by the Openzeppelin ERC20 implementation
               **/
              contract IncentivizedERC20 is Context, IERC20, IERC20Detailed {
                using SafeMath for uint256;
                IAaveIncentivesController internal immutable _incentivesController;
                mapping(address => uint256) internal _balances;
                mapping(address => mapping(address => uint256)) private _allowances;
                uint256 internal _totalSupply;
                string private _name;
                string private _symbol;
                uint8 private _decimals;
                constructor(
                  string memory name,
                  string memory symbol,
                  uint8 decimals,
                  address incentivesController
                ) public {
                  _name = name;
                  _symbol = symbol;
                  _decimals = decimals;
                  _incentivesController = IAaveIncentivesController(incentivesController);
                }
                /**
                 * @return The name of the token
                 **/
                function name() public view override returns (string memory) {
                  return _name;
                }
                /**
                 * @return The symbol of the token
                 **/
                function symbol() public view override returns (string memory) {
                  return _symbol;
                }
                /**
                 * @return The decimals of the token
                 **/
                function decimals() public view override returns (uint8) {
                  return _decimals;
                }
                /**
                 * @return The total supply of the token
                 **/
                function totalSupply() public view virtual override returns (uint256) {
                  return _totalSupply;
                }
                /**
                 * @return The balance of the token
                 **/
                function balanceOf(address account) public view virtual override returns (uint256) {
                  return _balances[account];
                }
                /**
                 * @dev Executes a transfer of tokens from _msgSender() to recipient
                 * @param recipient The recipient of the tokens
                 * @param amount The amount of tokens being transferred
                 * @return `true` if the transfer succeeds, `false` otherwise
                 **/
                function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                  _transfer(_msgSender(), recipient, amount);
                  emit Transfer(_msgSender(), recipient, amount);
                  return true;
                }
                /**
                 * @dev Returns the allowance of spender on the tokens owned by owner
                 * @param owner The owner of the tokens
                 * @param spender The user allowed to spend the owner's tokens
                 * @return The amount of owner's tokens spender is allowed to spend
                 **/
                function allowance(address owner, address spender)
                  public
                  view
                  virtual
                  override
                  returns (uint256)
                {
                  return _allowances[owner][spender];
                }
                /**
                 * @dev Allows `spender` to spend the tokens owned by _msgSender()
                 * @param spender The user allowed to spend _msgSender() tokens
                 * @return `true`
                 **/
                function approve(address spender, uint256 amount) public virtual override returns (bool) {
                  _approve(_msgSender(), spender, amount);
                  return true;
                }
                /**
                 * @dev Executes a transfer of token from sender to recipient, if _msgSender() is allowed to do so
                 * @param sender The owner of the tokens
                 * @param recipient The recipient of the tokens
                 * @param amount The amount of tokens being transferred
                 * @return `true` if the transfer succeeds, `false` otherwise
                 **/
                function transferFrom(
                  address sender,
                  address recipient,
                  uint256 amount
                ) public virtual override returns (bool) {
                  _transfer(sender, recipient, amount);
                  _approve(
                    sender,
                    _msgSender(),
                    _allowances[sender][_msgSender()].sub(amount, 'ERC20: transfer amount exceeds allowance')
                  );
                  emit Transfer(sender, recipient, amount);
                  return true;
                }
                /**
                 * @dev Increases the allowance of spender to spend _msgSender() tokens
                 * @param spender The user allowed to spend on behalf of _msgSender()
                 * @param addedValue The amount being added to the allowance
                 * @return `true`
                 **/
                function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
                  _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
                  return true;
                }
                /**
                 * @dev Decreases the allowance of spender to spend _msgSender() tokens
                 * @param spender The user allowed to spend on behalf of _msgSender()
                 * @param subtractedValue The amount being subtracted to the allowance
                 * @return `true`
                 **/
                function decreaseAllowance(address spender, uint256 subtractedValue)
                  public
                  virtual
                  returns (bool)
                {
                  _approve(
                    _msgSender(),
                    spender,
                    _allowances[_msgSender()][spender].sub(
                      subtractedValue,
                      'ERC20: decreased allowance below zero'
                    )
                  );
                  return true;
                }
                function _transfer(
                  address sender,
                  address recipient,
                  uint256 amount
                ) internal virtual {
                  require(sender != address(0), 'ERC20: transfer from the zero address');
                  require(recipient != address(0), 'ERC20: transfer to the zero address');
                  _beforeTokenTransfer(sender, recipient, amount);
                  uint256 oldSenderBalance = _balances[sender];
                  _balances[sender] = oldSenderBalance.sub(amount, 'ERC20: transfer amount exceeds balance');
                  uint256 oldRecipientBalance = _balances[recipient];
                  _balances[recipient] = _balances[recipient].add(amount);
                  if (address(_incentivesController) != address(0)) {
                    uint256 currentTotalSupply = _totalSupply;
                    _incentivesController.handleAction(sender, currentTotalSupply, oldSenderBalance);
                    if (sender != recipient) {
                      _incentivesController.handleAction(recipient, currentTotalSupply, oldRecipientBalance);
                    }
                  }
                }
                function _mint(address account, uint256 amount) internal virtual {
                  require(account != address(0), 'ERC20: mint to the zero address');
                  _beforeTokenTransfer(address(0), account, amount);
                  uint256 oldTotalSupply = _totalSupply;
                  _totalSupply = oldTotalSupply.add(amount);
                  uint256 oldAccountBalance = _balances[account];
                  _balances[account] = oldAccountBalance.add(amount);
                  if (address(_incentivesController) != address(0)) {
                    _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance);
                  }
                }
                function _burn(address account, uint256 amount) internal virtual {
                  require(account != address(0), 'ERC20: burn from the zero address');
                  _beforeTokenTransfer(account, address(0), amount);
                  uint256 oldTotalSupply = _totalSupply;
                  _totalSupply = oldTotalSupply.sub(amount);
                  uint256 oldAccountBalance = _balances[account];
                  _balances[account] = oldAccountBalance.sub(amount, 'ERC20: burn amount exceeds balance');
                  if (address(_incentivesController) != address(0)) {
                    _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance);
                  }
                }
                function _approve(
                  address owner,
                  address spender,
                  uint256 amount
                ) internal virtual {
                  require(owner != address(0), 'ERC20: approve from the zero address');
                  require(spender != address(0), 'ERC20: approve to the zero address');
                  _allowances[owner][spender] = amount;
                  emit Approval(owner, spender, amount);
                }
                function _setName(string memory newName) internal {
                  _name = newName;
                }
                function _setSymbol(string memory newSymbol) internal {
                  _symbol = newSymbol;
                }
                function _setDecimals(uint8 newDecimals) internal {
                  _decimals = newDecimals;
                }
                function _beforeTokenTransfer(
                  address from,
                  address to,
                  uint256 amount
                ) internal virtual {}
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              interface IAaveIncentivesController {
                function handleAction(
                  address user,
                  uint256 userBalance,
                  uint256 totalSupply
                ) external;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {AToken} from '../../protocol/tokenization/AToken.sol';
              import {LendingPool} from '../../protocol/lendingpool/LendingPool.sol';
              contract MockAToken is AToken {
                constructor(
                  LendingPool pool,
                  address underlyingAssetAddress,
                  address reserveTreasury,
                  string memory tokenName,
                  string memory tokenSymbol,
                  address incentivesController
                )
                  public
                  AToken(
                    pool,
                    underlyingAssetAddress,
                    reserveTreasury,
                    tokenName,
                    tokenSymbol,
                    incentivesController
                  )
                {}
                function getRevision() internal pure override returns (uint256) {
                  return 0x2;
                }
                function initialize(
                  uint8 _underlyingAssetDecimals,
                  string calldata _tokenName,
                  string calldata _tokenSymbol
                ) external virtual override initializer {
                  _setName(_tokenName);
                  _setSymbol(_tokenSymbol);
                  _setDecimals(_underlyingAssetDecimals);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
              import {SafeERC20} from '../../dependencies/openzeppelin/contracts/SafeERC20.sol';
              import {Address} from '../../dependencies/openzeppelin/contracts/Address.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              import {IAToken} from '../../interfaces/IAToken.sol';
              import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol';
              import {IFlashLoanReceiver} from '../../flashloan/interfaces/IFlashLoanReceiver.sol';
              import {IPriceOracleGetter} from '../../interfaces/IPriceOracleGetter.sol';
              import {IStableDebtToken} from '../../interfaces/IStableDebtToken.sol';
              import {ILendingPool} from '../../interfaces/ILendingPool.sol';
              import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
              import {Helpers} from '../libraries/helpers/Helpers.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              import {WadRayMath} from '../libraries/math/WadRayMath.sol';
              import {PercentageMath} from '../libraries/math/PercentageMath.sol';
              import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
              import {GenericLogic} from '../libraries/logic/GenericLogic.sol';
              import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol';
              import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol';
              import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol';
              import {DataTypes} from '../libraries/types/DataTypes.sol';
              import {LendingPoolStorage} from './LendingPoolStorage.sol';
              /**
               * @title LendingPool contract
               * @dev Main point of interaction with an Aave protocol's market
               * - Users can:
               *   # Deposit
               *   # Withdraw
               *   # Borrow
               *   # Repay
               *   # Swap their loans between variable and stable rate
               *   # Enable/disable their deposits as collateral rebalance stable rate borrow positions
               *   # Liquidate positions
               *   # Execute Flash Loans
               * - To be covered by a proxy contract, owned by the LendingPoolAddressesProvider of the specific market
               * - All admin functions are callable by the LendingPoolConfigurator contract defined also in the
               *   LendingPoolAddressesProvider
               * @author Aave
               **/
              contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage {
                using SafeMath for uint256;
                using WadRayMath for uint256;
                using PercentageMath for uint256;
                using SafeERC20 for IERC20;
                //main configuration parameters
                uint256 public constant MAX_STABLE_RATE_BORROW_SIZE_PERCENT = 2500;
                uint256 public constant FLASHLOAN_PREMIUM_TOTAL = 9;
                uint256 public constant MAX_NUMBER_RESERVES = 128;
                uint256 public constant LENDINGPOOL_REVISION = 0x3;
                modifier whenNotPaused() {
                  _whenNotPaused();
                  _;
                }
                modifier onlyLendingPoolConfigurator() {
                  _onlyLendingPoolConfigurator();
                  _;
                }
                function _whenNotPaused() internal view {
                  require(!_paused, Errors.LP_IS_PAUSED);
                }
                function _onlyLendingPoolConfigurator() internal view {
                  require(
                    _addressesProvider.getLendingPoolConfigurator() == msg.sender,
                    Errors.LP_CALLER_NOT_LENDING_POOL_CONFIGURATOR
                  );
                }
                function getRevision() internal pure override returns (uint256) {
                  return LENDINGPOOL_REVISION;
                }
                /**
                 * @dev Function is invoked by the proxy contract when the LendingPool contract is added to the
                 * LendingPoolAddressesProvider of the market.
                 * - Caching the address of the LendingPoolAddressesProvider in order to reduce gas consumption
                 *   on subsequent operations
                 * @param provider The address of the LendingPoolAddressesProvider
                 **/
                function initialize(ILendingPoolAddressesProvider provider) public initializer {
                  _addressesProvider = provider;
                }
                /**
                 * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
                 * - E.g. User deposits 100 USDC and gets in return 100 aUSDC
                 * @param asset The address of the underlying asset to deposit
                 * @param amount The amount to be deposited
                 * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
                 *   wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
                 *   is a different wallet
                 * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                 *   0 if the action is executed directly by the user, without any middle-man
                 **/
                function deposit(
                  address asset,
                  uint256 amount,
                  address onBehalfOf,
                  uint16 referralCode
                ) external override whenNotPaused {
                  DataTypes.ReserveData storage reserve = _reserves[asset];
                  ValidationLogic.validateDeposit(reserve, amount);
                  address aToken = reserve.aTokenAddress;
                  reserve.updateState();
                  reserve.updateInterestRates(asset, aToken, amount, 0);
                  IERC20(asset).safeTransferFrom(msg.sender, aToken, amount);
                  bool isFirstDeposit = IAToken(aToken).mint(onBehalfOf, amount, reserve.liquidityIndex);
                  if (isFirstDeposit) {
                    _usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true);
                    emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf);
                  }
                  emit Deposit(asset, msg.sender, onBehalfOf, amount, referralCode);
                }
                /**
                 * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
                 * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
                 * @param asset The address of the underlying asset to withdraw
                 * @param amount The underlying amount to be withdrawn
                 *   - Send the value type(uint256).max in order to withdraw the whole aToken balance
                 * @param to Address that will receive the underlying, same as msg.sender if the user
                 *   wants to receive it on his own wallet, or a different address if the beneficiary is a
                 *   different wallet
                 * @return The final amount withdrawn
                 **/
                function withdraw(
                  address asset,
                  uint256 amount,
                  address to
                ) external override whenNotPaused  returns (uint256) {
                  DataTypes.ReserveData storage reserve = _reserves[asset];
                  address aToken = reserve.aTokenAddress;
                  uint256 userBalance = IAToken(aToken).balanceOf(msg.sender);
                  uint256 amountToWithdraw = amount;
                  if (amount == type(uint256).max) {
                    amountToWithdraw = userBalance;
                  }
                  ValidationLogic.validateWithdraw(
                    asset,
                    amountToWithdraw,
                    userBalance,
                    _reserves,
                    _usersConfig[msg.sender],
                    _reservesList,
                    _reservesCount,
                    _addressesProvider.getPriceOracle()
                  );
                  reserve.updateState();
                  reserve.updateInterestRates(asset, aToken, 0, amountToWithdraw);
                  if (amountToWithdraw == userBalance) {
                    _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, false);
                    emit ReserveUsedAsCollateralDisabled(asset, msg.sender);
                  }
                  IAToken(aToken).burn(msg.sender, to, amountToWithdraw, reserve.liquidityIndex);
                  emit Withdraw(asset, msg.sender, to, amountToWithdraw);
                  return amountToWithdraw;
                }
                /**
                 * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower
                 * already deposited enough collateral, or he was given enough allowance by a credit delegator on the
                 * corresponding debt token (StableDebtToken or VariableDebtToken)
                 * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet
                 *   and 100 stable/variable debt tokens, depending on the `interestRateMode`
                 * @param asset The address of the underlying asset to borrow
                 * @param amount The amount to be borrowed
                 * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable
                 * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                 *   0 if the action is executed directly by the user, without any middle-man
                 * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself
                 * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator
                 * if he has been given credit delegation allowance
                 **/
                function borrow(
                  address asset,
                  uint256 amount,
                  uint256 interestRateMode,
                  uint16 referralCode,
                  address onBehalfOf
                ) external override whenNotPaused {
                  DataTypes.ReserveData storage reserve = _reserves[asset];
                  _executeBorrow(
                    ExecuteBorrowParams(
                      asset,
                      msg.sender,
                      onBehalfOf,
                      amount,
                      interestRateMode,
                      reserve.aTokenAddress,
                      referralCode,
                      true
                    )
                  );
                }
                /**
                 * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned
                 * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address
                 * @param asset The address of the borrowed underlying asset previously borrowed
                 * @param amount The amount to repay
                 * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode`
                 * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable
                 * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the
                 * user calling the function if he wants to reduce/remove his own debt, or the address of any other
                 * other borrower whose debt should be removed
                 * @return The final amount repaid
                 **/
                function repay(
                  address asset,
                  uint256 amount,
                  uint256 rateMode,
                  address onBehalfOf
                ) external override whenNotPaused returns (uint256) {
                  DataTypes.ReserveData storage reserve = _reserves[asset];
                  (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(onBehalfOf, reserve);
                  DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode);
                  ValidationLogic.validateRepay(
                    reserve,
                    amount,
                    interestRateMode,
                    onBehalfOf,
                    stableDebt,
                    variableDebt
                  );
                  uint256 paybackAmount =
                    interestRateMode == DataTypes.InterestRateMode.STABLE ? stableDebt : variableDebt;
                  if (amount < paybackAmount) {
                    paybackAmount = amount;
                  }
                  reserve.updateState();
                  if (interestRateMode == DataTypes.InterestRateMode.STABLE) {
                    IStableDebtToken(reserve.stableDebtTokenAddress).burn(onBehalfOf, paybackAmount);
                  } else {
                    IVariableDebtToken(reserve.variableDebtTokenAddress).burn(
                      onBehalfOf,
                      paybackAmount,
                      reserve.variableBorrowIndex
                    );
                  }
                  address aToken = reserve.aTokenAddress;
                  reserve.updateInterestRates(asset, aToken, paybackAmount, 0);
                  if (stableDebt.add(variableDebt).sub(paybackAmount) == 0) {
                    _usersConfig[onBehalfOf].setBorrowing(reserve.id, false);
                  }
                  IERC20(asset).safeTransferFrom(msg.sender, aToken, paybackAmount);
                  emit Repay(asset, onBehalfOf, msg.sender, paybackAmount);
                  return paybackAmount;
                }
                /**
                 * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa
                 * @param asset The address of the underlying asset borrowed
                 * @param rateMode The rate mode that the user wants to swap to
                 **/
                function swapBorrowRateMode(address asset, uint256 rateMode) external override whenNotPaused {
                  DataTypes.ReserveData storage reserve = _reserves[asset];
                  (uint256 stableDebt, uint256 variableDebt) = Helpers.getUserCurrentDebt(msg.sender, reserve);
                  DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode);
                  ValidationLogic.validateSwapRateMode(
                    reserve,
                    _usersConfig[msg.sender],
                    stableDebt,
                    variableDebt,
                    interestRateMode
                  );
                  reserve.updateState();
                  if (interestRateMode == DataTypes.InterestRateMode.STABLE) {
                    IStableDebtToken(reserve.stableDebtTokenAddress).burn(msg.sender, stableDebt);
                    IVariableDebtToken(reserve.variableDebtTokenAddress).mint(
                      msg.sender,
                      msg.sender,
                      stableDebt,
                      reserve.variableBorrowIndex
                    );
                  } else {
                    IVariableDebtToken(reserve.variableDebtTokenAddress).burn(
                      msg.sender,
                      variableDebt,
                      reserve.variableBorrowIndex
                    );
                    IStableDebtToken(reserve.stableDebtTokenAddress).mint(
                      msg.sender,
                      msg.sender,
                      variableDebt,
                      reserve.currentStableBorrowRate
                    );
                  }
                  reserve.updateInterestRates(asset, reserve.aTokenAddress, 0, 0);
                  emit Swap(asset, msg.sender, rateMode);
                }
                /**
                 * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve.
                 * - Users can be rebalanced if the following conditions are satisfied:
                 *     1. Usage ratio is above 95%
                 *     2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been
                 *        borrowed at a stable rate and depositors are not earning enough
                 * @param asset The address of the underlying asset borrowed
                 * @param user The address of the user to be rebalanced
                 **/
                function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused {
                  DataTypes.ReserveData storage reserve = _reserves[asset];
                  IERC20 stableDebtToken = IERC20(reserve.stableDebtTokenAddress);
                  IERC20 variableDebtToken = IERC20(reserve.variableDebtTokenAddress);
                  address aTokenAddress = reserve.aTokenAddress;
                  uint256 stableDebt = IERC20(stableDebtToken).balanceOf(user);
                  ValidationLogic.validateRebalanceStableBorrowRate(
                    reserve,
                    asset,
                    stableDebtToken,
                    variableDebtToken,
                    aTokenAddress
                  );
                  reserve.updateState();
                  IStableDebtToken(address(stableDebtToken)).burn(user, stableDebt);
                  IStableDebtToken(address(stableDebtToken)).mint(
                    user,
                    user,
                    stableDebt,
                    reserve.currentStableBorrowRate
                  );
                  reserve.updateInterestRates(asset, aTokenAddress, 0, 0);
                  emit RebalanceStableBorrowRate(asset, user);
                }
                /**
                 * @dev Allows depositors to enable/disable a specific deposited asset as collateral
                 * @param asset The address of the underlying asset deposited
                 * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise
                 **/
                function setUserUseReserveAsCollateral(address asset, bool useAsCollateral)
                  external
                  override
                  whenNotPaused
                {
                  DataTypes.ReserveData storage reserve = _reserves[asset];
                  ValidationLogic.validateSetUseReserveAsCollateral(
                    reserve,
                    asset,
                    useAsCollateral,
                    _reserves,
                    _usersConfig[msg.sender],
                    _reservesList,
                    _reservesCount,
                    _addressesProvider.getPriceOracle()
                  );
                  _usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral);
                  if (useAsCollateral) {
                    emit ReserveUsedAsCollateralEnabled(asset, msg.sender);
                  } else {
                    emit ReserveUsedAsCollateralDisabled(asset, msg.sender);
                  }
                }
                /**
                 * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1
                 * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
                 *   a proportionally amount of the `collateralAsset` plus a bonus to cover market risk
                 * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation
                 * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
                 * @param user The address of the borrower getting liquidated
                 * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover
                 * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants
                 * to receive the underlying collateral asset directly
                 **/
                function liquidationCall(
                  address collateralAsset,
                  address debtAsset,
                  address user,
                  uint256 debtToCover,
                  bool receiveAToken
                ) external override whenNotPaused {
                  address collateralManager = _addressesProvider.getLendingPoolCollateralManager();
                  //solium-disable-next-line
                  (bool success, bytes memory result) =
                    collateralManager.delegatecall(
                      abi.encodeWithSignature(
                        'liquidationCall(address,address,address,uint256,bool)',
                        collateralAsset,
                        debtAsset,
                        user,
                        debtToCover,
                        receiveAToken
                      )
                    );
                  require(success, Errors.LP_LIQUIDATION_CALL_FAILED);
                  (uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string));
                  require(returnCode == 0, string(abi.encodePacked(returnMessage)));
                }
                struct FlashLoanLocalVars {
                  IFlashLoanReceiver receiver;
                  address oracle;
                  uint256 i;
                  address currentAsset;
                  address currentATokenAddress;
                  uint256 currentAmount;
                  uint256 currentPremium;
                  uint256 currentAmountPlusPremium;
                  address debtToken;
                }
                /**
                 * @dev Allows smartcontracts to access the liquidity of the pool within one transaction,
                 * as long as the amount taken plus a fee is returned.
                 * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration.
                 * For further details please visit https://developers.aave.com
                 * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface
                 * @param assets The addresses of the assets being flash-borrowed
                 * @param amounts The amounts amounts being flash-borrowed
                 * @param modes Types of the debt to open if the flash loan is not returned:
                 *   0 -> Don't open any debt, just revert if funds can't be transferred from the receiver
                 *   1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
                 *   2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address
                 * @param onBehalfOf The address  that will receive the debt in the case of using on `modes` 1 or 2
                 * @param params Variadic packed params to pass to the receiver as extra information
                 * @param referralCode Code used to register the integrator originating the operation, for potential rewards.
                 *   0 if the action is executed directly by the user, without any middle-man
                 **/
                function flashLoan(
                  address receiverAddress,
                  address[] calldata assets,
                  uint256[] calldata amounts,
                  uint256[] calldata modes,
                  address onBehalfOf,
                  bytes calldata params,
                  uint16 referralCode
                ) external override whenNotPaused {
                  FlashLoanLocalVars memory vars;
                  ValidationLogic.validateFlashloan(assets, amounts);
                  address[] memory aTokenAddresses = new address[](assets.length);
                  uint256[] memory premiums = new uint256[](assets.length);
                  vars.receiver = IFlashLoanReceiver(receiverAddress);
                  for (vars.i = 0; vars.i < assets.length; vars.i++) {
                    aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress;
                    premiums[vars.i] = amounts[vars.i].mul(FLASHLOAN_PREMIUM_TOTAL).div(10000);
                    IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]);
                  }
                  require(
                    vars.receiver.executeOperation(assets, amounts, premiums, msg.sender, params),
                    Errors.LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN
                  );
                  for (vars.i = 0; vars.i < assets.length; vars.i++) {
                    vars.currentAsset = assets[vars.i];
                    vars.currentAmount = amounts[vars.i];
                    vars.currentPremium = premiums[vars.i];
                    vars.currentATokenAddress = aTokenAddresses[vars.i];
                    vars.currentAmountPlusPremium = vars.currentAmount.add(vars.currentPremium);
                    if (DataTypes.InterestRateMode(modes[vars.i]) == DataTypes.InterestRateMode.NONE) {
                      _reserves[vars.currentAsset].updateState();
                      _reserves[vars.currentAsset].cumulateToLiquidityIndex(
                        IERC20(vars.currentATokenAddress).totalSupply(),
                        vars.currentPremium
                      );
                      _reserves[vars.currentAsset].updateInterestRates(
                        vars.currentAsset,
                        vars.currentATokenAddress,
                        vars.currentAmountPlusPremium,
                        0
                      );
                      IERC20(vars.currentAsset).safeTransferFrom(
                        receiverAddress,
                        vars.currentATokenAddress,
                        vars.currentAmountPlusPremium
                      );
                    } else {
                      // If the user chose to not return the funds, the system checks if there is enough collateral and
                      // eventually opens a debt position
                      _executeBorrow(
                        ExecuteBorrowParams(
                          vars.currentAsset,
                          msg.sender,
                          onBehalfOf,
                          vars.currentAmount,
                          modes[vars.i],
                          vars.currentATokenAddress,
                          referralCode,
                          false
                        )
                      );
                    }
                    emit FlashLoan(
                      receiverAddress,
                      msg.sender,
                      vars.currentAsset,
                      vars.currentAmount,
                      vars.currentPremium,
                      referralCode
                    );
                  }
                }
                /**
                 * @dev Returns the state and configuration of the reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @return The state of the reserve
                 **/
                function getReserveData(address asset)
                  external
                  view
                  override
                  returns (DataTypes.ReserveData memory)
                {
                  return _reserves[asset];
                }
                /**
                 * @dev Returns the user account data across all the reserves
                 * @param user The address of the user
                 * @return totalCollateralETH the total collateral in ETH of the user
                 * @return totalDebtETH the total debt in ETH of the user
                 * @return availableBorrowsETH the borrowing power left of the user
                 * @return currentLiquidationThreshold the liquidation threshold of the user
                 * @return ltv the loan to value of the user
                 * @return healthFactor the current health factor of the user
                 **/
                function getUserAccountData(address user)
                  external
                  view
                  override
                  returns (
                    uint256 totalCollateralETH,
                    uint256 totalDebtETH,
                    uint256 availableBorrowsETH,
                    uint256 currentLiquidationThreshold,
                    uint256 ltv,
                    uint256 healthFactor
                  )
                {
                  (
                    totalCollateralETH,
                    totalDebtETH,
                    ltv,
                    currentLiquidationThreshold,
                    healthFactor
                  ) = GenericLogic.calculateUserAccountData(
                    user,
                    _reserves,
                    _usersConfig[user],
                    _reservesList,
                    _reservesCount,
                    _addressesProvider.getPriceOracle()
                  );
                  availableBorrowsETH = GenericLogic.calculateAvailableBorrowsETH(
                    totalCollateralETH,
                    totalDebtETH,
                    ltv
                  );
                }
                /**
                 * @dev Returns the configuration of the reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @return The configuration of the reserve
                 **/
                function getConfiguration(address asset)
                  external
                  view
                  override
                  returns (DataTypes.ReserveConfigurationMap memory)
                {
                  return _reserves[asset].configuration;
                }
                /**
                 * @dev Returns the configuration of the user across all the reserves
                 * @param user The user address
                 * @return The configuration of the user
                 **/
                function getUserConfiguration(address user)
                  external
                  view
                  override
                  returns (DataTypes.UserConfigurationMap memory)
                {
                  return _usersConfig[user];
                }
                /**
                 * @dev Returns the normalized income per unit of asset
                 * @param asset The address of the underlying asset of the reserve
                 * @return The reserve's normalized income
                 */
                function getReserveNormalizedIncome(address asset)
                  external
                  view
                  virtual
                  override
                  returns (uint256)
                {
                  return _reserves[asset].getNormalizedIncome();
                }
                /**
                 * @dev Returns the normalized variable debt per unit of asset
                 * @param asset The address of the underlying asset of the reserve
                 * @return The reserve normalized variable debt
                 */
                function getReserveNormalizedVariableDebt(address asset)
                  external
                  view
                  override
                  returns (uint256)
                {
                  return _reserves[asset].getNormalizedDebt();
                }
                /**
                 * @dev Returns if the LendingPool is paused
                 */
                function paused() external view override returns (bool) {
                  return _paused;
                }
                /**
                 * @dev Returns the list of the initialized reserves
                 **/
                function getReservesList() external view override returns (address[] memory) {
                  address[] memory _activeReserves = new address[](_reservesCount);
                  for (uint256 i = 0; i < _reservesCount; i++) {
                    _activeReserves[i] = _reservesList[i];
                  }
                  return _activeReserves;
                }
                /**
                 * @dev Returns the cached LendingPoolAddressesProvider connected to this contract
                 **/
                function getAddressesProvider() external view override returns (ILendingPoolAddressesProvider) {
                  return _addressesProvider;
                }
                /**
                 * @dev Validates and finalizes an aToken transfer
                 * - Only callable by the overlying aToken of the `asset`
                 * @param asset The address of the underlying asset of the aToken
                 * @param from The user from which the aTokens are transferred
                 * @param to The user receiving the aTokens
                 * @param amount The amount being transferred/withdrawn
                 * @param balanceFromBefore The aToken balance of the `from` user before the transfer
                 * @param balanceToBefore The aToken balance of the `to` user before the transfer
                 */
                function finalizeTransfer(
                  address asset,
                  address from,
                  address to,
                  uint256 amount,
                  uint256 balanceFromBefore,
                  uint256 balanceToBefore
                ) external override whenNotPaused {
                  require(msg.sender == _reserves[asset].aTokenAddress, Errors.LP_CALLER_MUST_BE_AN_ATOKEN);
                  ValidationLogic.validateTransfer(
                    from,
                    _reserves,
                    _usersConfig[from],
                    _reservesList,
                    _reservesCount,
                    _addressesProvider.getPriceOracle()
                  );
                  uint256 reserveId = _reserves[asset].id;
                  if (from != to) {
                    if (balanceFromBefore.sub(amount) == 0) {
                      DataTypes.UserConfigurationMap storage fromConfig = _usersConfig[from];
                      fromConfig.setUsingAsCollateral(reserveId, false);
                      emit ReserveUsedAsCollateralDisabled(asset, from);
                    }
                    if (balanceToBefore == 0 && amount != 0) {
                      DataTypes.UserConfigurationMap storage toConfig = _usersConfig[to];
                      toConfig.setUsingAsCollateral(reserveId, true);
                      emit ReserveUsedAsCollateralEnabled(asset, to);
                    }
                  }
                }
                /**
                 * @dev Initializes a reserve, activating it, assigning an aToken and debt tokens and an
                 * interest rate strategy
                 * - Only callable by the LendingPoolConfigurator contract
                 * @param asset The address of the underlying asset of the reserve
                 * @param aTokenAddress The address of the aToken that will be assigned to the reserve
                 * @param stableDebtAddress The address of the StableDebtToken that will be assigned to the reserve
                 * @param aTokenAddress The address of the VariableDebtToken that will be assigned to the reserve
                 * @param interestRateStrategyAddress The address of the interest rate strategy contract
                 **/
                function initReserve(
                  address asset,
                  address aTokenAddress,
                  address stableDebtAddress,
                  address variableDebtAddress,
                  address interestRateStrategyAddress
                ) external override onlyLendingPoolConfigurator {
                  require(Address.isContract(asset), Errors.LP_NOT_CONTRACT);
                  _reserves[asset].init(
                    aTokenAddress,
                    stableDebtAddress,
                    variableDebtAddress,
                    interestRateStrategyAddress
                  );
                  _addReserveToList(asset);
                }
                /**
                 * @dev Updates the address of the interest rate strategy contract
                 * - Only callable by the LendingPoolConfigurator contract
                 * @param asset The address of the underlying asset of the reserve
                 * @param rateStrategyAddress The address of the interest rate strategy contract
                 **/
                function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress)
                  external
                  override
                  onlyLendingPoolConfigurator
                {
                  _reserves[asset].interestRateStrategyAddress = rateStrategyAddress;
                }
                /**
                 * @dev Sets the configuration bitmap of the reserve as a whole
                 * - Only callable by the LendingPoolConfigurator contract
                 * @param asset The address of the underlying asset of the reserve
                 * @param configuration The new configuration bitmap
                 **/
                function setConfiguration(address asset, uint256 configuration)
                  external
                  override
                  onlyLendingPoolConfigurator
                {
                  _reserves[asset].configuration.data = configuration;
                }
                /**
                 * @dev Set the _pause state of a reserve
                 * - Only callable by the LendingPoolConfigurator contract
                 * @param val `true` to pause the reserve, `false` to un-pause it
                 */
                function setPause(bool val) external override onlyLendingPoolConfigurator {
                  _paused = val;
                  if (_paused) {
                    emit Paused();
                  } else {
                    emit Unpaused();
                  }
                }
                struct ExecuteBorrowParams {
                  address asset;
                  address user;
                  address onBehalfOf;
                  uint256 amount;
                  uint256 interestRateMode;
                  address aTokenAddress;
                  uint16 referralCode;
                  bool releaseUnderlying;
                }
                function _executeBorrow(ExecuteBorrowParams memory vars) internal {
                  DataTypes.ReserveData storage reserve = _reserves[vars.asset];
                  DataTypes.UserConfigurationMap storage userConfig = _usersConfig[vars.onBehalfOf];
                  address oracle = _addressesProvider.getPriceOracle();
                  uint256 amountInETH =
                    IPriceOracleGetter(oracle).getAssetPrice(vars.asset).mul(vars.amount).div(
                      10**reserve.configuration.getDecimals()
                    );
                  ValidationLogic.validateBorrow(
                    vars.asset,
                    reserve,
                    vars.onBehalfOf,
                    vars.amount,
                    amountInETH,
                    vars.interestRateMode,
                    MAX_STABLE_RATE_BORROW_SIZE_PERCENT,
                    _reserves,
                    userConfig,
                    _reservesList,
                    _reservesCount,
                    oracle
                  );
                  reserve.updateState();
                  uint256 currentStableRate = 0;
                  bool isFirstBorrowing = false;
                  if (DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE) {
                    currentStableRate = reserve.currentStableBorrowRate;
                    isFirstBorrowing = IStableDebtToken(reserve.stableDebtTokenAddress).mint(
                      vars.user,
                      vars.onBehalfOf,
                      vars.amount,
                      currentStableRate
                    );
                  } else {
                    isFirstBorrowing = IVariableDebtToken(reserve.variableDebtTokenAddress).mint(
                      vars.user,
                      vars.onBehalfOf,
                      vars.amount,
                      reserve.variableBorrowIndex
                    );
                  }
                  if (isFirstBorrowing) {
                    userConfig.setBorrowing(reserve.id, true);
                  }
                  reserve.updateInterestRates(
                    vars.asset,
                    vars.aTokenAddress,
                    0,
                    vars.releaseUnderlying ? vars.amount : 0
                  );
                  if (vars.releaseUnderlying) {
                    IAToken(vars.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount);
                  }
                  emit Borrow(
                    vars.asset,
                    vars.user,
                    vars.onBehalfOf,
                    vars.amount,
                    vars.interestRateMode,
                    DataTypes.InterestRateMode(vars.interestRateMode) == DataTypes.InterestRateMode.STABLE
                      ? currentStableRate
                      : reserve.currentVariableBorrowRate,
                    vars.referralCode
                  );
                }
                function _addReserveToList(address asset) internal {
                  uint256 reservesCount = _reservesCount;
                  require(reservesCount < MAX_NUMBER_RESERVES, Errors.LP_NO_MORE_RESERVES_ALLOWED);
                  bool reserveAlreadyAdded = _reserves[asset].id != 0 || _reservesList[0] == asset;
                  if (!reserveAlreadyAdded) {
                    _reserves[asset].id = uint8(reservesCount);
                    _reservesList[reservesCount] = asset;
                    _reservesCount = reservesCount + 1;
                  }
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol';
              import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
              import {IWETH} from './interfaces/IWETH.sol';
              import {IWETHGateway} from './interfaces/IWETHGateway.sol';
              import {ILendingPool} from '../interfaces/ILendingPool.sol';
              import {IAToken} from '../interfaces/IAToken.sol';
              import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol';
              import {UserConfiguration} from '../protocol/libraries/configuration/UserConfiguration.sol';
              import {Helpers} from '../protocol/libraries/helpers/Helpers.sol';
              import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
              contract WETHGateway is IWETHGateway, Ownable {
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                using UserConfiguration for DataTypes.UserConfigurationMap;
                IWETH internal immutable WETH;
                ILendingPool internal immutable POOL;
                IAToken internal immutable aWETH;
                /**
                 * @dev Sets the WETH address and the LendingPoolAddressesProvider address. Infinite approves lending pool.
                 * @param weth Address of the Wrapped Ether contract
                 * @param pool Address of the LendingPool contract
                 **/
                constructor(address weth, address pool) public {
                  ILendingPool poolInstance = ILendingPool(pool);
                  WETH = IWETH(weth);
                  POOL = poolInstance;
                  aWETH = IAToken(poolInstance.getReserveData(weth).aTokenAddress);
                  IWETH(weth).approve(pool, uint256(-1));
                }
                /**
                 * @dev deposits WETH into the reserve, using native ETH. A corresponding amount of the overlying asset (aTokens)
                 * is minted.
                 * @param onBehalfOf address of the user who will receive the aTokens representing the deposit
                 * @param referralCode integrators are assigned a referral code and can potentially receive rewards.
                 **/
                function depositETH(address onBehalfOf, uint16 referralCode) external payable override {
                  WETH.deposit{value: msg.value}();
                  POOL.deposit(address(WETH), msg.value, onBehalfOf, referralCode);
                }
                /**
                 * @dev withdraws the WETH _reserves of msg.sender.
                 * @param amount amount of aWETH to withdraw and receive native ETH
                 * @param to address of the user who will receive native ETH
                 */
                function withdrawETH(uint256 amount, address to) external override {
                  uint256 userBalance = aWETH.balanceOf(msg.sender);
                  uint256 amountToWithdraw = amount;
                  // if amount is equal to uint(-1), the user wants to redeem everything
                  if (amount == type(uint256).max) {
                    amountToWithdraw = userBalance;
                  }
                  aWETH.transferFrom(msg.sender, address(this), amountToWithdraw);
                  POOL.withdraw(address(WETH), amountToWithdraw, address(this));
                  WETH.withdraw(amountToWithdraw);
                  _safeTransferETH(to, amountToWithdraw);
                }
                /**
                 * @dev repays a borrow on the WETH reserve, for the specified amount (or for the whole amount, if uint256(-1) is specified).
                 * @param amount the amount to repay, or uint256(-1) if the user wants to repay everything
                 * @param rateMode the rate mode to repay
                 * @param onBehalfOf the address for which msg.sender is repaying
                 */
                function repayETH(
                  uint256 amount,
                  uint256 rateMode,
                  address onBehalfOf
                ) external payable override {
                  (uint256 stableDebt, uint256 variableDebt) =
                    Helpers.getUserCurrentDebtMemory(onBehalfOf, POOL.getReserveData(address(WETH)));
                  uint256 paybackAmount =
                    DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE
                      ? stableDebt
                      : variableDebt;
                  if (amount < paybackAmount) {
                    paybackAmount = amount;
                  }
                  require(msg.value >= paybackAmount, 'msg.value is less than repayment amount');
                  WETH.deposit{value: paybackAmount}();
                  POOL.repay(address(WETH), msg.value, rateMode, onBehalfOf);
                  // refund remaining dust eth
                  if (msg.value > paybackAmount) _safeTransferETH(msg.sender, msg.value - paybackAmount);
                }
                /**
                 * @dev borrow WETH, unwraps to ETH and send both the ETH and DebtTokens to msg.sender, via `approveDelegation` and onBehalf argument in `LendingPool.borrow`.
                 * @param amount the amount of ETH to borrow
                 * @param interesRateMode the interest rate mode
                 * @param referralCode integrators are assigned a referral code and can potentially receive rewards
                 */
                function borrowETH(
                  uint256 amount,
                  uint256 interesRateMode,
                  uint16 referralCode
                ) external override {
                  POOL.borrow(address(WETH), amount, interesRateMode, referralCode, msg.sender);
                  WETH.withdraw(amount);
                  _safeTransferETH(msg.sender, amount);
                }
                /**
                 * @dev transfer ETH to an address, revert if it fails.
                 * @param to recipient of the transfer
                 * @param value the amount to send
                 */
                function _safeTransferETH(address to, uint256 value) internal {
                  (bool success, ) = to.call{value: value}(new bytes(0));
                  require(success, 'ETH_TRANSFER_FAILED');
                }
                /**
                 * @dev transfer ERC20 from the utility contract, for ERC20 recovery in case of stuck tokens due
                 * direct transfers to the contract address.
                 * @param token token to transfer
                 * @param to recipient of the transfer
                 * @param amount amount to send
                 */
                function emergencyTokenTransfer(
                  address token,
                  address to,
                  uint256 amount
                ) external onlyOwner {
                  IERC20(token).transfer(to, amount);
                }
                /**
                 * @dev transfer native Ether from the utility contract, for native Ether recovery in case of stuck Ether
                 * due selfdestructs or transfer ether to pre-computated contract address before deployment.
                 * @param to recipient of the transfer
                 * @param amount amount to send
                 */
                function emergencyEtherTransfer(address to, uint256 amount) external onlyOwner {
                  _safeTransferETH(to, amount);
                }
                /**
                 * @dev Get WETH address used by WETHGateway
                 */
                function getWETHAddress() external view returns (address) {
                  return address(WETH);
                }
                /**
                 * @dev Get aWETH address used by WETHGateway
                 */
                function getAWETHAddress() external view returns (address) {
                  return address(aWETH);
                }
                /**
                 * @dev Get LendingPool address used by WETHGateway
                 */
                function getLendingPoolAddress() external view returns (address) {
                  return address(POOL);
                }
                /**
                 * @dev Only WETH contract is allowed to transfer ETH here. Prevent other addresses to send Ether to this contract.
                 */
                receive() external payable {
                  require(msg.sender == address(WETH), 'Receive not allowed');
                }
                /**
                 * @dev Revert fallback calls
                 */
                fallback() external payable {
                  revert('Fallback not allowed');
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              interface IWETH {
                function deposit() external payable;
                function withdraw(uint256) external;
                function approve(address guy, uint256 wad) external returns (bool);
                function transferFrom(
                  address src,
                  address dst,
                  uint256 wad
                ) external returns (bool);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              interface IWETHGateway {
                function depositETH(address onBehalfOf, uint16 referralCode) external payable;
                function withdrawETH(uint256 amount, address onBehalfOf) external;
                function repayETH(
                  uint256 amount,
                  uint256 rateMode,
                  address onBehalfOf
                ) external payable;
                function borrowETH(
                  uint256 amount,
                  uint256 interesRateMode,
                  uint16 referralCode
                ) external;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {VersionedInitializable} from '../libraries/aave-upgradeability/VersionedInitializable.sol';
              import {
                InitializableImmutableAdminUpgradeabilityProxy
              } from '../libraries/aave-upgradeability/InitializableImmutableAdminUpgradeabilityProxy.sol';
              import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              import {ILendingPool} from '../../interfaces/ILendingPool.sol';
              import {ITokenConfiguration} from '../../interfaces/ITokenConfiguration.sol';
              import {IERC20Detailed} from '../../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              import {PercentageMath} from '../libraries/math/PercentageMath.sol';
              import {DataTypes} from '../libraries/types/DataTypes.sol';
              /**
               * @title LendingPoolConfigurator contract
               * @author Aave
               * @dev Implements the configuration methods for the Aave protocol
               **/
              contract LendingPoolConfigurator is VersionedInitializable {
                using SafeMath for uint256;
                using PercentageMath for uint256;
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                /**
                 * @dev Emitted when a reserve is initialized.
                 * @param asset The address of the underlying asset of the reserve
                 * @param aToken The address of the associated aToken contract
                 * @param stableDebtToken The address of the associated stable rate debt token
                 * @param variableDebtToken The address of the associated variable rate debt token
                 * @param interestRateStrategyAddress The address of the interest rate strategy for the reserve
                 **/
                event ReserveInitialized(
                  address indexed asset,
                  address indexed aToken,
                  address stableDebtToken,
                  address variableDebtToken,
                  address interestRateStrategyAddress
                );
                /**
                 * @dev Emitted when borrowing is enabled on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @param stableRateEnabled True if stable rate borrowing is enabled, false otherwise
                 **/
                event BorrowingEnabledOnReserve(address indexed asset, bool stableRateEnabled);
                /**
                 * @dev Emitted when borrowing is disabled on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                event BorrowingDisabledOnReserve(address indexed asset);
                /**
                 * @dev Emitted when the collateralization risk parameters for the specified asset are updated.
                 * @param asset The address of the underlying asset of the reserve
                 * @param ltv The loan to value of the asset when used as collateral
                 * @param liquidationThreshold The threshold at which loans using this asset as collateral will be considered undercollateralized
                 * @param liquidationBonus The bonus liquidators receive to liquidate this asset
                 **/
                event CollateralConfigurationChanged(
                  address indexed asset,
                  uint256 ltv,
                  uint256 liquidationThreshold,
                  uint256 liquidationBonus
                );
                /**
                 * @dev Emitted when stable rate borrowing is enabled on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                event StableRateEnabledOnReserve(address indexed asset);
                /**
                 * @dev Emitted when stable rate borrowing is disabled on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                event StableRateDisabledOnReserve(address indexed asset);
                /**
                 * @dev Emitted when a reserve is activated
                 * @param asset The address of the underlying asset of the reserve
                 **/
                event ReserveActivated(address indexed asset);
                /**
                 * @dev Emitted when a reserve is deactivated
                 * @param asset The address of the underlying asset of the reserve
                 **/
                event ReserveDeactivated(address indexed asset);
                /**
                 * @dev Emitted when a reserve is frozen
                 * @param asset The address of the underlying asset of the reserve
                 **/
                event ReserveFrozen(address indexed asset);
                /**
                 * @dev Emitted when a reserve is unfrozen
                 * @param asset The address of the underlying asset of the reserve
                 **/
                event ReserveUnfrozen(address indexed asset);
                /**
                 * @dev Emitted when a reserve factor is updated
                 * @param asset The address of the underlying asset of the reserve
                 * @param factor The new reserve factor
                 **/
                event ReserveFactorChanged(address indexed asset, uint256 factor);
                /**
                 * @dev Emitted when the reserve decimals are updated
                 * @param asset The address of the underlying asset of the reserve
                 * @param decimals The new decimals
                 **/
                event ReserveDecimalsChanged(address indexed asset, uint256 decimals);
                /**
                 * @dev Emitted when a reserve interest strategy contract is updated
                 * @param asset The address of the underlying asset of the reserve
                 * @param strategy The new address of the interest strategy contract
                 **/
                event ReserveInterestRateStrategyChanged(address indexed asset, address strategy);
                /**
                 * @dev Emitted when an aToken implementation is upgraded
                 * @param asset The address of the underlying asset of the reserve
                 * @param proxy The aToken proxy address
                 * @param implementation The new aToken implementation
                 **/
                event ATokenUpgraded(
                  address indexed asset,
                  address indexed proxy,
                  address indexed implementation
                );
                /**
                 * @dev Emitted when the implementation of a stable debt token is upgraded
                 * @param asset The address of the underlying asset of the reserve
                 * @param proxy The stable debt token proxy address
                 * @param implementation The new aToken implementation
                 **/
                event StableDebtTokenUpgraded(
                  address indexed asset,
                  address indexed proxy,
                  address indexed implementation
                );
                /**
                 * @dev Emitted when the implementation of a variable debt token is upgraded
                 * @param asset The address of the underlying asset of the reserve
                 * @param proxy The variable debt token proxy address
                 * @param implementation The new aToken implementation
                 **/
                event VariableDebtTokenUpgraded(
                  address indexed asset,
                  address indexed proxy,
                  address indexed implementation
                );
                ILendingPoolAddressesProvider internal addressesProvider;
                ILendingPool internal pool;
                modifier onlyPoolAdmin {
                  require(addressesProvider.getPoolAdmin() == msg.sender, Errors.CALLER_NOT_POOL_ADMIN);
                  _;
                }
                modifier onlyEmergencyAdmin {
                  require(
                    addressesProvider.getEmergencyAdmin() == msg.sender,
                    Errors.LPC_CALLER_NOT_EMERGENCY_ADMIN
                  );
                  _;
                }
                uint256 internal constant CONFIGURATOR_REVISION = 0x1;
                function getRevision() internal pure override returns (uint256) {
                  return CONFIGURATOR_REVISION;
                }
                function initialize(ILendingPoolAddressesProvider provider) public initializer {
                  addressesProvider = provider;
                  pool = ILendingPool(addressesProvider.getLendingPool());
                }
                /**
                 * @dev Initializes a reserve
                 * @param aTokenImpl  The address of the aToken contract implementation
                 * @param stableDebtTokenImpl The address of the stable debt token contract
                 * @param variableDebtTokenImpl The address of the variable debt token contract
                 * @param underlyingAssetDecimals The decimals of the reserve underlying asset
                 * @param interestRateStrategyAddress The address of the interest rate strategy contract for this reserve
                 **/
                function initReserve(
                  address aTokenImpl,
                  address stableDebtTokenImpl,
                  address variableDebtTokenImpl,
                  uint8 underlyingAssetDecimals,
                  address interestRateStrategyAddress
                ) public onlyPoolAdmin {
                  address asset = ITokenConfiguration(aTokenImpl).UNDERLYING_ASSET_ADDRESS();
                  require(
                    address(pool) == ITokenConfiguration(aTokenImpl).POOL(),
                    Errors.LPC_INVALID_ATOKEN_POOL_ADDRESS
                  );
                  require(
                    address(pool) == ITokenConfiguration(stableDebtTokenImpl).POOL(),
                    Errors.LPC_INVALID_STABLE_DEBT_TOKEN_POOL_ADDRESS
                  );
                  require(
                    address(pool) == ITokenConfiguration(variableDebtTokenImpl).POOL(),
                    Errors.LPC_INVALID_VARIABLE_DEBT_TOKEN_POOL_ADDRESS
                  );
                  require(
                    asset == ITokenConfiguration(stableDebtTokenImpl).UNDERLYING_ASSET_ADDRESS(),
                    Errors.LPC_INVALID_STABLE_DEBT_TOKEN_UNDERLYING_ADDRESS
                  );
                  require(
                    asset == ITokenConfiguration(variableDebtTokenImpl).UNDERLYING_ASSET_ADDRESS(),
                    Errors.LPC_INVALID_VARIABLE_DEBT_TOKEN_UNDERLYING_ADDRESS
                  );
                  address aTokenProxyAddress = _initTokenWithProxy(aTokenImpl, underlyingAssetDecimals);
                  address stableDebtTokenProxyAddress =
                    _initTokenWithProxy(stableDebtTokenImpl, underlyingAssetDecimals);
                  address variableDebtTokenProxyAddress =
                    _initTokenWithProxy(variableDebtTokenImpl, underlyingAssetDecimals);
                  pool.initReserve(
                    asset,
                    aTokenProxyAddress,
                    stableDebtTokenProxyAddress,
                    variableDebtTokenProxyAddress,
                    interestRateStrategyAddress
                  );
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setDecimals(underlyingAssetDecimals);
                  currentConfig.setActive(true);
                  currentConfig.setFrozen(false);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit ReserveInitialized(
                    asset,
                    aTokenProxyAddress,
                    stableDebtTokenProxyAddress,
                    variableDebtTokenProxyAddress,
                    interestRateStrategyAddress
                  );
                }
                /**
                 * @dev Updates the aToken implementation for the reserve
                 * @param asset The address of the underlying asset of the reserve to be updated
                 * @param implementation The address of the new aToken implementation
                 **/
                function updateAToken(address asset, address implementation) external onlyPoolAdmin {
                  DataTypes.ReserveData memory reserveData = pool.getReserveData(asset);
                  _upgradeTokenImplementation(asset, reserveData.aTokenAddress, implementation);
                  emit ATokenUpgraded(asset, reserveData.aTokenAddress, implementation);
                }
                /**
                 * @dev Updates the stable debt token implementation for the reserve
                 * @param asset The address of the underlying asset of the reserve to be updated
                 * @param implementation The address of the new aToken implementation
                 **/
                function updateStableDebtToken(address asset, address implementation) external onlyPoolAdmin {
                  DataTypes.ReserveData memory reserveData = pool.getReserveData(asset);
                  _upgradeTokenImplementation(asset, reserveData.stableDebtTokenAddress, implementation);
                  emit StableDebtTokenUpgraded(asset, reserveData.stableDebtTokenAddress, implementation);
                }
                /**
                 * @dev Updates the variable debt token implementation for the asset
                 * @param asset The address of the underlying asset of the reserve to be updated
                 * @param implementation The address of the new aToken implementation
                 **/
                function updateVariableDebtToken(address asset, address implementation) external onlyPoolAdmin {
                  DataTypes.ReserveData memory reserveData = pool.getReserveData(asset);
                  _upgradeTokenImplementation(asset, reserveData.variableDebtTokenAddress, implementation);
                  emit VariableDebtTokenUpgraded(asset, reserveData.variableDebtTokenAddress, implementation);
                }
                /**
                 * @dev Enables borrowing on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @param stableBorrowRateEnabled True if stable borrow rate needs to be enabled by default on this reserve
                 **/
                function enableBorrowingOnReserve(address asset, bool stableBorrowRateEnabled)
                  external
                  onlyPoolAdmin
                {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setBorrowingEnabled(true);
                  currentConfig.setStableRateBorrowingEnabled(stableBorrowRateEnabled);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit BorrowingEnabledOnReserve(asset, stableBorrowRateEnabled);
                }
                /**
                 * @dev Disables borrowing on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                function disableBorrowingOnReserve(address asset) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setBorrowingEnabled(false);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit BorrowingDisabledOnReserve(asset);
                }
                /**
                 * @dev Configures the reserve collateralization parameters
                 * all the values are expressed in percentages with two decimals of precision. A valid value is 10000, which means 100.00%
                 * @param asset The address of the underlying asset of the reserve
                 * @param ltv The loan to value of the asset when used as collateral
                 * @param liquidationThreshold The threshold at which loans using this asset as collateral will be considered undercollateralized
                 * @param liquidationBonus The bonus liquidators receive to liquidate this asset. The values is always above 100%. A value of 105%
                 * means the liquidator will receive a 5% bonus
                 **/
                function configureReserveAsCollateral(
                  address asset,
                  uint256 ltv,
                  uint256 liquidationThreshold,
                  uint256 liquidationBonus
                ) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  //validation of the parameters: the LTV can
                  //only be lower or equal than the liquidation threshold
                  //(otherwise a loan against the asset would cause instantaneous liquidation)
                  require(ltv <= liquidationThreshold, Errors.LPC_INVALID_CONFIGURATION);
                  if (liquidationThreshold != 0) {
                    //liquidation bonus must be bigger than 100.00%, otherwise the liquidator would receive less
                    //collateral than needed to cover the debt
                    require(
                      liquidationBonus > PercentageMath.PERCENTAGE_FACTOR,
                      Errors.LPC_INVALID_CONFIGURATION
                    );
                    //if threshold * bonus is less than PERCENTAGE_FACTOR, it's guaranteed that at the moment
                    //a loan is taken there is enough collateral available to cover the liquidation bonus
                    require(
                      liquidationThreshold.percentMul(liquidationBonus) <= PercentageMath.PERCENTAGE_FACTOR,
                      Errors.LPC_INVALID_CONFIGURATION
                    );
                  } else {
                    require(liquidationBonus == 0, Errors.LPC_INVALID_CONFIGURATION);
                    //if the liquidation threshold is being set to 0,
                    // the reserve is being disabled as collateral. To do so,
                    //we need to ensure no liquidity is deposited
                    _checkNoLiquidity(asset);
                  }
                  currentConfig.setLtv(ltv);
                  currentConfig.setLiquidationThreshold(liquidationThreshold);
                  currentConfig.setLiquidationBonus(liquidationBonus);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit CollateralConfigurationChanged(asset, ltv, liquidationThreshold, liquidationBonus);
                }
                /**
                 * @dev Enable stable rate borrowing on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                function enableReserveStableRate(address asset) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setStableRateBorrowingEnabled(true);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit StableRateEnabledOnReserve(asset);
                }
                /**
                 * @dev Disable stable rate borrowing on a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                function disableReserveStableRate(address asset) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setStableRateBorrowingEnabled(false);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit StableRateDisabledOnReserve(asset);
                }
                /**
                 * @dev Activates a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                function activateReserve(address asset) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setActive(true);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit ReserveActivated(asset);
                }
                /**
                 * @dev Deactivates a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                function deactivateReserve(address asset) external onlyPoolAdmin {
                  _checkNoLiquidity(asset);
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setActive(false);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit ReserveDeactivated(asset);
                }
                /**
                 * @dev Freezes a reserve. A frozen reserve doesn't allow any new deposit, borrow or rate swap
                 *  but allows repayments, liquidations, rate rebalances and withdrawals
                 * @param asset The address of the underlying asset of the reserve
                 **/
                function freezeReserve(address asset) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setFrozen(true);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit ReserveFrozen(asset);
                }
                /**
                 * @dev Unfreezes a reserve
                 * @param asset The address of the underlying asset of the reserve
                 **/
                function unfreezeReserve(address asset) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setFrozen(false);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit ReserveUnfrozen(asset);
                }
                /**
                 * @dev Updates the reserve factor of a reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @param reserveFactor The new reserve factor of the reserve
                 **/
                function setReserveFactor(address asset, uint256 reserveFactor) external onlyPoolAdmin {
                  DataTypes.ReserveConfigurationMap memory currentConfig = pool.getConfiguration(asset);
                  currentConfig.setReserveFactor(reserveFactor);
                  pool.setConfiguration(asset, currentConfig.data);
                  emit ReserveFactorChanged(asset, reserveFactor);
                }
                /**
                 * @dev Sets the interest rate strategy of a reserve
                 * @param asset The address of the underlying asset of the reserve
                 * @param rateStrategyAddress The new address of the interest strategy contract
                 **/
                function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress)
                  external
                  onlyPoolAdmin
                {
                  pool.setReserveInterestRateStrategyAddress(asset, rateStrategyAddress);
                  emit ReserveInterestRateStrategyChanged(asset, rateStrategyAddress);
                }
                /**
                 * @dev pauses or unpauses all the actions of the protocol, including aToken transfers
                 * @param val true if protocol needs to be paused, false otherwise
                 **/
                function setPoolPause(bool val) external onlyEmergencyAdmin {
                  pool.setPause(val);
                }
                function _initTokenWithProxy(address implementation, uint8 decimals) internal returns (address) {
                  InitializableImmutableAdminUpgradeabilityProxy proxy =
                    new InitializableImmutableAdminUpgradeabilityProxy(address(this));
                  bytes memory params =
                    abi.encodeWithSignature(
                      'initialize(uint8,string,string)',
                      decimals,
                      IERC20Detailed(implementation).name(),
                      IERC20Detailed(implementation).symbol()
                    );
                  proxy.initialize(implementation, params);
                  return address(proxy);
                }
                function _upgradeTokenImplementation(
                  address asset,
                  address proxyAddress,
                  address implementation
                ) internal {
                  InitializableImmutableAdminUpgradeabilityProxy proxy =
                    InitializableImmutableAdminUpgradeabilityProxy(payable(proxyAddress));
                  DataTypes.ReserveConfigurationMap memory configuration = pool.getConfiguration(asset);
                  (, , , uint256 decimals, ) = configuration.getParamsMemory();
                  bytes memory params =
                    abi.encodeWithSignature(
                      'initialize(uint8,string,string)',
                      uint8(decimals),
                      IERC20Detailed(implementation).name(),
                      IERC20Detailed(implementation).symbol()
                    );
                  proxy.upgradeToAndCall(implementation, params);
                }
                function _checkNoLiquidity(address asset) internal view {
                  DataTypes.ReserveData memory reserveData = pool.getReserveData(asset);
                  uint256 availableLiquidity = IERC20Detailed(asset).balanceOf(reserveData.aTokenAddress);
                  require(
                    availableLiquidity == 0 && reserveData.currentLiquidityRate == 0,
                    Errors.LPC_RESERVE_LIQUIDITY_NOT_0
                  );
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import './BaseImmutableAdminUpgradeabilityProxy.sol';
              import '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
              /**
               * @title InitializableAdminUpgradeabilityProxy
               * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
               */
              contract InitializableImmutableAdminUpgradeabilityProxy is
                BaseImmutableAdminUpgradeabilityProxy,
                InitializableUpgradeabilityProxy
              {
                constructor(address admin) public BaseImmutableAdminUpgradeabilityProxy(admin) {}
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                  BaseImmutableAdminUpgradeabilityProxy._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity ^0.6.12;
              /**
               * @title ITokenConfiguration
               * @author Aave
               * @dev Common interface between aTokens and debt tokens to fetch the
               * token configuration
               **/
              interface ITokenConfiguration {
                function UNDERLYING_ASSET_ADDRESS() external view returns (address);
                function POOL() external view returns (address);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
              /**
               * @title BaseImmutableAdminUpgradeabilityProxy
               * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
               * @dev This contract combines an upgradeability proxy with an authorization
               * mechanism for administrative tasks. The admin role is stored in an immutable, which
               * helps saving transactions costs
               * 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 BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
                address immutable ADMIN;
                constructor(address admin) public {
                  ADMIN = admin;
                }
                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 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);
                }
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal virtual override {
                  require(msg.sender != ADMIN, 'Cannot call fallback function from the proxy admin');
                  super._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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);
                  }
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import './Proxy.sol';
              import '../contracts/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)
                  }
                }
              }
              // 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`.
                 */
                fallback() 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());
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {IERC20Detailed} from '../dependencies/openzeppelin/contracts/IERC20Detailed.sol';
              import {ILendingPoolAddressesProvider} from '../interfaces/ILendingPoolAddressesProvider.sol';
              import {ILendingPool} from '../interfaces/ILendingPool.sol';
              import {IStableDebtToken} from '../interfaces/IStableDebtToken.sol';
              import {IVariableDebtToken} from '../interfaces/IVariableDebtToken.sol';
              import {ReserveConfiguration} from '../protocol/libraries/configuration/ReserveConfiguration.sol';
              import {UserConfiguration} from '../protocol/libraries/configuration/UserConfiguration.sol';
              import {DataTypes} from '../protocol/libraries/types/DataTypes.sol';
              contract AaveProtocolDataProvider {
                using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
                using UserConfiguration for DataTypes.UserConfigurationMap;
                address constant MKR = 0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2;
                address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                struct TokenData {
                  string symbol;
                  address tokenAddress;
                }
                ILendingPoolAddressesProvider public immutable ADDRESSES_PROVIDER;
                constructor(ILendingPoolAddressesProvider addressesProvider) public {
                  ADDRESSES_PROVIDER = addressesProvider;
                }
                function getAllReservesTokens() external view returns (TokenData[] memory) {
                  ILendingPool pool = ILendingPool(ADDRESSES_PROVIDER.getLendingPool());
                  address[] memory reserves = pool.getReservesList();
                  TokenData[] memory reservesTokens = new TokenData[](reserves.length);
                  for (uint256 i = 0; i < reserves.length; i++) {
                    if (reserves[i] == MKR) {
                      reservesTokens[i] = TokenData({symbol: 'MKR', tokenAddress: reserves[i]});
                      continue;
                    }
                    if (reserves[i] == ETH) {
                      reservesTokens[i] = TokenData({symbol: 'ETH', tokenAddress: reserves[i]});
                      continue;
                    }
                    reservesTokens[i] = TokenData({
                      symbol: IERC20Detailed(reserves[i]).symbol(),
                      tokenAddress: reserves[i]
                    });
                  }
                  return reservesTokens;
                }
                function getAllATokens() external view returns (TokenData[] memory) {
                  ILendingPool pool = ILendingPool(ADDRESSES_PROVIDER.getLendingPool());
                  address[] memory reserves = pool.getReservesList();
                  TokenData[] memory aTokens = new TokenData[](reserves.length);
                  for (uint256 i = 0; i < reserves.length; i++) {
                    DataTypes.ReserveData memory reserveData = pool.getReserveData(reserves[i]);
                    aTokens[i] = TokenData({
                      symbol: IERC20Detailed(reserveData.aTokenAddress).symbol(),
                      tokenAddress: reserveData.aTokenAddress
                    });
                  }
                  return aTokens;
                }
                function getReserveConfigurationData(address asset)
                  external
                  view
                  returns (
                    uint256 decimals,
                    uint256 ltv,
                    uint256 liquidationThreshold,
                    uint256 liquidationBonus,
                    uint256 reserveFactor,
                    bool usageAsCollateralEnabled,
                    bool borrowingEnabled,
                    bool stableBorrowRateEnabled,
                    bool isActive,
                    bool isFrozen
                  )
                {
                  DataTypes.ReserveConfigurationMap memory configuration =
                    ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getConfiguration(asset);
                  (ltv, liquidationThreshold, liquidationBonus, decimals, reserveFactor) = configuration
                    .getParamsMemory();
                  (isActive, isFrozen, borrowingEnabled, stableBorrowRateEnabled) = configuration
                    .getFlagsMemory();
                  usageAsCollateralEnabled = liquidationThreshold > 0;
                }
                function getReserveData(address asset)
                  external
                  view
                  returns (
                    uint256 availableLiquidity,
                    uint256 totalStableDebt,
                    uint256 totalVariableDebt,
                    uint256 liquidityRate,
                    uint256 variableBorrowRate,
                    uint256 stableBorrowRate,
                    uint256 averageStableBorrowRate,
                    uint256 liquidityIndex,
                    uint256 variableBorrowIndex,
                    uint40 lastUpdateTimestamp
                  )
                {
                  DataTypes.ReserveData memory reserve =
                    ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getReserveData(asset);
                  return (
                    IERC20Detailed(asset).balanceOf(reserve.aTokenAddress),
                    IERC20Detailed(reserve.stableDebtTokenAddress).totalSupply(),
                    IERC20Detailed(reserve.variableDebtTokenAddress).totalSupply(),
                    reserve.currentLiquidityRate,
                    reserve.currentVariableBorrowRate,
                    reserve.currentStableBorrowRate,
                    IStableDebtToken(reserve.stableDebtTokenAddress).getAverageStableRate(),
                    reserve.liquidityIndex,
                    reserve.variableBorrowIndex,
                    reserve.lastUpdateTimestamp
                  );
                }
                function getUserReserveData(address asset, address user)
                  external
                  view
                  returns (
                    uint256 currentATokenBalance,
                    uint256 currentStableDebt,
                    uint256 currentVariableDebt,
                    uint256 principalStableDebt,
                    uint256 scaledVariableDebt,
                    uint256 stableBorrowRate,
                    uint256 liquidityRate,
                    uint40 stableRateLastUpdated,
                    bool usageAsCollateralEnabled
                  )
                {
                  DataTypes.ReserveData memory reserve =
                    ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getReserveData(asset);
                  DataTypes.UserConfigurationMap memory userConfig =
                    ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getUserConfiguration(user);
                  currentATokenBalance = IERC20Detailed(reserve.aTokenAddress).balanceOf(user);
                  currentVariableDebt = IERC20Detailed(reserve.variableDebtTokenAddress).balanceOf(user);
                  currentStableDebt = IERC20Detailed(reserve.stableDebtTokenAddress).balanceOf(user);
                  principalStableDebt = IStableDebtToken(reserve.stableDebtTokenAddress).principalBalanceOf(user);
                  scaledVariableDebt = IVariableDebtToken(reserve.variableDebtTokenAddress).scaledBalanceOf(user);
                  liquidityRate = reserve.currentLiquidityRate;
                  stableBorrowRate = IStableDebtToken(reserve.stableDebtTokenAddress).getUserStableRate(user);
                  stableRateLastUpdated = IStableDebtToken(reserve.stableDebtTokenAddress).getUserLastUpdated(
                    user
                  );
                  usageAsCollateralEnabled = userConfig.isUsingAsCollateral(reserve.id);
                }
                function getReserveTokensAddresses(address asset)
                  external
                  view
                  returns (
                    address aTokenAddress,
                    address stableDebtTokenAddress,
                    address variableDebtTokenAddress
                  )
                {
                  DataTypes.ReserveData memory reserve =
                    ILendingPool(ADDRESSES_PROVIDER.getLendingPool()).getReserveData(asset);
                  return (
                    reserve.aTokenAddress,
                    reserve.stableDebtTokenAddress,
                    reserve.variableDebtTokenAddress
                  );
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {IVariableDebtToken} from '../../interfaces/IVariableDebtToken.sol';
              import {WadRayMath} from '../libraries/math/WadRayMath.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              import {DebtTokenBase} from './base/DebtTokenBase.sol';
              /**
               * @title VariableDebtToken
               * @notice Implements a variable debt token to track the borrowing positions of users
               * at variable rate mode
               * @author Aave
               **/
              contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
                using WadRayMath for uint256;
                uint256 public constant DEBT_TOKEN_REVISION = 0x1;
                constructor(
                  address pool,
                  address underlyingAsset,
                  string memory name,
                  string memory symbol,
                  address incentivesController
                ) public DebtTokenBase(pool, underlyingAsset, name, symbol, incentivesController) {}
                /**
                 * @dev Gets the revision of the stable debt token implementation
                 * @return The debt token implementation revision
                 **/
                function getRevision() internal pure virtual override returns (uint256) {
                  return DEBT_TOKEN_REVISION;
                }
                /**
                 * @dev Calculates the accumulated debt balance of the user
                 * @return The debt balance of the user
                 **/
                function balanceOf(address user) public view virtual override returns (uint256) {
                  uint256 scaledBalance = super.balanceOf(user);
                  if (scaledBalance == 0) {
                    return 0;
                  }
                  return scaledBalance.rayMul(POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET_ADDRESS));
                }
                /**
                 * @dev Mints debt token to the `onBehalfOf` address
                 * -  Only callable by the LendingPool
                 * @param user The address receiving the borrowed underlying, being the delegatee in case
                 * of credit delegate, or same as `onBehalfOf` otherwise
                 * @param onBehalfOf The address receiving the debt tokens
                 * @param amount The amount of debt being minted
                 * @param index The variable debt index of the reserve
                 * @return `true` if the the previous balance of the user is 0
                 **/
                function mint(
                  address user,
                  address onBehalfOf,
                  uint256 amount,
                  uint256 index
                ) external override onlyLendingPool returns (bool) {
                  if (user != onBehalfOf) {
                    _decreaseBorrowAllowance(onBehalfOf, user, amount);
                  }
                  uint256 previousBalance = super.balanceOf(onBehalfOf);
                  uint256 amountScaled = amount.rayDiv(index);
                  require(amountScaled != 0, Errors.CT_INVALID_MINT_AMOUNT);
                  _mint(onBehalfOf, amountScaled);
                  emit Transfer(address(0), onBehalfOf, amount);
                  emit Mint(user, onBehalfOf, amount, index);
                  return previousBalance == 0;
                }
                /**
                 * @dev Burns user variable debt
                 * - Only callable by the LendingPool
                 * @param user The user whose debt is getting burned
                 * @param amount The amount getting burned
                 * @param index The variable debt index of the reserve
                 **/
                function burn(
                  address user,
                  uint256 amount,
                  uint256 index
                ) external override onlyLendingPool {
                  uint256 amountScaled = amount.rayDiv(index);
                  require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT);
                  _burn(user, amountScaled);
                  emit Transfer(user, address(0), amount);
                  emit Burn(user, amount, index);
                }
                /**
                 * @dev Returns the principal debt balance of the user from
                 * @return The debt balance of the user since the last burn/mint action
                 **/
                function scaledBalanceOf(address user) public view virtual override returns (uint256) {
                  return super.balanceOf(user);
                }
                /**
                 * @dev Returns the total supply of the variable debt token. Represents the total debt accrued by the users
                 * @return The total supply
                 **/
                function totalSupply() public view virtual override returns (uint256) {
                  return
                    super.totalSupply().rayMul(POOL.getReserveNormalizedVariableDebt(UNDERLYING_ASSET_ADDRESS));
                }
                /**
                 * @dev Returns the scaled total supply of the variable debt token. Represents sum(debt/index)
                 * @return the scaled total supply
                 **/
                function scaledTotalSupply() public view virtual override returns (uint256) {
                  return super.totalSupply();
                }
                /**
                 * @dev Returns the principal balance of the user and principal total supply.
                 * @param user The address of the user
                 * @return The principal balance of the user
                 * @return The principal total supply
                 **/
                function getScaledUserBalanceAndSupply(address user)
                  external
                  view
                  override
                  returns (uint256, uint256)
                {
                  return (super.balanceOf(user), super.totalSupply());
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {ILendingPool} from '../../../interfaces/ILendingPool.sol';
              import {ICreditDelegationToken} from '../../../interfaces/ICreditDelegationToken.sol';
              import {
                VersionedInitializable
              } from '../../libraries/aave-upgradeability/VersionedInitializable.sol';
              import {IncentivizedERC20} from '../IncentivizedERC20.sol';
              import {Errors} from '../../libraries/helpers/Errors.sol';
              /**
               * @title DebtTokenBase
               * @notice Base contract for different types of debt tokens, like StableDebtToken or VariableDebtToken
               * @author Aave
               */
              abstract contract DebtTokenBase is
                IncentivizedERC20,
                VersionedInitializable,
                ICreditDelegationToken
              {
                address public immutable UNDERLYING_ASSET_ADDRESS;
                ILendingPool public immutable POOL;
                mapping(address => mapping(address => uint256)) internal _borrowAllowances;
                /**
                 * @dev Only lending pool can call functions marked by this modifier
                 **/
                modifier onlyLendingPool {
                  require(_msgSender() == address(POOL), Errors.CT_CALLER_MUST_BE_LENDING_POOL);
                  _;
                }
                /**
                 * @dev The metadata of the token will be set on the proxy, that the reason of
                 * passing "NULL" and 0 as metadata
                 */
                constructor(
                  address pool,
                  address underlyingAssetAddress,
                  string memory name,
                  string memory symbol,
                  address incentivesController
                ) public IncentivizedERC20(name, symbol, 18, incentivesController) {
                  POOL = ILendingPool(pool);
                  UNDERLYING_ASSET_ADDRESS = underlyingAssetAddress;
                }
                /**
                 * @dev Initializes the debt token.
                 * @param name The name of the token
                 * @param symbol The symbol of the token
                 * @param decimals The decimals of the token
                 */
                function initialize(
                  uint8 decimals,
                  string memory name,
                  string memory symbol
                ) public initializer {
                  _setName(name);
                  _setSymbol(symbol);
                  _setDecimals(decimals);
                }
                /**
                 * @dev delegates borrowing power to a user on the specific debt token
                 * @param delegatee the address receiving the delegated borrowing power
                 * @param amount the maximum amount being delegated. Delegation will still
                 * respect the liquidation constraints (even if delegated, a delegatee cannot
                 * force a delegator HF to go below 1)
                 **/
                function approveDelegation(address delegatee, uint256 amount) external override {
                  _borrowAllowances[_msgSender()][delegatee] = amount;
                  emit BorrowAllowanceDelegated(_msgSender(), delegatee, UNDERLYING_ASSET_ADDRESS, amount);
                }
                /**
                 * @dev returns the borrow allowance of the user
                 * @param fromUser The user to giving allowance
                 * @param toUser The user to give allowance to
                 * @return the current allowance of toUser
                 **/
                function borrowAllowance(address fromUser, address toUser)
                  external
                  view
                  override
                  returns (uint256)
                {
                  return _borrowAllowances[fromUser][toUser];
                }
                /**
                 * @dev Being non transferrable, the debt token does not implement any of the
                 * standard ERC20 functions for transfer and allowance.
                 **/
                function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
                  recipient;
                  amount;
                  revert('TRANSFER_NOT_SUPPORTED');
                }
                function allowance(address owner, address spender)
                  public
                  view
                  virtual
                  override
                  returns (uint256)
                {
                  owner;
                  spender;
                  revert('ALLOWANCE_NOT_SUPPORTED');
                }
                function approve(address spender, uint256 amount) public virtual override returns (bool) {
                  spender;
                  amount;
                  revert('APPROVAL_NOT_SUPPORTED');
                }
                function transferFrom(
                  address sender,
                  address recipient,
                  uint256 amount
                ) public virtual override returns (bool) {
                  sender;
                  recipient;
                  amount;
                  revert('TRANSFER_NOT_SUPPORTED');
                }
                function increaseAllowance(address spender, uint256 addedValue)
                  public
                  virtual
                  override
                  returns (bool)
                {
                  spender;
                  addedValue;
                  revert('ALLOWANCE_NOT_SUPPORTED');
                }
                function decreaseAllowance(address spender, uint256 subtractedValue)
                  public
                  virtual
                  override
                  returns (bool)
                {
                  spender;
                  subtractedValue;
                  revert('ALLOWANCE_NOT_SUPPORTED');
                }
                function _decreaseBorrowAllowance(
                  address delegator,
                  address delegatee,
                  uint256 amount
                ) internal {
                  uint256 newAllowance =
                    _borrowAllowances[delegator][delegatee].sub(amount, Errors.BORROW_ALLOWANCE_NOT_ENOUGH);
                  _borrowAllowances[delegator][delegatee] = newAllowance;
                  emit BorrowAllowanceDelegated(delegator, delegatee, UNDERLYING_ASSET_ADDRESS, newAllowance);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              interface ICreditDelegationToken {
                event BorrowAllowanceDelegated(
                  address indexed fromUser,
                  address indexed toUser,
                  address asset,
                  uint256 amount
                );
                /**
                 * @dev delegates borrowing power to a user on the specific debt token
                 * @param delegatee the address receiving the delegated borrowing power
                 * @param amount the maximum amount being delegated. Delegation will still
                 * respect the liquidation constraints (even if delegated, a delegatee cannot
                 * force a delegator HF to go below 1)
                 **/
                function approveDelegation(address delegatee, uint256 amount) external;
                /**
                 * @dev returns the borrow allowance of the user
                 * @param fromUser The user to giving allowance
                 * @param toUser The user to give allowance to
                 * @return the current allowance of toUser
                 **/
                function borrowAllowance(address fromUser, address toUser) external view returns (uint256);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {VariableDebtToken} from '../../protocol/tokenization/VariableDebtToken.sol';
              contract MockVariableDebtToken is VariableDebtToken {
                constructor(
                  address _pool,
                  address _underlyingAssetAddress,
                  string memory _tokenName,
                  string memory _tokenSymbol,
                  address incentivesController
                )
                  public
                  VariableDebtToken(
                    _pool,
                    _underlyingAssetAddress,
                    _tokenName,
                    _tokenSymbol,
                    incentivesController
                  )
                {}
                function getRevision() internal pure override returns (uint256) {
                  return 0x2;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {IERC20} from '../dependencies/openzeppelin/contracts/IERC20.sol';
              interface IExchangeAdapter {
                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);
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {ERC20} from '../../dependencies/openzeppelin/contracts/ERC20.sol';
              /**
               * @title ERC20Mintable
               * @dev ERC20 minting logic
               */
              contract MintableDelegationERC20 is ERC20 {
                address public delegatee;
                constructor(
                  string memory name,
                  string memory symbol,
                  uint8 decimals
                ) public ERC20(name, symbol) {
                  _setupDecimals(decimals);
                }
                /**
                 * @dev Function to mint tokensp
                 * @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;
                }
                function delegate(address delegateeAddress) external {
                  delegatee = delegateeAddress;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {DebtTokenBase} from './base/DebtTokenBase.sol';
              import {MathUtils} from '../libraries/math/MathUtils.sol';
              import {WadRayMath} from '../libraries/math/WadRayMath.sol';
              import {IStableDebtToken} from '../../interfaces/IStableDebtToken.sol';
              import {Errors} from '../libraries/helpers/Errors.sol';
              /**
               * @title StableDebtToken
               * @notice Implements a stable debt token to track the borrowing positions of users
               * at stable rate mode
               * @author Aave
               **/
              contract StableDebtToken is IStableDebtToken, DebtTokenBase {
                using WadRayMath for uint256;
                uint256 public constant DEBT_TOKEN_REVISION = 0x1;
                uint256 internal _avgStableRate;
                mapping(address => uint40) internal _timestamps;
                mapping(address => uint256) internal _usersStableRate;
                uint40 internal _totalSupplyTimestamp;
                constructor(
                  address pool,
                  address underlyingAsset,
                  string memory name,
                  string memory symbol,
                  address incentivesController
                ) public DebtTokenBase(pool, underlyingAsset, name, symbol, incentivesController) {}
                /**
                 * @dev Gets the revision of the stable debt token implementation
                 * @return The debt token implementation revision
                 **/
                function getRevision() internal pure virtual override returns (uint256) {
                  return DEBT_TOKEN_REVISION;
                }
                /**
                 * @dev Returns the average stable rate across all the stable rate debt
                 * @return the average stable rate
                 **/
                function getAverageStableRate() external view virtual override returns (uint256) {
                  return _avgStableRate;
                }
                /**
                 * @dev Returns the timestamp of the last user action
                 * @return The last update timestamp
                 **/
                function getUserLastUpdated(address user) external view virtual override returns (uint40) {
                  return _timestamps[user];
                }
                /**
                 * @dev Returns the stable rate of the user
                 * @param user The address of the user
                 * @return The stable rate of user
                 **/
                function getUserStableRate(address user) external view virtual override returns (uint256) {
                  return _usersStableRate[user];
                }
                /**
                 * @dev Calculates the current user debt balance
                 * @return The accumulated debt of the user
                 **/
                function balanceOf(address account) public view virtual override returns (uint256) {
                  uint256 accountBalance = super.balanceOf(account);
                  uint256 stableRate = _usersStableRate[account];
                  if (accountBalance == 0) {
                    return 0;
                  }
                  uint256 cumulatedInterest =
                    MathUtils.calculateCompoundedInterest(stableRate, _timestamps[account]);
                  return accountBalance.rayMul(cumulatedInterest);
                }
                struct MintLocalVars {
                  uint256 previousSupply;
                  uint256 nextSupply;
                  uint256 amountInRay;
                  uint256 newStableRate;
                  uint256 currentAvgStableRate;
                }
                /**
                 * @dev Mints debt token to the `onBehalfOf` address.
                 * -  Only callable by the LendingPool
                 * - The resulting rate is the weighted average between the rate of the new debt
                 * and the rate of the previous debt
                 * @param user The address receiving the borrowed underlying, being the delegatee in case
                 * of credit delegate, or same as `onBehalfOf` otherwise
                 * @param onBehalfOf The address receiving the debt tokens
                 * @param amount The amount of debt tokens to mint
                 * @param rate The rate of the debt being minted
                 **/
                function mint(
                  address user,
                  address onBehalfOf,
                  uint256 amount,
                  uint256 rate
                ) external override onlyLendingPool returns (bool) {
                  MintLocalVars memory vars;
                  if (user != onBehalfOf) {
                    _decreaseBorrowAllowance(onBehalfOf, user, amount);
                  }
                  (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(onBehalfOf);
                  vars.previousSupply = totalSupply();
                  vars.currentAvgStableRate = _avgStableRate;
                  vars.nextSupply = _totalSupply = vars.previousSupply.add(amount);
                  vars.amountInRay = amount.wadToRay();
                  vars.newStableRate = _usersStableRate[onBehalfOf]
                    .rayMul(currentBalance.wadToRay())
                    .add(vars.amountInRay.rayMul(rate))
                    .rayDiv(currentBalance.add(amount).wadToRay());
                  require(vars.newStableRate <= type(uint128).max, Errors.SDT_STABLE_DEBT_OVERFLOW);
                  _usersStableRate[onBehalfOf] = vars.newStableRate;
                  //solium-disable-next-line
                  _totalSupplyTimestamp = _timestamps[onBehalfOf] = uint40(block.timestamp);
                  // Calculates the updated average stable rate
                  vars.currentAvgStableRate = _avgStableRate = vars
                    .currentAvgStableRate
                    .rayMul(vars.previousSupply.wadToRay())
                    .add(rate.rayMul(vars.amountInRay))
                    .rayDiv(vars.nextSupply.wadToRay());
                  _mint(onBehalfOf, amount.add(balanceIncrease), vars.previousSupply);
                  emit Transfer(address(0), onBehalfOf, amount);
                  emit Mint(
                    user,
                    onBehalfOf,
                    amount,
                    currentBalance,
                    balanceIncrease,
                    vars.newStableRate,
                    vars.currentAvgStableRate,
                    vars.nextSupply
                  );
                  return currentBalance == 0;
                }
                /**
                 * @dev Burns debt of `user`
                 * @param user The address of the user getting his debt burned
                 * @param amount The amount of debt tokens getting burned
                 **/
                function burn(address user, uint256 amount) external override onlyLendingPool {
                  (, uint256 currentBalance, uint256 balanceIncrease) = _calculateBalanceIncrease(user);
                  uint256 previousSupply = totalSupply();
                  uint256 newAvgStableRate = 0;
                  uint256 nextSupply = 0;
                  uint256 userStableRate = _usersStableRate[user];
                  // Since the total supply and each single user debt accrue separately,
                  // there might be accumulation errors so that the last borrower repaying
                  // mght actually try to repay more than the available debt supply.
                  // In this case we simply set the total supply and the avg stable rate to 0
                  if (previousSupply <= amount) {
                    _avgStableRate = 0;
                    _totalSupply = 0;
                  } else {
                    nextSupply = _totalSupply = previousSupply.sub(amount);
                    uint256 firstTerm = _avgStableRate.rayMul(previousSupply.wadToRay());
                    uint256 secondTerm = userStableRate.rayMul(amount.wadToRay());
                    // For the same reason described above, when the last user is repaying it might
                    // happen that user rate * user balance > avg rate * total supply. In that case,
                    // we simply set the avg rate to 0
                    if (secondTerm >= firstTerm) {
                      newAvgStableRate = _avgStableRate = _totalSupply = 0;
                    } else {
                      newAvgStableRate = _avgStableRate = firstTerm.sub(secondTerm).rayDiv(nextSupply.wadToRay());
                    }
                  }
                  if (amount == currentBalance) {
                    _usersStableRate[user] = 0;
                    _timestamps[user] = 0;
                  } else {
                    //solium-disable-next-line
                    _timestamps[user] = uint40(block.timestamp);
                  }
                  //solium-disable-next-line
                  _totalSupplyTimestamp = uint40(block.timestamp);
                  if (balanceIncrease > amount) {
                    uint256 amountToMint = balanceIncrease.sub(amount);
                    _mint(user, amountToMint, previousSupply);
                    emit Mint(
                      user,
                      user,
                      amountToMint,
                      currentBalance,
                      balanceIncrease,
                      userStableRate,
                      newAvgStableRate,
                      nextSupply
                    );
                  } else {
                    uint256 amountToBurn = amount.sub(balanceIncrease);
                    _burn(user, amountToBurn, previousSupply);
                    emit Burn(user, amountToBurn, currentBalance, balanceIncrease, newAvgStableRate, nextSupply);
                  }
                  emit Transfer(user, address(0), amount);
                }
                /**
                 * @dev Calculates the increase in balance since the last user interaction
                 * @param user The address of the user for which the interest is being accumulated
                 * @return The previous principal balance, the new principal balance and the balance increase
                 **/
                function _calculateBalanceIncrease(address user)
                  internal
                  view
                  returns (
                    uint256,
                    uint256,
                    uint256
                  )
                {
                  uint256 previousPrincipalBalance = super.balanceOf(user);
                  if (previousPrincipalBalance == 0) {
                    return (0, 0, 0);
                  }
                  // Calculation of the accrued interest since the last accumulation
                  uint256 balanceIncrease = balanceOf(user).sub(previousPrincipalBalance);
                  return (
                    previousPrincipalBalance,
                    previousPrincipalBalance.add(balanceIncrease),
                    balanceIncrease
                  );
                }
                /**
                 * @dev Returns the principal and total supply, the average borrow rate and the last supply update timestamp
                 **/
                function getSupplyData()
                  public
                  view
                  override
                  returns (
                    uint256,
                    uint256,
                    uint256,
                    uint40
                  )
                {
                  uint256 avgRate = _avgStableRate;
                  return (super.totalSupply(), _calcTotalSupply(avgRate), avgRate, _totalSupplyTimestamp);
                }
                /**
                 * @dev Returns the the total supply and the average stable rate
                 **/
                function getTotalSupplyAndAvgRate() public view override returns (uint256, uint256) {
                  uint256 avgRate = _avgStableRate;
                  return (_calcTotalSupply(avgRate), avgRate);
                }
                /**
                 * @dev Returns the total supply
                 **/
                function totalSupply() public view override returns (uint256) {
                  return _calcTotalSupply(_avgStableRate);
                }
                /**
                 * @dev Returns the timestamp at which the total supply was updated
                 **/
                function getTotalSupplyLastUpdated() public view override returns (uint40) {
                  return _totalSupplyTimestamp;
                }
                /**
                 * @dev Returns the principal debt balance of the user from
                 * @param user The user's address
                 * @return The debt balance of the user since the last burn/mint action
                 **/
                function principalBalanceOf(address user) external view virtual override returns (uint256) {
                  return super.balanceOf(user);
                }
                /**
                 * @dev Calculates the total supply
                 * @param avgRate The average rate at which the total supply increases
                 * @return The debt balance of the user since the last burn/mint action
                 **/
                function _calcTotalSupply(uint256 avgRate) internal view virtual returns (uint256) {
                  uint256 principalSupply = super.totalSupply();
                  if (principalSupply == 0) {
                    return 0;
                  }
                  uint256 cumulatedInterest =
                    MathUtils.calculateCompoundedInterest(avgRate, _totalSupplyTimestamp);
                  return principalSupply.rayMul(cumulatedInterest);
                }
                /**
                 * @dev Mints stable debt tokens to an user
                 * @param account The account receiving the debt tokens
                 * @param amount The amount being minted
                 * @param oldTotalSupply the total supply before the minting event
                 **/
                function _mint(
                  address account,
                  uint256 amount,
                  uint256 oldTotalSupply
                ) internal {
                  uint256 oldAccountBalance = _balances[account];
                  _balances[account] = oldAccountBalance.add(amount);
                  if (address(_incentivesController) != address(0)) {
                    _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance);
                  }
                }
                /**
                 * @dev Burns stable debt tokens of an user
                 * @param account The user getting his debt burned
                 * @param amount The amount being burned
                 * @param oldTotalSupply The total supply before the burning event
                 **/
                function _burn(
                  address account,
                  uint256 amount,
                  uint256 oldTotalSupply
                ) internal {
                  uint256 oldAccountBalance = _balances[account];
                  _balances[account] = oldAccountBalance.sub(amount, Errors.SDT_BURN_EXCEEDS_BALANCE);
                  if (address(_incentivesController) != address(0)) {
                    _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance);
                  }
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {StableDebtToken} from '../../protocol/tokenization/StableDebtToken.sol';
              contract MockStableDebtToken is StableDebtToken {
                constructor(
                  address _pool,
                  address _underlyingAssetAddress,
                  string memory _tokenName,
                  string memory _tokenSymbol,
                  address incentivesController
                )
                  public
                  StableDebtToken(_pool, _underlyingAssetAddress, _tokenName, _tokenSymbol, incentivesController)
                {}
                function getRevision() internal pure override returns (uint256) {
                  return 0x2;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {StableDebtToken} from '../protocol/tokenization/StableDebtToken.sol';
              import {VariableDebtToken} from '../protocol/tokenization/VariableDebtToken.sol';
              import {LendingRateOracle} from '../mocks/oracle/LendingRateOracle.sol';
              import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol';
              import {StringLib} from './StringLib.sol';
              contract StableAndVariableTokensHelper is Ownable {
                address payable private pool;
                address private addressesProvider;
                event deployedContracts(address stableToken, address variableToken);
                constructor(address payable _pool, address _addressesProvider) public {
                  pool = _pool;
                  addressesProvider = _addressesProvider;
                }
                function initDeployment(
                  address[] calldata tokens,
                  string[] calldata symbols,
                  address incentivesController
                ) external onlyOwner {
                  require(tokens.length == symbols.length, 'Arrays not same length');
                  require(pool != address(0), 'Pool can not be zero address');
                  for (uint256 i = 0; i < tokens.length; i++) {
                    emit deployedContracts(
                      address(
                        new StableDebtToken(
                          pool,
                          tokens[i],
                          StringLib.concat('Aave stable debt bearing ', symbols[i]),
                          StringLib.concat('stableDebt', symbols[i]),
                          incentivesController
                        )
                      ),
                      address(
                        new VariableDebtToken(
                          pool,
                          tokens[i],
                          StringLib.concat('Aave variable debt bearing ', symbols[i]),
                          StringLib.concat('variableDebt', symbols[i]),
                          incentivesController
                        )
                      )
                    );
                  }
                }
                function setOracleBorrowRates(
                  address[] calldata assets,
                  uint256[] calldata rates,
                  address oracle
                ) external onlyOwner {
                  require(assets.length == rates.length, 'Arrays not same length');
                  for (uint256 i = 0; i < assets.length; i++) {
                    // LendingRateOracle owner must be this contract
                    LendingRateOracle(oracle).setMarketBorrowRate(assets[i], rates[i]);
                  }
                }
                function setOracleOwnership(address oracle, address admin) external onlyOwner {
                  require(admin != address(0), 'owner can not be zero');
                  require(LendingRateOracle(oracle).owner() == address(this), 'helper is not owner');
                  LendingRateOracle(oracle).transferOwnership(admin);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              library StringLib {
                function concat(string memory a, string memory b) internal pure returns (string memory) {
                  return string(abi.encodePacked(a, b));
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              pragma experimental ABIEncoderV2;
              import {LendingPool} from '../protocol/lendingpool/LendingPool.sol';
              import {
                LendingPoolAddressesProvider
              } from '../protocol/configuration/LendingPoolAddressesProvider.sol';
              import {LendingPoolConfigurator} from '../protocol/lendingpool/LendingPoolConfigurator.sol';
              import {AToken} from '../protocol/tokenization/AToken.sol';
              import {
                DefaultReserveInterestRateStrategy
              } from '../protocol/lendingpool/DefaultReserveInterestRateStrategy.sol';
              import {Ownable} from '../dependencies/openzeppelin/contracts/Ownable.sol';
              import {StringLib} from './StringLib.sol';
              contract ATokensAndRatesHelper is Ownable {
                address payable private pool;
                address private addressesProvider;
                address private poolConfigurator;
                event deployedContracts(address aToken, address strategy);
                constructor(
                  address payable _pool,
                  address _addressesProvider,
                  address _poolConfigurator
                ) public {
                  pool = _pool;
                  addressesProvider = _addressesProvider;
                  poolConfigurator = _poolConfigurator;
                }
                function initDeployment(
                  address[] calldata assets,
                  string[] calldata symbols,
                  uint256[6][] calldata rates,
                  address treasuryAddress,
                  address incentivesController
                ) external onlyOwner {
                  require(assets.length == symbols.length, 't Arrays not same length');
                  require(rates.length == symbols.length, 'r Arrays not same length');
                  for (uint256 i = 0; i < assets.length; i++) {
                    emit deployedContracts(
                      address(
                        new AToken(
                          LendingPool(pool),
                          assets[i],
                          treasuryAddress,
                          StringLib.concat('Aave interest bearing ', symbols[i]),
                          StringLib.concat('a', symbols[i]),
                          incentivesController
                        )
                      ),
                      address(
                        new DefaultReserveInterestRateStrategy(
                          LendingPoolAddressesProvider(addressesProvider),
                          rates[i][0],
                          rates[i][1],
                          rates[i][2],
                          rates[i][3],
                          rates[i][4],
                          rates[i][5]
                        )
                      )
                    );
                  }
                }
                function initReserve(
                  address[] calldata stables,
                  address[] calldata variables,
                  address[] calldata aTokens,
                  address[] calldata strategies,
                  uint8[] calldata reserveDecimals
                ) external onlyOwner {
                  require(variables.length == stables.length);
                  require(aTokens.length == stables.length);
                  require(strategies.length == stables.length);
                  require(reserveDecimals.length == stables.length);
                  for (uint256 i = 0; i < stables.length; i++) {
                    LendingPoolConfigurator(poolConfigurator).initReserve(
                      aTokens[i],
                      stables[i],
                      variables[i],
                      reserveDecimals[i],
                      strategies[i]
                    );
                  }
                }
                function configureReserves(
                  address[] calldata assets,
                  uint256[] calldata baseLTVs,
                  uint256[] calldata liquidationThresholds,
                  uint256[] calldata liquidationBonuses,
                  uint256[] calldata reserveFactors,
                  bool[] calldata stableBorrowingEnabled
                ) external onlyOwner {
                  require(baseLTVs.length == assets.length);
                  require(liquidationThresholds.length == assets.length);
                  require(liquidationBonuses.length == assets.length);
                  require(stableBorrowingEnabled.length == assets.length);
                  require(reserveFactors.length == assets.length);
                  LendingPoolConfigurator configurator = LendingPoolConfigurator(poolConfigurator);
                  for (uint256 i = 0; i < assets.length; i++) {
                    configurator.configureReserveAsCollateral(
                      assets[i],
                      baseLTVs[i],
                      liquidationThresholds[i],
                      liquidationBonuses[i]
                    );
                    configurator.enableBorrowingOnReserve(
                      assets[i],
                      stableBorrowingEnabled[i]
                    );
                    configurator.setReserveFactor(assets[i], reserveFactors[i]);
                  }
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Ownable} from '../../dependencies/openzeppelin/contracts/Ownable.sol';
              // Prettier ignore to prevent buidler flatter bug
              // prettier-ignore
              import {InitializableImmutableAdminUpgradeabilityProxy} from '../libraries/aave-upgradeability/InitializableImmutableAdminUpgradeabilityProxy.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              /**
               * @title LendingPoolAddressesProvider contract
               * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles
               * - Acting also as factory of proxies and admin of those, so with right to change its implementations
               * - Owned by the Aave Governance
               * @author Aave
               **/
              contract LendingPoolAddressesProvider is Ownable, ILendingPoolAddressesProvider {
                string private _marketId;
                mapping(bytes32 => address) private _addresses;
                bytes32 private constant LENDING_POOL = 'LENDING_POOL';
                bytes32 private constant LENDING_POOL_CONFIGURATOR = 'LENDING_POOL_CONFIGURATOR';
                bytes32 private constant POOL_ADMIN = 'POOL_ADMIN';
                bytes32 private constant EMERGENCY_ADMIN = 'EMERGENCY_ADMIN';
                bytes32 private constant LENDING_POOL_COLLATERAL_MANAGER = 'COLLATERAL_MANAGER';
                bytes32 private constant PRICE_ORACLE = 'PRICE_ORACLE';
                bytes32 private constant LENDING_RATE_ORACLE = 'LENDING_RATE_ORACLE';
                constructor(string memory marketId) public {
                  _setMarketId(marketId);
                }
                /**
                 * @dev Returns the id of the Aave market to which this contracts points to
                 * @return The market id
                 **/
                function getMarketId() external view override returns (string memory) {
                  return _marketId;
                }
                /**
                 * @dev Allows to set the market which this LendingPoolAddressesProvider represents
                 * @param marketId The market id
                 */
                function setMarketId(string memory marketId) external override onlyOwner {
                  _setMarketId(marketId);
                }
                /**
                 * @dev General function to update the implementation of a proxy registered with
                 * certain `id`. If there is no proxy registered, it will instantiate one and
                 * set as implementation the `implementationAddress`
                 * IMPORTANT Use this function carefully, only for ids that don't have an explicit
                 * setter function, in order to avoid unexpected consequences
                 * @param id The id
                 * @param implementationAddress The address of the new implementation
                 */
                function setAddressAsProxy(bytes32 id, address implementationAddress)
                  external
                  override
                  onlyOwner
                {
                  _updateImpl(id, implementationAddress);
                  emit AddressSet(id, implementationAddress, true);
                }
                /**
                 * @dev Sets an address for an id replacing the address saved in the addresses map
                 * IMPORTANT Use this function carefully, as it will do a hard replacement
                 * @param id The id
                 * @param newAddress The address to set
                 */
                function setAddress(bytes32 id, address newAddress) external override onlyOwner {
                  _addresses[id] = newAddress;
                  emit AddressSet(id, newAddress, false);
                }
                /**
                 * @dev Returns an address by id
                 * @return The address
                 */
                function getAddress(bytes32 id) public view override returns (address) {
                  return _addresses[id];
                }
                /**
                 * @dev Returns the address of the LendingPool proxy
                 * @return The LendingPool proxy address
                 **/
                function getLendingPool() external view override returns (address) {
                  return getAddress(LENDING_POOL);
                }
                /**
                 * @dev Updates the implementation of the LendingPool, or creates the proxy
                 * setting the new `pool` implementation on the first time calling it
                 * @param pool The new LendingPool implementation
                 **/
                function setLendingPoolImpl(address pool) external override onlyOwner {
                  _updateImpl(LENDING_POOL, pool);
                  emit LendingPoolUpdated(pool);
                }
                /**
                 * @dev Returns the address of the LendingPoolConfigurator proxy
                 * @return The LendingPoolConfigurator proxy address
                 **/
                function getLendingPoolConfigurator() external view override returns (address) {
                  return getAddress(LENDING_POOL_CONFIGURATOR);
                }
                /**
                 * @dev Updates the implementation of the LendingPoolConfigurator, or creates the proxy
                 * setting the new `configurator` implementation on the first time calling it
                 * @param configurator The new LendingPoolConfigurator implementation
                 **/
                function setLendingPoolConfiguratorImpl(address configurator) external override onlyOwner {
                  _updateImpl(LENDING_POOL_CONFIGURATOR, configurator);
                  emit LendingPoolConfiguratorUpdated(configurator);
                }
                /**
                 * @dev Returns the address of the LendingPoolCollateralManager. 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 LendingPoolCollateralManager
                 **/
                function getLendingPoolCollateralManager() external view override returns (address) {
                  return getAddress(LENDING_POOL_COLLATERAL_MANAGER);
                }
                /**
                 * @dev Updates the address of the LendingPoolCollateralManager
                 * @param manager The new LendingPoolCollateralManager address
                 **/
                function setLendingPoolCollateralManager(address manager) external override onlyOwner {
                  _addresses[LENDING_POOL_COLLATERAL_MANAGER] = manager;
                  emit LendingPoolCollateralManagerUpdated(manager);
                }
                /**
                 * @dev The functions below are getters/setters of addresses that are outside the context
                 * of the protocol hence the upgradable proxy pattern is not used
                 **/
                function getPoolAdmin() external view override returns (address) {
                  return getAddress(POOL_ADMIN);
                }
                function setPoolAdmin(address admin) external override onlyOwner {
                  _addresses[POOL_ADMIN] = admin;
                  emit ConfigurationAdminUpdated(admin);
                }
                function getEmergencyAdmin() external view override returns (address) {
                  return getAddress(EMERGENCY_ADMIN);
                }
                function setEmergencyAdmin(address emergencyAdmin) external override onlyOwner {
                  _addresses[EMERGENCY_ADMIN] = emergencyAdmin;
                  emit EmergencyAdminUpdated(emergencyAdmin);
                }
                function getPriceOracle() external view override returns (address) {
                  return getAddress(PRICE_ORACLE);
                }
                function setPriceOracle(address priceOracle) external override onlyOwner {
                  _addresses[PRICE_ORACLE] = priceOracle;
                  emit PriceOracleUpdated(priceOracle);
                }
                function getLendingRateOracle() external view override returns (address) {
                  return getAddress(LENDING_RATE_ORACLE);
                }
                function setLendingRateOracle(address lendingRateOracle) external override onlyOwner {
                  _addresses[LENDING_RATE_ORACLE] = lendingRateOracle;
                  emit LendingRateOracleUpdated(lendingRateOracle);
                }
                /**
                 * @dev Internal function to update the implementation of a specific proxied component of the protocol
                 * - If there is no proxy registered in the given `id`, it creates the proxy setting `newAdress`
                 *   as implementation and calls the initialize() function on the proxy
                 * - If there is already a proxy registered, it just updates the implementation to `newAddress` and
                 *   calls the initialize() function via upgradeToAndCall() in the proxy
                 * @param id The id of the proxy to be updated
                 * @param newAddress The address of the new implementation
                 **/
                function _updateImpl(bytes32 id, address newAddress) internal {
                  address payable proxyAddress = payable(_addresses[id]);
                  InitializableImmutableAdminUpgradeabilityProxy proxy =
                    InitializableImmutableAdminUpgradeabilityProxy(proxyAddress);
                  bytes memory params = abi.encodeWithSignature('initialize(address)', address(this));
                  if (proxyAddress == address(0)) {
                    proxy = new InitializableImmutableAdminUpgradeabilityProxy(address(this));
                    proxy.initialize(newAddress, params);
                    _addresses[id] = address(proxy);
                    emit ProxyCreated(id, address(proxy));
                  } else {
                    proxy.upgradeToAndCall(newAddress, params);
                  }
                }
                function _setMarketId(string memory marketId) internal {
                  _marketId = marketId;
                  emit MarketIdSet(marketId);
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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);
                  }
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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 virtual override {
                  require(msg.sender != _admin(), 'Cannot call fallback function from the proxy admin');
                  super._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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();
                }
              }
              

              File 13 of 15: InitializableImmutableAdminUpgradeabilityProxy
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import './BaseImmutableAdminUpgradeabilityProxy.sol';
              import '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
              /**
               * @title InitializableAdminUpgradeabilityProxy
               * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
               */
              contract InitializableImmutableAdminUpgradeabilityProxy is
                BaseImmutableAdminUpgradeabilityProxy,
                InitializableUpgradeabilityProxy
              {
                constructor(address admin) public BaseImmutableAdminUpgradeabilityProxy(admin) {}
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                  BaseImmutableAdminUpgradeabilityProxy._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
              /**
               * @title BaseImmutableAdminUpgradeabilityProxy
               * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
               * @dev This contract combines an upgradeability proxy with an authorization
               * mechanism for administrative tasks. The admin role is stored in an immutable, which
               * helps saving transactions costs
               * 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 BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
                address immutable ADMIN;
                constructor(address admin) public {
                  ADMIN = admin;
                }
                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 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);
                }
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal virtual override {
                  require(msg.sender != ADMIN, 'Cannot call fallback function from the proxy admin');
                  super._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import './Proxy.sol';
              import '../contracts/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)
                  }
                }
              }
              // 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`.
                 */
                fallback() 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());
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                  // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                  // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                  // for accounts without code, i.e. `keccak256('')`
                  bytes32 codehash;
                  bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                  // solhint-disable-next-line no-inline-assembly
                  assembly {
                    codehash := extcodehash(account)
                  }
                  return (codehash != accountHash && codehash != 0x0);
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                  require(address(this).balance >= amount, 'Address: insufficient balance');
                  // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                  (bool success, ) = recipient.call{value: amount}('');
                  require(success, 'Address: unable to send value, recipient may have reverted');
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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);
                  }
                }
              }
              

              File 14 of 15: InitializableImmutableAdminUpgradeabilityProxy
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import './BaseImmutableAdminUpgradeabilityProxy.sol';
              import '../../../dependencies/openzeppelin/upgradeability/InitializableUpgradeabilityProxy.sol';
              /**
               * @title InitializableAdminUpgradeabilityProxy
               * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
               */
              contract InitializableImmutableAdminUpgradeabilityProxy is
                BaseImmutableAdminUpgradeabilityProxy,
                InitializableUpgradeabilityProxy
              {
                constructor(address admin) public BaseImmutableAdminUpgradeabilityProxy(admin) {}
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                  BaseImmutableAdminUpgradeabilityProxy._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import '../../../dependencies/openzeppelin/upgradeability/BaseUpgradeabilityProxy.sol';
              /**
               * @title BaseImmutableAdminUpgradeabilityProxy
               * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
               * @dev This contract combines an upgradeability proxy with an authorization
               * mechanism for administrative tasks. The admin role is stored in an immutable, which
               * helps saving transactions costs
               * 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 BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
                address immutable ADMIN;
                constructor(address admin) public {
                  ADMIN = admin;
                }
                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 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);
                }
                /**
                 * @dev Only fall back when the sender is not the admin.
                 */
                function _willFallback() internal virtual override {
                  require(msg.sender != ADMIN, 'Cannot call fallback function from the proxy admin');
                  super._willFallback();
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import './Proxy.sol';
              import '../contracts/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)
                  }
                }
              }
              // 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`.
                 */
                fallback() 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());
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @dev Collection of functions related to the address type
               */
              library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                  // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                  // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                  // for accounts without code, i.e. `keccak256('')`
                  bytes32 codehash;
                  bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                  // solhint-disable-next-line no-inline-assembly
                  assembly {
                    codehash := extcodehash(account)
                  }
                  return (codehash != accountHash && codehash != 0x0);
                }
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                  require(address(this).balance >= amount, 'Address: insufficient balance');
                  // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                  (bool success, ) = recipient.call{value: amount}('');
                  require(success, 'Address: unable to send value, recipient may have reverted');
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              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);
                  }
                }
              }
              

              File 15 of 15: DefaultReserveInterestRateStrategy
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {SafeMath} from '../../dependencies/openzeppelin/contracts/SafeMath.sol';
              import {IReserveInterestRateStrategy} from '../../interfaces/IReserveInterestRateStrategy.sol';
              import {WadRayMath} from '../libraries/math/WadRayMath.sol';
              import {PercentageMath} from '../libraries/math/PercentageMath.sol';
              import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
              import {ILendingRateOracle} from '../../interfaces/ILendingRateOracle.sol';
              /**
               * @title DefaultReserveInterestRateStrategy contract
               * @notice Implements the calculation of the interest rates depending on the reserve state
               * @dev The model of interest rate is based on 2 slopes, one before the `OPTIMAL_UTILIZATION_RATE`
               * point of utilization and another from that one to 100%
               * - An instance of this same contract, can't be used across different Aave markets, due to the caching
               *   of the LendingPoolAddressesProvider
               * @author Aave
               **/
              contract DefaultReserveInterestRateStrategy is IReserveInterestRateStrategy {
                using WadRayMath for uint256;
                using SafeMath for uint256;
                using PercentageMath 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 immutable OPTIMAL_UTILIZATION_RATE;
                /**
                 * @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 immutable EXCESS_UTILIZATION_RATE;
                ILendingPoolAddressesProvider public immutable addressesProvider;
                // Base variable borrow rate when Utilization rate = 0. Expressed in ray
                uint256 internal immutable _baseVariableBorrowRate;
                // Slope of the variable interest curve when utilization rate > 0 and <= OPTIMAL_UTILIZATION_RATE. Expressed in ray
                uint256 internal immutable _variableRateSlope1;
                // Slope of the variable interest curve when utilization rate > OPTIMAL_UTILIZATION_RATE. Expressed in ray
                uint256 internal immutable _variableRateSlope2;
                // Slope of the stable interest curve when utilization rate > 0 and <= OPTIMAL_UTILIZATION_RATE. Expressed in ray
                uint256 internal immutable _stableRateSlope1;
                // Slope of the stable interest curve when utilization rate > OPTIMAL_UTILIZATION_RATE. Expressed in ray
                uint256 internal immutable _stableRateSlope2;
                constructor(
                  ILendingPoolAddressesProvider provider,
                  uint256 optimalUtilizationRate,
                  uint256 baseVariableBorrowRate,
                  uint256 variableRateSlope1,
                  uint256 variableRateSlope2,
                  uint256 stableRateSlope1,
                  uint256 stableRateSlope2
                ) public {
                  OPTIMAL_UTILIZATION_RATE = optimalUtilizationRate;
                  EXCESS_UTILIZATION_RATE = WadRayMath.ray().sub(optimalUtilizationRate);
                  addressesProvider = provider;
                  _baseVariableBorrowRate = baseVariableBorrowRate;
                  _variableRateSlope1 = variableRateSlope1;
                  _variableRateSlope2 = variableRateSlope2;
                  _stableRateSlope1 = stableRateSlope1;
                  _stableRateSlope2 = stableRateSlope2;
                }
                function variableRateSlope1() external view returns (uint256) {
                  return _variableRateSlope1;
                }
                function variableRateSlope2() external view returns (uint256) {
                  return _variableRateSlope2;
                }
                function stableRateSlope1() external view returns (uint256) {
                  return _stableRateSlope1;
                }
                function stableRateSlope2() external view returns (uint256) {
                  return _stableRateSlope2;
                }
                function baseVariableBorrowRate() external view override returns (uint256) {
                  return _baseVariableBorrowRate;
                }
                function getMaxVariableBorrowRate() external view override returns (uint256) {
                  return _baseVariableBorrowRate.add(_variableRateSlope1).add(_variableRateSlope2);
                }
                struct CalcInterestRatesLocalVars {
                  uint256 totalDebt;
                  uint256 currentVariableBorrowRate;
                  uint256 currentStableBorrowRate;
                  uint256 currentLiquidityRate;
                  uint256 utilizationRate;
                }
                /**
                 * @dev Calculates the interest rates depending on the reserve's state and configurations
                 * @param reserve The address of the reserve
                 * @param availableLiquidity The liquidity available in the reserve
                 * @param totalStableDebt The total borrowed from the reserve a stable rate
                 * @param totalVariableDebt The total borrowed from the reserve at a variable rate
                 * @param averageStableBorrowRate The weighted average of all the stable rate loans
                 * @param reserveFactor The reserve portion of the interest that goes to the treasury of the market
                 * @return The liquidity rate, the stable borrow rate and the variable borrow rate
                 **/
                function calculateInterestRates(
                  address reserve,
                  uint256 availableLiquidity,
                  uint256 totalStableDebt,
                  uint256 totalVariableDebt,
                  uint256 averageStableBorrowRate,
                  uint256 reserveFactor
                )
                  external
                  view
                  override
                  returns (
                    uint256,
                    uint256,
                    uint256
                  )
                {
                  CalcInterestRatesLocalVars memory vars;
                  vars.totalDebt = totalStableDebt.add(totalVariableDebt);
                  vars.currentVariableBorrowRate = 0;
                  vars.currentStableBorrowRate = 0;
                  vars.currentLiquidityRate = 0;
                  uint256 utilizationRate =
                    vars.totalDebt == 0
                      ? 0
                      : vars.totalDebt.rayDiv(availableLiquidity.add(vars.totalDebt));
                  vars.currentStableBorrowRate = ILendingRateOracle(addressesProvider.getLendingRateOracle())
                    .getMarketBorrowRate(reserve);
                  if (utilizationRate > OPTIMAL_UTILIZATION_RATE) {
                    uint256 excessUtilizationRateRatio =
                      utilizationRate.sub(OPTIMAL_UTILIZATION_RATE).rayDiv(EXCESS_UTILIZATION_RATE);
                    vars.currentStableBorrowRate = vars.currentStableBorrowRate.add(_stableRateSlope1).add(
                      _stableRateSlope2.rayMul(excessUtilizationRateRatio)
                    );
                    vars.currentVariableBorrowRate = _baseVariableBorrowRate.add(_variableRateSlope1).add(
                      _variableRateSlope2.rayMul(excessUtilizationRateRatio)
                    );
                  } else {
                    vars.currentStableBorrowRate = vars.currentStableBorrowRate.add(
                      _stableRateSlope1.rayMul(utilizationRate.rayDiv(OPTIMAL_UTILIZATION_RATE))
                    );
                    vars.currentVariableBorrowRate = _baseVariableBorrowRate.add(
                      utilizationRate.rayMul(_variableRateSlope1).rayDiv(OPTIMAL_UTILIZATION_RATE)
                    );
                  }
                  vars.currentLiquidityRate = _getOverallBorrowRate(
                    totalStableDebt,
                    totalVariableDebt,
                    vars
                      .currentVariableBorrowRate,
                    averageStableBorrowRate
                  )
                    .rayMul(utilizationRate)
                    .percentMul(PercentageMath.PERCENTAGE_FACTOR.sub(reserveFactor));
                  return (
                    vars.currentLiquidityRate,
                    vars.currentStableBorrowRate,
                    vars.currentVariableBorrowRate
                  );
                }
                /**
                 * @dev Calculates the overall borrow rate as the weighted average between the total variable debt and total stable debt
                 * @param totalStableDebt The total borrowed from the reserve a stable rate
                 * @param totalVariableDebt The total borrowed from the reserve at a variable rate
                 * @param currentVariableBorrowRate The current variable borrow rate of the reserve
                 * @param currentAverageStableBorrowRate The current weighted average of all the stable rate loans
                 * @return The weighted averaged borrow rate
                 **/
                function _getOverallBorrowRate(
                  uint256 totalStableDebt,
                  uint256 totalVariableDebt,
                  uint256 currentVariableBorrowRate,
                  uint256 currentAverageStableBorrowRate
                ) internal pure returns (uint256) {
                  uint256 totalDebt = totalStableDebt.add(totalVariableDebt);
                  if (totalDebt == 0) return 0;
                  uint256 weightedVariableRate = totalVariableDebt.wadToRay().rayMul(currentVariableBorrowRate);
                  uint256 weightedStableRate = totalStableDebt.wadToRay().rayMul(currentAverageStableBorrowRate);
                  uint256 overallBorrowRate =
                    weightedVariableRate.add(weightedStableRate).rayDiv(totalDebt.wadToRay());
                  return overallBorrowRate;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @dev Wrappers over Solidity's arithmetic operations with added overflow
               * checks.
               *
               * Arithmetic operations in Solidity wrap on overflow. This can easily result
               * in bugs, because programmers usually assume that an overflow raises an
               * error, which is the standard behavior in high level programming languages.
               * `SafeMath` restores this intuition by reverting the transaction when an
               * operation overflows.
               *
               * Using this library instead of the unchecked operations eliminates an entire
               * class of bugs, so it's recommended to use it always.
               */
              library SafeMath {
                /**
                 * @dev Returns the addition of two unsigned integers, reverting on
                 * overflow.
                 *
                 * Counterpart to Solidity's `+` operator.
                 *
                 * Requirements:
                 * - Addition cannot overflow.
                 */
                function add(uint256 a, uint256 b) internal pure returns (uint256) {
                  uint256 c = a + b;
                  require(c >= a, 'SafeMath: addition overflow');
                  return c;
                }
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting on
                 * overflow (when the result is negative).
                 *
                 * Counterpart to Solidity's `-` operator.
                 *
                 * Requirements:
                 * - Subtraction cannot overflow.
                 */
                function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                  return sub(a, b, 'SafeMath: subtraction overflow');
                }
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                 * overflow (when the result is negative).
                 *
                 * Counterpart to Solidity's `-` operator.
                 *
                 * Requirements:
                 * - Subtraction cannot overflow.
                 */
                function sub(
                  uint256 a,
                  uint256 b,
                  string memory errorMessage
                ) internal pure returns (uint256) {
                  require(b <= a, errorMessage);
                  uint256 c = a - b;
                  return c;
                }
                /**
                 * @dev Returns the multiplication of two unsigned integers, reverting on
                 * overflow.
                 *
                 * Counterpart to Solidity's `*` operator.
                 *
                 * Requirements:
                 * - Multiplication cannot overflow.
                 */
                function mul(uint256 a, uint256 b) internal pure returns (uint256) {
                  // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                  // benefit is lost if 'b' is also tested.
                  // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                  if (a == 0) {
                    return 0;
                  }
                  uint256 c = a * b;
                  require(c / a == b, 'SafeMath: multiplication overflow');
                  return c;
                }
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts on
                 * division by zero. The result is rounded towards zero.
                 *
                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                 * uses an invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function div(uint256 a, uint256 b) internal pure returns (uint256) {
                  return div(a, b, 'SafeMath: division by zero');
                }
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
                 * division by zero. The result is rounded towards zero.
                 *
                 * Counterpart to Solidity's `/` operator. Note: this function uses a
                 * `revert` opcode (which leaves remaining gas untouched) while Solidity
                 * uses an invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function div(
                  uint256 a,
                  uint256 b,
                  string memory errorMessage
                ) internal pure returns (uint256) {
                  // Solidity only automatically asserts when dividing by 0
                  require(b > 0, errorMessage);
                  uint256 c = a / b;
                  // assert(a == b * c + a % b); // There is no case in which this doesn't hold
                  return c;
                }
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts when dividing by zero.
                 *
                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                 * invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                  return mod(a, b, 'SafeMath: modulo by zero');
                }
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts with custom message when dividing by zero.
                 *
                 * Counterpart to Solidity's `%` operator. This function uses a `revert`
                 * opcode (which leaves remaining gas untouched) while Solidity uses an
                 * invalid opcode to revert (consuming all remaining gas).
                 *
                 * Requirements:
                 * - The divisor cannot be zero.
                 */
                function mod(
                  uint256 a,
                  uint256 b,
                  string memory errorMessage
                ) internal pure returns (uint256) {
                  require(b != 0, errorMessage);
                  return a % b;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title IReserveInterestRateStrategyInterface interface
               * @dev Interface for the calculation of the interest rates
               * @author Aave
               */
              interface IReserveInterestRateStrategy {
                function baseVariableBorrowRate() external view returns (uint256);
                function getMaxVariableBorrowRate() external view returns (uint256);
                function calculateInterestRates(
                  address reserve,
                  uint256 utilizationRate,
                  uint256 totalStableDebt,
                  uint256 totalVariableDebt,
                  uint256 averageStableBorrowRate,
                  uint256 reserveFactor
                )
                  external
                  view
                  returns (
                    uint256 liquidityRate,
                    uint256 stableBorrowRate,
                    uint256 variableBorrowRate
                  );
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Errors} from '../helpers/Errors.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 {
                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) {
                  if (a == 0 || b == 0) {
                    return 0;
                  }
                  require(a <= (type(uint256).max - halfWAD) / b, Errors.MATH_MULTIPLICATION_OVERFLOW);
                  return (a * b + halfWAD) / 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) {
                  require(b != 0, Errors.MATH_DIVISION_BY_ZERO);
                  uint256 halfB = b / 2;
                  require(a <= (type(uint256).max - halfB) / WAD, Errors.MATH_MULTIPLICATION_OVERFLOW);
                  return (a * WAD + halfB) / 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) {
                  if (a == 0 || b == 0) {
                    return 0;
                  }
                  require(a <= (type(uint256).max - halfRAY) / b, Errors.MATH_MULTIPLICATION_OVERFLOW);
                  return (a * b + halfRAY) / 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) {
                  require(b != 0, Errors.MATH_DIVISION_BY_ZERO);
                  uint256 halfB = b / 2;
                  require(a <= (type(uint256).max - halfB) / RAY, Errors.MATH_MULTIPLICATION_OVERFLOW);
                  return (a * RAY + halfB) / 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;
                  uint256 result = halfRatio + a;
                  require(result >= halfRatio, Errors.MATH_ADDITION_OVERFLOW);
                  return result / WAD_RAY_RATIO;
                }
                /**
                 * @dev Converts wad up to ray
                 * @param a Wad
                 * @return a converted in ray
                 **/
                function wadToRay(uint256 a) internal pure returns (uint256) {
                  uint256 result = a * WAD_RAY_RATIO;
                  require(result / WAD_RAY_RATIO == a, Errors.MATH_MULTIPLICATION_OVERFLOW);
                  return result;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title Errors library
               * @author Aave
               * @notice Defines the error messages emitted by the different contracts of the Aave protocol
               * @dev Error messages prefix glossary:
               *  - VL = ValidationLogic
               *  - MATH = Math libraries
               *  - CT = Common errors between tokens (AToken, VariableDebtToken and StableDebtToken)
               *  - AT = AToken
               *  - SDT = StableDebtToken
               *  - VDT = VariableDebtToken
               *  - LP = LendingPool
               *  - LPAPR = LendingPoolAddressesProviderRegistry
               *  - LPC = LendingPoolConfiguration
               *  - RL = ReserveLogic
               *  - LPCM = LendingPoolCollateralManager
               *  - P = Pausable
               */
              library Errors {
                //common errors
                string public constant CALLER_NOT_POOL_ADMIN = '33'; // 'The caller must be the pool admin'
                string public constant BORROW_ALLOWANCE_NOT_ENOUGH = '59'; // User borrows on behalf, but allowance are too small
                //contract specific errors
                string public constant VL_INVALID_AMOUNT = '1'; // 'Amount must be greater than 0'
                string public constant VL_NO_ACTIVE_RESERVE = '2'; // 'Action requires an active reserve'
                string public constant VL_RESERVE_FROZEN = '3'; // 'Action cannot be performed because the reserve is frozen'
                string public constant VL_CURRENT_AVAILABLE_LIQUIDITY_NOT_ENOUGH = '4'; // 'The current liquidity is not enough'
                string public constant VL_NOT_ENOUGH_AVAILABLE_USER_BALANCE = '5'; // 'User cannot withdraw more than the available balance'
                string public constant VL_TRANSFER_NOT_ALLOWED = '6'; // 'Transfer cannot be allowed.'
                string public constant VL_BORROWING_NOT_ENABLED = '7'; // 'Borrowing is not enabled'
                string public constant VL_INVALID_INTEREST_RATE_MODE_SELECTED = '8'; // 'Invalid interest rate mode selected'
                string public constant VL_COLLATERAL_BALANCE_IS_0 = '9'; // 'The collateral balance is 0'
                string public constant VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD = '10'; // 'Health factor is lesser than the liquidation threshold'
                string public constant VL_COLLATERAL_CANNOT_COVER_NEW_BORROW = '11'; // 'There is not enough collateral to cover a new borrow'
                string public constant VL_STABLE_BORROWING_NOT_ENABLED = '12'; // stable borrowing not enabled
                string public constant VL_COLLATERAL_SAME_AS_BORROWING_CURRENCY = '13'; // collateral is (mostly) the same currency that is being borrowed
                string public constant VL_AMOUNT_BIGGER_THAN_MAX_LOAN_SIZE_STABLE = '14'; // 'The requested amount is greater than the max loan size in stable rate mode
                string public constant VL_NO_DEBT_OF_SELECTED_TYPE = '15'; // 'for repayment of stable debt, the user needs to have stable debt, otherwise, he needs to have variable debt'
                string public constant VL_NO_EXPLICIT_AMOUNT_TO_REPAY_ON_BEHALF = '16'; // 'To repay on behalf of an user an explicit amount to repay is needed'
                string public constant VL_NO_STABLE_RATE_LOAN_IN_RESERVE = '17'; // 'User does not have a stable rate loan in progress on this reserve'
                string public constant VL_NO_VARIABLE_RATE_LOAN_IN_RESERVE = '18'; // 'User does not have a variable rate loan in progress on this reserve'
                string public constant VL_UNDERLYING_BALANCE_NOT_GREATER_THAN_0 = '19'; // 'The underlying balance needs to be greater than 0'
                string public constant VL_DEPOSIT_ALREADY_IN_USE = '20'; // 'User deposit is already being used as collateral'
                string public constant LP_NOT_ENOUGH_STABLE_BORROW_BALANCE = '21'; // 'User does not have any stable rate loan for this reserve'
                string public constant LP_INTEREST_RATE_REBALANCE_CONDITIONS_NOT_MET = '22'; // 'Interest rate rebalance conditions were not met'
                string public constant LP_LIQUIDATION_CALL_FAILED = '23'; // 'Liquidation call failed'
                string public constant LP_NOT_ENOUGH_LIQUIDITY_TO_BORROW = '24'; // 'There is not enough liquidity available to borrow'
                string public constant LP_REQUESTED_AMOUNT_TOO_SMALL = '25'; // 'The requested amount is too small for a FlashLoan.'
                string public constant LP_INCONSISTENT_PROTOCOL_ACTUAL_BALANCE = '26'; // 'The actual balance of the protocol is inconsistent'
                string public constant LP_CALLER_NOT_LENDING_POOL_CONFIGURATOR = '27'; // 'The caller of the function is not the lending pool configurator'
                string public constant LP_INCONSISTENT_FLASHLOAN_PARAMS = '28';
                string public constant CT_CALLER_MUST_BE_LENDING_POOL = '29'; // 'The caller of this function must be a lending pool'
                string public constant CT_CANNOT_GIVE_ALLOWANCE_TO_HIMSELF = '30'; // 'User cannot give allowance to himself'
                string public constant CT_TRANSFER_AMOUNT_NOT_GT_0 = '31'; // 'Transferred amount needs to be greater than zero'
                string public constant RL_RESERVE_ALREADY_INITIALIZED = '32'; // 'Reserve has already been initialized'
                string public constant LPC_RESERVE_LIQUIDITY_NOT_0 = '34'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_ATOKEN_POOL_ADDRESS = '35'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_STABLE_DEBT_TOKEN_POOL_ADDRESS = '36'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_VARIABLE_DEBT_TOKEN_POOL_ADDRESS = '37'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_STABLE_DEBT_TOKEN_UNDERLYING_ADDRESS = '38'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_VARIABLE_DEBT_TOKEN_UNDERLYING_ADDRESS = '39'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_ADDRESSES_PROVIDER_ID = '40'; // 'The liquidity of the reserve needs to be 0'
                string public constant LPC_INVALID_CONFIGURATION = '75'; // 'Invalid risk parameters for the reserve'
                string public constant LPC_CALLER_NOT_EMERGENCY_ADMIN = '76'; // 'The caller must be the emergency admin'
                string public constant LPAPR_PROVIDER_NOT_REGISTERED = '41'; // 'Provider is not registered'
                string public constant LPCM_HEALTH_FACTOR_NOT_BELOW_THRESHOLD = '42'; // 'Health factor is not below the threshold'
                string public constant LPCM_COLLATERAL_CANNOT_BE_LIQUIDATED = '43'; // 'The collateral chosen cannot be liquidated'
                string public constant LPCM_SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER = '44'; // 'User did not borrow the specified currency'
                string public constant LPCM_NOT_ENOUGH_LIQUIDITY_TO_LIQUIDATE = '45'; // "There isn't enough liquidity available to liquidate"
                string public constant LPCM_NO_ERRORS = '46'; // 'No errors'
                string public constant LP_INVALID_FLASHLOAN_MODE = '47'; //Invalid flashloan mode selected
                string public constant MATH_MULTIPLICATION_OVERFLOW = '48';
                string public constant MATH_ADDITION_OVERFLOW = '49';
                string public constant MATH_DIVISION_BY_ZERO = '50';
                string public constant RL_LIQUIDITY_INDEX_OVERFLOW = '51'; //  Liquidity index overflows uint128
                string public constant RL_VARIABLE_BORROW_INDEX_OVERFLOW = '52'; //  Variable borrow index overflows uint128
                string public constant RL_LIQUIDITY_RATE_OVERFLOW = '53'; //  Liquidity rate overflows uint128
                string public constant RL_VARIABLE_BORROW_RATE_OVERFLOW = '54'; //  Variable borrow rate overflows uint128
                string public constant RL_STABLE_BORROW_RATE_OVERFLOW = '55'; //  Stable borrow rate overflows uint128
                string public constant CT_INVALID_MINT_AMOUNT = '56'; //invalid amount to mint
                string public constant LP_FAILED_REPAY_WITH_COLLATERAL = '57';
                string public constant CT_INVALID_BURN_AMOUNT = '58'; //invalid amount to burn
                string public constant LP_FAILED_COLLATERAL_SWAP = '60';
                string public constant LP_INVALID_EQUAL_ASSETS_TO_SWAP = '61';
                string public constant LP_REENTRANCY_NOT_ALLOWED = '62';
                string public constant LP_CALLER_MUST_BE_AN_ATOKEN = '63';
                string public constant LP_IS_PAUSED = '64'; // 'Pool is paused'
                string public constant LP_NO_MORE_RESERVES_ALLOWED = '65';
                string public constant LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN = '66';
                string public constant RC_INVALID_LTV = '67';
                string public constant RC_INVALID_LIQ_THRESHOLD = '68';
                string public constant RC_INVALID_LIQ_BONUS = '69';
                string public constant RC_INVALID_DECIMALS = '70';
                string public constant RC_INVALID_RESERVE_FACTOR = '71';
                string public constant LPAPR_INVALID_ADDRESSES_PROVIDER_ID = '72';
                string public constant VL_INCONSISTENT_FLASHLOAN_PARAMS = '73';
                string public constant LP_INCONSISTENT_PARAMS_LENGTH = '74';
                string public constant UL_INVALID_INDEX = '77';
                string public constant LP_NOT_CONTRACT = '78';
                string public constant SDT_STABLE_DEBT_OVERFLOW = '79';
                string public constant SDT_BURN_EXCEEDS_BALANCE = '80';
                enum CollateralManagerErrors {
                  NO_ERROR,
                  NO_COLLATERAL_AVAILABLE,
                  COLLATERAL_CANNOT_BE_LIQUIDATED,
                  CURRRENCY_NOT_BORROWED,
                  HEALTH_FACTOR_ABOVE_THRESHOLD,
                  NOT_ENOUGH_LIQUIDITY,
                  NO_ACTIVE_RESERVE,
                  HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD,
                  INVALID_EQUAL_ASSETS_TO_SWAP,
                  FROZEN_RESERVE
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              import {Errors} from '../helpers/Errors.sol';
              /**
               * @title PercentageMath library
               * @author Aave
               * @notice Provides functions to perform percentage calculations
               * @dev Percentages are defined by default with 2 decimals of precision (100.00). The precision is indicated by PERCENTAGE_FACTOR
               * @dev Operations are rounded half up
               **/
              library PercentageMath {
                uint256 constant PERCENTAGE_FACTOR = 1e4; //percentage plus two decimals
                uint256 constant HALF_PERCENT = PERCENTAGE_FACTOR / 2;
                /**
                 * @dev Executes a percentage multiplication
                 * @param value The value of which the percentage needs to be calculated
                 * @param percentage The percentage of the value to be calculated
                 * @return The percentage of value
                 **/
                function percentMul(uint256 value, uint256 percentage) internal pure returns (uint256) {
                  if (value == 0 || percentage == 0) {
                    return 0;
                  }
                  require(
                    value <= (type(uint256).max - HALF_PERCENT) / percentage,
                    Errors.MATH_MULTIPLICATION_OVERFLOW
                  );
                  return (value * percentage + HALF_PERCENT) / PERCENTAGE_FACTOR;
                }
                /**
                 * @dev Executes a percentage division
                 * @param value The value of which the percentage needs to be calculated
                 * @param percentage The percentage of the value to be calculated
                 * @return The value divided the percentage
                 **/
                function percentDiv(uint256 value, uint256 percentage) internal pure returns (uint256) {
                  require(percentage != 0, Errors.MATH_DIVISION_BY_ZERO);
                  uint256 halfPercentage = percentage / 2;
                  require(
                    value <= (type(uint256).max - halfPercentage) / PERCENTAGE_FACTOR,
                    Errors.MATH_MULTIPLICATION_OVERFLOW
                  );
                  return (value * PERCENTAGE_FACTOR + halfPercentage) / percentage;
                }
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @title LendingPoolAddressesProvider contract
               * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles
               * - Acting also as factory of proxies and admin of those, so with right to change its implementations
               * - Owned by the Aave Governance
               * @author Aave
               **/
              interface ILendingPoolAddressesProvider {
                event MarketIdSet(string newMarketId);
                event LendingPoolUpdated(address indexed newAddress);
                event ConfigurationAdminUpdated(address indexed newAddress);
                event EmergencyAdminUpdated(address indexed newAddress);
                event LendingPoolConfiguratorUpdated(address indexed newAddress);
                event LendingPoolCollateralManagerUpdated(address indexed newAddress);
                event PriceOracleUpdated(address indexed newAddress);
                event LendingRateOracleUpdated(address indexed newAddress);
                event ProxyCreated(bytes32 id, address indexed newAddress);
                event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy);
                function getMarketId() external view returns (string memory);
                function setMarketId(string calldata marketId) external;
                function setAddress(bytes32 id, address newAddress) external;
                function setAddressAsProxy(bytes32 id, address impl) external;
                function getAddress(bytes32 id) external view returns (address);
                function getLendingPool() external view returns (address);
                function setLendingPoolImpl(address pool) external;
                function getLendingPoolConfigurator() external view returns (address);
                function setLendingPoolConfiguratorImpl(address configurator) external;
                function getLendingPoolCollateralManager() external view returns (address);
                function setLendingPoolCollateralManager(address manager) external;
                function getPoolAdmin() external view returns (address);
                function setPoolAdmin(address admin) external;
                function getEmergencyAdmin() external view returns (address);
                function setEmergencyAdmin(address admin) external;
                function getPriceOracle() external view returns (address);
                function setPriceOracle(address priceOracle) external;
                function getLendingRateOracle() external view returns (address);
                function setLendingRateOracle(address lendingRateOracle) external;
              }
              // SPDX-License-Identifier: agpl-3.0
              pragma solidity 0.6.12;
              /**
               * @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;
              }