ETH Price: $1,976.06 (-5.18%)

Transaction Decoder

Block:
11638510 at Jan-12-2021 06:25:32 AM +UTC
Transaction Fee:
0.010284099 ETH $20.32
Gas Used:
201,649 Gas / 51 Gwei

Emitted Events:

130 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000464c71f6c2f760dda6093dcb91c24c39e5d6e18c, 00000000000000000000000000000000000000000000000001d463f3ad7eaba2 )
131 InitializableImmutableAdminUpgradeabilityProxy.0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f( 0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f, 0x000000000000000000000000464c71f6c2f760dda6093dcb91c24c39e5d6e18c, 00000000000000000000000000000000000000000000000001d463f3ad7eaba2, 0000000000000000000000000000000000000000034288d826ffc982d47a9068 )
132 InitializableImmutableAdminUpgradeabilityProxy.0x44bd20a79e993bdcc7cbedf54a3b4d19fb78490124b6b90d04fe3242eea579e8( 0x44bd20a79e993bdcc7cbedf54a3b4d19fb78490124b6b90d04fe3242eea579e8, 0x0000000000000000000000005c7c6d069ba232718f37c27a9549b547c359e31c, 00000000000000000000000000000000000000000000d87eec820948ba9c6915, 00000000000000000000000000000000000000000000d8823db2e3d0b607c1ed, 0000000000000000000000000000000000000000000000035130da87fb6b58d8, 0000000000000000000000000000000000000000004db5a2118bbdc0e90f3799, 000000000000000000000000000000000000000000098799ec17a3b01afc262c )
133 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000005c7c6d069ba232718f37c27a9549b547c359e31c, 0x0000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000d8823db2e3d0b607c1ed )
134 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000005c7c6d069ba232718f37c27a9549b547c359e31c, 00000000000000000000000000000000000000000000d8823db2e3d0b607c1ed )
135 InitializableImmutableAdminUpgradeabilityProxy.0x2f00e3cdd69a77be7ed215ec7b2a36784dd158f921fca79ac29deffa353fe6ee( 0x2f00e3cdd69a77be7ed215ec7b2a36784dd158f921fca79ac29deffa353fe6ee, 0x0000000000000000000000005c7c6d069ba232718f37c27a9549b547c359e31c, 0x0000000000000000000000005c7c6d069ba232718f37c27a9549b547c359e31c, 00000000000000000000000000000000000000000000d8823db2e3d0b607c1ed, 00000000000000000000000000000000000000000346e65e3b5a0f060114c59e )
136 InitializableImmutableAdminUpgradeabilityProxy.0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a( 0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a, 0x0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f, 000000000000000000000000000000000000000000246b3ec1592c963dffb957, 00000000000000000000000000000000000000000047bb156530783218d12b16, 0000000000000000000000000000000000000000001ba8039548de7025a2562b, 0000000000000000000000000000000000000000034288d826ffc982d47a9068, 00000000000000000000000000000000000000000346e65e3b5a0f060114c59e )
137 InitializableImmutableAdminUpgradeabilityProxy.0xea368a40e9570069bb8e6511d668293ad2e1f03b0d982431fd223de9f3b70ca6( 0xea368a40e9570069bb8e6511d668293ad2e1f03b0d982431fd223de9f3b70ca6, 0x0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f, 0x0000000000000000000000005c7c6d069ba232718f37c27a9549b547c359e31c, 0000000000000000000000000000000000000000000000000000000000000001 )

Account State Difference:

  Address   Before After State Difference Code
0x028171bC...aC55b68A3
0x5C7c6d06...7C359E31C
10.402207732776009443 Eth
Nonce: 7384
10.391923633776009443 Eth
Nonce: 7385
0.010284099
0x6C3c7883...2A6E4379d
0x778A13D3...119a08E92
0x7d2768dE...A69DDc7A9
(Aave: Lending Pool V2)
(Ethermine)
2,030.90209312537596888 Eth2,030.91237722437596888 Eth0.010284099

Execution Trace

InitializableImmutableAdminUpgradeabilityProxy.94ba89a2( )
  • LendingPool.swapBorrowRateMode( asset=0x6B175474E89094C44Da98b954EedeAC495271d0F, rateMode=1 )
    • InitializableImmutableAdminUpgradeabilityProxy.70a08231( )
      • StableDebtToken.balanceOf( account=0x5C7c6d069ba232718f37C27A9549b547C359E31C ) => ( 1022433682895693322633709 )
      • InitializableImmutableAdminUpgradeabilityProxy.70a08231( )
        • VariableDebtToken.balanceOf( user=0x5C7c6d069ba232718f37C27A9549b547C359E31C ) => ( 0 )
        • ValidationLogic.a8695b1d( )
        • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
          • VariableDebtToken.DELEGATECALL( )
          • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
            • StableDebtToken.DELEGATECALL( )
            • InitializableImmutableAdminUpgradeabilityProxy.7df5bd3b( )
              • AToken.mintToTreasury( amount=131840187339549602, index=1008890362707177180967374952 )
              • InitializableImmutableAdminUpgradeabilityProxy.9dc29fac( )
                • StableDebtToken.burn( user=0x5C7c6d069ba232718f37C27A9549b547C359E31C, amount=1022433682895693322633709 )
                • InitializableImmutableAdminUpgradeabilityProxy.b3f1c93d( )
                  • VariableDebtToken.mint( user=0x5C7c6d069ba232718f37C27A9549b547C359E31C, onBehalfOf=0x5C7c6d069ba232718f37C27A9549b547C359E31C, amount=1022433682895693322633709, index=1014167719398809510104843678 ) => ( True )
                  • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
                    • StableDebtToken.DELEGATECALL( )
                    • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
                      • VariableDebtToken.DELEGATECALL( )
                      • Dai.balanceOf( 0x028171bCA77440897B824Ca71D1c56caC55b68A3 ) => ( 8695147511946713700852379 )
                      • DefaultReserveInterestRateStrategy.calculateInterestRates( reserve=0x6B175474E89094C44Da98b954EedeAC495271d0F, availableLiquidity=8695147511946713700852379, totalStableDebt=11520691215808436415243820, totalVariableDebt=6028783947040837270656420, averageStableBorrowRate=93945026080578549832431513, reserveFactor=1000 ) => ( 44027780350142218363713879, 86717210398020796232575766, 33434420796041592465151531 )
                        • LendingPoolAddressesProvider.STATICCALL( )
                        • LendingRateOracle.getMarketBorrowRate( asset=0x6B175474E89094C44Da98b954EedeAC495271d0F ) => ( 70000000000000000000000000 )
                          File 1 of 13: 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 2 of 13: 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 13: 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 13: 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 5 of 13: 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 6 of 13: StableDebtToken
                          // 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 {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;
                          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: 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;
                          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;
                          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;
                          /**
                           * @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;
                          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: 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;
                          /**
                           * @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;
                          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;
                          /**
                           * @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;
                          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;
                          /**
                           * @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 {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 {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 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);
                          }
                          

                          File 7 of 13: VariableDebtToken
                          // 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 {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;
                          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 {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 {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;
                          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: 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;
                          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;
                          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;
                          /**
                           * @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;
                          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: 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;
                          /**
                           * @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;
                          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;
                          /**
                           * @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;
                          pragma experimental ABIEncoderV2;
                          interface IAaveIncentivesController {
                            function handleAction(
                              address user,
                              uint256 userBalance,
                              uint256 totalSupply
                            ) external;
                          }
                          

                          File 8 of 13: 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 9 of 13: AToken
                          // 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;
                          /**
                           * @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: 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;
                          /**
                           * @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;
                          /**
                           * @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 {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: 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;
                          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;
                          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;
                          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 {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;
                          /**
                           * @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;
                          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: 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 {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;
                          interface IAaveIncentivesController {
                            function handleAction(
                              address user,
                              uint256 userBalance,
                              uint256 totalSupply
                            ) external;
                          }
                          

                          File 10 of 13: Dai
                          // hevm: flattened sources of /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
                          pragma solidity =0.5.12;
                          
                          ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/lib.sol
                          // This program is free software: you can redistribute it and/or modify
                          // it under the terms of the GNU General Public License as published by
                          // the Free Software Foundation, either version 3 of the License, or
                          // (at your option) any later version.
                          
                          // This program is distributed in the hope that it will be useful,
                          // but WITHOUT ANY WARRANTY; without even the implied warranty of
                          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                          // GNU General Public License for more details.
                          
                          // You should have received a copy of the GNU General Public License
                          // along with this program.  If not, see <http://www.gnu.org/licenses/>.
                          
                          /* pragma solidity 0.5.12; */
                          
                          contract LibNote {
                              event LogNote(
                                  bytes4   indexed  sig,
                                  address  indexed  usr,
                                  bytes32  indexed  arg1,
                                  bytes32  indexed  arg2,
                                  bytes             data
                              ) anonymous;
                          
                              modifier note {
                                  _;
                                  assembly {
                                      // log an 'anonymous' event with a constant 6 words of calldata
                                      // and four indexed topics: selector, caller, arg1 and arg2
                                      let mark := msize                         // end of memory ensures zero
                                      mstore(0x40, add(mark, 288))              // update free memory pointer
                                      mstore(mark, 0x20)                        // bytes type data offset
                                      mstore(add(mark, 0x20), 224)              // bytes size (padded)
                                      calldatacopy(add(mark, 0x40), 0, 224)     // bytes payload
                                      log4(mark, 288,                           // calldata
                                           shl(224, shr(224, calldataload(0))), // msg.sig
                                           caller,                              // msg.sender
                                           calldataload(4),                     // arg1
                                           calldataload(36)                     // arg2
                                          )
                                  }
                              }
                          }
                          
                          ////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
                          // Copyright (C) 2017, 2018, 2019 dbrock, rain, mrchico
                          
                          // This program is free software: you can redistribute it and/or modify
                          // it under the terms of the GNU Affero General Public License as published by
                          // the Free Software Foundation, either version 3 of the License, or
                          // (at your option) any later version.
                          //
                          // This program is distributed in the hope that it will be useful,
                          // but WITHOUT ANY WARRANTY; without even the implied warranty of
                          // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                          // GNU Affero General Public License for more details.
                          //
                          // You should have received a copy of the GNU Affero General Public License
                          // along with this program.  If not, see <https://www.gnu.org/licenses/>.
                          
                          /* pragma solidity 0.5.12; */
                          
                          /* import "./lib.sol"; */
                          
                          contract Dai is LibNote {
                              // --- Auth ---
                              mapping (address => uint) public wards;
                              function rely(address guy) external note auth { wards[guy] = 1; }
                              function deny(address guy) external note auth { wards[guy] = 0; }
                              modifier auth {
                                  require(wards[msg.sender] == 1, "Dai/not-authorized");
                                  _;
                              }
                          
                              // --- ERC20 Data ---
                              string  public constant name     = "Dai Stablecoin";
                              string  public constant symbol   = "DAI";
                              string  public constant version  = "1";
                              uint8   public constant decimals = 18;
                              uint256 public totalSupply;
                          
                              mapping (address => uint)                      public balanceOf;
                              mapping (address => mapping (address => uint)) public allowance;
                              mapping (address => uint)                      public nonces;
                          
                              event Approval(address indexed src, address indexed guy, uint wad);
                              event Transfer(address indexed src, address indexed dst, uint wad);
                          
                              // --- Math ---
                              function add(uint x, uint y) internal pure returns (uint z) {
                                  require((z = x + y) >= x);
                              }
                              function sub(uint x, uint y) internal pure returns (uint z) {
                                  require((z = x - y) <= x);
                              }
                          
                              // --- EIP712 niceties ---
                              bytes32 public DOMAIN_SEPARATOR;
                              // bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
                              bytes32 public constant PERMIT_TYPEHASH = 0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
                          
                              constructor(uint256 chainId_) public {
                                  wards[msg.sender] = 1;
                                  DOMAIN_SEPARATOR = keccak256(abi.encode(
                                      keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                                      keccak256(bytes(name)),
                                      keccak256(bytes(version)),
                                      chainId_,
                                      address(this)
                                  ));
                              }
                          
                              // --- Token ---
                              function transfer(address dst, uint wad) external returns (bool) {
                                  return transferFrom(msg.sender, dst, wad);
                              }
                              function transferFrom(address src, address dst, uint wad)
                                  public returns (bool)
                              {
                                  require(balanceOf[src] >= wad, "Dai/insufficient-balance");
                                  if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
                                      require(allowance[src][msg.sender] >= wad, "Dai/insufficient-allowance");
                                      allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad);
                                  }
                                  balanceOf[src] = sub(balanceOf[src], wad);
                                  balanceOf[dst] = add(balanceOf[dst], wad);
                                  emit Transfer(src, dst, wad);
                                  return true;
                              }
                              function mint(address usr, uint wad) external auth {
                                  balanceOf[usr] = add(balanceOf[usr], wad);
                                  totalSupply    = add(totalSupply, wad);
                                  emit Transfer(address(0), usr, wad);
                              }
                              function burn(address usr, uint wad) external {
                                  require(balanceOf[usr] >= wad, "Dai/insufficient-balance");
                                  if (usr != msg.sender && allowance[usr][msg.sender] != uint(-1)) {
                                      require(allowance[usr][msg.sender] >= wad, "Dai/insufficient-allowance");
                                      allowance[usr][msg.sender] = sub(allowance[usr][msg.sender], wad);
                                  }
                                  balanceOf[usr] = sub(balanceOf[usr], wad);
                                  totalSupply    = sub(totalSupply, wad);
                                  emit Transfer(usr, address(0), wad);
                              }
                              function approve(address usr, uint wad) external returns (bool) {
                                  allowance[msg.sender][usr] = wad;
                                  emit Approval(msg.sender, usr, wad);
                                  return true;
                              }
                          
                              // --- Alias ---
                              function push(address usr, uint wad) external {
                                  transferFrom(msg.sender, usr, wad);
                              }
                              function pull(address usr, uint wad) external {
                                  transferFrom(usr, msg.sender, wad);
                              }
                              function move(address src, address dst, uint wad) external {
                                  transferFrom(src, dst, wad);
                              }
                          
                              // --- Approve by signature ---
                              function permit(address holder, address spender, uint256 nonce, uint256 expiry,
                                              bool allowed, uint8 v, bytes32 r, bytes32 s) external
                              {
                                  bytes32 digest =
                                      keccak256(abi.encodePacked(
                                          "\x19\x01",
                                          DOMAIN_SEPARATOR,
                                          keccak256(abi.encode(PERMIT_TYPEHASH,
                                                               holder,
                                                               spender,
                                                               nonce,
                                                               expiry,
                                                               allowed))
                                  ));
                          
                                  require(holder != address(0), "Dai/invalid-address-0");
                                  require(holder == ecrecover(digest, v, r, s), "Dai/invalid-permit");
                                  require(expiry == 0 || now <= expiry, "Dai/permit-expired");
                                  require(nonce == nonces[holder]++, "Dai/invalid-nonce");
                                  uint wad = allowed ? uint(-1) : 0;
                                  allowance[holder][spender] = wad;
                                  emit Approval(holder, spender, wad);
                              }
                          }

                          File 11 of 13: 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;
                          }
                          

                          File 12 of 13: 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 13 of 13: LendingRateOracle
                          // SPDX-License-Identifier: agpl-3.0
                          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;
                            }
                          }
                          
                          /**
                           * @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;
                            }
                          }
                          
                          /**
                           * @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 {
                          
                            event MarketBorrowRateSet(address indexed asset, uint256 rate);
                              
                            /**
                              @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;
                            
                          }
                          
                          
                          
                          contract LendingRateOracle is ILendingRateOracle, Ownable {
                            mapping(address => uint256) internal _borrowRates;
                          
                            function getMarketBorrowRate(address asset) external override view returns (uint256) {
                              return _borrowRates[asset];
                            }
                          
                            function setMarketBorrowRate(address asset, uint256 rate) external override onlyOwner {
                              _borrowRates[asset] = rate;
                              
                              emit MarketBorrowRateSet(asset, rate);
                            }
                          }