ETH Price: $1,923.76 (-4.99%)
Gas: 0.59 Gwei

Transaction Decoder

Block:
14751671 at May-11-2022 12:24:51 AM +UTC
Transaction Fee:
0.01554442275960963 ETH $29.90
Gas Used:
504,585 Gas / 30.806351278 Gwei

Emitted Events:

66 DSProxy.0x1cff79cd00000000000000000000000000000000000000000000000000000000( 0x1cff79cd00000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000769a1ef6fbfa918d685ba02f3c2e45cfb902ebf7, 0x000000000000000000000000234e8219f25f6af4be90d40c79dede31b1f21d4f, 0x0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000004000000000, 000000000000000000000000000000000000000000000000000001041cff79cd, 000000000000000000000000234e8219f25f6af4be90d40c79dede31b1f21d4f, 0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000000000000000000000000000000000000000000084, fbf37739000000000000000000000000b53c1a33016b2dc2ff3653530bff1848, a515c8c50000000000000000000000006b175474e89094c44da98b954eedeac4, 95271d0f000000000000000000000000000000000000000000000a968163f0a5, 7b40000000000000000000000000000000000000000000000000000000000000, 0000000200000000000000000000000000000000000000000000000000000000 )
67 InitializableImmutableAdminUpgradeabilityProxy.0x5777ca300dfe5bead41006fbce4389794dbc0ed8d6cccebfaf94630aa04184bc( 0x5777ca300dfe5bead41006fbce4389794dbc0ed8d6cccebfaf94630aa04184bc, 0x000000000000000000000000028171bca77440897b824ca71d1c56cac55b68a3, 0000000000000000000000000000000000000000000000000000217c28436312 )
68 InitializableImmutableAdminUpgradeabilityProxy.0xbb123b5c06d5408bbea3c4fef481578175cfb432e3b482c6186f02ed9086585b( 0xbb123b5c06d5408bbea3c4fef481578175cfb432e3b482c6186f02ed9086585b, 0x000000000000000000000000464c71f6c2f760dda6093dcb91c24c39e5d6e18c, 0x000000000000000000000000028171bca77440897b824ca71d1c56cac55b68a3, 0000000000000000000000000000000000000000000000000000217c28436312 )
69 InitializableImmutableAdminUpgradeabilityProxy.0x2468f9268c60ad90e2d49edb0032c8a001e733ae888b3ab8e982edf535be1a76( 0x2468f9268c60ad90e2d49edb0032c8a001e733ae888b3ab8e982edf535be1a76, 0x000000000000000000000000464c71f6c2f760dda6093dcb91c24c39e5d6e18c, 000000000000000000000000000000000000000000000000002a89c2699ecd67 )
70 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000464c71f6c2f760dda6093dcb91c24c39e5d6e18c, 0000000000000000000000000000000000000000000000048b1e96f98f233f93 )
71 InitializableImmutableAdminUpgradeabilityProxy.0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f( 0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f, 0x000000000000000000000000464c71f6c2f760dda6093dcb91c24c39e5d6e18c, 0000000000000000000000000000000000000000000000048b1e96f98f233f93, 000000000000000000000000000000000000000003763a400abf89476f7d7ac7 )
72 InitializableImmutableAdminUpgradeabilityProxy.0x5777ca300dfe5bead41006fbce4389794dbc0ed8d6cccebfaf94630aa04184bc( 0x5777ca300dfe5bead41006fbce4389794dbc0ed8d6cccebfaf94630aa04184bc, 0x0000000000000000000000006c3c78838c761c6ac7be9f59fe808ea2a6e4379d, 00000000000000000000000000000000000000000000000000003e5cd1245ecc )
73 InitializableImmutableAdminUpgradeabilityProxy.0xbb123b5c06d5408bbea3c4fef481578175cfb432e3b482c6186f02ed9086585b( 0xbb123b5c06d5408bbea3c4fef481578175cfb432e3b482c6186f02ed9086585b, 0x0000000000000000000000007e48bc779c6d37ee678c126edaf76787cf828c62, 0x0000000000000000000000006c3c78838c761c6ac7be9f59fe808ea2a6e4379d, 00000000000000000000000000000000000000000000000000003e5cd1245ecc )
74 InitializableImmutableAdminUpgradeabilityProxy.0x2468f9268c60ad90e2d49edb0032c8a001e733ae888b3ab8e982edf535be1a76( 0x2468f9268c60ad90e2d49edb0032c8a001e733ae888b3ab8e982edf535be1a76, 0x0000000000000000000000007e48bc779c6d37ee678c126edaf76787cf828c62, 000000000000000000000000000000000000000000000000000360a7eceadc4f )
75 InitializableImmutableAdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0000000000000000000000007e48bc779c6d37ee678c126edaf76787cf828c62, 000000000000000000000000000000000000000000000a968163f0a57b400000 )
76 InitializableImmutableAdminUpgradeabilityProxy.0x2f00e3cdd69a77be7ed215ec7b2a36784dd158f921fca79ac29deffa353fe6ee( 0x2f00e3cdd69a77be7ed215ec7b2a36784dd158f921fca79ac29deffa353fe6ee, 0x0000000000000000000000007e48bc779c6d37ee678c126edaf76787cf828c62, 0x0000000000000000000000007e48bc779c6d37ee678c126edaf76787cf828c62, 000000000000000000000000000000000000000000000a968163f0a57b400000, 00000000000000000000000000000000000000000397ca271e5b2bb2f9616265 )
77 InitializableImmutableAdminUpgradeabilityProxy.0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a( 0x804c9b842b2748a22bb64b345453a3de7ca54a6ca45ce00d415894979e22897a, 0x0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f, 0000000000000000000000000000000000000000001299087ff7e97cb4d81c69, 000000000000000000000000000000000000000000613731ea50d6fa933f720e, 0000000000000000000000000000000000000000001cfebe1b11944f5e7ee41d, 000000000000000000000000000000000000000003763a400abf89476f7d7ac7, 00000000000000000000000000000000000000000397ca271e5b2bb2f9616265 )
78 Dai.Transfer( src=InitializableImmutableAdminUpgradeabilityProxy, dst=[Receiver] DSProxy, wad=50000000000000000000000 )
79 InitializableImmutableAdminUpgradeabilityProxy.0xc6a898309e823ee50bac64e45ca8adba6690e99e7841c45d754e2a38e9019d9b( 0xc6a898309e823ee50bac64e45ca8adba6690e99e7841c45d754e2a38e9019d9b, 0x0000000000000000000000006b175474e89094c44da98b954eedeac495271d0f, 0x0000000000000000000000007e48bc779c6d37ee678c126edaf76787cf828c62, 0x0000000000000000000000000000000000000000000000000000000000000040, 0000000000000000000000007e48bc779c6d37ee678c126edaf76787cf828c62, 000000000000000000000000000000000000000000000a968163f0a57b400000, 0000000000000000000000000000000000000000000000000000000000000002, 0000000000000000000000000000000000000000001cfebe1b11944f5e7ee41d )
80 Dai.Transfer( src=[Receiver] DSProxy, dst=[Sender] 0x769a1ef6fbfa918d685ba02f3c2e45cfb902ebf7, wad=50000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x028171bC...aC55b68A3
0x6B175474...495271d0F
0x6C3c7883...2A6E4379d
0x769a1ef6...FB902EBF7
3.822203274366092142 Eth
Nonce: 1596
3.806658851606482512 Eth
Nonce: 1597
0.01554442275960963
0x7d2768dE...A69DDc7A9
(Aave: Lending Pool V2)
0xd784927F...F3495f6b5
(Aave: Incentives Controller)
(Ethermine)
1,819.284622243249734379 Eth1,819.285379120749734379 Eth0.0007568775

Execution Trace

DSProxy.execute( _target=0x234e8219f25F6AF4bE90d40C79DEdE31B1f21d4f, _data=0xFBF37739000000000000000000000000B53C1A33016B2DC2FF3653530BFF1848A515C8C50000000000000000000000006B175474E89094C44DA98B954EEDEAC495271D0F000000000000000000000000000000000000000000000A968163F0A57B4000000000000000000000000000000000000000000000000000000000000000000002 ) => ( response=0000000000000000000000000000000000000000000000000000000000000000 )
  • AaveBasicProxyV2.borrow( _market=0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5, _tokenAddr=0x6B175474E89094C44Da98b954EedeAC495271d0F, _amount=50000000000000000000000, _type=2 )
    • GasToken2.balanceOf( owner=0x7e48bc779C6D37eE678c126EdAF76787CF828c62 ) => ( balance=0 )
    • LendingPoolAddressesProvider.STATICCALL( )
    • InitializableImmutableAdminUpgradeabilityProxy.a415bcad( )
      • LendingPool.borrow( asset=0x6B175474E89094C44Da98b954EedeAC495271d0F, amount=50000000000000000000000, interestRateMode=2, referralCode=64, onBehalfOf=0x7e48bc779C6D37eE678c126EdAF76787CF828c62 )
        • LendingPoolAddressesProvider.STATICCALL( )
        • AaveOracle.getAssetPrice( asset=0x6B175474E89094C44Da98b954EedeAC495271d0F ) => ( 427521386484285 )
          • EACAggregatorProxy.STATICCALL( )
            • AccessControlledOffchainAggregator.STATICCALL( )
            • ValidationLogic.721a92f9( )
              • AaveOracle.getAssetPrice( asset=0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599 ) => ( 13416867230000000000 )
                • EACAggregatorProxy.STATICCALL( )
                • InitializableImmutableAdminUpgradeabilityProxy.70a08231( )
                  • AToken.balanceOf( user=0x7e48bc779C6D37eE678c126EdAF76787CF828c62 ) => ( 436788539 )
                  • AaveOracle.getAssetPrice( asset=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 ) => ( 1000000000000000000 )
                  • InitializableImmutableAdminUpgradeabilityProxy.70a08231( )
                    • AToken.balanceOf( user=0x7e48bc779C6D37eE678c126EdAF76787CF828c62 ) => ( 304624319681802479972 )
                    • AaveOracle.getAssetPrice( asset=0x6B175474E89094C44Da98b954EedeAC495271d0F ) => ( 427521386484285 )
                      • EACAggregatorProxy.STATICCALL( )
                      • InitializableImmutableAdminUpgradeabilityProxy.70a08231( )
                        • StableDebtToken.balanceOf( account=0x7e48bc779C6D37eE678c126EdAF76787CF828c62 ) => ( 0 )
                        • InitializableImmutableAdminUpgradeabilityProxy.70a08231( )
                          • VariableDebtToken.balanceOf( user=0x7e48bc779C6D37eE678c126EdAF76787CF828c62 ) => ( 50051711776579616878692 )
                          • AaveOracle.getAssetPrice( asset=0xa693B19d2931d498c5B318dF961919BB4aee87a5 ) => ( 346711600000000 )
                            • EACAggregatorProxy.STATICCALL( )
                            • InitializableImmutableAdminUpgradeabilityProxy.70a08231( )
                              • StableDebtToken.balanceOf( account=0x7e48bc779C6D37eE678c126EdAF76787CF828c62 ) => ( 0 )
                              • InitializableImmutableAdminUpgradeabilityProxy.70a08231( )
                                • VariableDebtToken.balanceOf( user=0x7e48bc779C6D37eE678c126EdAF76787CF828c62 ) => ( 526299375614 )
                                • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
                                  • VariableDebtToken.DELEGATECALL( )
                                  • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
                                    • StableDebtToken.DELEGATECALL( )
                                    • InitializableImmutableAdminUpgradeabilityProxy.7df5bd3b( )
                                      • AToken.mintToTreasury( amount=83811592114003984275, index=1071383354800677206460562119 )
                                        • InitializableImmutableAdminUpgradeabilityProxy.31873e2e( )
                                        • InitializableImmutableAdminUpgradeabilityProxy.b3f1c93d( )
                                          • VariableDebtToken.mint( user=0x7e48bc779C6D37eE678c126EdAF76787CF828c62, onBehalfOf=0x7e48bc779C6D37eE678c126EdAF76787CF828c62, amount=50000000000000000000000, index=1111957467865792846267245157 ) => ( False )
                                            • InitializableImmutableAdminUpgradeabilityProxy.31873e2e( )
                                            • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
                                              • StableDebtToken.DELEGATECALL( )
                                              • InitializableImmutableAdminUpgradeabilityProxy.STATICCALL( )
                                                • VariableDebtToken.DELEGATECALL( )
                                                • Dai.balanceOf( 0x028171bCA77440897B824Ca71D1c56caC55b68A3 ) => ( 183978820224510881862942912 )
                                                • DefaultReserveInterestRateStrategy.calculateInterestRates( reserve=0x6B175474E89094C44Da98b954EedeAC495271d0F, availableLiquidity=183928820224510881862942912, totalStableDebt=3722839454019326817876166, totalVariableDebt=427614692843206088312881298, averageStableBorrowRate=102376778451150915752492065, reserveFactor=1000 ) => ( 22483343619990454422477929, 117526455433867846913847822, 35052910867735693827695645 )
                                                  • LendingPoolAddressesProvider.STATICCALL( )
                                                  • LendingRateOracle.getMarketBorrowRate( asset=0x6B175474E89094C44Da98b954EedeAC495271d0F ) => ( 100000000000000000000000000 )
                                                  • InitializableImmutableAdminUpgradeabilityProxy.4efecaa5( )
                                                    • AToken.transferUnderlyingTo( target=0x7e48bc779C6D37eE678c126EdAF76787CF828c62, amount=50000000000000000000000 ) => ( 50000000000000000000000 )
                                                      • Dai.transfer( dst=0x7e48bc779C6D37eE678c126EdAF76787CF828c62, wad=50000000000000000000000 ) => ( True )
                                                      • Dai.balanceOf( 0x7e48bc779C6D37eE678c126EdAF76787CF828c62 ) => ( 50000000000000000000000 )
                                                      • Dai.transfer( dst=0x769a1ef6FBfa918d685ba02F3c2E45CFB902EBF7, wad=50000000000000000000000 ) => ( True )
                                                        File 1 of 30: DSProxy
                                                        // proxy.sol - execute actions atomically through the proxy's identity
                                                        
                                                        // Copyright (C) 2017  DappHub, LLC
                                                        
                                                        // 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.4.23;
                                                        
                                                        contract DSAuthority {
                                                            function canCall(
                                                                address src, address dst, bytes4 sig
                                                            ) public view returns (bool);
                                                        }
                                                        
                                                        contract DSAuthEvents {
                                                            event LogSetAuthority (address indexed authority);
                                                            event LogSetOwner     (address indexed owner);
                                                        }
                                                        
                                                        contract DSAuth is DSAuthEvents {
                                                            DSAuthority  public  authority;
                                                            address      public  owner;
                                                        
                                                            constructor() public {
                                                                owner = msg.sender;
                                                                emit LogSetOwner(msg.sender);
                                                            }
                                                        
                                                            function setOwner(address owner_)
                                                                public
                                                                auth
                                                            {
                                                                owner = owner_;
                                                                emit LogSetOwner(owner);
                                                            }
                                                        
                                                            function setAuthority(DSAuthority authority_)
                                                                public
                                                                auth
                                                            {
                                                                authority = authority_;
                                                                emit LogSetAuthority(authority);
                                                            }
                                                        
                                                            modifier auth {
                                                                require(isAuthorized(msg.sender, msg.sig));
                                                                _;
                                                            }
                                                        
                                                            function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
                                                                if (src == address(this)) {
                                                                    return true;
                                                                } else if (src == owner) {
                                                                    return true;
                                                                } else if (authority == DSAuthority(0)) {
                                                                    return false;
                                                                } else {
                                                                    return authority.canCall(src, this, sig);
                                                                }
                                                            }
                                                        }
                                                        
                                                        contract DSNote {
                                                            event LogNote(
                                                                bytes4   indexed  sig,
                                                                address  indexed  guy,
                                                                bytes32  indexed  foo,
                                                                bytes32  indexed  bar,
                                                                uint              wad,
                                                                bytes             fax
                                                            ) anonymous;
                                                        
                                                            modifier note {
                                                                bytes32 foo;
                                                                bytes32 bar;
                                                        
                                                                assembly {
                                                                    foo := calldataload(4)
                                                                    bar := calldataload(36)
                                                                }
                                                        
                                                                emit LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);
                                                        
                                                                _;
                                                            }
                                                        }
                                                        
                                                        // DSProxy
                                                        // Allows code execution using a persistant identity This can be very
                                                        // useful to execute a sequence of atomic actions. Since the owner of
                                                        // the proxy can be changed, this allows for dynamic ownership models
                                                        // i.e. a multisig
                                                        contract DSProxy is DSAuth, DSNote {
                                                            DSProxyCache public cache;  // global cache for contracts
                                                        
                                                            constructor(address _cacheAddr) public {
                                                                require(setCache(_cacheAddr));
                                                            }
                                                        
                                                            function() public payable {
                                                            }
                                                        
                                                            // use the proxy to execute calldata _data on contract _code
                                                            function execute(bytes _code, bytes _data)
                                                                public
                                                                payable
                                                                returns (address target, bytes32 response)
                                                            {
                                                                target = cache.read(_code);
                                                                if (target == 0x0) {
                                                                    // deploy contract & store its address in cache
                                                                    target = cache.write(_code);
                                                                }
                                                        
                                                                response = execute(target, _data);
                                                            }
                                                        
                                                            function execute(address _target, bytes _data)
                                                                public
                                                                auth
                                                                note
                                                                payable
                                                                returns (bytes32 response)
                                                            {
                                                                require(_target != 0x0);
                                                        
                                                                // call contract in current context
                                                                assembly {
                                                                    let succeeded := delegatecall(sub(gas, 5000), _target, add(_data, 0x20), mload(_data), 0, 32)
                                                                    response := mload(0)      // load delegatecall output
                                                                    switch iszero(succeeded)
                                                                    case 1 {
                                                                        // throw if delegatecall failed
                                                                        revert(0, 0)
                                                                    }
                                                                }
                                                            }
                                                        
                                                            //set new cache
                                                            function setCache(address _cacheAddr)
                                                                public
                                                                auth
                                                                note
                                                                returns (bool)
                                                            {
                                                                require(_cacheAddr != 0x0);        // invalid cache address
                                                                cache = DSProxyCache(_cacheAddr);  // overwrite cache
                                                                return true;
                                                            }
                                                        }
                                                        
                                                        // DSProxyFactory
                                                        // This factory deploys new proxy instances through build()
                                                        // Deployed proxy addresses are logged
                                                        contract DSProxyFactory {
                                                            event Created(address indexed sender, address indexed owner, address proxy, address cache);
                                                            mapping(address=>bool) public isProxy;
                                                            DSProxyCache public cache = new DSProxyCache();
                                                        
                                                            // deploys a new proxy instance
                                                            // sets owner of proxy to caller
                                                            function build() public returns (DSProxy proxy) {
                                                                proxy = build(msg.sender);
                                                            }
                                                        
                                                            // deploys a new proxy instance
                                                            // sets custom owner of proxy
                                                            function build(address owner) public returns (DSProxy proxy) {
                                                                proxy = new DSProxy(cache);
                                                                emit Created(msg.sender, owner, address(proxy), address(cache));
                                                                proxy.setOwner(owner);
                                                                isProxy[proxy] = true;
                                                            }
                                                        }
                                                        
                                                        // DSProxyCache
                                                        // This global cache stores addresses of contracts previously deployed
                                                        // by a proxy. This saves gas from repeat deployment of the same
                                                        // contracts and eliminates blockchain bloat.
                                                        
                                                        // By default, all proxies deployed from the same factory store
                                                        // contracts in the same cache. The cache a proxy instance uses can be
                                                        // changed.  The cache uses the sha3 hash of a contract's bytecode to
                                                        // lookup the address
                                                        contract DSProxyCache {
                                                            mapping(bytes32 => address) cache;
                                                        
                                                            function read(bytes _code) public view returns (address) {
                                                                bytes32 hash = keccak256(_code);
                                                                return cache[hash];
                                                            }
                                                        
                                                            function write(bytes _code) public returns (address target) {
                                                                assembly {
                                                                    target := create(0, add(_code, 0x20), mload(_code))
                                                                    switch iszero(extcodesize(target))
                                                                    case 1 {
                                                                        // throw if contract failed to deploy
                                                                        revert(0, 0)
                                                                    }
                                                                }
                                                                bytes32 hash = keccak256(_code);
                                                                cache[hash] = target;
                                                            }
                                                        }

                                                        File 2 of 30: InitializableImmutableAdminUpgradeabilityProxy
                                                        // SPDX-License-Identifier: agpl-3.0
                                                        pragma solidity 0.6.12;
                                                        
                                                        /**
                                                         * @dev Collection of functions related to the address type
                                                         */
                                                        library Address {
                                                          /**
                                                           * @dev Returns true if `account` is a contract.
                                                           *
                                                           * [IMPORTANT]
                                                           * ====
                                                           * It is unsafe to assume that an address for which this function returns
                                                           * false is an externally-owned account (EOA) and not a contract.
                                                           *
                                                           * Among others, `isContract` will return false for the following
                                                           * types of addresses:
                                                           *
                                                           *  - an externally-owned account
                                                           *  - a contract in construction
                                                           *  - an address where a contract will be created
                                                           *  - an address where a contract lived, but was destroyed
                                                           * ====
                                                           */
                                                          function isContract(address account) internal view returns (bool) {
                                                            // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                                                            // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                                                            // for accounts without code, i.e. `keccak256('')`
                                                            bytes32 codehash;
                                                            bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                                                            // solhint-disable-next-line no-inline-assembly
                                                            assembly {
                                                              codehash := extcodehash(account)
                                                            }
                                                            return (codehash != accountHash && codehash != 0x0);
                                                          }
                                                        
                                                          /**
                                                           * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                                                           * `recipient`, forwarding all available gas and reverting on errors.
                                                           *
                                                           * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                                                           * of certain opcodes, possibly making contracts go over the 2300 gas limit
                                                           * imposed by `transfer`, making them unable to receive funds via
                                                           * `transfer`. {sendValue} removes this limitation.
                                                           *
                                                           * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                                                           *
                                                           * IMPORTANT: because control is transferred to `recipient`, care must be
                                                           * taken to not create reentrancy vulnerabilities. Consider using
                                                           * {ReentrancyGuard} or the
                                                           * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                                                           */
                                                          function sendValue(address payable recipient, uint256 amount) internal {
                                                            require(address(this).balance >= amount, 'Address: insufficient balance');
                                                        
                                                            // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                                                            (bool success, ) = recipient.call{value: amount}('');
                                                            require(success, 'Address: unable to send value, recipient may have reverted');
                                                          }
                                                        }
                                                        
                                                        /**
                                                         * @title Proxy
                                                         * @dev Implements delegation of calls to other contracts, with proper
                                                         * forwarding of return values and bubbling of failures.
                                                         * It defines a fallback function that delegates all calls to the address
                                                         * returned by the abstract _implementation() internal function.
                                                         */
                                                        abstract contract Proxy {
                                                          /**
                                                           * @dev Fallback function.
                                                           * Implemented entirely in `_fallback`.
                                                           */
                                                          fallback() external payable {
                                                            _fallback();
                                                          }
                                                        
                                                          /**
                                                           * @return The Address of the implementation.
                                                           */
                                                          function _implementation() internal view virtual returns (address);
                                                        
                                                          /**
                                                           * @dev Delegates execution to an implementation contract.
                                                           * This is a low level function that doesn't return to its internal call site.
                                                           * It will return to the external caller whatever the implementation returns.
                                                           * @param implementation Address to delegate.
                                                           */
                                                          function _delegate(address implementation) internal {
                                                            //solium-disable-next-line
                                                            assembly {
                                                              // Copy msg.data. We take full control of memory in this inline assembly
                                                              // block because it will not return to Solidity code. We overwrite the
                                                              // Solidity scratch pad at memory position 0.
                                                              calldatacopy(0, 0, calldatasize())
                                                        
                                                              // Call the implementation.
                                                              // out and outsize are 0 because we don't know the size yet.
                                                              let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
                                                        
                                                              // Copy the returned data.
                                                              returndatacopy(0, 0, returndatasize())
                                                        
                                                              switch result
                                                                // delegatecall returns 0 on error.
                                                                case 0 {
                                                                  revert(0, returndatasize())
                                                                }
                                                                default {
                                                                  return(0, returndatasize())
                                                                }
                                                            }
                                                          }
                                                        
                                                          /**
                                                           * @dev Function that is run as the first thing in the fallback function.
                                                           * Can be redefined in derived contracts to add functionality.
                                                           * Redefinitions must call super._willFallback().
                                                           */
                                                          function _willFallback() internal virtual {}
                                                        
                                                          /**
                                                           * @dev fallback implementation.
                                                           * Extracted to enable manual triggering.
                                                           */
                                                          function _fallback() internal {
                                                            _willFallback();
                                                            _delegate(_implementation());
                                                          }
                                                        }
                                                        
                                                        /**
                                                         * @title BaseUpgradeabilityProxy
                                                         * @dev This contract implements a proxy that allows to change the
                                                         * implementation address to which it will delegate.
                                                         * Such a change is called an implementation upgrade.
                                                         */
                                                        contract BaseUpgradeabilityProxy is Proxy {
                                                          /**
                                                           * @dev Emitted when the implementation is upgraded.
                                                           * @param implementation Address of the new implementation.
                                                           */
                                                          event Upgraded(address indexed implementation);
                                                        
                                                          /**
                                                           * @dev Storage slot with the address of the current implementation.
                                                           * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
                                                           * validated in the constructor.
                                                           */
                                                          bytes32 internal constant IMPLEMENTATION_SLOT =
                                                            0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                                                        
                                                          /**
                                                           * @dev Returns the current implementation.
                                                           * @return impl Address of the current implementation
                                                           */
                                                          function _implementation() internal view override returns (address impl) {
                                                            bytes32 slot = IMPLEMENTATION_SLOT;
                                                            //solium-disable-next-line
                                                            assembly {
                                                              impl := sload(slot)
                                                            }
                                                          }
                                                        
                                                          /**
                                                           * @dev Upgrades the proxy to a new implementation.
                                                           * @param newImplementation Address of the new implementation.
                                                           */
                                                          function _upgradeTo(address newImplementation) internal {
                                                            _setImplementation(newImplementation);
                                                            emit Upgraded(newImplementation);
                                                          }
                                                        
                                                          /**
                                                           * @dev Sets the implementation address of the proxy.
                                                           * @param newImplementation Address of the new implementation.
                                                           */
                                                          function _setImplementation(address newImplementation) internal {
                                                            require(
                                                              Address.isContract(newImplementation),
                                                              'Cannot set a proxy implementation to a non-contract address'
                                                            );
                                                        
                                                            bytes32 slot = IMPLEMENTATION_SLOT;
                                                        
                                                            //solium-disable-next-line
                                                            assembly {
                                                              sstore(slot, newImplementation)
                                                            }
                                                          }
                                                        }
                                                        
                                                        /**
                                                         * @title InitializableUpgradeabilityProxy
                                                         * @dev Extends BaseUpgradeabilityProxy with an initializer for initializing
                                                         * implementation and init data.
                                                         */
                                                        contract InitializableUpgradeabilityProxy is BaseUpgradeabilityProxy {
                                                          /**
                                                           * @dev Contract initializer.
                                                           * @param _logic Address of the initial implementation.
                                                           * @param _data Data to send as msg.data to the implementation to initialize the proxied contract.
                                                           * It should include the signature and the parameters of the function to be called, as described in
                                                           * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                                                           * This parameter is optional, if no data is given the initialization call to proxied contract will be skipped.
                                                           */
                                                          function initialize(address _logic, bytes memory _data) public payable {
                                                            require(_implementation() == address(0));
                                                            assert(IMPLEMENTATION_SLOT == bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1));
                                                            _setImplementation(_logic);
                                                            if (_data.length > 0) {
                                                              (bool success, ) = _logic.delegatecall(_data);
                                                              require(success);
                                                            }
                                                          }
                                                        }
                                                        
                                                        /**
                                                         * @title BaseImmutableAdminUpgradeabilityProxy
                                                         * @author Aave, inspired by the OpenZeppelin upgradeability proxy pattern
                                                         * @dev This contract combines an upgradeability proxy with an authorization
                                                         * mechanism for administrative tasks. The admin role is stored in an immutable, which
                                                         * helps saving transactions costs
                                                         * All external functions in this contract must be guarded by the
                                                         * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity
                                                         * feature proposal that would enable this to be done automatically.
                                                         */
                                                        contract BaseImmutableAdminUpgradeabilityProxy is BaseUpgradeabilityProxy {
                                                          address immutable ADMIN;
                                                        
                                                          constructor(address admin) public {
                                                            ADMIN = admin;
                                                          }
                                                        
                                                          modifier ifAdmin() {
                                                            if (msg.sender == ADMIN) {
                                                              _;
                                                            } else {
                                                              _fallback();
                                                            }
                                                          }
                                                        
                                                          /**
                                                           * @return The address of the proxy admin.
                                                           */
                                                          function admin() external ifAdmin returns (address) {
                                                            return ADMIN;
                                                          }
                                                        
                                                          /**
                                                           * @return The address of the implementation.
                                                           */
                                                          function implementation() external ifAdmin returns (address) {
                                                            return _implementation();
                                                          }
                                                        
                                                          /**
                                                           * @dev Upgrade the backing implementation of the proxy.
                                                           * Only the admin can call this function.
                                                           * @param newImplementation Address of the new implementation.
                                                           */
                                                          function upgradeTo(address newImplementation) external ifAdmin {
                                                            _upgradeTo(newImplementation);
                                                          }
                                                        
                                                          /**
                                                           * @dev Upgrade the backing implementation of the proxy and call a function
                                                           * on the new implementation.
                                                           * This is useful to initialize the proxied contract.
                                                           * @param newImplementation Address of the new implementation.
                                                           * @param data Data to send as msg.data in the low level call.
                                                           * It should include the signature and the parameters of the function to be called, as described in
                                                           * https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector-and-argument-encoding.
                                                           */
                                                          function upgradeToAndCall(address newImplementation, bytes calldata data)
                                                            external
                                                            payable
                                                            ifAdmin
                                                          {
                                                            _upgradeTo(newImplementation);
                                                            (bool success, ) = newImplementation.delegatecall(data);
                                                            require(success);
                                                          }
                                                        
                                                          /**
                                                           * @dev Only fall back when the sender is not the admin.
                                                           */
                                                          function _willFallback() internal virtual override {
                                                            require(msg.sender != ADMIN, 'Cannot call fallback function from the proxy admin');
                                                            super._willFallback();
                                                          }
                                                        }
                                                        
                                                        /**
                                                         * @title InitializableAdminUpgradeabilityProxy
                                                         * @dev Extends BaseAdminUpgradeabilityProxy with an initializer function
                                                         */
                                                        contract InitializableImmutableAdminUpgradeabilityProxy is
                                                          BaseImmutableAdminUpgradeabilityProxy,
                                                          InitializableUpgradeabilityProxy
                                                        {
                                                          constructor(address admin) public BaseImmutableAdminUpgradeabilityProxy(admin) {}
                                                        
                                                          /**
                                                           * @dev Only fall back when the sender is not the admin.
                                                           */
                                                          function _willFallback() internal override(BaseImmutableAdminUpgradeabilityProxy, Proxy) {
                                                            BaseImmutableAdminUpgradeabilityProxy._willFallback();
                                                          }
                                                        }

                                                        File 3 of 30: 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 30: 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 30: 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 6 of 30: 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 7 of 30: AaveBasicProxyV2
                                                        pragma solidity ^0.6.0; 
                                                        
                                                        pragma experimental ABIEncoderV2;
                                                        
                                                        interface ERC20 {
                                                            function totalSupply() external view returns (uint256 supply);
                                                        
                                                            function balanceOf(address _owner) external view returns (uint256 balance);
                                                        
                                                            function transfer(address _to, uint256 _value) external returns (bool success);
                                                        
                                                            function transferFrom(address _from, address _to, uint256 _value)
                                                                external
                                                                returns (bool success);
                                                        
                                                            function approve(address _spender, uint256 _value) external returns (bool success);
                                                        
                                                            function allowance(address _owner, address _spender) external view returns (uint256 remaining);
                                                        
                                                            function decimals() external view returns (uint256 digits);
                                                        
                                                            event Approval(address indexed _owner, address indexed _spender, uint256 _value);
                                                        } abstract contract GasTokenInterface is ERC20 {
                                                            function free(uint256 value) public virtual returns (bool success);
                                                        
                                                            function freeUpTo(uint256 value) public virtual returns (uint256 freed);
                                                        
                                                            function freeFrom(address from, uint256 value) public virtual returns (bool success);
                                                        
                                                            function freeFromUpTo(address from, uint256 value) public virtual returns (uint256 freed);
                                                        } contract GasBurner {
                                                            // solhint-disable-next-line const-name-snakecase
                                                            GasTokenInterface public constant gasToken = GasTokenInterface(0x0000000000b3F879cb30FE243b4Dfee438691c04);
                                                        
                                                            modifier burnGas(uint _amount) {
                                                                if (gasToken.balanceOf(address(this)) >= _amount) {
                                                                    gasToken.free(_amount);
                                                                }
                                                        
                                                                _;
                                                            }
                                                        } abstract contract TokenInterface {
                                                        	address public constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
                                                            
                                                            function allowance(address, address) public virtual returns (uint256);
                                                        
                                                            function balanceOf(address) public virtual returns (uint256);
                                                        
                                                            function approve(address, uint256) public virtual;
                                                        
                                                            function transfer(address, uint256) public virtual returns (bool);
                                                        
                                                            function transferFrom(address, address, uint256) public virtual returns (bool);
                                                        
                                                            function deposit() public virtual payable;
                                                        
                                                            function withdraw(uint256) public virtual;
                                                        } abstract contract IAToken {
                                                            function redeem(uint256 _amount) external virtual;
                                                            function balanceOf(address _owner) external virtual view returns (uint256 balance);
                                                        } 
                                                        
                                                        
                                                        /**
                                                         * @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 ILendingPoolAddressesProviderV2 {
                                                          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 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;
                                                        }
                                                        
                                                        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}
                                                        }
                                                        
                                                        interface ILendingPoolV2 {
                                                          /**
                                                           * @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
                                                           **/
                                                          function withdraw(
                                                            address asset,
                                                            uint256 amount,
                                                            address to
                                                          ) external;
                                                        
                                                          /**
                                                           * @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
                                                           **/
                                                          function repay(
                                                            address asset,
                                                            uint256 amount,
                                                            uint256 rateMode,
                                                            address onBehalfOf
                                                          ) external;
                                                        
                                                          /**
                                                           * @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 (ILendingPoolAddressesProviderV2);
                                                        
                                                          function setPause(bool val) external;
                                                        
                                                          function paused() external view returns (bool);
                                                        } contract DSMath {
                                                            function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                                                require((z = x + y) >= x);
                                                            }
                                                        
                                                            function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                                                require((z = x - y) <= x);
                                                            }
                                                        
                                                            function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                                                require(y == 0 || (z = x * y) / y == x);
                                                            }
                                                        
                                                            function div(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                                                return x / y;
                                                            }
                                                        
                                                            function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                                                return x <= y ? x : y;
                                                            }
                                                        
                                                            function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                                                return x >= y ? x : y;
                                                            }
                                                        
                                                            function imin(int256 x, int256 y) internal pure returns (int256 z) {
                                                                return x <= y ? x : y;
                                                            }
                                                        
                                                            function imax(int256 x, int256 y) internal pure returns (int256 z) {
                                                                return x >= y ? x : y;
                                                            }
                                                        
                                                            uint256 constant WAD = 10**18;
                                                            uint256 constant RAY = 10**27;
                                                        
                                                            function wmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                                                z = add(mul(x, y), WAD / 2) / WAD;
                                                            }
                                                        
                                                            function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                                                z = add(mul(x, y), RAY / 2) / RAY;
                                                            }
                                                        
                                                            function wdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                                                z = add(mul(x, WAD), y / 2) / y;
                                                            }
                                                        
                                                            function rdiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
                                                                z = add(mul(x, RAY), y / 2) / y;
                                                            }
                                                        
                                                            // This famous algorithm is called "exponentiation by squaring"
                                                            // and calculates x^n with x as fixed-point and n as regular unsigned.
                                                            //
                                                            // It's O(log n), instead of O(n) for naive repeated multiplication.
                                                            //
                                                            // These facts are why it works:
                                                            //
                                                            //  If n is even, then x^n = (x^2)^(n/2).
                                                            //  If n is odd,  then x^n = x * x^(n-1),
                                                            //   and applying the equation for even x gives
                                                            //    x^n = x * (x^2)^((n-1) / 2).
                                                            //
                                                            //  Also, EVM division is flooring and
                                                            //    floor[(n-1) / 2] = floor[n / 2].
                                                            //
                                                            function rpow(uint256 x, uint256 n) internal pure returns (uint256 z) {
                                                                z = n % 2 != 0 ? x : RAY;
                                                        
                                                                for (n /= 2; n != 0; n /= 2) {
                                                                    x = rmul(x, x);
                                                        
                                                                    if (n % 2 != 0) {
                                                                        z = rmul(z, x);
                                                                    }
                                                                }
                                                            }
                                                        } abstract contract DSAuthority {
                                                            function canCall(address src, address dst, bytes4 sig) public virtual view returns (bool);
                                                        } contract DSAuthEvents {
                                                            event LogSetAuthority(address indexed authority);
                                                            event LogSetOwner(address indexed owner);
                                                        }
                                                        
                                                        
                                                        contract DSAuth is DSAuthEvents {
                                                            DSAuthority public authority;
                                                            address public owner;
                                                        
                                                            constructor() public {
                                                                owner = msg.sender;
                                                                emit LogSetOwner(msg.sender);
                                                            }
                                                        
                                                            function setOwner(address owner_) public auth {
                                                                owner = owner_;
                                                                emit LogSetOwner(owner);
                                                            }
                                                        
                                                            function setAuthority(DSAuthority authority_) public auth {
                                                                authority = authority_;
                                                                emit LogSetAuthority(address(authority));
                                                            }
                                                        
                                                            modifier auth {
                                                                require(isAuthorized(msg.sender, msg.sig));
                                                                _;
                                                            }
                                                        
                                                            function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
                                                                if (src == address(this)) {
                                                                    return true;
                                                                } else if (src == owner) {
                                                                    return true;
                                                                } else if (authority == DSAuthority(0)) {
                                                                    return false;
                                                                } else {
                                                                    return authority.canCall(src, address(this), sig);
                                                                }
                                                            }
                                                        } contract DSNote {
                                                            event LogNote(
                                                                bytes4 indexed sig,
                                                                address indexed guy,
                                                                bytes32 indexed foo,
                                                                bytes32 indexed bar,
                                                                uint256 wad,
                                                                bytes fax
                                                            ) anonymous;
                                                        
                                                            modifier note {
                                                                bytes32 foo;
                                                                bytes32 bar;
                                                        
                                                                assembly {
                                                                    foo := calldataload(4)
                                                                    bar := calldataload(36)
                                                                }
                                                        
                                                                emit LogNote(msg.sig, msg.sender, foo, bar, msg.value, msg.data);
                                                        
                                                                _;
                                                            }
                                                        } abstract contract DSProxy is DSAuth, DSNote {
                                                            DSProxyCache public cache; // global cache for contracts
                                                        
                                                            constructor(address _cacheAddr) public {
                                                                require(setCache(_cacheAddr));
                                                            }
                                                        
                                                            // solhint-disable-next-line no-empty-blocks
                                                            receive() external payable {}
                                                        
                                                            // use the proxy to execute calldata _data on contract _code
                                                            // function execute(bytes memory _code, bytes memory _data)
                                                            //     public
                                                            //     payable
                                                            //     virtual
                                                            //     returns (address target, bytes32 response);
                                                        
                                                            function execute(address _target, bytes memory _data)
                                                                public
                                                                payable
                                                                virtual
                                                                returns (bytes32 response);
                                                        
                                                            //set new cache
                                                            function setCache(address _cacheAddr) public virtual payable returns (bool);
                                                        }
                                                        
                                                        
                                                        contract DSProxyCache {
                                                            mapping(bytes32 => address) cache;
                                                        
                                                            function read(bytes memory _code) public view returns (address) {
                                                                bytes32 hash = keccak256(_code);
                                                                return cache[hash];
                                                            }
                                                        
                                                            function write(bytes memory _code) public returns (address target) {
                                                                assembly {
                                                                    target := create(0, add(_code, 0x20), mload(_code))
                                                                    switch iszero(extcodesize(target))
                                                                        case 1 {
                                                                            // throw if contract failed to deploy
                                                                            revert(0, 0)
                                                                        }
                                                                }
                                                                bytes32 hash = keccak256(_code);
                                                                cache[hash] = target;
                                                            }
                                                        } contract Discount {
                                                            address public owner;
                                                            mapping(address => CustomServiceFee) public serviceFees;
                                                        
                                                            uint256 constant MAX_SERVICE_FEE = 400;
                                                        
                                                            struct CustomServiceFee {
                                                                bool active;
                                                                uint256 amount;
                                                            }
                                                        
                                                            constructor() public {
                                                                owner = msg.sender;
                                                            }
                                                        
                                                            function isCustomFeeSet(address _user) public view returns (bool) {
                                                                return serviceFees[_user].active;
                                                            }
                                                        
                                                            function getCustomServiceFee(address _user) public view returns (uint256) {
                                                                return serviceFees[_user].amount;
                                                            }
                                                        
                                                            function setServiceFee(address _user, uint256 _fee) public {
                                                                require(msg.sender == owner, "Only owner");
                                                                require(_fee >= MAX_SERVICE_FEE || _fee == 0);
                                                        
                                                                serviceFees[_user] = CustomServiceFee({active: true, amount: _fee});
                                                            }
                                                        
                                                            function disableServiceFee(address _user) public {
                                                                require(msg.sender == owner, "Only owner");
                                                        
                                                                serviceFees[_user] = CustomServiceFee({active: false, amount: 0});
                                                            }
                                                        } 
                                                        
                                                        
                                                        
                                                        abstract contract IFeeRecipient {
                                                            function getFeeAddr() public view virtual returns (address);
                                                            function changeWalletAddr(address _newWallet) public virtual;
                                                        } /************
                                                        @title IPriceOracleGetterAave interface
                                                        @notice Interface for the Aave price oracle.*/
                                                        abstract contract IPriceOracleGetterAave {
                                                            function getAssetPrice(address _asset) external virtual view returns (uint256);
                                                            function getAssetsPrices(address[] calldata _assets) external virtual view returns(uint256[] memory);
                                                            function getSourceOfAsset(address _asset) external virtual view returns(address);
                                                            function getFallbackOracle() external virtual view returns(address);
                                                        }
                                                        
                                                        
                                                        abstract contract IAaveProtocolDataProviderV2 {
                                                        
                                                          struct TokenData {
                                                            string symbol;
                                                            address tokenAddress;
                                                          }
                                                        
                                                          function getAllReservesTokens() external virtual view returns (TokenData[] memory);
                                                        
                                                          function getAllATokens() external virtual view returns (TokenData[] memory);
                                                        
                                                          function getReserveConfigurationData(address asset)
                                                            external virtual
                                                            view
                                                            returns (
                                                              uint256 decimals,
                                                              uint256 ltv,
                                                              uint256 liquidationThreshold,
                                                              uint256 liquidationBonus,
                                                              uint256 reserveFactor,
                                                              bool usageAsCollateralEnabled,
                                                              bool borrowingEnabled,
                                                              bool stableBorrowRateEnabled,
                                                              bool isActive,
                                                              bool isFrozen
                                                            );
                                                        
                                                          function getReserveData(address asset)
                                                            external virtual
                                                            view
                                                            returns (
                                                              uint256 availableLiquidity,
                                                              uint256 totalStableDebt,
                                                              uint256 totalVariableDebt,
                                                              uint256 liquidityRate,
                                                              uint256 variableBorrowRate,
                                                              uint256 stableBorrowRate,
                                                              uint256 averageStableBorrowRate,
                                                              uint256 liquidityIndex,
                                                              uint256 variableBorrowIndex,
                                                              uint40 lastUpdateTimestamp
                                                            );
                                                        
                                                          function getUserReserveData(address asset, address user)
                                                            external virtual
                                                            view
                                                            returns (
                                                              uint256 currentATokenBalance,
                                                              uint256 currentStableDebt,
                                                              uint256 currentVariableDebt,
                                                              uint256 principalStableDebt,
                                                              uint256 scaledVariableDebt,
                                                              uint256 stableBorrowRate,
                                                              uint256 liquidityRate,
                                                              uint40 stableRateLastUpdated,
                                                              bool usageAsCollateralEnabled
                                                            );
                                                        
                                                          function getReserveTokensAddresses(address asset)
                                                            external virtual
                                                            view
                                                            returns (
                                                              address aTokenAddress,
                                                              address stableDebtTokenAddress,
                                                              address variableDebtTokenAddress
                                                            );
                                                        } library Address {
                                                            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);
                                                            }
                                                        
                                                            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");
                                                            }
                                                        
                                                            function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                                                              return functionCall(target, data, "Address: low-level call failed");
                                                            }
                                                        
                                                            function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                                                                return _functionCallWithValue(target, data, 0, errorMessage);
                                                            }
                                                        
                                                            function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                                                                return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                                                            }
                                                        
                                                            function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                                                                require(address(this).balance >= value, "Address: insufficient balance for call");
                                                                return _functionCallWithValue(target, data, value, errorMessage);
                                                            }
                                                        
                                                            function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
                                                                require(isContract(target), "Address: call to non-contract");
                                                        
                                                                // solhint-disable-next-line avoid-low-level-calls
                                                                (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
                                                                if (success) {
                                                                    return returndata;
                                                                } else {
                                                                    // Look for revert reason and bubble it up if present
                                                                    if (returndata.length > 0) {
                                                                        // The easiest way to bubble the revert reason is using memory via assembly
                                                        
                                                                        // solhint-disable-next-line no-inline-assembly
                                                                        assembly {
                                                                            let returndata_size := mload(returndata)
                                                                            revert(add(32, returndata), returndata_size)
                                                                        }
                                                                    } else {
                                                                        revert(errorMessage);
                                                                    }
                                                                }
                                                            }
                                                        } library SafeMath {
                                                            function add(uint256 a, uint256 b) internal pure returns (uint256) {
                                                                uint256 c = a + b;
                                                                require(c >= a, "SafeMath: addition overflow");
                                                        
                                                                return c;
                                                            }
                                                        
                                                            function sub(uint256 a, uint256 b) internal pure returns (uint256) {
                                                                return sub(a, b, "SafeMath: subtraction overflow");
                                                            }
                                                        
                                                            function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                                                require(b <= a, errorMessage);
                                                                uint256 c = a - b;
                                                        
                                                                return c;
                                                            }
                                                        
                                                            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;
                                                            }
                                                        
                                                            function div(uint256 a, uint256 b) internal pure returns (uint256) {
                                                                return div(a, b, "SafeMath: division by zero");
                                                            }
                                                        
                                                            function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                                                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;
                                                            }
                                                        
                                                            function mod(uint256 a, uint256 b) internal pure returns (uint256) {
                                                                return mod(a, b, "SafeMath: modulo by zero");
                                                            }
                                                        
                                                            function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
                                                                require(b != 0, errorMessage);
                                                                return a % b;
                                                            }
                                                        } library SafeERC20 {
                                                            using SafeMath for uint256;
                                                            using Address for address;
                                                        
                                                            function safeTransfer(ERC20 token, address to, uint256 value) internal {
                                                                _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                                                            }
                                                        
                                                            function safeTransferFrom(ERC20 token, address from, address to, uint256 value) internal {
                                                                _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                                                            }
                                                        
                                                            /**
                                                             * @dev Deprecated. This function has issues similar to the ones found in
                                                             * {IERC20-approve}, and its usage is discouraged.
                                                             */
                                                            function safeApprove(ERC20 token, address spender, uint256 value) internal {
                                                                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
                                                                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                                                            }
                                                        
                                                            function safeIncreaseAllowance(ERC20 token, address spender, uint256 value) internal {
                                                                uint256 newAllowance = token.allowance(address(this), spender).add(value);
                                                                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                                                            }
                                                        
                                                            function safeDecreaseAllowance(ERC20 token, address spender, uint256 value) internal {
                                                                uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                                                                _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                                                            }
                                                        
                                                            function _callOptionalReturn(ERC20 token, bytes memory data) private {
                                                        
                                                                bytes memory returndata = address(token).functionCall(data, "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");
                                                                }
                                                            }
                                                        } contract AdminAuth {
                                                        
                                                            using SafeERC20 for ERC20;
                                                        
                                                            address public owner;
                                                            address public admin;
                                                        
                                                            modifier onlyOwner() {
                                                                require(owner == msg.sender);
                                                                _;
                                                            }
                                                        
                                                            modifier onlyAdmin() {
                                                                require(admin == msg.sender);
                                                                _;
                                                            }
                                                        
                                                            constructor() public {
                                                                owner = msg.sender;
                                                                admin = 0x25eFA336886C74eA8E282ac466BdCd0199f85BB9;
                                                            }
                                                        
                                                            /// @notice Admin is set by owner first time, after that admin is super role and has permission to change owner
                                                            /// @param _admin Address of multisig that becomes admin
                                                            function setAdminByOwner(address _admin) public {
                                                                require(msg.sender == owner);
                                                                require(admin == address(0));
                                                        
                                                                admin = _admin;
                                                            }
                                                        
                                                            /// @notice Admin is able to set new admin
                                                            /// @param _admin Address of multisig that becomes new admin
                                                            function setAdminByAdmin(address _admin) public {
                                                                require(msg.sender == admin);
                                                        
                                                                admin = _admin;
                                                            }
                                                        
                                                            /// @notice Admin is able to change owner
                                                            /// @param _owner Address of new owner
                                                            function setOwnerByAdmin(address _owner) public {
                                                                require(msg.sender == admin);
                                                        
                                                                owner = _owner;
                                                            }
                                                        
                                                            /// @notice Destroy the contract
                                                            function kill() public onlyOwner {
                                                                selfdestruct(payable(owner));
                                                            }
                                                        
                                                            /// @notice  withdraw stuck funds
                                                            function withdrawStuckFunds(address _token, uint _amount) public onlyOwner {
                                                                if (_token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
                                                                    payable(owner).transfer(_amount);
                                                                } else {
                                                                    ERC20(_token).safeTransfer(owner, _amount);
                                                                }
                                                            }
                                                        } contract BotRegistry is AdminAuth {
                                                        
                                                            mapping (address => bool) public botList;
                                                        
                                                            constructor() public {
                                                                botList[0x776B4a13093e30B05781F97F6A4565B6aa8BE330] = true;
                                                        
                                                                botList[0xAED662abcC4FA3314985E67Ea993CAD064a7F5cF] = true;
                                                                botList[0xa5d330F6619d6bF892A5B87D80272e1607b3e34D] = true;
                                                                botList[0x5feB4DeE5150B589a7f567EA7CADa2759794A90A] = true;
                                                                botList[0x7ca06417c1d6f480d3bB195B80692F95A6B66158] = true;
                                                            }
                                                        
                                                            function setBot(address _botAddr, bool _state) public onlyOwner {
                                                                botList[_botAddr] = _state;
                                                            }
                                                        
                                                        } contract AaveHelperV2 is DSMath {
                                                        
                                                            using SafeERC20 for ERC20;
                                                        
                                                            IFeeRecipient public constant feeRecipient = IFeeRecipient(0x39C4a92Dc506300c3Ea4c67ca4CA611102ee6F2A);
                                                        
                                                            address public constant DISCOUNT_ADDR = 0x1b14E8D511c9A4395425314f849bD737BAF8208F;
                                                            address public constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; // mainnet
                                                        
                                                            uint public constant MANUAL_SERVICE_FEE = 400; // 0.25% Fee
                                                            uint public constant AUTOMATIC_SERVICE_FEE = 333; // 0.3% Fee
                                                        
                                                            address public constant BOT_REGISTRY_ADDRESS = 0x637726f8b08a7ABE3aE3aCaB01A80E2d8ddeF77B;
                                                        
                                                        	address public constant ETH_ADDR = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
                                                            uint public constant NINETY_NINE_PERCENT_WEI = 990000000000000000;
                                                            uint16 public constant AAVE_REFERRAL_CODE = 64;
                                                        
                                                            uint public constant STABLE_ID = 1;
                                                            uint public constant VARIABLE_ID = 2;
                                                        
                                                            /// @notice Calculates the gas cost for transaction
                                                            /// @param _oracleAddress address of oracle used
                                                            /// @param _amount Amount that is converted
                                                            /// @param _user Actuall user addr not DSProxy
                                                            /// @param _gasCost Ether amount of gas we are spending for tx
                                                            /// @param _tokenAddr token addr. of token we are getting for the fee
                                                            /// @return gasCost The amount we took for the gas cost
                                                            function getGasCost(address _oracleAddress, uint _amount, address _user, uint _gasCost, address _tokenAddr) internal returns (uint gasCost) {
                                                                if (_gasCost == 0) return 0;
                                                        
                                                                // in case its ETH, we need to get price for WETH
                                                                // everywhere else  we still use ETH as thats the token we have in this moment
                                                                address priceToken = _tokenAddr == ETH_ADDR ? WETH_ADDRESS : _tokenAddr;
                                                                uint256 price = IPriceOracleGetterAave(_oracleAddress).getAssetPrice(priceToken);
                                                                _gasCost = wdiv(_gasCost, price) / (10 ** (18 - _getDecimals(_tokenAddr)));
                                                                gasCost = _gasCost;
                                                        
                                                                // gas cost can't go over 20% of the whole amount
                                                                if (gasCost > (_amount / 20)) {
                                                                    gasCost = _amount / 20;
                                                                }
                                                        
                                                                address walletAddr = feeRecipient.getFeeAddr();
                                                        
                                                                if (_tokenAddr == ETH_ADDR) {
                                                                    payable(walletAddr).transfer(gasCost);
                                                                } else {
                                                                    ERC20(_tokenAddr).safeTransfer(walletAddr, gasCost);
                                                                }
                                                            }
                                                        
                                                        
                                                            /// @notice Returns the owner of the DSProxy that called the contract
                                                            function getUserAddress() internal view returns (address) {
                                                                DSProxy proxy = DSProxy(payable(address(this)));
                                                        
                                                                return proxy.owner();
                                                            }
                                                        
                                                            /// @notice Approves token contract to pull underlying tokens from the DSProxy
                                                            /// @param _tokenAddr Token we are trying to approve
                                                            /// @param _caller Address which will gain the approval
                                                            function approveToken(address _tokenAddr, address _caller) internal {
                                                                if (_tokenAddr != ETH_ADDR) {
                                                                    ERC20(_tokenAddr).safeApprove(_caller, uint256(-1));
                                                                }
                                                            }
                                                        
                                                            /// @notice Send specific amount from contract to specific user
                                                            /// @param _token Token we are trying to send
                                                            /// @param _user User that should receive funds
                                                            /// @param _amount Amount that should be sent
                                                            function sendContractBalance(address _token, address _user, uint _amount) internal {
                                                                if (_amount == 0) return;
                                                        
                                                                if (_token == ETH_ADDR) {
                                                                    payable(_user).transfer(_amount);
                                                                } else {
                                                                    ERC20(_token).safeTransfer(_user, _amount);
                                                                }
                                                            }
                                                        
                                                            function sendFullContractBalance(address _token, address _user) internal {
                                                                if (_token == ETH_ADDR) {
                                                                    sendContractBalance(_token, _user, address(this).balance);
                                                                } else {
                                                                    sendContractBalance(_token, _user, ERC20(_token).balanceOf(address(this)));
                                                                }
                                                            }
                                                        
                                                            function _getDecimals(address _token) internal view returns (uint256) {
                                                                if (_token == ETH_ADDR) return 18;
                                                        
                                                                return ERC20(_token).decimals();
                                                            }
                                                        
                                                            function getDataProvider(address _market) internal view returns(IAaveProtocolDataProviderV2) {
                                                                return IAaveProtocolDataProviderV2(ILendingPoolAddressesProviderV2(_market).getAddress(0x0100000000000000000000000000000000000000000000000000000000000000));
                                                            }
                                                        } /// @title Basic compound interactions through the DSProxy
                                                        contract AaveBasicProxyV2 is GasBurner, AaveHelperV2 {
                                                        
                                                            using SafeERC20 for ERC20;
                                                        
                                                            /// @notice User deposits tokens to the Aave protocol
                                                            /// @dev User needs to approve the DSProxy to pull the _tokenAddr tokens
                                                            /// @param _market address provider for specific market
                                                            /// @param _tokenAddr The address of the token to be deposited
                                                            /// @param _amount Amount of tokens to be deposited
                                                            function deposit(address _market, address _tokenAddr, uint256 _amount) public burnGas(5) payable {
                                                                address lendingPool = ILendingPoolAddressesProviderV2(_market).getLendingPool();
                                                        
                                                                if (_tokenAddr == ETH_ADDR) {
                                                                    require(msg.value == _amount);
                                                                    TokenInterface(WETH_ADDRESS).deposit{value: _amount}();
                                                                    _tokenAddr = WETH_ADDRESS;
                                                                } else {
                                                                    ERC20(_tokenAddr).safeTransferFrom(msg.sender, address(this), _amount);
                                                                }
                                                        
                                                                approveToken(_tokenAddr, lendingPool);
                                                                ILendingPoolV2(lendingPool).deposit(_tokenAddr, _amount, address(this), AAVE_REFERRAL_CODE);
                                                        
                                                                setUserUseReserveAsCollateralIfNeeded(_market, _tokenAddr);
                                                            }
                                                        
                                                            /// @notice User withdraws tokens from the Aave protocol
                                                            /// @param _market address provider for specific market
                                                            /// @param _tokenAddr The address of the token to be withdrawn
                                                            /// @param _amount Amount of tokens to be withdrawn -> send -1 for whole amount
                                                            function withdraw(address _market, address _tokenAddr, uint256 _amount) public burnGas(8) {
                                                                address lendingPool = ILendingPoolAddressesProviderV2(_market).getLendingPool();
                                                                _tokenAddr = changeToWeth(_tokenAddr);
                                                        
                                                                if (_tokenAddr == WETH_ADDRESS) {
                                                                    // if weth, pull to proxy and return ETH to user
                                                                    ILendingPoolV2(lendingPool).withdraw(_tokenAddr, _amount, address(this));
                                                                    // needs to use balance of in case that amount is -1 for whole debt
                                                                    TokenInterface(WETH_ADDRESS).withdraw(TokenInterface(WETH_ADDRESS).balanceOf(address(this)));
                                                                    msg.sender.transfer(address(this).balance);
                                                                } else {
                                                                    // if not eth send directly to user
                                                                    ILendingPoolV2(lendingPool).withdraw(_tokenAddr, _amount, msg.sender);
                                                                }
                                                            }
                                                        
                                                            /// @notice User borrows tokens to the Aave protocol
                                                            /// @param _market address provider for specific market
                                                            /// @param _tokenAddr The address of the token to be borrowed
                                                            /// @param _amount Amount of tokens to be borrowed
                                                            /// @param _type Send 1 for stable rate and 2 for variable
                                                            function borrow(address _market, address _tokenAddr, uint256 _amount, uint256 _type) public burnGas(8) {
                                                                address lendingPool = ILendingPoolAddressesProviderV2(_market).getLendingPool();
                                                                _tokenAddr = changeToWeth(_tokenAddr);
                                                        
                                                                ILendingPoolV2(lendingPool).borrow(_tokenAddr, _amount, _type, AAVE_REFERRAL_CODE, address(this));
                                                        
                                                                if (_tokenAddr == WETH_ADDRESS) {
                                                                    // we do this so the user gets eth instead of weth
                                                                    TokenInterface(WETH_ADDRESS).withdraw(_amount);
                                                                    _tokenAddr = ETH_ADDR;
                                                                }
                                                        
                                                                withdrawTokens(_tokenAddr);
                                                            }
                                                        
                                                            /// @dev User needs to approve the DSProxy to pull the _tokenAddr tokens
                                                            /// @notice User paybacks tokens to the Aave protocol
                                                            /// @param _market address provider for specific market
                                                            /// @param _tokenAddr The address of the token to be paybacked
                                                            /// @param _amount Amount of tokens to be payed back
                                                            function payback(address _market, address _tokenAddr, uint256 _amount, uint256 _rateMode) public burnGas(3) payable {
                                                                address lendingPool = ILendingPoolAddressesProviderV2(_market).getLendingPool();
                                                                _tokenAddr = changeToWeth(_tokenAddr);
                                                        
                                                                if (_tokenAddr == WETH_ADDRESS) {
                                                                    TokenInterface(WETH_ADDRESS).deposit{value: msg.value}();
                                                                } else {
                                                                    uint amountToPull = min(_amount, ERC20(_tokenAddr).balanceOf(msg.sender));
                                                                    ERC20(_tokenAddr).safeTransferFrom(msg.sender, address(this), amountToPull);
                                                                }
                                                        
                                                                approveToken(_tokenAddr, lendingPool);
                                                                ILendingPoolV2(lendingPool).repay(_tokenAddr, _amount, _rateMode, payable(address(this)));
                                                        
                                                                if (_tokenAddr == WETH_ADDRESS) {
                                                                    // Pull if we have any eth leftover
                                                                    TokenInterface(WETH_ADDRESS).withdraw(ERC20(WETH_ADDRESS).balanceOf(address(this)));
                                                                    _tokenAddr = ETH_ADDR;
                                                                }
                                                        
                                                                withdrawTokens(_tokenAddr);
                                                            }
                                                        
                                                            /// @dev User needs to approve the DSProxy to pull the _tokenAddr tokens
                                                            /// @notice User paybacks tokens to the Aave protocol
                                                            /// @param _market address provider for specific market
                                                            /// @param _tokenAddr The address of the token to be paybacked
                                                            /// @param _amount Amount of tokens to be payed back
                                                            function paybackOnBehalf(address _market, address _tokenAddr, uint256 _amount, uint256 _rateMode, address _onBehalf) public burnGas(3) payable {
                                                                address lendingPool = ILendingPoolAddressesProviderV2(_market).getLendingPool();
                                                                _tokenAddr = changeToWeth(_tokenAddr);
                                                        
                                                                if (_tokenAddr == WETH_ADDRESS) {
                                                                    TokenInterface(WETH_ADDRESS).deposit{value: msg.value}();
                                                                } else {
                                                                    uint amountToPull = min(_amount, ERC20(_tokenAddr).allowance(msg.sender, address(this)));
                                                                    ERC20(_tokenAddr).safeTransferFrom(msg.sender, address(this), amountToPull);
                                                                }
                                                        
                                                                approveToken(_tokenAddr, lendingPool);
                                                                ILendingPoolV2(lendingPool).repay(_tokenAddr, _amount, _rateMode, _onBehalf);
                                                        
                                                                if (_tokenAddr == WETH_ADDRESS) {
                                                                    // we do this so the user gets eth instead of weth
                                                                    TokenInterface(WETH_ADDRESS).withdraw(_amount);
                                                                    _tokenAddr = ETH_ADDR;
                                                                }
                                                        
                                                                withdrawTokens(_tokenAddr);
                                                            }
                                                        
                                                        
                                                            /// @notice Helper method to withdraw tokens from the DSProxy
                                                            /// @param _tokenAddr Address of the token to be withdrawn
                                                            function withdrawTokens(address _tokenAddr) public {
                                                                uint256 amount = _tokenAddr == ETH_ADDR ? address(this).balance : ERC20(_tokenAddr).balanceOf(address(this));
                                                        
                                                                if (amount > 0) {
                                                                    if (_tokenAddr != ETH_ADDR) {
                                                                        ERC20(_tokenAddr).safeTransfer(msg.sender, amount);
                                                                    } else {
                                                                        msg.sender.transfer(amount);
                                                                    }
                                                                }
                                                            }
                                                        
                                                            function setUserUseReserveAsCollateralIfNeeded(address _market, address _tokenAddr) public {
                                                                address lendingPool = ILendingPoolAddressesProviderV2(_market).getLendingPool();
                                                                IAaveProtocolDataProviderV2 dataProvider = getDataProvider(_market);
                                                        
                                                                (,,,,,,,,bool collateralEnabled) = dataProvider.getUserReserveData(_tokenAddr, address(this));
                                                        
                                                                if (!collateralEnabled) {
                                                                    ILendingPoolV2(lendingPool).setUserUseReserveAsCollateral(_tokenAddr, true);
                                                                }
                                                            }
                                                        
                                                            function setUserUseReserveAsCollateral(address _market, address _tokenAddr, bool _true) public {
                                                                address lendingPool = ILendingPoolAddressesProviderV2(_market).getLendingPool();
                                                        
                                                                ILendingPoolV2(lendingPool).setUserUseReserveAsCollateral(_tokenAddr, _true);
                                                            }
                                                        
                                                            // stable = 1, variable = 2
                                                            function swapBorrowRateMode(address _market, address _reserve, uint _rateMode) public {
                                                                address lendingPool = ILendingPoolAddressesProviderV2(_market).getLendingPool();
                                                        
                                                                ILendingPoolV2(lendingPool).swapBorrowRateMode(_reserve, _rateMode);
                                                            }
                                                        
                                                            function changeToWeth(address _token) private view returns(address) {
                                                                if (_token == ETH_ADDR) {
                                                                    return WETH_ADDRESS;
                                                                }
                                                        
                                                                return _token;
                                                            }
                                                        
                                                        
                                                            // solhint-disable-next-line no-empty-blocks
                                                            receive() external virtual payable {}
                                                        }

                                                        File 8 of 30: GasToken2
                                                        pragma solidity ^0.4.10;
                                                        
                                                        contract GasToken2 {
                                                            //////////////////////////////////////////////////////////////////////////
                                                            // RLP.sol
                                                            // Due to some unexplained bug, we get a slightly different bytecode if 
                                                            // we use an import, and are then unable to verify the code in Etherscan
                                                            //////////////////////////////////////////////////////////////////////////
                                                            
                                                            uint256 constant ADDRESS_BYTES = 20;
                                                            uint256 constant MAX_SINGLE_BYTE = 128;
                                                            uint256 constant MAX_NONCE = 256**9 - 1;
                                                        
                                                            // count number of bytes required to represent an unsigned integer
                                                            function count_bytes(uint256 n) constant internal returns (uint256 c) {
                                                                uint i = 0;
                                                                uint mask = 1;
                                                                while (n >= mask) {
                                                                    i += 1;
                                                                    mask *= 256;
                                                                }
                                                        
                                                                return i;
                                                            }
                                                        
                                                            function mk_contract_address(address a, uint256 n) constant internal returns (address rlp) {
                                                                /*
                                                                 * make sure the RLP encoding fits in one word:
                                                                 * total_length      1 byte
                                                                 * address_length    1 byte
                                                                 * address          20 bytes
                                                                 * nonce_length      1 byte (or 0)
                                                                 * nonce           1-9 bytes
                                                                 *                ==========
                                                                 *                24-32 bytes
                                                                 */
                                                                require(n <= MAX_NONCE);
                                                        
                                                                // number of bytes required to write down the nonce
                                                                uint256 nonce_bytes;
                                                                // length in bytes of the RLP encoding of the nonce
                                                                uint256 nonce_rlp_len;
                                                        
                                                                if (0 < n && n < MAX_SINGLE_BYTE) {
                                                                    // nonce fits in a single byte
                                                                    // RLP(nonce) = nonce
                                                                    nonce_bytes = 1;
                                                                    nonce_rlp_len = 1;
                                                                } else {
                                                                    // RLP(nonce) = [num_bytes_in_nonce nonce]
                                                                    nonce_bytes = count_bytes(n);
                                                                    nonce_rlp_len = nonce_bytes + 1;
                                                                }
                                                        
                                                                // [address_length(1) address(20) nonce_length(0 or 1) nonce(1-9)]
                                                                uint256 tot_bytes = 1 + ADDRESS_BYTES + nonce_rlp_len;
                                                        
                                                                // concatenate all parts of the RLP encoding in the leading bytes of
                                                                // one 32-byte word
                                                                uint256 word = ((192 + tot_bytes) * 256**31) +
                                                                               ((128 + ADDRESS_BYTES) * 256**30) +
                                                                               (uint256(a) * 256**10);
                                                        
                                                                if (0 < n && n < MAX_SINGLE_BYTE) {
                                                                    word += n * 256**9;
                                                                } else {
                                                                    word += (128 + nonce_bytes) * 256**9;
                                                                    word += n * 256**(9 - nonce_bytes);
                                                                }
                                                        
                                                                uint256 hash;
                                                        
                                                                assembly {
                                                                    let mem_start := mload(0x40)        // get a pointer to free memory
                                                                    mstore(0x40, add(mem_start, 0x20))  // update the pointer
                                                        
                                                                    mstore(mem_start, word)             // store the rlp encoding
                                                                    hash := sha3(mem_start,
                                                                                 add(tot_bytes, 1))     // hash the rlp encoding
                                                                }
                                                        
                                                                // interpret hash as address (20 least significant bytes)
                                                                return address(hash);
                                                            }
                                                            
                                                            //////////////////////////////////////////////////////////////////////////
                                                            // Generic ERC20
                                                            //////////////////////////////////////////////////////////////////////////
                                                        
                                                            // owner -> amount
                                                            mapping(address => uint256) s_balances;
                                                            // owner -> spender -> max amount
                                                            mapping(address => mapping(address => uint256)) s_allowances;
                                                        
                                                            event Transfer(address indexed from, address indexed to, uint256 value);
                                                        
                                                            event Approval(address indexed owner, address indexed spender, uint256 value);
                                                        
                                                            // Spec: Get the account balance of another account with address `owner`
                                                            function balanceOf(address owner) public constant returns (uint256 balance) {
                                                                return s_balances[owner];
                                                            }
                                                        
                                                            function internalTransfer(address from, address to, uint256 value) internal returns (bool success) {
                                                                if (value <= s_balances[from]) {
                                                                    s_balances[from] -= value;
                                                                    s_balances[to] += value;
                                                                    Transfer(from, to, value);
                                                                    return true;
                                                                } else {
                                                                    return false;
                                                                }
                                                            }
                                                        
                                                            // Spec: Send `value` amount of tokens to address `to`
                                                            function transfer(address to, uint256 value) public returns (bool success) {
                                                                address from = msg.sender;
                                                                return internalTransfer(from, to, value);
                                                            }
                                                        
                                                            // Spec: Send `value` amount of tokens from address `from` to address `to`
                                                            function transferFrom(address from, address to, uint256 value) public returns (bool success) {
                                                                address spender = msg.sender;
                                                                if(value <= s_allowances[from][spender] && internalTransfer(from, to, value)) {
                                                                    s_allowances[from][spender] -= value;
                                                                    return true;
                                                                } else {
                                                                    return false;
                                                                }
                                                            }
                                                        
                                                            // Spec: Allow `spender` to withdraw from your account, multiple times, up
                                                            // to the `value` amount. If this function is called again it overwrites the
                                                            // current allowance with `value`.
                                                            function approve(address spender, uint256 value) public returns (bool success) {
                                                                address owner = msg.sender;
                                                                if (value != 0 && s_allowances[owner][spender] != 0) {
                                                                    return false;
                                                                }
                                                                s_allowances[owner][spender] = value;
                                                                Approval(owner, spender, value);
                                                                return true;
                                                            }
                                                        
                                                            // Spec: Returns the `amount` which `spender` is still allowed to withdraw
                                                            // from `owner`.
                                                            // What if the allowance is higher than the balance of the `owner`?
                                                            // Callers should be careful to use min(allowance, balanceOf) to make sure
                                                            // that the allowance is actually present in the account!
                                                            function allowance(address owner, address spender) public constant returns (uint256 remaining) {
                                                                return s_allowances[owner][spender];
                                                            }
                                                        
                                                            //////////////////////////////////////////////////////////////////////////
                                                            // GasToken specifics
                                                            //////////////////////////////////////////////////////////////////////////
                                                        
                                                            uint8 constant public decimals = 2;
                                                            string constant public name = "Gastoken.io";
                                                            string constant public symbol = "GST2";
                                                        
                                                            // We build a queue of nonces at which child contracts are stored. s_head is
                                                            // the nonce at the head of the queue, s_tail is the nonce behind the tail
                                                            // of the queue. The queue grows at the head and shrinks from the tail.
                                                            // Note that when and only when a contract CREATEs another contract, the
                                                            // creating contract's nonce is incremented.
                                                            // The first child contract is created with nonce == 1, the second child
                                                            // contract is created with nonce == 2, and so on...
                                                            // For example, if there are child contracts at nonces [2,3,4],
                                                            // then s_head == 4 and s_tail == 1. If there are no child contracts,
                                                            // s_head == s_tail.
                                                            uint256 s_head;
                                                            uint256 s_tail;
                                                        
                                                            // totalSupply gives  the number of tokens currently in existence
                                                            // Each token corresponds to one child contract that can be SELFDESTRUCTed
                                                            // for a gas refund.
                                                            function totalSupply() public constant returns (uint256 supply) {
                                                                return s_head - s_tail;
                                                            }
                                                        
                                                            // Creates a child contract that can only be destroyed by this contract.
                                                            function makeChild() internal returns (address addr) {
                                                                assembly {
                                                                    // EVM assembler of runtime portion of child contract:
                                                                    //     ;; Pseudocode: if (msg.sender != 0x0000000000b3f879cb30fe243b4dfee438691c04) { throw; }
                                                                    //     ;;             suicide(msg.sender)
                                                                    //     PUSH15 0xb3f879cb30fe243b4dfee438691c04 ;; hardcoded address of this contract
                                                                    //     CALLER
                                                                    //     XOR
                                                                    //     PC
                                                                    //     JUMPI
                                                                    //     CALLER
                                                                    //     SELFDESTRUCT
                                                                    // Or in binary: 6eb3f879cb30fe243b4dfee438691c043318585733ff
                                                                    // Since the binary is so short (22 bytes), we can get away
                                                                    // with a very simple initcode:
                                                                    //     PUSH22 0x6eb3f879cb30fe243b4dfee438691c043318585733ff
                                                                    //     PUSH1 0
                                                                    //     MSTORE ;; at this point, memory locations mem[10] through
                                                                    //            ;; mem[31] contain the runtime portion of the child
                                                                    //            ;; contract. all that's left to do is to RETURN this
                                                                    //            ;; chunk of memory.
                                                                    //     PUSH1 22 ;; length
                                                                    //     PUSH1 10 ;; offset
                                                                    //     RETURN
                                                                    // Or in binary: 756eb3f879cb30fe243b4dfee438691c043318585733ff6000526016600af3
                                                                    // Almost done! All we have to do is put this short (31 bytes) blob into
                                                                    // memory and call CREATE with the appropriate offsets.
                                                                    let solidity_free_mem_ptr := mload(0x40)
                                                                    mstore(solidity_free_mem_ptr, 0x00756eb3f879cb30fe243b4dfee438691c043318585733ff6000526016600af3)
                                                                    addr := create(0, add(solidity_free_mem_ptr, 1), 31)
                                                                }
                                                            }
                                                        
                                                            // Mints `value` new sub-tokens (e.g. cents, pennies, ...) by creating `value`
                                                            // new child contracts. The minted tokens are owned by the caller of this
                                                            // function.
                                                            function mint(uint256 value) public {
                                                                for (uint256 i = 0; i < value; i++) {
                                                                    makeChild();
                                                                }
                                                                s_head += value;
                                                                s_balances[msg.sender] += value;
                                                            }
                                                        
                                                            // Destroys `value` child contracts and updates s_tail.
                                                            //
                                                            // This function is affected by an issue in solc: https://github.com/ethereum/solidity/issues/2999
                                                            // The `mk_contract_address(this, i).call();` doesn't forward all available gas, but only GAS - 25710.
                                                            // As a result, when this line is executed with e.g. 30000 gas, the callee will have less than 5000 gas
                                                            // available and its SELFDESTRUCT operation will fail leading to no gas refund occurring.
                                                            // The remaining ~29000 gas left after the call is enough to update s_tail and the caller's balance.
                                                            // Hence tokens will have been destroyed without a commensurate gas refund.
                                                            // Fortunately, there is a simple workaround:
                                                            // Whenever you call free, freeUpTo, freeFrom, or freeUpToFrom, ensure that you pass at least
                                                            // 25710 + `value` * (1148 + 5722 + 150) gas. (It won't all be used)
                                                            function destroyChildren(uint256 value) internal {
                                                                uint256 tail = s_tail;
                                                                // tail points to slot behind the last contract in the queue
                                                                for (uint256 i = tail + 1; i <= tail + value; i++) {
                                                                    mk_contract_address(this, i).call();
                                                                }
                                                        
                                                                s_tail = tail + value;
                                                            }
                                                        
                                                            // Frees `value` sub-tokens (e.g. cents, pennies, ...) belonging to the
                                                            // caller of this function by destroying `value` child contracts, which
                                                            // will trigger a partial gas refund.
                                                            // You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
                                                            // when calling this function. For details, see the comment above `destroyChilden`.
                                                            function free(uint256 value) public returns (bool success) {
                                                                uint256 from_balance = s_balances[msg.sender];
                                                                if (value > from_balance) {
                                                                    return false;
                                                                }
                                                        
                                                                destroyChildren(value);
                                                        
                                                                s_balances[msg.sender] = from_balance - value;
                                                        
                                                                return true;
                                                            }
                                                        
                                                            // Frees up to `value` sub-tokens. Returns how many tokens were freed.
                                                            // Otherwise, identical to free.
                                                            // You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
                                                            // when calling this function. For details, see the comment above `destroyChilden`.
                                                            function freeUpTo(uint256 value) public returns (uint256 freed) {
                                                                uint256 from_balance = s_balances[msg.sender];
                                                                if (value > from_balance) {
                                                                    value = from_balance;
                                                                }
                                                        
                                                                destroyChildren(value);
                                                        
                                                                s_balances[msg.sender] = from_balance - value;
                                                        
                                                                return value;
                                                            }
                                                        
                                                            // Frees `value` sub-tokens owned by address `from`. Requires that `msg.sender`
                                                            // has been approved by `from`.
                                                            // You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
                                                            // when calling this function. For details, see the comment above `destroyChilden`.
                                                            function freeFrom(address from, uint256 value) public returns (bool success) {
                                                                address spender = msg.sender;
                                                                uint256 from_balance = s_balances[from];
                                                                if (value > from_balance) {
                                                                    return false;
                                                                }
                                                        
                                                                mapping(address => uint256) from_allowances = s_allowances[from];
                                                                uint256 spender_allowance = from_allowances[spender];
                                                                if (value > spender_allowance) {
                                                                    return false;
                                                                }
                                                        
                                                                destroyChildren(value);
                                                        
                                                                s_balances[from] = from_balance - value;
                                                                from_allowances[spender] = spender_allowance - value;
                                                        
                                                                return true;
                                                            }
                                                        
                                                            // Frees up to `value` sub-tokens owned by address `from`. Returns how many tokens were freed.
                                                            // Otherwise, identical to `freeFrom`.
                                                            // You should ensure that you pass at least 25710 + `value` * (1148 + 5722 + 150) gas
                                                            // when calling this function. For details, see the comment above `destroyChilden`.
                                                            function freeFromUpTo(address from, uint256 value) public returns (uint256 freed) {
                                                                address spender = msg.sender;
                                                                uint256 from_balance = s_balances[from];
                                                                if (value > from_balance) {
                                                                    value = from_balance;
                                                                }
                                                        
                                                                mapping(address => uint256) from_allowances = s_allowances[from];
                                                                uint256 spender_allowance = from_allowances[spender];
                                                                if (value > spender_allowance) {
                                                                    value = spender_allowance;
                                                                }
                                                        
                                                                destroyChildren(value);
                                                        
                                                                s_balances[from] = from_balance - value;
                                                                from_allowances[spender] = spender_allowance - value;
                                                        
                                                                return value;
                                                            }
                                                        }

                                                        File 9 of 30: 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 10 of 30: 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 11 of 30: AaveOracle
                                                        // 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;
                                                          }
                                                        }
                                                        
                                                        interface IPriceOracleGetter {
                                                          function getAssetPrice(address asset) external view returns (uint256);
                                                        }
                                                        
                                                        interface IChainlinkAggregator {
                                                          function latestAnswer() external view returns (int256);
                                                        }
                                                        
                                                        /// @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 {
                                                          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);
                                                          }
                                                        }

                                                        File 12 of 30: EACAggregatorProxy
                                                        pragma solidity 0.6.6;
                                                        
                                                        
                                                        /**
                                                         * @title The Owned contract
                                                         * @notice A contract with helpers for basic contract ownership.
                                                         */
                                                        contract Owned {
                                                        
                                                          address payable public owner;
                                                          address private pendingOwner;
                                                        
                                                          event OwnershipTransferRequested(
                                                            address indexed from,
                                                            address indexed to
                                                          );
                                                          event OwnershipTransferred(
                                                            address indexed from,
                                                            address indexed to
                                                          );
                                                        
                                                          constructor() public {
                                                            owner = msg.sender;
                                                          }
                                                        
                                                          /**
                                                           * @dev Allows an owner to begin transferring ownership to a new address,
                                                           * pending.
                                                           */
                                                          function transferOwnership(address _to)
                                                            external
                                                            onlyOwner()
                                                          {
                                                            pendingOwner = _to;
                                                        
                                                            emit OwnershipTransferRequested(owner, _to);
                                                          }
                                                        
                                                          /**
                                                           * @dev Allows an ownership transfer to be completed by the recipient.
                                                           */
                                                          function acceptOwnership()
                                                            external
                                                          {
                                                            require(msg.sender == pendingOwner, "Must be proposed owner");
                                                        
                                                            address oldOwner = owner;
                                                            owner = msg.sender;
                                                            pendingOwner = address(0);
                                                        
                                                            emit OwnershipTransferred(oldOwner, msg.sender);
                                                          }
                                                        
                                                          /**
                                                           * @dev Reverts if called by anyone other than the contract owner.
                                                           */
                                                          modifier onlyOwner() {
                                                            require(msg.sender == owner, "Only callable by owner");
                                                            _;
                                                          }
                                                        
                                                        }
                                                        
                                                        interface AggregatorInterface {
                                                          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 updatedAt);
                                                          event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
                                                        }
                                                        
                                                        interface AggregatorV3Interface {
                                                        
                                                          function decimals() external view returns (uint8);
                                                          function description() external view returns (string memory);
                                                          function version() external view returns (uint256);
                                                        
                                                          // getRoundData and latestRoundData should both raise "No data present"
                                                          // if they do not have data to report, instead of returning unset values
                                                          // which could be misinterpreted as actual reported values.
                                                          function getRoundData(uint80 _roundId)
                                                            external
                                                            view
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            );
                                                          function latestRoundData()
                                                            external
                                                            view
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            );
                                                        
                                                        }
                                                        
                                                        interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface
                                                        {
                                                        }
                                                        
                                                        /**
                                                         * @title A trusted proxy for updating where current answers are read from
                                                         * @notice This contract provides a consistent address for the
                                                         * CurrentAnwerInterface but delegates where it reads from to the owner, who is
                                                         * trusted to update it.
                                                         */
                                                        contract AggregatorProxy is AggregatorV2V3Interface, Owned {
                                                        
                                                          struct Phase {
                                                            uint16 id;
                                                            AggregatorV2V3Interface aggregator;
                                                          }
                                                          Phase private currentPhase;
                                                          AggregatorV2V3Interface public proposedAggregator;
                                                          mapping(uint16 => AggregatorV2V3Interface) public phaseAggregators;
                                                        
                                                          uint256 constant private PHASE_OFFSET = 64;
                                                          uint256 constant private PHASE_SIZE = 16;
                                                          uint256 constant private MAX_ID = 2**(PHASE_OFFSET+PHASE_SIZE) - 1;
                                                        
                                                          constructor(address _aggregator) public Owned() {
                                                            setAggregator(_aggregator);
                                                          }
                                                        
                                                          /**
                                                           * @notice Reads the current answer from aggregator delegated to.
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestAnswer()
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (int256 answer)
                                                          {
                                                            return currentPhase.aggregator.latestAnswer();
                                                          }
                                                        
                                                          /**
                                                           * @notice Reads the last updated height from aggregator delegated to.
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestTimestamp()
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (uint256 updatedAt)
                                                          {
                                                            return currentPhase.aggregator.latestTimestamp();
                                                          }
                                                        
                                                          /**
                                                           * @notice get past rounds answers
                                                           * @param _roundId the answer number to retrieve the answer for
                                                           *
                                                           * @dev #[deprecated] Use getRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended getRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function getAnswer(uint256 _roundId)
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (int256 answer)
                                                          {
                                                            if (_roundId > MAX_ID) return 0;
                                                        
                                                            (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
                                                            AggregatorV2V3Interface aggregator = phaseAggregators[phaseId];
                                                            if (address(aggregator) == address(0)) return 0;
                                                        
                                                            return aggregator.getAnswer(aggregatorRoundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get block timestamp when an answer was last updated
                                                           * @param _roundId the answer number to retrieve the updated timestamp for
                                                           *
                                                           * @dev #[deprecated] Use getRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended getRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function getTimestamp(uint256 _roundId)
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (uint256 updatedAt)
                                                          {
                                                            if (_roundId > MAX_ID) return 0;
                                                        
                                                            (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
                                                            AggregatorV2V3Interface aggregator = phaseAggregators[phaseId];
                                                            if (address(aggregator) == address(0)) return 0;
                                                        
                                                            return aggregator.getTimestamp(aggregatorRoundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get the latest completed round where the answer was updated. This
                                                           * ID includes the proxy's phase, to make sure round IDs increase even when
                                                           * switching to a newly deployed aggregator.
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestRound()
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (uint256 roundId)
                                                          {
                                                            Phase memory phase = currentPhase; // cache storage reads
                                                            return addPhase(phase.id, uint64(phase.aggregator.latestRound()));
                                                          }
                                                        
                                                          /**
                                                           * @notice get data about a round. Consumers are encouraged to check
                                                           * that they're receiving fresh data by inspecting the updatedAt and
                                                           * answeredInRound return values.
                                                           * Note that different underlying implementations of AggregatorV3Interface
                                                           * have slightly different semantics for some of the return values. Consumers
                                                           * should determine what implementations they expect to receive
                                                           * data from and validate that they can properly handle return data from all
                                                           * of them.
                                                           * @param _roundId the requested round ID as presented through the proxy, this
                                                           * is made up of the aggregator's round ID with the phase ID encoded in the
                                                           * two highest order bytes
                                                           * @return roundId is the round ID from the aggregator for which the data was
                                                           * retrieved combined with an phase to ensure that round IDs get larger as
                                                           * time moves forward.
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @dev Note that answer and updatedAt may change between queries.
                                                           */
                                                          function getRoundData(uint80 _roundId)
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
                                                        
                                                            (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 ansIn
                                                            ) = phaseAggregators[phaseId].getRoundData(aggregatorRoundId);
                                                        
                                                            return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, phaseId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get data about the latest round. Consumers are encouraged to check
                                                           * that they're receiving fresh data by inspecting the updatedAt and
                                                           * answeredInRound return values.
                                                           * Note that different underlying implementations of AggregatorV3Interface
                                                           * have slightly different semantics for some of the return values. Consumers
                                                           * should determine what implementations they expect to receive
                                                           * data from and validate that they can properly handle return data from all
                                                           * of them.
                                                           * @return roundId is the round ID from the aggregator for which the data was
                                                           * retrieved combined with an phase to ensure that round IDs get larger as
                                                           * time moves forward.
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @dev Note that answer and updatedAt may change between queries.
                                                           */
                                                          function latestRoundData()
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            Phase memory current = currentPhase; // cache storage reads
                                                        
                                                            (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 ansIn
                                                            ) = current.aggregator.latestRoundData();
                                                        
                                                            return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, current.id);
                                                          }
                                                        
                                                          /**
                                                           * @notice Used if an aggregator contract has been proposed.
                                                           * @param _roundId the round ID to retrieve the round data for
                                                           * @return roundId is the round ID for which data was retrieved
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                          */
                                                          function proposedGetRoundData(uint80 _roundId)
                                                            public
                                                            view
                                                            virtual
                                                            hasProposal()
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return proposedAggregator.getRoundData(_roundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice Used if an aggregator contract has been proposed.
                                                           * @return roundId is the round ID for which data was retrieved
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                          */
                                                          function proposedLatestRoundData()
                                                            public
                                                            view
                                                            virtual
                                                            hasProposal()
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return proposedAggregator.latestRoundData();
                                                          }
                                                        
                                                          /**
                                                           * @notice returns the current phase's aggregator address.
                                                           */
                                                          function aggregator()
                                                            external
                                                            view
                                                            returns (address)
                                                          {
                                                            return address(currentPhase.aggregator);
                                                          }
                                                        
                                                          /**
                                                           * @notice returns the current phase's ID.
                                                           */
                                                          function phaseId()
                                                            external
                                                            view
                                                            returns (uint16)
                                                          {
                                                            return currentPhase.id;
                                                          }
                                                        
                                                          /**
                                                           * @notice represents the number of decimals the aggregator responses represent.
                                                           */
                                                          function decimals()
                                                            external
                                                            view
                                                            override
                                                            returns (uint8)
                                                          {
                                                            return currentPhase.aggregator.decimals();
                                                          }
                                                        
                                                          /**
                                                           * @notice the version number representing the type of aggregator the proxy
                                                           * points to.
                                                           */
                                                          function version()
                                                            external
                                                            view
                                                            override
                                                            returns (uint256)
                                                          {
                                                            return currentPhase.aggregator.version();
                                                          }
                                                        
                                                          /**
                                                           * @notice returns the description of the aggregator the proxy points to.
                                                           */
                                                          function description()
                                                            external
                                                            view
                                                            override
                                                            returns (string memory)
                                                          {
                                                            return currentPhase.aggregator.description();
                                                          }
                                                        
                                                          /**
                                                           * @notice Allows the owner to propose a new address for the aggregator
                                                           * @param _aggregator The new address for the aggregator contract
                                                           */
                                                          function proposeAggregator(address _aggregator)
                                                            external
                                                            onlyOwner()
                                                          {
                                                            proposedAggregator = AggregatorV2V3Interface(_aggregator);
                                                          }
                                                        
                                                          /**
                                                           * @notice Allows the owner to confirm and change the address
                                                           * to the proposed aggregator
                                                           * @dev Reverts if the given address doesn't match what was previously
                                                           * proposed
                                                           * @param _aggregator The new address for the aggregator contract
                                                           */
                                                          function confirmAggregator(address _aggregator)
                                                            external
                                                            onlyOwner()
                                                          {
                                                            require(_aggregator == address(proposedAggregator), "Invalid proposed aggregator");
                                                            delete proposedAggregator;
                                                            setAggregator(_aggregator);
                                                          }
                                                        
                                                        
                                                          /*
                                                           * Internal
                                                           */
                                                        
                                                          function setAggregator(address _aggregator)
                                                            internal
                                                          {
                                                            uint16 id = currentPhase.id + 1;
                                                            currentPhase = Phase(id, AggregatorV2V3Interface(_aggregator));
                                                            phaseAggregators[id] = AggregatorV2V3Interface(_aggregator);
                                                          }
                                                        
                                                          function addPhase(
                                                            uint16 _phase,
                                                            uint64 _originalId
                                                          )
                                                            internal
                                                            view
                                                            returns (uint80)
                                                          {
                                                            return uint80(uint256(_phase) << PHASE_OFFSET | _originalId);
                                                          }
                                                        
                                                          function parseIds(
                                                            uint256 _roundId
                                                          )
                                                            internal
                                                            view
                                                            returns (uint16, uint64)
                                                          {
                                                            uint16 phaseId = uint16(_roundId >> PHASE_OFFSET);
                                                            uint64 aggregatorRoundId = uint64(_roundId);
                                                        
                                                            return (phaseId, aggregatorRoundId);
                                                          }
                                                        
                                                          function addPhaseIds(
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound,
                                                              uint16 phaseId
                                                          )
                                                            internal
                                                            view
                                                            returns (uint80, int256, uint256, uint256, uint80)
                                                          {
                                                            return (
                                                              addPhase(phaseId, uint64(roundId)),
                                                              answer,
                                                              startedAt,
                                                              updatedAt,
                                                              addPhase(phaseId, uint64(answeredInRound))
                                                            );
                                                          }
                                                        
                                                          /*
                                                           * Modifiers
                                                           */
                                                        
                                                          modifier hasProposal() {
                                                            require(address(proposedAggregator) != address(0), "No proposed aggregator present");
                                                            _;
                                                          }
                                                        
                                                        }
                                                        
                                                        interface AccessControllerInterface {
                                                          function hasAccess(address user, bytes calldata data) external view returns (bool);
                                                        }
                                                        
                                                        /**
                                                         * @title External Access Controlled Aggregator Proxy
                                                         * @notice A trusted proxy for updating where current answers are read from
                                                         * @notice This contract provides a consistent address for the
                                                         * Aggregator and AggregatorV3Interface but delegates where it reads from to the owner, who is
                                                         * trusted to update it.
                                                         * @notice Only access enabled addresses are allowed to access getters for
                                                         * aggregated answers and round information.
                                                         */
                                                        contract EACAggregatorProxy is AggregatorProxy {
                                                        
                                                          AccessControllerInterface public accessController;
                                                        
                                                          constructor(
                                                            address _aggregator,
                                                            address _accessController
                                                          )
                                                            public
                                                            AggregatorProxy(_aggregator)
                                                          {
                                                            setController(_accessController);
                                                          }
                                                        
                                                          /**
                                                           * @notice Allows the owner to update the accessController contract address.
                                                           * @param _accessController The new address for the accessController contract
                                                           */
                                                          function setController(address _accessController)
                                                            public
                                                            onlyOwner()
                                                          {
                                                            accessController = AccessControllerInterface(_accessController);
                                                          }
                                                        
                                                          /**
                                                           * @notice Reads the current answer from aggregator delegated to.
                                                           * @dev overridden function to add the checkAccess() modifier
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestAnswer()
                                                            public
                                                            view
                                                            override
                                                            checkAccess()
                                                            returns (int256)
                                                          {
                                                            return super.latestAnswer();
                                                          }
                                                        
                                                          /**
                                                           * @notice get the latest completed round where the answer was updated. This
                                                           * ID includes the proxy's phase, to make sure round IDs increase even when
                                                           * switching to a newly deployed aggregator.
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestTimestamp()
                                                            public
                                                            view
                                                            override
                                                            checkAccess()
                                                            returns (uint256)
                                                          {
                                                            return super.latestTimestamp();
                                                          }
                                                        
                                                          /**
                                                           * @notice get past rounds answers
                                                           * @param _roundId the answer number to retrieve the answer for
                                                           * @dev overridden function to add the checkAccess() modifier
                                                           *
                                                           * @dev #[deprecated] Use getRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended getRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function getAnswer(uint256 _roundId)
                                                            public
                                                            view
                                                            override
                                                            checkAccess()
                                                            returns (int256)
                                                          {
                                                            return super.getAnswer(_roundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get block timestamp when an answer was last updated
                                                           * @param _roundId the answer number to retrieve the updated timestamp for
                                                           * @dev overridden function to add the checkAccess() modifier
                                                           *
                                                           * @dev #[deprecated] Use getRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended getRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function getTimestamp(uint256 _roundId)
                                                            public
                                                            view
                                                            override
                                                            checkAccess()
                                                            returns (uint256)
                                                          {
                                                            return super.getTimestamp(_roundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get the latest completed round where the answer was updated
                                                           * @dev overridden function to add the checkAccess() modifier
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestRound()
                                                            public
                                                            view
                                                            override
                                                            checkAccess()
                                                            returns (uint256)
                                                          {
                                                            return super.latestRound();
                                                          }
                                                        
                                                          /**
                                                           * @notice get data about a round. Consumers are encouraged to check
                                                           * that they're receiving fresh data by inspecting the updatedAt and
                                                           * answeredInRound return values.
                                                           * Note that different underlying implementations of AggregatorV3Interface
                                                           * have slightly different semantics for some of the return values. Consumers
                                                           * should determine what implementations they expect to receive
                                                           * data from and validate that they can properly handle return data from all
                                                           * of them.
                                                           * @param _roundId the round ID to retrieve the round data for
                                                           * @return roundId is the round ID from the aggregator for which the data was
                                                           * retrieved combined with a phase to ensure that round IDs get larger as
                                                           * time moves forward.
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @dev Note that answer and updatedAt may change between queries.
                                                           */
                                                          function getRoundData(uint80 _roundId)
                                                            public
                                                            view
                                                            checkAccess()
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return super.getRoundData(_roundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get data about the latest round. Consumers are encouraged to check
                                                           * that they're receiving fresh data by inspecting the updatedAt and
                                                           * answeredInRound return values.
                                                           * Note that different underlying implementations of AggregatorV3Interface
                                                           * have slightly different semantics for some of the return values. Consumers
                                                           * should determine what implementations they expect to receive
                                                           * data from and validate that they can properly handle return data from all
                                                           * of them.
                                                           * @return roundId is the round ID from the aggregator for which the data was
                                                           * retrieved combined with a phase to ensure that round IDs get larger as
                                                           * time moves forward.
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @dev Note that answer and updatedAt may change between queries.
                                                           */
                                                          function latestRoundData()
                                                            public
                                                            view
                                                            checkAccess()
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return super.latestRoundData();
                                                          }
                                                        
                                                          /**
                                                           * @notice Used if an aggregator contract has been proposed.
                                                           * @param _roundId the round ID to retrieve the round data for
                                                           * @return roundId is the round ID for which data was retrieved
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                          */
                                                          function proposedGetRoundData(uint80 _roundId)
                                                            public
                                                            view
                                                            checkAccess()
                                                            hasProposal()
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return super.proposedGetRoundData(_roundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice Used if an aggregator contract has been proposed.
                                                           * @return roundId is the round ID for which data was retrieved
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                          */
                                                          function proposedLatestRoundData()
                                                            public
                                                            view
                                                            checkAccess()
                                                            hasProposal()
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return super.proposedLatestRoundData();
                                                          }
                                                        
                                                          /**
                                                           * @dev reverts if the caller does not have access by the accessController
                                                           * contract or is the contract itself.
                                                           */
                                                          modifier checkAccess() {
                                                            AccessControllerInterface ac = accessController;
                                                            require(address(ac) == address(0) || ac.hasAccess(msg.sender, msg.data), "No access");
                                                            _;
                                                          }
                                                        }

                                                        File 13 of 30: AccessControlledOffchainAggregator
                                                        // SPDX-License-Identifier: MIT
                                                        pragma solidity ^0.7.1;
                                                        import "./OffchainAggregator.sol";
                                                        import "./SimpleReadAccessController.sol";
                                                        /**
                                                         * @notice Wrapper of OffchainAggregator which checks read access on Aggregator-interface methods
                                                         */
                                                        contract AccessControlledOffchainAggregator is OffchainAggregator, SimpleReadAccessController {
                                                          constructor(
                                                            uint32 _maximumGasPrice,
                                                            uint32 _reasonableGasPrice,
                                                            uint32 _microLinkPerEth,
                                                            uint32 _linkGweiPerObservation,
                                                            uint32 _linkGweiPerTransmission,
                                                            address _link,
                                                            address _validator,
                                                            int192 _minAnswer,
                                                            int192 _maxAnswer,
                                                            AccessControllerInterface _billingAccessController,
                                                            AccessControllerInterface _requesterAccessController,
                                                            uint8 _decimals,
                                                            string memory description
                                                          )
                                                            OffchainAggregator(
                                                              _maximumGasPrice,
                                                              _reasonableGasPrice,
                                                              _microLinkPerEth,
                                                              _linkGweiPerObservation,
                                                              _linkGweiPerTransmission,
                                                              _link,
                                                              _validator,
                                                              _minAnswer,
                                                              _maxAnswer,
                                                              _billingAccessController,
                                                              _requesterAccessController,
                                                              _decimals,
                                                              description
                                                            ) {
                                                            }
                                                          /*
                                                           * v2 Aggregator interface
                                                           */
                                                          /// @inheritdoc OffchainAggregator
                                                          function latestAnswer()
                                                            public
                                                            override
                                                            view
                                                            checkAccess()
                                                            returns (int256)
                                                          {
                                                            return super.latestAnswer();
                                                          }
                                                          /// @inheritdoc OffchainAggregator
                                                          function latestTimestamp()
                                                            public
                                                            override
                                                            view
                                                            checkAccess()
                                                            returns (uint256)
                                                          {
                                                            return super.latestTimestamp();
                                                          }
                                                          /// @inheritdoc OffchainAggregator
                                                          function latestRound()
                                                            public
                                                            override
                                                            view
                                                            checkAccess()
                                                            returns (uint256)
                                                          {
                                                            return super.latestRound();
                                                          }
                                                          /// @inheritdoc OffchainAggregator
                                                          function getAnswer(uint256 _roundId)
                                                            public
                                                            override
                                                            view
                                                            checkAccess()
                                                            returns (int256)
                                                          {
                                                            return super.getAnswer(_roundId);
                                                          }
                                                          /// @inheritdoc OffchainAggregator
                                                          function getTimestamp(uint256 _roundId)
                                                            public
                                                            override
                                                            view
                                                            checkAccess()
                                                            returns (uint256)
                                                          {
                                                            return super.getTimestamp(_roundId);
                                                          }
                                                          /*
                                                           * v3 Aggregator interface
                                                           */
                                                          /// @inheritdoc OffchainAggregator
                                                          function description()
                                                            public
                                                            override
                                                            view
                                                            checkAccess()
                                                            returns (string memory)
                                                          {
                                                            return super.description();
                                                          }
                                                          /// @inheritdoc OffchainAggregator
                                                          function getRoundData(uint80 _roundId)
                                                            public
                                                            override
                                                            view
                                                            checkAccess()
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return super.getRoundData(_roundId);
                                                          }
                                                          /// @inheritdoc OffchainAggregator
                                                          function latestRoundData()
                                                            public
                                                            override
                                                            view
                                                            checkAccess()
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return super.latestRoundData();
                                                          }
                                                        }
                                                        // SPDX-License-Identifier: MIT
                                                        pragma solidity ^0.7.0;
                                                        import "./AccessControllerInterface.sol";
                                                        import "./AggregatorV2V3Interface.sol";
                                                        import "./AggregatorValidatorInterface.sol";
                                                        import "./LinkTokenInterface.sol";
                                                        import "./Owned.sol";
                                                        import "./OffchainAggregatorBilling.sol";
                                                        /**
                                                          * @notice Onchain verification of reports from the offchain reporting protocol
                                                          * @dev For details on its operation, see the offchain reporting protocol design
                                                          * @dev doc, which refers to this contract as simply the "contract".
                                                        */
                                                        contract OffchainAggregator is Owned, OffchainAggregatorBilling, AggregatorV2V3Interface {
                                                          uint256 constant private maxUint32 = (1 << 32) - 1;
                                                          // Storing these fields used on the hot path in a HotVars variable reduces the
                                                          // retrieval of all of them to a single SLOAD. If any further fields are
                                                          // added, make sure that storage of the struct still takes at most 32 bytes.
                                                          struct HotVars {
                                                            // Provides 128 bits of security against 2nd pre-image attacks, but only
                                                            // 64 bits against collisions. This is acceptable, since a malicious owner has
                                                            // easier way of messing up the protocol than to find hash collisions.
                                                            bytes16 latestConfigDigest;
                                                            uint40 latestEpochAndRound; // 32 most sig bits for epoch, 8 least sig bits for round
                                                            // Current bound assumed on number of faulty/dishonest oracles participating
                                                            // in the protocol, this value is referred to as f in the design
                                                            uint8 threshold;
                                                            // Chainlink Aggregators expose a roundId to consumers. The offchain reporting
                                                            // protocol does not use this id anywhere. We increment it whenever a new
                                                            // transmission is made to provide callers with contiguous ids for successive
                                                            // reports.
                                                            uint32 latestAggregatorRoundId;
                                                          }
                                                          HotVars internal s_hotVars;
                                                          // Transmission records the median answer from the transmit transaction at
                                                          // time timestamp
                                                          struct Transmission {
                                                            int192 answer; // 192 bits ought to be enough for anyone
                                                            uint64 timestamp;
                                                          }
                                                          mapping(uint32 /* aggregator round ID */ => Transmission) internal s_transmissions;
                                                          // incremented each time a new config is posted. This count is incorporated
                                                          // into the config digest, to prevent replay attacks.
                                                          uint32 internal s_configCount;
                                                          uint32 internal s_latestConfigBlockNumber; // makes it easier for offchain systems
                                                                                                     // to extract config from logs.
                                                          // Lowest answer the system is allowed to report in response to transmissions
                                                          int192 immutable public minAnswer;
                                                          // Highest answer the system is allowed to report in response to transmissions
                                                          int192 immutable public maxAnswer;
                                                          /*
                                                           * @param _maximumGasPrice highest gas price for which transmitter will be compensated
                                                           * @param _reasonableGasPrice transmitter will receive reward for gas prices under this value
                                                           * @param _microLinkPerEth reimbursement per ETH of gas cost, in 1e-6LINK units
                                                           * @param _linkGweiPerObservation reward to oracle for contributing an observation to a successfully transmitted report, in 1e-9LINK units
                                                           * @param _linkGweiPerTransmission reward to transmitter of a successful report, in 1e-9LINK units
                                                           * @param _link address of the LINK contract
                                                           * @param _validator address of validator contract (must satisfy AggregatorValidatorInterface)
                                                           * @param _minAnswer lowest answer the median of a report is allowed to be
                                                           * @param _maxAnswer highest answer the median of a report is allowed to be
                                                           * @param _billingAccessController access controller for billing admin functions
                                                           * @param _requesterAccessController access controller for requesting new rounds
                                                           * @param _decimals answers are stored in fixed-point format, with this many digits of precision
                                                           * @param _description short human-readable description of observable this contract's answers pertain to
                                                           */
                                                          constructor(
                                                            uint32 _maximumGasPrice,
                                                            uint32 _reasonableGasPrice,
                                                            uint32 _microLinkPerEth,
                                                            uint32 _linkGweiPerObservation,
                                                            uint32 _linkGweiPerTransmission,
                                                            address _link,
                                                            address _validator,
                                                            int192 _minAnswer,
                                                            int192 _maxAnswer,
                                                            AccessControllerInterface _billingAccessController,
                                                            AccessControllerInterface _requesterAccessController,
                                                            uint8 _decimals,
                                                            string memory _description
                                                          )
                                                            OffchainAggregatorBilling(_maximumGasPrice, _reasonableGasPrice, _microLinkPerEth,
                                                              _linkGweiPerObservation, _linkGweiPerTransmission, _link,
                                                              _billingAccessController
                                                            )
                                                          {
                                                            decimals = _decimals;
                                                            s_description = _description;
                                                            setRequesterAccessController(_requesterAccessController);
                                                            setValidator(_validator);
                                                            minAnswer = _minAnswer;
                                                            maxAnswer = _maxAnswer;
                                                          }
                                                          /*
                                                           * Config logic
                                                           */
                                                          /**
                                                           * @notice triggers a new run of the offchain reporting protocol
                                                           * @param previousConfigBlockNumber block in which the previous config was set, to simplify historic analysis
                                                           * @param configCount ordinal number of this config setting among all config settings over the life of this contract
                                                           * @param signers ith element is address ith oracle uses to sign a report
                                                           * @param transmitters ith element is address ith oracle uses to transmit a report via the transmit method
                                                           * @param threshold maximum number of faulty/dishonest oracles the protocol can tolerate while still working correctly
                                                           * @param encodedConfigVersion version of the serialization format used for "encoded" parameter
                                                           * @param encoded serialized data used by oracles to configure their offchain operation
                                                           */
                                                          event ConfigSet(
                                                            uint32 previousConfigBlockNumber,
                                                            uint64 configCount,
                                                            address[] signers,
                                                            address[] transmitters,
                                                            uint8 threshold,
                                                            uint64 encodedConfigVersion,
                                                            bytes encoded
                                                          );
                                                          // Reverts transaction if config args are invalid
                                                          modifier checkConfigValid (
                                                            uint256 _numSigners, uint256 _numTransmitters, uint256 _threshold
                                                          ) {
                                                            require(_numSigners <= maxNumOracles, "too many signers");
                                                            require(_threshold > 0, "threshold must be positive");
                                                            require(
                                                              _numSigners == _numTransmitters,
                                                              "oracle addresses out of registration"
                                                            );
                                                            require(_numSigners > 3*_threshold, "faulty-oracle threshold too high");
                                                            _;
                                                          }
                                                          /**
                                                           * @notice sets offchain reporting protocol configuration incl. participating oracles
                                                           * @param _signers addresses with which oracles sign the reports
                                                           * @param _transmitters addresses oracles use to transmit the reports
                                                           * @param _threshold number of faulty oracles the system can tolerate
                                                           * @param _encodedConfigVersion version number for offchainEncoding schema
                                                           * @param _encoded encoded off-chain oracle configuration
                                                           */
                                                          function setConfig(
                                                            address[] calldata _signers,
                                                            address[] calldata _transmitters,
                                                            uint8 _threshold,
                                                            uint64 _encodedConfigVersion,
                                                            bytes calldata _encoded
                                                          )
                                                            external
                                                            checkConfigValid(_signers.length, _transmitters.length, _threshold)
                                                            onlyOwner()
                                                          {
                                                            while (s_signers.length != 0) { // remove any old signer/transmitter addresses
                                                              uint lastIdx = s_signers.length - 1;
                                                              address signer = s_signers[lastIdx];
                                                              address transmitter = s_transmitters[lastIdx];
                                                              payOracle(transmitter);
                                                              delete s_oracles[signer];
                                                              delete s_oracles[transmitter];
                                                              s_signers.pop();
                                                              s_transmitters.pop();
                                                            }
                                                            for (uint i = 0; i < _signers.length; i++) { // add new signer/transmitter addresses
                                                              require(
                                                                s_oracles[_signers[i]].role == Role.Unset,
                                                                "repeated signer address"
                                                              );
                                                              s_oracles[_signers[i]] = Oracle(uint8(i), Role.Signer);
                                                              require(s_payees[_transmitters[i]] != address(0), "payee must be set");
                                                              require(
                                                                s_oracles[_transmitters[i]].role == Role.Unset,
                                                                "repeated transmitter address"
                                                              );
                                                              s_oracles[_transmitters[i]] = Oracle(uint8(i), Role.Transmitter);
                                                              s_signers.push(_signers[i]);
                                                              s_transmitters.push(_transmitters[i]);
                                                            }
                                                            s_hotVars.threshold = _threshold;
                                                            uint32 previousConfigBlockNumber = s_latestConfigBlockNumber;
                                                            s_latestConfigBlockNumber = uint32(block.number);
                                                            s_configCount += 1;
                                                            uint64 configCount = s_configCount;
                                                            {
                                                              s_hotVars.latestConfigDigest = configDigestFromConfigData(
                                                                address(this),
                                                                configCount,
                                                                _signers,
                                                                _transmitters,
                                                                _threshold,
                                                                _encodedConfigVersion,
                                                                _encoded
                                                              );
                                                              s_hotVars.latestEpochAndRound = 0;
                                                            }
                                                            emit ConfigSet(
                                                              previousConfigBlockNumber,
                                                              configCount,
                                                              _signers,
                                                              _transmitters,
                                                              _threshold,
                                                              _encodedConfigVersion,
                                                              _encoded
                                                            );
                                                          }
                                                          function configDigestFromConfigData(
                                                            address _contractAddress,
                                                            uint64 _configCount,
                                                            address[] calldata _signers,
                                                            address[] calldata _transmitters,
                                                            uint8 _threshold,
                                                            uint64 _encodedConfigVersion,
                                                            bytes calldata _encodedConfig
                                                          ) internal pure returns (bytes16) {
                                                            return bytes16(keccak256(abi.encode(_contractAddress, _configCount,
                                                              _signers, _transmitters, _threshold, _encodedConfigVersion, _encodedConfig
                                                            )));
                                                          }
                                                          /**
                                                           * @notice information about current offchain reporting protocol configuration
                                                           * @return configCount ordinal number of current config, out of all configs applied to this contract so far
                                                           * @return blockNumber block at which this config was set
                                                           * @return configDigest domain-separation tag for current config (see configDigestFromConfigData)
                                                           */
                                                          function latestConfigDetails()
                                                            external
                                                            view
                                                            returns (
                                                              uint32 configCount,
                                                              uint32 blockNumber,
                                                              bytes16 configDigest
                                                            )
                                                          {
                                                            return (s_configCount, s_latestConfigBlockNumber, s_hotVars.latestConfigDigest);
                                                          }
                                                          /**
                                                           * @return list of addresses permitted to transmit reports to this contract
                                                           * @dev The list will match the order used to specify the transmitter during setConfig
                                                           */
                                                          function transmitters()
                                                            external
                                                            view
                                                            returns(address[] memory)
                                                          {
                                                              return s_transmitters;
                                                          }
                                                          /*
                                                           * On-chain validation logc
                                                           */
                                                          // Maximum gas the validation logic can use
                                                          uint256 private constant VALIDATOR_GAS_LIMIT = 100000;
                                                          // Contract containing the validation logic
                                                          AggregatorValidatorInterface private s_validator;
                                                          /**
                                                           * @notice indicates that the address of the validator contract has been set
                                                           * @param previous setting of the address prior to this event
                                                           * @param current the new value for the address
                                                           */
                                                          event ValidatorUpdated(
                                                            address indexed previous,
                                                            address indexed current
                                                          );
                                                          /**
                                                           * @notice address of the contract which does external data validation
                                                           * @return validator address
                                                           */
                                                          function validator()
                                                            external
                                                            view
                                                            returns (AggregatorValidatorInterface)
                                                          {
                                                            return s_validator;
                                                          }
                                                          /**
                                                           * @notice sets the address which does external data validation
                                                           * @param _newValidator designates the address of the new validation contract
                                                           */
                                                          function setValidator(address _newValidator)
                                                            public
                                                            onlyOwner()
                                                          {
                                                            address previous = address(s_validator);
                                                            if (previous != _newValidator) {
                                                              s_validator = AggregatorValidatorInterface(_newValidator);
                                                              emit ValidatorUpdated(previous, _newValidator);
                                                            }
                                                          }
                                                          function validateAnswer(
                                                            uint32 _aggregatorRoundId,
                                                            int256 _answer
                                                          )
                                                            private
                                                          {
                                                            AggregatorValidatorInterface av = s_validator; // cache storage reads
                                                            if (address(av) == address(0)) return;
                                                            uint32 prevAggregatorRoundId = _aggregatorRoundId - 1;
                                                            int256 prevAggregatorRoundAnswer = s_transmissions[prevAggregatorRoundId].answer;
                                                            // We do not want the validator to ever prevent reporting, so we limit its
                                                            // gas usage and catch any errors that may arise.
                                                            try av.validate{gas: VALIDATOR_GAS_LIMIT}(
                                                              prevAggregatorRoundId,
                                                              prevAggregatorRoundAnswer,
                                                              _aggregatorRoundId,
                                                              _answer
                                                            ) {} catch {}
                                                          }
                                                          /*
                                                           * requestNewRound logic
                                                           */
                                                          AccessControllerInterface internal s_requesterAccessController;
                                                          /**
                                                           * @notice emitted when a new requester access controller contract is set
                                                           * @param old the address prior to the current setting
                                                           * @param current the address of the new access controller contract
                                                           */
                                                          event RequesterAccessControllerSet(AccessControllerInterface old, AccessControllerInterface current);
                                                          /**
                                                           * @notice emitted to immediately request a new round
                                                           * @param requester the address of the requester
                                                           * @param configDigest the latest transmission's configDigest
                                                           * @param epoch the latest transmission's epoch
                                                           * @param round the latest transmission's round
                                                           */
                                                          event RoundRequested(address indexed requester, bytes16 configDigest, uint32 epoch, uint8 round);
                                                          /**
                                                           * @notice address of the requester access controller contract
                                                           * @return requester access controller address
                                                           */
                                                          function requesterAccessController()
                                                            external
                                                            view
                                                            returns (AccessControllerInterface)
                                                          {
                                                            return s_requesterAccessController;
                                                          }
                                                          /**
                                                           * @notice sets the requester access controller
                                                           * @param _requesterAccessController designates the address of the new requester access controller
                                                           */
                                                          function setRequesterAccessController(AccessControllerInterface _requesterAccessController)
                                                            public
                                                            onlyOwner()
                                                          {
                                                            AccessControllerInterface oldController = s_requesterAccessController;
                                                            if (_requesterAccessController != oldController) {
                                                              s_requesterAccessController = AccessControllerInterface(_requesterAccessController);
                                                              emit RequesterAccessControllerSet(oldController, _requesterAccessController);
                                                            }
                                                          }
                                                          /**
                                                           * @notice immediately requests a new round
                                                           * @return the aggregatorRoundId of the next round. Note: The report for this round may have been
                                                           * transmitted (but not yet mined) *before* requestNewRound() was even called. There is *no*
                                                           * guarantee of causality between the request and the report at aggregatorRoundId.
                                                           */
                                                          function requestNewRound() external returns (uint80) {
                                                            require(msg.sender == owner || s_requesterAccessController.hasAccess(msg.sender, msg.data),
                                                              "Only owner&requester can call");
                                                            HotVars memory hotVars = s_hotVars;
                                                            emit RoundRequested(
                                                              msg.sender,
                                                              hotVars.latestConfigDigest,
                                                              uint32(s_hotVars.latestEpochAndRound >> 8),
                                                              uint8(s_hotVars.latestEpochAndRound)
                                                            );
                                                            return hotVars.latestAggregatorRoundId + 1;
                                                          }
                                                          /*
                                                           * Transmission logic
                                                           */
                                                          /**
                                                           * @notice indicates that a new report was transmitted
                                                           * @param aggregatorRoundId the round to which this report was assigned
                                                           * @param answer median of the observations attached this report
                                                           * @param transmitter address from which the report was transmitted
                                                           * @param observations observations transmitted with this report
                                                           * @param rawReportContext signature-replay-prevention domain-separation tag
                                                           */
                                                          event NewTransmission(
                                                            uint32 indexed aggregatorRoundId,
                                                            int192 answer,
                                                            address transmitter,
                                                            int192[] observations,
                                                            bytes observers,
                                                            bytes32 rawReportContext
                                                          );
                                                          // decodeReport is used to check that the solidity and go code are using the
                                                          // same format. See TestOffchainAggregator.testDecodeReport and TestReportParsing
                                                          function decodeReport(bytes memory _report)
                                                            internal
                                                            pure
                                                            returns (
                                                              bytes32 rawReportContext,
                                                              bytes32 rawObservers,
                                                              int192[] memory observations
                                                            )
                                                          {
                                                            (rawReportContext, rawObservers, observations) = abi.decode(_report,
                                                              (bytes32, bytes32, int192[]));
                                                          }
                                                          // Used to relieve stack pressure in transmit
                                                          struct ReportData {
                                                            HotVars hotVars; // Only read from storage once
                                                            bytes observers; // ith element is the index of the ith observer
                                                            int192[] observations; // ith element is the ith observation
                                                            bytes vs; // jth element is the v component of the jth signature
                                                            bytes32 rawReportContext;
                                                          }
                                                          /*
                                                           * @notice details about the most recent report
                                                           * @return configDigest domain separation tag for the latest report
                                                           * @return epoch epoch in which the latest report was generated
                                                           * @return round OCR round in which the latest report was generated
                                                           * @return latestAnswer median value from latest report
                                                           * @return latestTimestamp when the latest report was transmitted
                                                           */
                                                          function latestTransmissionDetails()
                                                            external
                                                            view
                                                            returns (
                                                              bytes16 configDigest,
                                                              uint32 epoch,
                                                              uint8 round,
                                                              int192 latestAnswer,
                                                              uint64 latestTimestamp
                                                            )
                                                          {
                                                            require(msg.sender == tx.origin, "Only callable by EOA");
                                                            return (
                                                              s_hotVars.latestConfigDigest,
                                                              uint32(s_hotVars.latestEpochAndRound >> 8),
                                                              uint8(s_hotVars.latestEpochAndRound),
                                                              s_transmissions[s_hotVars.latestAggregatorRoundId].answer,
                                                              s_transmissions[s_hotVars.latestAggregatorRoundId].timestamp
                                                            );
                                                          }
                                                          // The constant-length components of the msg.data sent to transmit.
                                                          // See the "If we wanted to call sam" example on for example reasoning
                                                          // https://solidity.readthedocs.io/en/v0.7.2/abi-spec.html
                                                          uint16 private constant TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT =
                                                            4 + // function selector
                                                            32 + // word containing start location of abiencoded _report value
                                                            32 + // word containing location start of abiencoded  _rs value
                                                            32 + // word containing start location of abiencoded _ss value
                                                            32 + // _rawVs value
                                                            32 + // word containing length of _report
                                                            32 + // word containing length _rs
                                                            32 + // word containing length of _ss
                                                            0; // placeholder
                                                          function expectedMsgDataLength(
                                                            bytes calldata _report, bytes32[] calldata _rs, bytes32[] calldata _ss
                                                          ) private pure returns (uint256 length)
                                                          {
                                                            // calldata will never be big enough to make this overflow
                                                            return uint256(TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT) +
                                                              _report.length + // one byte pure entry in _report
                                                              _rs.length * 32 + // 32 bytes per entry in _rs
                                                              _ss.length * 32 + // 32 bytes per entry in _ss
                                                              0; // placeholder
                                                          }
                                                          /**
                                                           * @notice transmit is called to post a new report to the contract
                                                           * @param _report serialized report, which the signatures are signing. See parsing code below for format. The ith element of the observers component must be the index in s_signers of the address for the ith signature
                                                           * @param _rs ith element is the R components of the ith signature on report. Must have at most maxNumOracles entries
                                                           * @param _ss ith element is the S components of the ith signature on report. Must have at most maxNumOracles entries
                                                           * @param _rawVs ith element is the the V component of the ith signature
                                                           */
                                                          function transmit(
                                                            // NOTE: If these parameters are changed, expectedMsgDataLength and/or
                                                            // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly
                                                            bytes calldata _report,
                                                            bytes32[] calldata _rs, bytes32[] calldata _ss, bytes32 _rawVs // signatures
                                                          )
                                                            external
                                                          {
                                                            uint256 initialGas = gasleft(); // This line must come first
                                                            // Make sure the transmit message-length matches the inputs. Otherwise, the
                                                            // transmitter could append an arbitrarily long (up to gas-block limit)
                                                            // string of 0 bytes, which we would reimburse at a rate of 16 gas/byte, but
                                                            // which would only cost the transmitter 4 gas/byte. (Appendix G of the
                                                            // yellow paper, p. 25, for G_txdatazero and EIP 2028 for G_txdatanonzero.)
                                                            // This could amount to reimbursement profit of 36 million gas, given a 3MB
                                                            // zero tail.
                                                            require(msg.data.length == expectedMsgDataLength(_report, _rs, _ss),
                                                              "transmit message too long");
                                                            ReportData memory r; // Relieves stack pressure
                                                            {
                                                              r.hotVars = s_hotVars; // cache read from storage
                                                              bytes32 rawObservers;
                                                              (r.rawReportContext, rawObservers, r.observations) = abi.decode(
                                                                _report, (bytes32, bytes32, int192[])
                                                              );
                                                              // rawReportContext consists of:
                                                              // 11-byte zero padding
                                                              // 16-byte configDigest
                                                              // 4-byte epoch
                                                              // 1-byte round
                                                              bytes16 configDigest = bytes16(r.rawReportContext << 88);
                                                              require(
                                                                r.hotVars.latestConfigDigest == configDigest,
                                                                "configDigest mismatch"
                                                              );
                                                              uint40 epochAndRound = uint40(uint256(r.rawReportContext));
                                                              // direct numerical comparison works here, because
                                                              //
                                                              //   ((e,r) <= (e',r')) implies (epochAndRound <= epochAndRound')
                                                              //
                                                              // because alphabetic ordering implies e <= e', and if e = e', then r<=r',
                                                              // so e*256+r <= e'*256+r', because r, r' < 256
                                                              require(r.hotVars.latestEpochAndRound < epochAndRound, "stale report");
                                                              require(_rs.length > r.hotVars.threshold, "not enough signatures");
                                                              require(_rs.length <= maxNumOracles, "too many signatures");
                                                              require(_ss.length == _rs.length, "signatures out of registration");
                                                              require(r.observations.length <= maxNumOracles,
                                                                      "num observations out of bounds");
                                                              require(r.observations.length > 2 * r.hotVars.threshold,
                                                                      "too few values to trust median");
                                                              // Copy signature parities in bytes32 _rawVs to bytes r.v
                                                              r.vs = new bytes(_rs.length);
                                                              for (uint8 i = 0; i < _rs.length; i++) {
                                                                r.vs[i] = _rawVs[i];
                                                              }
                                                              // Copy observer identities in bytes32 rawObservers to bytes r.observers
                                                              r.observers = new bytes(r.observations.length);
                                                              bool[maxNumOracles] memory seen;
                                                              for (uint8 i = 0; i < r.observations.length; i++) {
                                                                uint8 observerIdx = uint8(rawObservers[i]);
                                                                require(!seen[observerIdx], "observer index repeated");
                                                                seen[observerIdx] = true;
                                                                r.observers[i] = rawObservers[i];
                                                              }
                                                              Oracle memory transmitter = s_oracles[msg.sender];
                                                              require( // Check that sender is authorized to report
                                                                transmitter.role == Role.Transmitter &&
                                                                msg.sender == s_transmitters[transmitter.index],
                                                                "unauthorized transmitter"
                                                              );
                                                              // record epochAndRound here, so that we don't have to carry the local
                                                              // variable in transmit. The change is reverted if something fails later.
                                                              r.hotVars.latestEpochAndRound = epochAndRound;
                                                            }
                                                            { // Verify signatures attached to report
                                                              bytes32 h = keccak256(_report);
                                                              bool[maxNumOracles] memory signed;
                                                              Oracle memory o;
                                                              for (uint i = 0; i < _rs.length; i++) {
                                                                address signer = ecrecover(h, uint8(r.vs[i])+27, _rs[i], _ss[i]);
                                                                o = s_oracles[signer];
                                                                require(o.role == Role.Signer, "address not authorized to sign");
                                                                require(!signed[o.index], "non-unique signature");
                                                                signed[o.index] = true;
                                                              }
                                                            }
                                                            { // Check the report contents, and record the result
                                                              for (uint i = 0; i < r.observations.length - 1; i++) {
                                                                bool inOrder = r.observations[i] <= r.observations[i+1];
                                                                require(inOrder, "observations not sorted");
                                                              }
                                                              int192 median = r.observations[r.observations.length/2];
                                                              require(minAnswer <= median && median <= maxAnswer, "median is out of min-max range");
                                                              r.hotVars.latestAggregatorRoundId++;
                                                              s_transmissions[r.hotVars.latestAggregatorRoundId] =
                                                                Transmission(median, uint64(block.timestamp));
                                                              emit NewTransmission(
                                                                r.hotVars.latestAggregatorRoundId,
                                                                median,
                                                                msg.sender,
                                                                r.observations,
                                                                r.observers,
                                                                r.rawReportContext
                                                              );
                                                              // Emit these for backwards compatability with offchain consumers
                                                              // that only support legacy events
                                                              emit NewRound(
                                                                r.hotVars.latestAggregatorRoundId,
                                                                address(0x0), // use zero address since we don't have anybody "starting" the round here
                                                                block.timestamp
                                                              );
                                                              emit AnswerUpdated(
                                                                median,
                                                                r.hotVars.latestAggregatorRoundId,
                                                                block.timestamp
                                                              );
                                                              validateAnswer(r.hotVars.latestAggregatorRoundId, median);
                                                            }
                                                            s_hotVars = r.hotVars;
                                                            assert(initialGas < maxUint32);
                                                            reimburseAndRewardOracles(uint32(initialGas), r.observers);
                                                          }
                                                          /*
                                                           * v2 Aggregator interface
                                                           */
                                                          /**
                                                           * @notice median from the most recent report
                                                           */
                                                          function latestAnswer()
                                                            public
                                                            override
                                                            view
                                                            virtual
                                                            returns (int256)
                                                          {
                                                            return s_transmissions[s_hotVars.latestAggregatorRoundId].answer;
                                                          }
                                                          /**
                                                           * @notice timestamp of block in which last report was transmitted
                                                           */
                                                          function latestTimestamp()
                                                            public
                                                            override
                                                            view
                                                            virtual
                                                            returns (uint256)
                                                          {
                                                            return s_transmissions[s_hotVars.latestAggregatorRoundId].timestamp;
                                                          }
                                                          /**
                                                           * @notice Aggregator round (NOT OCR round) in which last report was transmitted
                                                           */
                                                          function latestRound()
                                                            public
                                                            override
                                                            view
                                                            virtual
                                                            returns (uint256)
                                                          {
                                                            return s_hotVars.latestAggregatorRoundId;
                                                          }
                                                          /**
                                                           * @notice median of report from given aggregator round (NOT OCR round)
                                                           * @param _roundId the aggregator round of the target report
                                                           */
                                                          function getAnswer(uint256 _roundId)
                                                            public
                                                            override
                                                            view
                                                            virtual
                                                            returns (int256)
                                                          {
                                                            if (_roundId > 0xFFFFFFFF) { return 0; }
                                                            return s_transmissions[uint32(_roundId)].answer;
                                                          }
                                                          /**
                                                           * @notice timestamp of block in which report from given aggregator round was transmitted
                                                           * @param _roundId aggregator round (NOT OCR round) of target report
                                                           */
                                                          function getTimestamp(uint256 _roundId)
                                                            public
                                                            override
                                                            view
                                                            virtual
                                                            returns (uint256)
                                                          {
                                                            if (_roundId > 0xFFFFFFFF) { return 0; }
                                                            return s_transmissions[uint32(_roundId)].timestamp;
                                                          }
                                                          /*
                                                           * v3 Aggregator interface
                                                           */
                                                          string constant private V3_NO_DATA_ERROR = "No data present";
                                                          /**
                                                           * @return answers are stored in fixed-point format, with this many digits of precision
                                                           */
                                                          uint8 immutable public override decimals;
                                                          /**
                                                           * @notice aggregator contract version
                                                           */
                                                          uint256 constant public override version = 4;
                                                          string internal s_description;
                                                          /**
                                                           * @notice human-readable description of observable this contract is reporting on
                                                           */
                                                          function description()
                                                            public
                                                            override
                                                            view
                                                            virtual
                                                            returns (string memory)
                                                          {
                                                            return s_description;
                                                          }
                                                          /**
                                                           * @notice details for the given aggregator round
                                                           * @param _roundId target aggregator round (NOT OCR round). Must fit in uint32
                                                           * @return roundId _roundId
                                                           * @return answer median of report from given _roundId
                                                           * @return startedAt timestamp of block in which report from given _roundId was transmitted
                                                           * @return updatedAt timestamp of block in which report from given _roundId was transmitted
                                                           * @return answeredInRound _roundId
                                                           */
                                                          function getRoundData(uint80 _roundId)
                                                            public
                                                            override
                                                            view
                                                            virtual
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            require(_roundId <= 0xFFFFFFFF, V3_NO_DATA_ERROR);
                                                            Transmission memory transmission = s_transmissions[uint32(_roundId)];
                                                            return (
                                                              _roundId,
                                                              transmission.answer,
                                                              transmission.timestamp,
                                                              transmission.timestamp,
                                                              _roundId
                                                            );
                                                          }
                                                          /**
                                                           * @notice aggregator details for the most recently transmitted report
                                                           * @return roundId aggregator round of latest report (NOT OCR round)
                                                           * @return answer median of latest report
                                                           * @return startedAt timestamp of block containing latest report
                                                           * @return updatedAt timestamp of block containing latest report
                                                           * @return answeredInRound aggregator round of latest report
                                                           */
                                                          function latestRoundData()
                                                            public
                                                            override
                                                            view
                                                            virtual
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            roundId = s_hotVars.latestAggregatorRoundId;
                                                            // Skipped for compatability with existing FluxAggregator in which latestRoundData never reverts.
                                                            // require(roundId != 0, V3_NO_DATA_ERROR);
                                                            Transmission memory transmission = s_transmissions[uint32(roundId)];
                                                            return (
                                                              roundId,
                                                              transmission.answer,
                                                              transmission.timestamp,
                                                              transmission.timestamp,
                                                              roundId
                                                            );
                                                          }
                                                        }
                                                        // SPDX-License-Identifier: MIT
                                                        pragma solidity ^0.7.0;
                                                        interface AccessControllerInterface {
                                                          function hasAccess(address user, bytes calldata data) external view returns (bool);
                                                        }
                                                        // SPDX-License-Identifier: MIT
                                                        pragma solidity ^0.7.0;
                                                        import "./AggregatorInterface.sol";
                                                        import "./AggregatorV3Interface.sol";
                                                        interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface
                                                        {
                                                        }// SPDX-License-Identifier: MIT
                                                        pragma solidity ^0.7.0;
                                                        interface AggregatorInterface {
                                                          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 updatedAt);
                                                          event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
                                                        }
                                                        // SPDX-License-Identifier: MIT
                                                        pragma solidity ^0.7.0;
                                                        interface AggregatorV3Interface {
                                                          function decimals() external view returns (uint8);
                                                          function description() external view returns (string memory);
                                                          function version() external view returns (uint256);
                                                          // getRoundData and latestRoundData should both raise "No data present"
                                                          // if they do not have data to report, instead of returning unset values
                                                          // which could be misinterpreted as actual reported values.
                                                          function getRoundData(uint80 _roundId)
                                                            external
                                                            view
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            );
                                                          function latestRoundData()
                                                            external
                                                            view
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            );
                                                        }
                                                        // SPDX-License-Identifier: MIT
                                                        pragma solidity ^0.7.0;
                                                        interface AggregatorValidatorInterface {
                                                          function validate(
                                                            uint256 previousRoundId,
                                                            int256 previousAnswer,
                                                            uint256 currentRoundId,
                                                            int256 currentAnswer
                                                          ) external returns (bool);
                                                        }// SPDX-License-Identifier: MIT
                                                        pragma solidity ^0.7.1;
                                                        interface LinkTokenInterface {
                                                          function allowance(address owner, address spender) external view returns (uint256 remaining);
                                                          function approve(address spender, uint256 value) external returns (bool success);
                                                          function balanceOf(address owner) external view returns (uint256 balance);
                                                          function decimals() external view returns (uint8 decimalPlaces);
                                                          function decreaseApproval(address spender, uint256 addedValue) external returns (bool success);
                                                          function increaseApproval(address spender, uint256 subtractedValue) external;
                                                          function name() external view returns (string memory tokenName);
                                                          function symbol() external view returns (string memory tokenSymbol);
                                                          function totalSupply() external view returns (uint256 totalTokensIssued);
                                                          function transfer(address to, uint256 value) external returns (bool success);
                                                          function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool success);
                                                          function transferFrom(address from, address to, uint256 value) external returns (bool success);
                                                        }
                                                        // SPDX-License-Identifier: MIT
                                                        pragma solidity ^0.7.0;
                                                        /**
                                                         * @title The Owned contract
                                                         * @notice A contract with helpers for basic contract ownership.
                                                         */
                                                        contract Owned {
                                                          address payable public owner;
                                                          address private pendingOwner;
                                                          event OwnershipTransferRequested(
                                                            address indexed from,
                                                            address indexed to
                                                          );
                                                          event OwnershipTransferred(
                                                            address indexed from,
                                                            address indexed to
                                                          );
                                                          constructor() {
                                                            owner = msg.sender;
                                                          }
                                                          /**
                                                           * @dev Allows an owner to begin transferring ownership to a new address,
                                                           * pending.
                                                           */
                                                          function transferOwnership(address _to)
                                                            external
                                                            onlyOwner()
                                                          {
                                                            pendingOwner = _to;
                                                            emit OwnershipTransferRequested(owner, _to);
                                                          }
                                                          /**
                                                           * @dev Allows an ownership transfer to be completed by the recipient.
                                                           */
                                                          function acceptOwnership()
                                                            external
                                                          {
                                                            require(msg.sender == pendingOwner, "Must be proposed owner");
                                                            address oldOwner = owner;
                                                            owner = msg.sender;
                                                            pendingOwner = address(0);
                                                            emit OwnershipTransferred(oldOwner, msg.sender);
                                                          }
                                                          /**
                                                           * @dev Reverts if called by anyone other than the contract owner.
                                                           */
                                                          modifier onlyOwner() {
                                                            require(msg.sender == owner, "Only callable by owner");
                                                            _;
                                                          }
                                                        }
                                                        // SPDX-License-Identifier: MIT
                                                        pragma solidity ^0.7.0;
                                                        import "./AccessControllerInterface.sol";
                                                        import "./LinkTokenInterface.sol";
                                                        import "./Owned.sol";
                                                        /**
                                                         * @notice tracks administration of oracle-reward and gas-reimbursement parameters.
                                                         * @dev
                                                         * If you read or change this, be sure to read or adjust the comments. They
                                                         * track the units of the values under consideration, and are crucial to
                                                         * the readability of the operations it specifies.
                                                         * @notice
                                                         * Trust Model:
                                                         * Nothing in this contract prevents a billing admin from setting insane
                                                         * values for the billing parameters in setBilling. Oracles
                                                         * participating in this contract should regularly check that the
                                                         * parameters make sense. Similarly, the outstanding obligations of this
                                                         * contract to the oracles can exceed the funds held by the contract.
                                                         * Oracles participating in this contract should regularly check that it
                                                         * holds sufficient funds and stop interacting with it if funding runs
                                                         * out.
                                                         * This still leaves oracles with some risk due to TOCTOU issues.
                                                         * However, since the sums involved are pretty small (Ethereum
                                                         * transactions aren't that expensive in the end) and an oracle would
                                                         * likely stop participating in a contract it repeatedly lost money on,
                                                         * this risk is deemed acceptable. Oracles should also regularly
                                                         * withdraw any funds in the contract to prevent issues where the
                                                         * contract becomes underfunded at a later time, and different oracles
                                                         * are competing for the left-over funds.
                                                         * Finally, note that any change to the set of oracles or to the billing
                                                         * parameters will trigger payout of all oracles first (using the old
                                                         * parameters), a billing admin cannot take away funds that are already
                                                         * marked for payment.
                                                        */
                                                        contract OffchainAggregatorBilling is Owned {
                                                          // Maximum number of oracles the offchain reporting protocol is designed for
                                                          uint256 constant internal maxNumOracles = 31;
                                                          // Parameters for oracle payments
                                                          struct Billing {
                                                            // Highest compensated gas price, in ETH-gwei uints
                                                            uint32 maximumGasPrice;
                                                            // If gas price is less (in ETH-gwei units), transmitter gets half the savings
                                                            uint32 reasonableGasPrice;
                                                            // Pay transmitter back this much LINK per unit eth spent on gas
                                                            // (1e-6LINK/ETH units)
                                                            uint32 microLinkPerEth;
                                                            // Fixed LINK reward for each observer, in LINK-gwei units
                                                            uint32 linkGweiPerObservation;
                                                            // Fixed reward for transmitter, in linkGweiPerObservation units
                                                            uint32 linkGweiPerTransmission;
                                                          }
                                                          Billing internal s_billing;
                                                          /**
                                                          * @return LINK token contract used for billing
                                                          */
                                                          LinkTokenInterface immutable public LINK;
                                                          AccessControllerInterface internal s_billingAccessController;
                                                          // ith element is number of observation rewards due to ith process, plus one.
                                                          // This is expected to saturate after an oracle has submitted 65,535
                                                          // observations, or about 65535/(3*24*20) = 45 days, given a transmission
                                                          // every 3 minutes.
                                                          //
                                                          // This is always one greater than the actual value, so that when the value is
                                                          // reset to zero, we don't end up with a zero value in storage (which would
                                                          // result in a higher gas cost, the next time the value is incremented.)
                                                          // Calculations using this variable need to take that offset into account.
                                                          uint16[maxNumOracles] internal s_oracleObservationsCounts;
                                                          // Addresses at which oracles want to receive payments, by transmitter address
                                                          mapping (address /* transmitter */ => address /* payment address */)
                                                            internal
                                                            s_payees;
                                                          // Payee addresses which must be approved by the owner
                                                          mapping (address /* transmitter */ => address /* payment address */)
                                                            internal
                                                            s_proposedPayees;
                                                          // LINK-wei-denominated reimbursements for gas used by transmitters.
                                                          //
                                                          // This is always one greater than the actual value, so that when the value is
                                                          // reset to zero, we don't end up with a zero value in storage (which would
                                                          // result in a higher gas cost, the next time the value is incremented.)
                                                          // Calculations using this variable need to take that offset into account.
                                                          //
                                                          // Argument for overflow safety:
                                                          // We have the following maximum intermediate values:
                                                          // - 2**40 additions to this variable (epochAndRound is a uint40)
                                                          // - 2**32 gas price in ethgwei/gas
                                                          // - 1e9 ethwei/ethgwei
                                                          // - 2**32 gas since the block gas limit is at ~20 million
                                                          // - 2**32 (microlink/eth)
                                                          // And we have 2**40 * 2**32 * 1e9 * 2**32 * 2**32 < 2**166
                                                          // (we also divide in some places, but that only makes the value smaller)
                                                          // We can thus safely use uint256 intermediate values for the computation
                                                          // updating this variable.
                                                          uint256[maxNumOracles] internal s_gasReimbursementsLinkWei;
                                                          // Used for s_oracles[a].role, where a is an address, to track the purpose
                                                          // of the address, or to indicate that the address is unset.
                                                          enum Role {
                                                            // No oracle role has been set for address a
                                                            Unset,
                                                            // Signing address for the s_oracles[a].index'th oracle. I.e., report
                                                            // signatures from this oracle should ecrecover back to address a.
                                                            Signer,
                                                            // Transmission address for the s_oracles[a].index'th oracle. I.e., if a
                                                            // report is received by OffchainAggregator.transmit in which msg.sender is
                                                            // a, it is attributed to the s_oracles[a].index'th oracle.
                                                            Transmitter
                                                          }
                                                          struct Oracle {
                                                            uint8 index; // Index of oracle in s_signers/s_transmitters
                                                            Role role;   // Role of the address which mapped to this struct
                                                          }
                                                          mapping (address /* signer OR transmitter address */ => Oracle)
                                                            internal s_oracles;
                                                          // s_signers contains the signing address of each oracle
                                                          address[] internal s_signers;
                                                          // s_transmitters contains the transmission address of each oracle,
                                                          // i.e. the address the oracle actually sends transactions to the contract from
                                                          address[] internal s_transmitters;
                                                          uint256 constant private  maxUint16 = (1 << 16) - 1;
                                                          uint256 constant internal maxUint128 = (1 << 128) - 1;
                                                          constructor(
                                                            uint32 _maximumGasPrice,
                                                            uint32 _reasonableGasPrice,
                                                            uint32 _microLinkPerEth,
                                                            uint32 _linkGweiPerObservation,
                                                            uint32 _linkGweiPerTransmission,
                                                            address _link,
                                                            AccessControllerInterface _billingAccessController
                                                          )
                                                          {
                                                            setBillingInternal(_maximumGasPrice, _reasonableGasPrice, _microLinkPerEth,
                                                              _linkGweiPerObservation, _linkGweiPerTransmission);
                                                            setBillingAccessControllerInternal(_billingAccessController);
                                                            LINK = LinkTokenInterface(_link);
                                                            uint16[maxNumOracles] memory counts; // See s_oracleObservationsCounts docstring
                                                            uint256[maxNumOracles] memory gas; // see s_gasReimbursementsLinkWei docstring
                                                            for (uint8 i = 0; i < maxNumOracles; i++) {
                                                              counts[i] = 1;
                                                              gas[i] = 1;
                                                            }
                                                            s_oracleObservationsCounts = counts;
                                                            s_gasReimbursementsLinkWei = gas;
                                                          }
                                                          /**
                                                           * @notice emitted when billing parameters are set
                                                           * @param maximumGasPrice highest gas price for which transmitter will be compensated
                                                           * @param reasonableGasPrice transmitter will receive reward for gas prices under this value
                                                           * @param microLinkPerEth reimbursement per ETH of gas cost, in 1e-6LINK units
                                                           * @param linkGweiPerObservation reward to oracle for contributing an observation to a successfully transmitted report, in 1e-9LINK units
                                                           * @param linkGweiPerTransmission reward to transmitter of a successful report, in 1e-9LINK units
                                                           */
                                                          event BillingSet(
                                                            uint32 maximumGasPrice,
                                                            uint32 reasonableGasPrice,
                                                            uint32 microLinkPerEth,
                                                            uint32 linkGweiPerObservation,
                                                            uint32 linkGweiPerTransmission
                                                          );
                                                          function setBillingInternal(
                                                            uint32 _maximumGasPrice,
                                                            uint32 _reasonableGasPrice,
                                                            uint32 _microLinkPerEth,
                                                            uint32 _linkGweiPerObservation,
                                                            uint32 _linkGweiPerTransmission
                                                          )
                                                            internal
                                                          {
                                                            s_billing = Billing(_maximumGasPrice, _reasonableGasPrice, _microLinkPerEth,
                                                              _linkGweiPerObservation, _linkGweiPerTransmission);
                                                            emit BillingSet(_maximumGasPrice, _reasonableGasPrice, _microLinkPerEth,
                                                              _linkGweiPerObservation, _linkGweiPerTransmission);
                                                          }
                                                          /**
                                                           * @notice sets billing parameters
                                                           * @param _maximumGasPrice highest gas price for which transmitter will be compensated
                                                           * @param _reasonableGasPrice transmitter will receive reward for gas prices under this value
                                                           * @param _microLinkPerEth reimbursement per ETH of gas cost, in 1e-6LINK units
                                                           * @param _linkGweiPerObservation reward to oracle for contributing an observation to a successfully transmitted report, in 1e-9LINK units
                                                           * @param _linkGweiPerTransmission reward to transmitter of a successful report, in 1e-9LINK units
                                                           * @dev access control provided by billingAccessController
                                                           */
                                                          function setBilling(
                                                            uint32 _maximumGasPrice,
                                                            uint32 _reasonableGasPrice,
                                                            uint32 _microLinkPerEth,
                                                            uint32 _linkGweiPerObservation,
                                                            uint32 _linkGweiPerTransmission
                                                          )
                                                            external
                                                          {
                                                            AccessControllerInterface access = s_billingAccessController;
                                                            require(msg.sender == owner || access.hasAccess(msg.sender, msg.data),
                                                              "Only owner&billingAdmin can call");
                                                            payOracles();
                                                            setBillingInternal(_maximumGasPrice, _reasonableGasPrice, _microLinkPerEth,
                                                              _linkGweiPerObservation, _linkGweiPerTransmission);
                                                          }
                                                          /**
                                                           * @notice gets billing parameters
                                                           * @param maximumGasPrice highest gas price for which transmitter will be compensated
                                                           * @param reasonableGasPrice transmitter will receive reward for gas prices under this value
                                                           * @param microLinkPerEth reimbursement per ETH of gas cost, in 1e-6LINK units
                                                           * @param linkGweiPerObservation reward to oracle for contributing an observation to a successfully transmitted report, in 1e-9LINK units
                                                           * @param linkGweiPerTransmission reward to transmitter of a successful report, in 1e-9LINK units
                                                           */
                                                          function getBilling()
                                                            external
                                                            view
                                                            returns (
                                                              uint32 maximumGasPrice,
                                                              uint32 reasonableGasPrice,
                                                              uint32 microLinkPerEth,
                                                              uint32 linkGweiPerObservation,
                                                              uint32 linkGweiPerTransmission
                                                            )
                                                          {
                                                            Billing memory billing = s_billing;
                                                            return (
                                                              billing.maximumGasPrice,
                                                              billing.reasonableGasPrice,
                                                              billing.microLinkPerEth,
                                                              billing.linkGweiPerObservation,
                                                              billing.linkGweiPerTransmission
                                                            );
                                                          }
                                                          /**
                                                           * @notice emitted when a new access-control contract is set
                                                           * @param old the address prior to the current setting
                                                           * @param current the address of the new access-control contract
                                                           */
                                                          event BillingAccessControllerSet(AccessControllerInterface old, AccessControllerInterface current);
                                                          function setBillingAccessControllerInternal(AccessControllerInterface _billingAccessController)
                                                            internal
                                                          {
                                                            AccessControllerInterface oldController = s_billingAccessController;
                                                            if (_billingAccessController != oldController) {
                                                              s_billingAccessController = _billingAccessController;
                                                              emit BillingAccessControllerSet(
                                                                oldController,
                                                                _billingAccessController
                                                              );
                                                            }
                                                          }
                                                          /**
                                                           * @notice sets billingAccessController
                                                           * @param _billingAccessController new billingAccessController contract address
                                                           * @dev only owner can call this
                                                           */
                                                          function setBillingAccessController(AccessControllerInterface _billingAccessController)
                                                            external
                                                            onlyOwner
                                                          {
                                                            setBillingAccessControllerInternal(_billingAccessController);
                                                          }
                                                          /**
                                                           * @notice gets billingAccessController
                                                           * @return address of billingAccessController contract
                                                           */
                                                          function billingAccessController()
                                                            external
                                                            view
                                                            returns (AccessControllerInterface)
                                                          {
                                                            return s_billingAccessController;
                                                          }
                                                          /**
                                                           * @notice withdraws an oracle's payment from the contract
                                                           * @param _transmitter the transmitter address of the oracle
                                                           * @dev must be called by oracle's payee address
                                                           */
                                                          function withdrawPayment(address _transmitter)
                                                            external
                                                          {
                                                            require(msg.sender == s_payees[_transmitter], "Only payee can withdraw");
                                                            payOracle(_transmitter);
                                                          }
                                                          /**
                                                           * @notice query an oracle's payment amount
                                                           * @param _transmitter the transmitter address of the oracle
                                                           */
                                                          function owedPayment(address _transmitter)
                                                            public
                                                            view
                                                            returns (uint256)
                                                          {
                                                            Oracle memory oracle = s_oracles[_transmitter];
                                                            if (oracle.role == Role.Unset) { return 0; }
                                                            Billing memory billing = s_billing;
                                                            uint256 linkWeiAmount =
                                                              uint256(s_oracleObservationsCounts[oracle.index] - 1) *
                                                              uint256(billing.linkGweiPerObservation) *
                                                              (1 gwei);
                                                            linkWeiAmount += s_gasReimbursementsLinkWei[oracle.index] - 1;
                                                            return linkWeiAmount;
                                                          }
                                                          /**
                                                           * @notice emitted when an oracle has been paid LINK
                                                           * @param transmitter address from which the oracle sends reports to the transmit method
                                                           * @param payee address to which the payment is sent
                                                           * @param amount amount of LINK sent
                                                           */
                                                          event OraclePaid(address transmitter, address payee, uint256 amount);
                                                          // payOracle pays out _transmitter's balance to the corresponding payee, and zeros it out
                                                          function payOracle(address _transmitter)
                                                            internal
                                                          {
                                                            Oracle memory oracle = s_oracles[_transmitter];
                                                            uint256 linkWeiAmount = owedPayment(_transmitter);
                                                            if (linkWeiAmount > 0) {
                                                              address payee = s_payees[_transmitter];
                                                              // Poses no re-entrancy issues, because LINK.transfer does not yield
                                                              // control flow.
                                                              require(LINK.transfer(payee, linkWeiAmount), "insufficient funds");
                                                              s_oracleObservationsCounts[oracle.index] = 1; // "zero" the counts. see var's docstring
                                                              s_gasReimbursementsLinkWei[oracle.index] = 1; // "zero" the counts. see var's docstring
                                                              emit OraclePaid(_transmitter, payee, linkWeiAmount);
                                                            }
                                                          }
                                                          // payOracles pays out all transmitters, and zeros out their balances.
                                                          //
                                                          // It's much more gas-efficient to do this as a single operation, to avoid
                                                          // hitting storage too much.
                                                          function payOracles()
                                                            internal
                                                          {
                                                            Billing memory billing = s_billing;
                                                            uint16[maxNumOracles] memory observationsCounts = s_oracleObservationsCounts;
                                                            uint256[maxNumOracles] memory gasReimbursementsLinkWei =
                                                              s_gasReimbursementsLinkWei;
                                                            address[] memory transmitters = s_transmitters;
                                                            for (uint transmitteridx = 0; transmitteridx < transmitters.length; transmitteridx++) {
                                                              uint256 reimbursementAmountLinkWei = gasReimbursementsLinkWei[transmitteridx] - 1;
                                                              uint256 obsCount = observationsCounts[transmitteridx] - 1;
                                                              uint256 linkWeiAmount =
                                                                obsCount * uint256(billing.linkGweiPerObservation) * (1 gwei) + reimbursementAmountLinkWei;
                                                              if (linkWeiAmount > 0) {
                                                                  address payee = s_payees[transmitters[transmitteridx]];
                                                                  // Poses no re-entrancy issues, because LINK.transfer does not yield
                                                                  // control flow.
                                                                  require(LINK.transfer(payee, linkWeiAmount), "insufficient funds");
                                                                  observationsCounts[transmitteridx] = 1;       // "zero" the counts.
                                                                  gasReimbursementsLinkWei[transmitteridx] = 1; // "zero" the counts.
                                                                  emit OraclePaid(transmitters[transmitteridx], payee, linkWeiAmount);
                                                                }
                                                            }
                                                            // "Zero" the accounting storage variables
                                                            s_oracleObservationsCounts = observationsCounts;
                                                            s_gasReimbursementsLinkWei = gasReimbursementsLinkWei;
                                                          }
                                                          function oracleRewards(
                                                            bytes memory observers,
                                                            uint16[maxNumOracles] memory observations
                                                          )
                                                            internal
                                                            pure
                                                            returns (uint16[maxNumOracles] memory)
                                                          {
                                                            // reward each observer-participant with the observer reward
                                                            for (uint obsIdx = 0; obsIdx < observers.length; obsIdx++) {
                                                              uint8 observer = uint8(observers[obsIdx]);
                                                              observations[observer] = saturatingAddUint16(observations[observer], 1);
                                                            }
                                                            return observations;
                                                          }
                                                          // This value needs to change if maxNumOracles is increased, or the accounting
                                                          // calculations at the bottom of reimburseAndRewardOracles change.
                                                          //
                                                          // To recalculate it, run the profiler as described in
                                                          // ../../profile/README.md, and add up the gas-usage values reported for the
                                                          // lines in reimburseAndRewardOracles following the "gasLeft = gasleft()"
                                                          // line. E.g., you will see output like this:
                                                          //
                                                          //      7        uint256 gasLeft = gasleft();
                                                          //     29        uint256 gasCostEthWei = transmitterGasCostEthWei(
                                                          //      9          uint256(initialGas),
                                                          //      3          gasPrice,
                                                          //      3          callDataGasCost,
                                                          //      3          gasLeft
                                                          //      .
                                                          //      .
                                                          //      .
                                                          //     59        uint256 gasCostLinkWei = (gasCostEthWei * billing.microLinkPerEth)/ 1e6;
                                                          //      .
                                                          //      .
                                                          //      .
                                                          //   5047        s_gasReimbursementsLinkWei[txOracle.index] =
                                                          //    856          s_gasReimbursementsLinkWei[txOracle.index] + gasCostLinkWei +
                                                          //     26          uint256(billing.linkGweiPerTransmission) * (1 gwei);
                                                          //
                                                          // If those were the only lines to be accounted for, you would add up
                                                          // 29+9+3+3+3+59+5047+856+26=6035.
                                                          uint256 internal constant accountingGasCost = 6035;
                                                          // Uncomment the following declaration to compute the remaining gas cost after
                                                          // above gasleft(). (This must exist in a base class to OffchainAggregator, so
                                                          // it can't go in TestOffchainAggregator.)
                                                          //
                                                          // uint256 public gasUsedInAccounting;
                                                          // Gas price at which the transmitter should be reimbursed, in ETH-gwei/gas
                                                          function impliedGasPrice(
                                                            uint256 txGasPrice,         // ETH-gwei/gas units
                                                            uint256 reasonableGasPrice, // ETH-gwei/gas units
                                                            uint256 maximumGasPrice     // ETH-gwei/gas units
                                                          )
                                                            internal
                                                            pure
                                                            returns (uint256)
                                                          {
                                                            // Reward the transmitter for choosing an efficient gas price: if they manage
                                                            // to come in lower than considered reasonable, give them half the savings.
                                                            //
                                                            // The following calculations are all in units of gwei/gas, i.e. 1e-9ETH/gas
                                                            uint256 gasPrice = txGasPrice;
                                                            if (txGasPrice < reasonableGasPrice) {
                                                              // Give transmitter half the savings for coming in under the reasonable gas price
                                                              gasPrice += (reasonableGasPrice - txGasPrice) / 2;
                                                            }
                                                            // Don't reimburse a gas price higher than maximumGasPrice
                                                            return min(gasPrice, maximumGasPrice);
                                                          }
                                                          // gas reimbursement due the transmitter, in ETH-wei
                                                          //
                                                          // If this function is changed, accountingGasCost needs to change, too. See
                                                          // its docstring
                                                          function transmitterGasCostEthWei(
                                                            uint256 initialGas,
                                                            uint256 gasPrice, // ETH-gwei/gas units
                                                            uint256 callDataCost, // gas units
                                                            uint256 gasLeft
                                                          )
                                                            internal
                                                            pure
                                                            returns (uint128 gasCostEthWei)
                                                          {
                                                            require(initialGas >= gasLeft, "gasLeft cannot exceed initialGas");
                                                            uint256 gasUsed = // gas units
                                                              initialGas - gasLeft + // observed gas usage
                                                              callDataCost + accountingGasCost; // estimated gas usage
                                                            // gasUsed is in gas units, gasPrice is in ETH-gwei/gas units; convert to ETH-wei
                                                            uint256 fullGasCostEthWei = gasUsed * gasPrice * (1 gwei);
                                                            assert(fullGasCostEthWei < maxUint128); // the entire ETH supply fits in a uint128...
                                                            return uint128(fullGasCostEthWei);
                                                          }
                                                          /**
                                                           * @notice withdraw any available funds left in the contract, up to _amount, after accounting for the funds due to participants in past reports
                                                           * @param _recipient address to send funds to
                                                           * @param _amount maximum amount to withdraw, denominated in LINK-wei.
                                                           * @dev access control provided by billingAccessController
                                                           */
                                                          function withdrawFunds(address _recipient, uint256 _amount)
                                                            external
                                                          {
                                                            require(msg.sender == owner || s_billingAccessController.hasAccess(msg.sender, msg.data),
                                                              "Only owner&billingAdmin can call");
                                                            uint256 linkDue = totalLINKDue();
                                                            uint256 linkBalance = LINK.balanceOf(address(this));
                                                            require(linkBalance >= linkDue, "insufficient balance");
                                                            require(LINK.transfer(_recipient, min(linkBalance - linkDue, _amount)), "insufficient funds");
                                                          }
                                                          // Total LINK due to participants in past reports.
                                                          function totalLINKDue()
                                                            internal
                                                            view
                                                            returns (uint256 linkDue)
                                                          {
                                                            // Argument for overflow safety: We do all computations in
                                                            // uint256s. The inputs to linkDue are:
                                                            // - the <= 31 observation rewards each of which has less than
                                                            //   64 bits (32 bits for billing.linkGweiPerObservation, 32 bits
                                                            //   for wei/gwei conversion). Hence 69 bits are sufficient for this part.
                                                            // - the <= 31 gas reimbursements, each of which consists of at most 166
                                                            //   bits (see s_gasReimbursementsLinkWei docstring). Hence 171 bits are
                                                            //   sufficient for this part
                                                            // In total, 172 bits are enough.
                                                            uint16[maxNumOracles] memory observationCounts = s_oracleObservationsCounts;
                                                            for (uint i = 0; i < maxNumOracles; i++) {
                                                              linkDue += observationCounts[i] - 1; // Stored value is one greater than actual value
                                                            }
                                                            Billing memory billing = s_billing;
                                                            // Convert linkGweiPerObservation to uint256, or this overflows!
                                                            linkDue *= uint256(billing.linkGweiPerObservation) * (1 gwei);
                                                            address[] memory transmitters = s_transmitters;
                                                            uint256[maxNumOracles] memory gasReimbursementsLinkWei =
                                                              s_gasReimbursementsLinkWei;
                                                            for (uint i = 0; i < transmitters.length; i++) {
                                                              linkDue += uint256(gasReimbursementsLinkWei[i]-1); // Stored value is one greater than actual value
                                                            }
                                                          }
                                                          /**
                                                           * @notice allows oracles to check that sufficient LINK balance is available
                                                           * @return availableBalance LINK available on this contract, after accounting for outstanding obligations. can become negative
                                                           */
                                                          function linkAvailableForPayment()
                                                            external
                                                            view
                                                            returns (int256 availableBalance)
                                                          {
                                                            // there are at most one billion LINK, so this cast is safe
                                                            int256 balance = int256(LINK.balanceOf(address(this)));
                                                            // according to the argument in the definition of totalLINKDue,
                                                            // totalLINKDue is never greater than 2**172, so this cast is safe
                                                            int256 due = int256(totalLINKDue());
                                                            // safe from overflow according to above sizes
                                                            return int256(balance) - int256(due);
                                                          }
                                                          /**
                                                           * @notice number of observations oracle is due to be reimbursed for
                                                           * @param _signerOrTransmitter address used by oracle for signing or transmitting reports
                                                           */
                                                          function oracleObservationCount(address _signerOrTransmitter)
                                                            external
                                                            view
                                                            returns (uint16)
                                                          {
                                                            Oracle memory oracle = s_oracles[_signerOrTransmitter];
                                                            if (oracle.role == Role.Unset) { return 0; }
                                                            return s_oracleObservationsCounts[oracle.index] - 1;
                                                          }
                                                          function reimburseAndRewardOracles(
                                                            uint32 initialGas,
                                                            bytes memory observers
                                                          )
                                                            internal
                                                          {
                                                            Oracle memory txOracle = s_oracles[msg.sender];
                                                            Billing memory billing = s_billing;
                                                            // Reward oracles for providing observations. Oracles are not rewarded
                                                            // for providing signatures, because signing is essentially free.
                                                            s_oracleObservationsCounts =
                                                              oracleRewards(observers, s_oracleObservationsCounts);
                                                            // Reimburse transmitter of the report for gas usage
                                                            require(txOracle.role == Role.Transmitter,
                                                              "sent by undesignated transmitter"
                                                            );
                                                            uint256 gasPrice = impliedGasPrice(
                                                              tx.gasprice / (1 gwei), // convert to ETH-gwei units
                                                              billing.reasonableGasPrice,
                                                              billing.maximumGasPrice
                                                            );
                                                            // The following is only an upper bound, as it ignores the cheaper cost for
                                                            // 0 bytes. Safe from overflow, because calldata just isn't that long.
                                                            uint256 callDataGasCost = 16 * msg.data.length;
                                                            // If any changes are made to subsequent calculations, accountingGasCost
                                                            // needs to change, too.
                                                            uint256 gasLeft = gasleft();
                                                            uint256 gasCostEthWei = transmitterGasCostEthWei(
                                                              uint256(initialGas),
                                                              gasPrice,
                                                              callDataGasCost,
                                                              gasLeft
                                                            );
                                                            // microLinkPerEth is 1e-6LINK/ETH units, gasCostEthWei is 1e-18ETH units
                                                            // (ETH-wei), product is 1e-24LINK-wei units, dividing by 1e6 gives
                                                            // 1e-18LINK units, i.e. LINK-wei units
                                                            // Safe from over/underflow, since all components are non-negative,
                                                            // gasCostEthWei will always fit into uint128 and microLinkPerEth is a
                                                            // uint32 (128+32 < 256!).
                                                            uint256 gasCostLinkWei = (gasCostEthWei * billing.microLinkPerEth)/ 1e6;
                                                            // Safe from overflow, because gasCostLinkWei < 2**160 and
                                                            // billing.linkGweiPerTransmission * (1 gwei) < 2**64 and we increment
                                                            // s_gasReimbursementsLinkWei[txOracle.index] at most 2**40 times.
                                                            s_gasReimbursementsLinkWei[txOracle.index] =
                                                              s_gasReimbursementsLinkWei[txOracle.index] + gasCostLinkWei +
                                                              uint256(billing.linkGweiPerTransmission) * (1 gwei); // convert from linkGwei to linkWei
                                                            // Uncomment next line to compute the remaining gas cost after above gasleft().
                                                            // See OffchainAggregatorBilling.accountingGasCost docstring for more information.
                                                            //
                                                            // gasUsedInAccounting = gasLeft - gasleft();
                                                          }
                                                          /*
                                                           * Payee management
                                                           */
                                                          /**
                                                           * @notice emitted when a transfer of an oracle's payee address has been initiated
                                                           * @param transmitter address from which the oracle sends reports to the transmit method
                                                           * @param current the payeee address for the oracle, prior to this setting
                                                           * @param proposed the proposed new payee address for the oracle
                                                           */
                                                          event PayeeshipTransferRequested(
                                                            address indexed transmitter,
                                                            address indexed current,
                                                            address indexed proposed
                                                          );
                                                          /**
                                                           * @notice emitted when a transfer of an oracle's payee address has been completed
                                                           * @param transmitter address from which the oracle sends reports to the transmit method
                                                           * @param current the payeee address for the oracle, prior to this setting
                                                           */
                                                          event PayeeshipTransferred(
                                                            address indexed transmitter,
                                                            address indexed previous,
                                                            address indexed current
                                                          );
                                                          /**
                                                           * @notice sets the payees for transmitting addresses
                                                           * @param _transmitters addresses oracles use to transmit the reports
                                                           * @param _payees addresses of payees corresponding to list of transmitters
                                                           * @dev must be called by owner
                                                           * @dev cannot be used to change payee addresses, only to initially populate them
                                                           */
                                                          function setPayees(
                                                            address[] calldata _transmitters,
                                                            address[] calldata _payees
                                                          )
                                                            external
                                                            onlyOwner()
                                                          {
                                                            require(_transmitters.length == _payees.length, "transmitters.size != payees.size");
                                                            for (uint i = 0; i < _transmitters.length; i++) {
                                                              address transmitter = _transmitters[i];
                                                              address payee = _payees[i];
                                                              address currentPayee = s_payees[transmitter];
                                                              bool zeroedOut = currentPayee == address(0);
                                                              require(zeroedOut || currentPayee == payee, "payee already set");
                                                              s_payees[transmitter] = payee;
                                                              if (currentPayee != payee) {
                                                                emit PayeeshipTransferred(transmitter, currentPayee, payee);
                                                              }
                                                            }
                                                          }
                                                          /**
                                                           * @notice first step of payeeship transfer (safe transfer pattern)
                                                           * @param _transmitter transmitter address of oracle whose payee is changing
                                                           * @param _proposed new payee address
                                                           * @dev can only be called by payee address
                                                           */
                                                          function transferPayeeship(
                                                            address _transmitter,
                                                            address _proposed
                                                          )
                                                            external
                                                          {
                                                              require(msg.sender == s_payees[_transmitter], "only current payee can update");
                                                              require(msg.sender != _proposed, "cannot transfer to self");
                                                              address previousProposed = s_proposedPayees[_transmitter];
                                                              s_proposedPayees[_transmitter] = _proposed;
                                                              if (previousProposed != _proposed) {
                                                                emit PayeeshipTransferRequested(_transmitter, msg.sender, _proposed);
                                                              }
                                                          }
                                                          /**
                                                           * @notice second step of payeeship transfer (safe transfer pattern)
                                                           * @param _transmitter transmitter address of oracle whose payee is changing
                                                           * @dev can only be called by proposed new payee address
                                                           */
                                                          function acceptPayeeship(
                                                            address _transmitter
                                                          )
                                                            external
                                                          {
                                                            require(msg.sender == s_proposedPayees[_transmitter], "only proposed payees can accept");
                                                            address currentPayee = s_payees[_transmitter];
                                                            s_payees[_transmitter] = msg.sender;
                                                            s_proposedPayees[_transmitter] = address(0);
                                                            emit PayeeshipTransferred(_transmitter, currentPayee, msg.sender);
                                                          }
                                                          /*
                                                           * Helper functions
                                                           */
                                                          function saturatingAddUint16(uint16 _x, uint16 _y)
                                                            internal
                                                            pure
                                                            returns (uint16)
                                                          {
                                                            return uint16(min(uint256(_x)+uint256(_y), maxUint16));
                                                          }
                                                          function min(uint256 a, uint256 b)
                                                            internal
                                                            pure
                                                            returns (uint256)
                                                          {
                                                            if (a < b) { return a; }
                                                            return b;
                                                          }
                                                        }
                                                        // SPDX-License-Identifier: MIT
                                                        pragma solidity ^0.7.1;
                                                        import "./SimpleWriteAccessController.sol";
                                                        /**
                                                         * @title SimpleReadAccessController
                                                         * @notice Gives access to:
                                                         * - any externally owned account (note that offchain actors can always read
                                                         * any contract storage regardless of onchain access control measures, so this
                                                         * does not weaken the access control while improving usability)
                                                         * - accounts explicitly added to an access list
                                                         * @dev SimpleReadAccessController is not suitable for access controlling writes
                                                         * since it grants any externally owned account access! See
                                                         * SimpleWriteAccessController for that.
                                                         */
                                                        contract SimpleReadAccessController is SimpleWriteAccessController {
                                                          /**
                                                           * @notice Returns the access of an address
                                                           * @param _user The address to query
                                                           */
                                                          function hasAccess(
                                                            address _user,
                                                            bytes memory _calldata
                                                          )
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (bool)
                                                          {
                                                            return super.hasAccess(_user, _calldata) || _user == tx.origin;
                                                          }
                                                        }
                                                        // SPDX-License-Identifier: MIT
                                                        pragma solidity ^0.7.0;
                                                        import "./Owned.sol";
                                                        import "./AccessControllerInterface.sol";
                                                        /**
                                                         * @title SimpleWriteAccessController
                                                         * @notice Gives access to accounts explicitly added to an access list by the
                                                         * controller's owner.
                                                         * @dev does not make any special permissions for externally, see
                                                         * SimpleReadAccessController for that.
                                                         */
                                                        contract SimpleWriteAccessController is AccessControllerInterface, Owned {
                                                          bool public checkEnabled;
                                                          mapping(address => bool) internal accessList;
                                                          event AddedAccess(address user);
                                                          event RemovedAccess(address user);
                                                          event CheckAccessEnabled();
                                                          event CheckAccessDisabled();
                                                          constructor()
                                                          {
                                                            checkEnabled = true;
                                                          }
                                                          /**
                                                           * @notice Returns the access of an address
                                                           * @param _user The address to query
                                                           */
                                                          function hasAccess(
                                                            address _user,
                                                            bytes memory
                                                          )
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (bool)
                                                          {
                                                            return accessList[_user] || !checkEnabled;
                                                          }
                                                          /**
                                                           * @notice Adds an address to the access list
                                                           * @param _user The address to add
                                                           */
                                                          function addAccess(address _user) external onlyOwner() {
                                                            addAccessInternal(_user);
                                                          }
                                                          function addAccessInternal(address _user) internal {
                                                            if (!accessList[_user]) {
                                                              accessList[_user] = true;
                                                              emit AddedAccess(_user);
                                                            }
                                                          }
                                                          /**
                                                           * @notice Removes an address from the access list
                                                           * @param _user The address to remove
                                                           */
                                                          function removeAccess(address _user)
                                                            external
                                                            onlyOwner()
                                                          {
                                                            if (accessList[_user]) {
                                                              accessList[_user] = false;
                                                              emit RemovedAccess(_user);
                                                            }
                                                          }
                                                          /**
                                                           * @notice makes the access check enforced
                                                           */
                                                          function enableAccessCheck()
                                                            external
                                                            onlyOwner()
                                                          {
                                                            if (!checkEnabled) {
                                                              checkEnabled = true;
                                                              emit CheckAccessEnabled();
                                                            }
                                                          }
                                                          /**
                                                           * @notice makes the access check unenforced
                                                           */
                                                          function disableAccessCheck()
                                                            external
                                                            onlyOwner()
                                                          {
                                                            if (checkEnabled) {
                                                              checkEnabled = false;
                                                              emit CheckAccessDisabled();
                                                            }
                                                          }
                                                          /**
                                                           * @dev reverts if the caller does not have access
                                                           */
                                                          modifier checkAccess() {
                                                            require(hasAccess(msg.sender, msg.data), "No access");
                                                            _;
                                                          }
                                                        }
                                                        

                                                        File 14 of 30: 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 15 of 30: EACAggregatorProxy
                                                        pragma solidity 0.6.6;
                                                        
                                                        
                                                        /**
                                                         * @title The Owned contract
                                                         * @notice A contract with helpers for basic contract ownership.
                                                         */
                                                        contract Owned {
                                                        
                                                          address payable public owner;
                                                          address private pendingOwner;
                                                        
                                                          event OwnershipTransferRequested(
                                                            address indexed from,
                                                            address indexed to
                                                          );
                                                          event OwnershipTransferred(
                                                            address indexed from,
                                                            address indexed to
                                                          );
                                                        
                                                          constructor() public {
                                                            owner = msg.sender;
                                                          }
                                                        
                                                          /**
                                                           * @dev Allows an owner to begin transferring ownership to a new address,
                                                           * pending.
                                                           */
                                                          function transferOwnership(address _to)
                                                            external
                                                            onlyOwner()
                                                          {
                                                            pendingOwner = _to;
                                                        
                                                            emit OwnershipTransferRequested(owner, _to);
                                                          }
                                                        
                                                          /**
                                                           * @dev Allows an ownership transfer to be completed by the recipient.
                                                           */
                                                          function acceptOwnership()
                                                            external
                                                          {
                                                            require(msg.sender == pendingOwner, "Must be proposed owner");
                                                        
                                                            address oldOwner = owner;
                                                            owner = msg.sender;
                                                            pendingOwner = address(0);
                                                        
                                                            emit OwnershipTransferred(oldOwner, msg.sender);
                                                          }
                                                        
                                                          /**
                                                           * @dev Reverts if called by anyone other than the contract owner.
                                                           */
                                                          modifier onlyOwner() {
                                                            require(msg.sender == owner, "Only callable by owner");
                                                            _;
                                                          }
                                                        
                                                        }
                                                        
                                                        interface AggregatorInterface {
                                                          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 updatedAt);
                                                          event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
                                                        }
                                                        
                                                        interface AggregatorV3Interface {
                                                        
                                                          function decimals() external view returns (uint8);
                                                          function description() external view returns (string memory);
                                                          function version() external view returns (uint256);
                                                        
                                                          // getRoundData and latestRoundData should both raise "No data present"
                                                          // if they do not have data to report, instead of returning unset values
                                                          // which could be misinterpreted as actual reported values.
                                                          function getRoundData(uint80 _roundId)
                                                            external
                                                            view
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            );
                                                          function latestRoundData()
                                                            external
                                                            view
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            );
                                                        
                                                        }
                                                        
                                                        interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface
                                                        {
                                                        }
                                                        
                                                        /**
                                                         * @title A trusted proxy for updating where current answers are read from
                                                         * @notice This contract provides a consistent address for the
                                                         * CurrentAnwerInterface but delegates where it reads from to the owner, who is
                                                         * trusted to update it.
                                                         */
                                                        contract AggregatorProxy is AggregatorV2V3Interface, Owned {
                                                        
                                                          struct Phase {
                                                            uint16 id;
                                                            AggregatorV2V3Interface aggregator;
                                                          }
                                                          Phase private currentPhase;
                                                          AggregatorV2V3Interface public proposedAggregator;
                                                          mapping(uint16 => AggregatorV2V3Interface) public phaseAggregators;
                                                        
                                                          uint256 constant private PHASE_OFFSET = 64;
                                                          uint256 constant private PHASE_SIZE = 16;
                                                          uint256 constant private MAX_ID = 2**(PHASE_OFFSET+PHASE_SIZE) - 1;
                                                        
                                                          constructor(address _aggregator) public Owned() {
                                                            setAggregator(_aggregator);
                                                          }
                                                        
                                                          /**
                                                           * @notice Reads the current answer from aggregator delegated to.
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestAnswer()
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (int256 answer)
                                                          {
                                                            return currentPhase.aggregator.latestAnswer();
                                                          }
                                                        
                                                          /**
                                                           * @notice Reads the last updated height from aggregator delegated to.
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestTimestamp()
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (uint256 updatedAt)
                                                          {
                                                            return currentPhase.aggregator.latestTimestamp();
                                                          }
                                                        
                                                          /**
                                                           * @notice get past rounds answers
                                                           * @param _roundId the answer number to retrieve the answer for
                                                           *
                                                           * @dev #[deprecated] Use getRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended getRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function getAnswer(uint256 _roundId)
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (int256 answer)
                                                          {
                                                            if (_roundId > MAX_ID) return 0;
                                                        
                                                            (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
                                                            AggregatorV2V3Interface aggregator = phaseAggregators[phaseId];
                                                            if (address(aggregator) == address(0)) return 0;
                                                        
                                                            return aggregator.getAnswer(aggregatorRoundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get block timestamp when an answer was last updated
                                                           * @param _roundId the answer number to retrieve the updated timestamp for
                                                           *
                                                           * @dev #[deprecated] Use getRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended getRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function getTimestamp(uint256 _roundId)
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (uint256 updatedAt)
                                                          {
                                                            if (_roundId > MAX_ID) return 0;
                                                        
                                                            (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
                                                            AggregatorV2V3Interface aggregator = phaseAggregators[phaseId];
                                                            if (address(aggregator) == address(0)) return 0;
                                                        
                                                            return aggregator.getTimestamp(aggregatorRoundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get the latest completed round where the answer was updated. This
                                                           * ID includes the proxy's phase, to make sure round IDs increase even when
                                                           * switching to a newly deployed aggregator.
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestRound()
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (uint256 roundId)
                                                          {
                                                            Phase memory phase = currentPhase; // cache storage reads
                                                            return addPhase(phase.id, uint64(phase.aggregator.latestRound()));
                                                          }
                                                        
                                                          /**
                                                           * @notice get data about a round. Consumers are encouraged to check
                                                           * that they're receiving fresh data by inspecting the updatedAt and
                                                           * answeredInRound return values.
                                                           * Note that different underlying implementations of AggregatorV3Interface
                                                           * have slightly different semantics for some of the return values. Consumers
                                                           * should determine what implementations they expect to receive
                                                           * data from and validate that they can properly handle return data from all
                                                           * of them.
                                                           * @param _roundId the requested round ID as presented through the proxy, this
                                                           * is made up of the aggregator's round ID with the phase ID encoded in the
                                                           * two highest order bytes
                                                           * @return roundId is the round ID from the aggregator for which the data was
                                                           * retrieved combined with an phase to ensure that round IDs get larger as
                                                           * time moves forward.
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @dev Note that answer and updatedAt may change between queries.
                                                           */
                                                          function getRoundData(uint80 _roundId)
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
                                                        
                                                            (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 ansIn
                                                            ) = phaseAggregators[phaseId].getRoundData(aggregatorRoundId);
                                                        
                                                            return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, phaseId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get data about the latest round. Consumers are encouraged to check
                                                           * that they're receiving fresh data by inspecting the updatedAt and
                                                           * answeredInRound return values.
                                                           * Note that different underlying implementations of AggregatorV3Interface
                                                           * have slightly different semantics for some of the return values. Consumers
                                                           * should determine what implementations they expect to receive
                                                           * data from and validate that they can properly handle return data from all
                                                           * of them.
                                                           * @return roundId is the round ID from the aggregator for which the data was
                                                           * retrieved combined with an phase to ensure that round IDs get larger as
                                                           * time moves forward.
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @dev Note that answer and updatedAt may change between queries.
                                                           */
                                                          function latestRoundData()
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            Phase memory current = currentPhase; // cache storage reads
                                                        
                                                            (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 ansIn
                                                            ) = current.aggregator.latestRoundData();
                                                        
                                                            return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, current.id);
                                                          }
                                                        
                                                          /**
                                                           * @notice Used if an aggregator contract has been proposed.
                                                           * @param _roundId the round ID to retrieve the round data for
                                                           * @return roundId is the round ID for which data was retrieved
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                          */
                                                          function proposedGetRoundData(uint80 _roundId)
                                                            public
                                                            view
                                                            virtual
                                                            hasProposal()
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return proposedAggregator.getRoundData(_roundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice Used if an aggregator contract has been proposed.
                                                           * @return roundId is the round ID for which data was retrieved
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                          */
                                                          function proposedLatestRoundData()
                                                            public
                                                            view
                                                            virtual
                                                            hasProposal()
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return proposedAggregator.latestRoundData();
                                                          }
                                                        
                                                          /**
                                                           * @notice returns the current phase's aggregator address.
                                                           */
                                                          function aggregator()
                                                            external
                                                            view
                                                            returns (address)
                                                          {
                                                            return address(currentPhase.aggregator);
                                                          }
                                                        
                                                          /**
                                                           * @notice returns the current phase's ID.
                                                           */
                                                          function phaseId()
                                                            external
                                                            view
                                                            returns (uint16)
                                                          {
                                                            return currentPhase.id;
                                                          }
                                                        
                                                          /**
                                                           * @notice represents the number of decimals the aggregator responses represent.
                                                           */
                                                          function decimals()
                                                            external
                                                            view
                                                            override
                                                            returns (uint8)
                                                          {
                                                            return currentPhase.aggregator.decimals();
                                                          }
                                                        
                                                          /**
                                                           * @notice the version number representing the type of aggregator the proxy
                                                           * points to.
                                                           */
                                                          function version()
                                                            external
                                                            view
                                                            override
                                                            returns (uint256)
                                                          {
                                                            return currentPhase.aggregator.version();
                                                          }
                                                        
                                                          /**
                                                           * @notice returns the description of the aggregator the proxy points to.
                                                           */
                                                          function description()
                                                            external
                                                            view
                                                            override
                                                            returns (string memory)
                                                          {
                                                            return currentPhase.aggregator.description();
                                                          }
                                                        
                                                          /**
                                                           * @notice Allows the owner to propose a new address for the aggregator
                                                           * @param _aggregator The new address for the aggregator contract
                                                           */
                                                          function proposeAggregator(address _aggregator)
                                                            external
                                                            onlyOwner()
                                                          {
                                                            proposedAggregator = AggregatorV2V3Interface(_aggregator);
                                                          }
                                                        
                                                          /**
                                                           * @notice Allows the owner to confirm and change the address
                                                           * to the proposed aggregator
                                                           * @dev Reverts if the given address doesn't match what was previously
                                                           * proposed
                                                           * @param _aggregator The new address for the aggregator contract
                                                           */
                                                          function confirmAggregator(address _aggregator)
                                                            external
                                                            onlyOwner()
                                                          {
                                                            require(_aggregator == address(proposedAggregator), "Invalid proposed aggregator");
                                                            delete proposedAggregator;
                                                            setAggregator(_aggregator);
                                                          }
                                                        
                                                        
                                                          /*
                                                           * Internal
                                                           */
                                                        
                                                          function setAggregator(address _aggregator)
                                                            internal
                                                          {
                                                            uint16 id = currentPhase.id + 1;
                                                            currentPhase = Phase(id, AggregatorV2V3Interface(_aggregator));
                                                            phaseAggregators[id] = AggregatorV2V3Interface(_aggregator);
                                                          }
                                                        
                                                          function addPhase(
                                                            uint16 _phase,
                                                            uint64 _originalId
                                                          )
                                                            internal
                                                            view
                                                            returns (uint80)
                                                          {
                                                            return uint80(uint256(_phase) << PHASE_OFFSET | _originalId);
                                                          }
                                                        
                                                          function parseIds(
                                                            uint256 _roundId
                                                          )
                                                            internal
                                                            view
                                                            returns (uint16, uint64)
                                                          {
                                                            uint16 phaseId = uint16(_roundId >> PHASE_OFFSET);
                                                            uint64 aggregatorRoundId = uint64(_roundId);
                                                        
                                                            return (phaseId, aggregatorRoundId);
                                                          }
                                                        
                                                          function addPhaseIds(
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound,
                                                              uint16 phaseId
                                                          )
                                                            internal
                                                            view
                                                            returns (uint80, int256, uint256, uint256, uint80)
                                                          {
                                                            return (
                                                              addPhase(phaseId, uint64(roundId)),
                                                              answer,
                                                              startedAt,
                                                              updatedAt,
                                                              addPhase(phaseId, uint64(answeredInRound))
                                                            );
                                                          }
                                                        
                                                          /*
                                                           * Modifiers
                                                           */
                                                        
                                                          modifier hasProposal() {
                                                            require(address(proposedAggregator) != address(0), "No proposed aggregator present");
                                                            _;
                                                          }
                                                        
                                                        }
                                                        
                                                        interface AccessControllerInterface {
                                                          function hasAccess(address user, bytes calldata data) external view returns (bool);
                                                        }
                                                        
                                                        /**
                                                         * @title External Access Controlled Aggregator Proxy
                                                         * @notice A trusted proxy for updating where current answers are read from
                                                         * @notice This contract provides a consistent address for the
                                                         * Aggregator and AggregatorV3Interface but delegates where it reads from to the owner, who is
                                                         * trusted to update it.
                                                         * @notice Only access enabled addresses are allowed to access getters for
                                                         * aggregated answers and round information.
                                                         */
                                                        contract EACAggregatorProxy is AggregatorProxy {
                                                        
                                                          AccessControllerInterface public accessController;
                                                        
                                                          constructor(
                                                            address _aggregator,
                                                            address _accessController
                                                          )
                                                            public
                                                            AggregatorProxy(_aggregator)
                                                          {
                                                            setController(_accessController);
                                                          }
                                                        
                                                          /**
                                                           * @notice Allows the owner to update the accessController contract address.
                                                           * @param _accessController The new address for the accessController contract
                                                           */
                                                          function setController(address _accessController)
                                                            public
                                                            onlyOwner()
                                                          {
                                                            accessController = AccessControllerInterface(_accessController);
                                                          }
                                                        
                                                          /**
                                                           * @notice Reads the current answer from aggregator delegated to.
                                                           * @dev overridden function to add the checkAccess() modifier
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestAnswer()
                                                            public
                                                            view
                                                            override
                                                            checkAccess()
                                                            returns (int256)
                                                          {
                                                            return super.latestAnswer();
                                                          }
                                                        
                                                          /**
                                                           * @notice get the latest completed round where the answer was updated. This
                                                           * ID includes the proxy's phase, to make sure round IDs increase even when
                                                           * switching to a newly deployed aggregator.
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestTimestamp()
                                                            public
                                                            view
                                                            override
                                                            checkAccess()
                                                            returns (uint256)
                                                          {
                                                            return super.latestTimestamp();
                                                          }
                                                        
                                                          /**
                                                           * @notice get past rounds answers
                                                           * @param _roundId the answer number to retrieve the answer for
                                                           * @dev overridden function to add the checkAccess() modifier
                                                           *
                                                           * @dev #[deprecated] Use getRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended getRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function getAnswer(uint256 _roundId)
                                                            public
                                                            view
                                                            override
                                                            checkAccess()
                                                            returns (int256)
                                                          {
                                                            return super.getAnswer(_roundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get block timestamp when an answer was last updated
                                                           * @param _roundId the answer number to retrieve the updated timestamp for
                                                           * @dev overridden function to add the checkAccess() modifier
                                                           *
                                                           * @dev #[deprecated] Use getRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended getRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function getTimestamp(uint256 _roundId)
                                                            public
                                                            view
                                                            override
                                                            checkAccess()
                                                            returns (uint256)
                                                          {
                                                            return super.getTimestamp(_roundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get the latest completed round where the answer was updated
                                                           * @dev overridden function to add the checkAccess() modifier
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestRound()
                                                            public
                                                            view
                                                            override
                                                            checkAccess()
                                                            returns (uint256)
                                                          {
                                                            return super.latestRound();
                                                          }
                                                        
                                                          /**
                                                           * @notice get data about a round. Consumers are encouraged to check
                                                           * that they're receiving fresh data by inspecting the updatedAt and
                                                           * answeredInRound return values.
                                                           * Note that different underlying implementations of AggregatorV3Interface
                                                           * have slightly different semantics for some of the return values. Consumers
                                                           * should determine what implementations they expect to receive
                                                           * data from and validate that they can properly handle return data from all
                                                           * of them.
                                                           * @param _roundId the round ID to retrieve the round data for
                                                           * @return roundId is the round ID from the aggregator for which the data was
                                                           * retrieved combined with a phase to ensure that round IDs get larger as
                                                           * time moves forward.
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @dev Note that answer and updatedAt may change between queries.
                                                           */
                                                          function getRoundData(uint80 _roundId)
                                                            public
                                                            view
                                                            checkAccess()
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return super.getRoundData(_roundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get data about the latest round. Consumers are encouraged to check
                                                           * that they're receiving fresh data by inspecting the updatedAt and
                                                           * answeredInRound return values.
                                                           * Note that different underlying implementations of AggregatorV3Interface
                                                           * have slightly different semantics for some of the return values. Consumers
                                                           * should determine what implementations they expect to receive
                                                           * data from and validate that they can properly handle return data from all
                                                           * of them.
                                                           * @return roundId is the round ID from the aggregator for which the data was
                                                           * retrieved combined with a phase to ensure that round IDs get larger as
                                                           * time moves forward.
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @dev Note that answer and updatedAt may change between queries.
                                                           */
                                                          function latestRoundData()
                                                            public
                                                            view
                                                            checkAccess()
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return super.latestRoundData();
                                                          }
                                                        
                                                          /**
                                                           * @notice Used if an aggregator contract has been proposed.
                                                           * @param _roundId the round ID to retrieve the round data for
                                                           * @return roundId is the round ID for which data was retrieved
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                          */
                                                          function proposedGetRoundData(uint80 _roundId)
                                                            public
                                                            view
                                                            checkAccess()
                                                            hasProposal()
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return super.proposedGetRoundData(_roundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice Used if an aggregator contract has been proposed.
                                                           * @return roundId is the round ID for which data was retrieved
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                          */
                                                          function proposedLatestRoundData()
                                                            public
                                                            view
                                                            checkAccess()
                                                            hasProposal()
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return super.proposedLatestRoundData();
                                                          }
                                                        
                                                          /**
                                                           * @dev reverts if the caller does not have access by the accessController
                                                           * contract or is the contract itself.
                                                           */
                                                          modifier checkAccess() {
                                                            AccessControllerInterface ac = accessController;
                                                            require(address(ac) == address(0) || ac.hasAccess(msg.sender, msg.data), "No access");
                                                            _;
                                                          }
                                                        }

                                                        File 16 of 30: 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 17 of 30: 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';
                                                        import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol';
                                                        /**
                                                         * @title Aave ERC20 AToken
                                                         * @dev Implementation of the interest bearing token for the Aave protocol
                                                         * @author Aave
                                                         */
                                                        contract AToken is VersionedInitializable, IncentivizedERC20, IAToken {
                                                          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 = 0x2;
                                                          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);
                                                            emit Initialized(
                                                              UNDERLYING_ASSET_ADDRESS,
                                                              address(POOL),
                                                              RESERVE_TREASURY_ADDRESS,
                                                              address(_incentivesController),
                                                              underlyingAssetDecimals,
                                                              tokenName,
                                                              tokenSymbol,
                                                              ''
                                                            );
                                                          }
                                                          /**
                                                           * @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 address of the incentives controller contract
                                                           **/
                                                          function getIncentivesController() external view override returns (IAaveIncentivesController) {
                                                            return _incentivesController;
                                                          }
                                                          /**
                                                           * @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';
                                                        import {IAaveIncentivesController} from './IAaveIncentivesController.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 Emitted when an aToken is initialized
                                                           * @param underlyingAsset The address of the underlying asset
                                                           * @param pool The address of the associated lending pool
                                                           * @param treasury The address of the treasury
                                                           * @param incentivesController The address of the incentives controller for this aToken
                                                           * @param aTokenDecimals the decimals of the underlying
                                                           * @param aTokenName the name of the aToken
                                                           * @param aTokenSymbol the symbol of the aToken
                                                           * @param params A set of encoded parameters for additional initialization
                                                           **/
                                                          event Initialized(
                                                            address indexed underlyingAsset,
                                                            address indexed pool,
                                                            address treasury,
                                                            address incentivesController,
                                                            uint8 aTokenDecimals,
                                                            string aTokenName,
                                                            string aTokenSymbol,
                                                            bytes params
                                                          );
                                                          /**
                                                           * @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);
                                                          /**
                                                           * @dev Returns the address of the incentives controller contract
                                                           **/
                                                          function getIncentivesController() external view returns (IAaveIncentivesController);
                                                        }
                                                        // 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;
                                                        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 {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 view virtual returns (address payable) {
                                                            return msg.sender;
                                                          }
                                                          function _msgData() internal view virtual 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);
                                                        }
                                                        

                                                        File 18 of 30: 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 19 of 30: 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';
                                                        import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol';
                                                        /**
                                                         * @title Aave ERC20 AToken
                                                         * @dev Implementation of the interest bearing token for the Aave protocol
                                                         * @author Aave
                                                         */
                                                        contract AToken is VersionedInitializable, IncentivizedERC20, IAToken {
                                                          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 = 0x2;
                                                          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);
                                                            emit Initialized(
                                                              UNDERLYING_ASSET_ADDRESS,
                                                              address(POOL),
                                                              RESERVE_TREASURY_ADDRESS,
                                                              address(_incentivesController),
                                                              underlyingAssetDecimals,
                                                              tokenName,
                                                              tokenSymbol,
                                                              ''
                                                            );
                                                          }
                                                          /**
                                                           * @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 address of the incentives controller contract
                                                           **/
                                                          function getIncentivesController() external view override returns (IAaveIncentivesController) {
                                                            return _incentivesController;
                                                          }
                                                          /**
                                                           * @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';
                                                        import {IAaveIncentivesController} from './IAaveIncentivesController.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 Emitted when an aToken is initialized
                                                           * @param underlyingAsset The address of the underlying asset
                                                           * @param pool The address of the associated lending pool
                                                           * @param treasury The address of the treasury
                                                           * @param incentivesController The address of the incentives controller for this aToken
                                                           * @param aTokenDecimals the decimals of the underlying
                                                           * @param aTokenName the name of the aToken
                                                           * @param aTokenSymbol the symbol of the aToken
                                                           * @param params A set of encoded parameters for additional initialization
                                                           **/
                                                          event Initialized(
                                                            address indexed underlyingAsset,
                                                            address indexed pool,
                                                            address treasury,
                                                            address incentivesController,
                                                            uint8 aTokenDecimals,
                                                            string aTokenName,
                                                            string aTokenSymbol,
                                                            bytes params
                                                          );
                                                          /**
                                                           * @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);
                                                          /**
                                                           * @dev Returns the address of the incentives controller contract
                                                           **/
                                                          function getIncentivesController() external view returns (IAaveIncentivesController);
                                                        }
                                                        // 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;
                                                        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 {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 view virtual returns (address payable) {
                                                            return msg.sender;
                                                          }
                                                          function _msgData() internal view virtual 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);
                                                        }
                                                        

                                                        File 20 of 30: 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 21 of 30: 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 22 of 30: 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';
                                                        import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.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 = 0x2;
                                                          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());
                                                          }
                                                          /**
                                                           * @dev Returns the address of the incentives controller contract
                                                           * @return incentives address
                                                           **/
                                                          function getIncentivesController() external view override returns (IAaveIncentivesController) {
                                                            return _incentivesController;
                                                          }
                                                        }
                                                        // SPDX-License-Identifier: agpl-3.0
                                                        pragma solidity 0.6.12;
                                                        import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
                                                        import {IAaveIncentivesController} from './IAaveIncentivesController.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;
                                                          /**
                                                           * @dev Returns the address of the incentives controller contract
                                                           **/
                                                          function getIncentivesController() external view returns (IAaveIncentivesController);
                                                        }
                                                        // 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;
                                                        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 {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 {IDebtTokenBase} from '../../../interfaces/IDebtTokenBase.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,
                                                          IDebtTokenBase
                                                        {
                                                          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);
                                                            emit Initialized(
                                                              UNDERLYING_ASSET_ADDRESS,
                                                              address(POOL),
                                                              address(_incentivesController),
                                                              decimals,
                                                              name,
                                                              symbol,
                                                              ''
                                                            );
                                                          }
                                                          /**
                                                           * @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;
                                                        interface IDebtTokenBase {
                                                          /**
                                                           * @dev Emitted when a debt token is initialized
                                                           * @param underlyingAsset The address of the underlying asset
                                                           * @param pool The address of the associated lending pool
                                                           * @param incentivesController The address of the incentives controller for this aToken
                                                           * @param debtTokenDecimals the decimals of the debt token
                                                           * @param debtTokenName the name of the debt token
                                                           * @param debtTokenSymbol the symbol of the debt token
                                                           * @param params A set of encoded parameters for additional initialization
                                                           **/
                                                          event Initialized(
                                                            address indexed underlyingAsset,
                                                            address indexed pool,
                                                            address incentivesController,
                                                            uint8 debtTokenDecimals,
                                                            string debtTokenName,
                                                            string debtTokenSymbol,
                                                            bytes params
                                                          );
                                                        }
                                                        // 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 view virtual returns (address payable) {
                                                            return msg.sender;
                                                          }
                                                          function _msgData() internal view virtual 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;
                                                          }
                                                        }
                                                        

                                                        File 23 of 30: EACAggregatorProxy
                                                        pragma solidity 0.6.6;
                                                        
                                                        
                                                        /**
                                                         * @title The Owned contract
                                                         * @notice A contract with helpers for basic contract ownership.
                                                         */
                                                        contract Owned {
                                                        
                                                          address public owner;
                                                          address private pendingOwner;
                                                        
                                                          event OwnershipTransferRequested(
                                                            address indexed from,
                                                            address indexed to
                                                          );
                                                          event OwnershipTransferred(
                                                            address indexed from,
                                                            address indexed to
                                                          );
                                                        
                                                          constructor() public {
                                                            owner = msg.sender;
                                                          }
                                                        
                                                          /**
                                                           * @dev Allows an owner to begin transferring ownership to a new address,
                                                           * pending.
                                                           */
                                                          function transferOwnership(address _to)
                                                            external
                                                            onlyOwner()
                                                          {
                                                            pendingOwner = _to;
                                                        
                                                            emit OwnershipTransferRequested(owner, _to);
                                                          }
                                                        
                                                          /**
                                                           * @dev Allows an ownership transfer to be completed by the recipient.
                                                           */
                                                          function acceptOwnership()
                                                            external
                                                          {
                                                            require(msg.sender == pendingOwner, "Must be proposed owner");
                                                        
                                                            address oldOwner = owner;
                                                            owner = msg.sender;
                                                            pendingOwner = address(0);
                                                        
                                                            emit OwnershipTransferred(oldOwner, msg.sender);
                                                          }
                                                        
                                                          /**
                                                           * @dev Reverts if called by anyone other than the contract owner.
                                                           */
                                                          modifier onlyOwner() {
                                                            require(msg.sender == owner, "Only callable by owner");
                                                            _;
                                                          }
                                                        
                                                        }
                                                        
                                                        interface AggregatorInterface {
                                                          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 updatedAt);
                                                          event NewRound(uint256 indexed roundId, address indexed startedBy, uint256 startedAt);
                                                        }
                                                        
                                                        interface AggregatorV3Interface {
                                                        
                                                          function decimals() external view returns (uint8);
                                                          function description() external view returns (string memory);
                                                          function version() external view returns (uint256);
                                                        
                                                          // getRoundData and latestRoundData should both raise "No data present"
                                                          // if they do not have data to report, instead of returning unset values
                                                          // which could be misinterpreted as actual reported values.
                                                          function getRoundData(uint80 _roundId)
                                                            external
                                                            view
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            );
                                                          function latestRoundData()
                                                            external
                                                            view
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            );
                                                        
                                                        }
                                                        
                                                        interface AggregatorV2V3Interface is AggregatorInterface, AggregatorV3Interface
                                                        {
                                                        }
                                                        
                                                        /**
                                                         * @title A trusted proxy for updating where current answers are read from
                                                         * @notice This contract provides a consistent address for the
                                                         * CurrentAnwerInterface but delegates where it reads from to the owner, who is
                                                         * trusted to update it.
                                                         */
                                                        contract AggregatorProxy is AggregatorV2V3Interface, Owned {
                                                        
                                                          struct Phase {
                                                            uint16 id;
                                                            AggregatorV2V3Interface aggregator;
                                                          }
                                                          Phase private currentPhase;
                                                          AggregatorV2V3Interface public proposedAggregator;
                                                          mapping(uint16 => AggregatorV2V3Interface) public phaseAggregators;
                                                        
                                                          uint256 constant private PHASE_OFFSET = 64;
                                                          uint256 constant private PHASE_SIZE = 16;
                                                          uint256 constant private MAX_ID = 2**(PHASE_OFFSET+PHASE_SIZE) - 1;
                                                        
                                                          constructor(address _aggregator) public Owned() {
                                                            setAggregator(_aggregator);
                                                          }
                                                        
                                                          /**
                                                           * @notice Reads the current answer from aggregator delegated to.
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestAnswer()
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (int256 answer)
                                                          {
                                                            return currentPhase.aggregator.latestAnswer();
                                                          }
                                                        
                                                          /**
                                                           * @notice Reads the last updated height from aggregator delegated to.
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestTimestamp()
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (uint256 updatedAt)
                                                          {
                                                            return currentPhase.aggregator.latestTimestamp();
                                                          }
                                                        
                                                          /**
                                                           * @notice get past rounds answers
                                                           * @param _roundId the answer number to retrieve the answer for
                                                           *
                                                           * @dev #[deprecated] Use getRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended getRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function getAnswer(uint256 _roundId)
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (int256 answer)
                                                          {
                                                            if (_roundId > MAX_ID) return 0;
                                                        
                                                            (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
                                                            AggregatorV2V3Interface aggregator = phaseAggregators[phaseId];
                                                            if (address(aggregator) == address(0)) return 0;
                                                        
                                                            return aggregator.getAnswer(aggregatorRoundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get block timestamp when an answer was last updated
                                                           * @param _roundId the answer number to retrieve the updated timestamp for
                                                           *
                                                           * @dev #[deprecated] Use getRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended getRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function getTimestamp(uint256 _roundId)
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (uint256 updatedAt)
                                                          {
                                                            if (_roundId > MAX_ID) return 0;
                                                        
                                                            (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
                                                            AggregatorV2V3Interface aggregator = phaseAggregators[phaseId];
                                                            if (address(aggregator) == address(0)) return 0;
                                                        
                                                            return aggregator.getTimestamp(aggregatorRoundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get the latest completed round where the answer was updated. This
                                                           * ID includes the proxy's phase, to make sure round IDs increase even when
                                                           * switching to a newly deployed aggregator.
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestRound()
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (uint256 roundId)
                                                          {
                                                            Phase memory phase = currentPhase; // cache storage reads
                                                            return addPhase(phase.id, uint64(phase.aggregator.latestRound()));
                                                          }
                                                        
                                                          /**
                                                           * @notice get data about a round. Consumers are encouraged to check
                                                           * that they're receiving fresh data by inspecting the updatedAt and
                                                           * answeredInRound return values.
                                                           * Note that different underlying implementations of AggregatorV3Interface
                                                           * have slightly different semantics for some of the return values. Consumers
                                                           * should determine what implementations they expect to receive
                                                           * data from and validate that they can properly handle return data from all
                                                           * of them.
                                                           * @param _roundId the requested round ID as presented through the proxy, this
                                                           * is made up of the aggregator's round ID with the phase ID encoded in the
                                                           * two highest order bytes
                                                           * @return roundId is the round ID from the aggregator for which the data was
                                                           * retrieved combined with an phase to ensure that round IDs get larger as
                                                           * time moves forward.
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @dev Note that answer and updatedAt may change between queries.
                                                           */
                                                          function getRoundData(uint80 _roundId)
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            (uint16 phaseId, uint64 aggregatorRoundId) = parseIds(_roundId);
                                                        
                                                            (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 ansIn
                                                            ) = phaseAggregators[phaseId].getRoundData(aggregatorRoundId);
                                                        
                                                            return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, phaseId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get data about the latest round. Consumers are encouraged to check
                                                           * that they're receiving fresh data by inspecting the updatedAt and
                                                           * answeredInRound return values.
                                                           * Note that different underlying implementations of AggregatorV3Interface
                                                           * have slightly different semantics for some of the return values. Consumers
                                                           * should determine what implementations they expect to receive
                                                           * data from and validate that they can properly handle return data from all
                                                           * of them.
                                                           * @return roundId is the round ID from the aggregator for which the data was
                                                           * retrieved combined with an phase to ensure that round IDs get larger as
                                                           * time moves forward.
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @dev Note that answer and updatedAt may change between queries.
                                                           */
                                                          function latestRoundData()
                                                            public
                                                            view
                                                            virtual
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            Phase memory current = currentPhase; // cache storage reads
                                                        
                                                            (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 ansIn
                                                            ) = current.aggregator.latestRoundData();
                                                        
                                                            return addPhaseIds(roundId, answer, startedAt, updatedAt, ansIn, current.id);
                                                          }
                                                        
                                                          /**
                                                           * @notice Used if an aggregator contract has been proposed.
                                                           * @param _roundId the round ID to retrieve the round data for
                                                           * @return roundId is the round ID for which data was retrieved
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                          */
                                                          function proposedGetRoundData(uint80 _roundId)
                                                            public
                                                            view
                                                            virtual
                                                            hasProposal()
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return proposedAggregator.getRoundData(_roundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice Used if an aggregator contract has been proposed.
                                                           * @return roundId is the round ID for which data was retrieved
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                          */
                                                          function proposedLatestRoundData()
                                                            public
                                                            view
                                                            virtual
                                                            hasProposal()
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return proposedAggregator.latestRoundData();
                                                          }
                                                        
                                                          /**
                                                           * @notice returns the current phase's aggregator address.
                                                           */
                                                          function aggregator()
                                                            external
                                                            view
                                                            returns (address)
                                                          {
                                                            return address(currentPhase.aggregator);
                                                          }
                                                        
                                                          /**
                                                           * @notice returns the current phase's ID.
                                                           */
                                                          function phaseId()
                                                            external
                                                            view
                                                            returns (uint16)
                                                          {
                                                            return currentPhase.id;
                                                          }
                                                        
                                                          /**
                                                           * @notice represents the number of decimals the aggregator responses represent.
                                                           */
                                                          function decimals()
                                                            external
                                                            view
                                                            override
                                                            returns (uint8)
                                                          {
                                                            return currentPhase.aggregator.decimals();
                                                          }
                                                        
                                                          /**
                                                           * @notice the version number representing the type of aggregator the proxy
                                                           * points to.
                                                           */
                                                          function version()
                                                            external
                                                            view
                                                            override
                                                            returns (uint256)
                                                          {
                                                            return currentPhase.aggregator.version();
                                                          }
                                                        
                                                          /**
                                                           * @notice returns the description of the aggregator the proxy points to.
                                                           */
                                                          function description()
                                                            external
                                                            view
                                                            override
                                                            returns (string memory)
                                                          {
                                                            return currentPhase.aggregator.description();
                                                          }
                                                        
                                                          /**
                                                           * @notice Allows the owner to propose a new address for the aggregator
                                                           * @param _aggregator The new address for the aggregator contract
                                                           */
                                                          function proposeAggregator(address _aggregator)
                                                            external
                                                            onlyOwner()
                                                          {
                                                            proposedAggregator = AggregatorV2V3Interface(_aggregator);
                                                          }
                                                        
                                                          /**
                                                           * @notice Allows the owner to confirm and change the address
                                                           * to the proposed aggregator
                                                           * @dev Reverts if the given address doesn't match what was previously
                                                           * proposed
                                                           * @param _aggregator The new address for the aggregator contract
                                                           */
                                                          function confirmAggregator(address _aggregator)
                                                            external
                                                            onlyOwner()
                                                          {
                                                            require(_aggregator == address(proposedAggregator), "Invalid proposed aggregator");
                                                            delete proposedAggregator;
                                                            setAggregator(_aggregator);
                                                          }
                                                        
                                                        
                                                          /*
                                                           * Internal
                                                           */
                                                        
                                                          function setAggregator(address _aggregator)
                                                            internal
                                                          {
                                                            uint16 id = currentPhase.id + 1;
                                                            currentPhase = Phase(id, AggregatorV2V3Interface(_aggregator));
                                                            phaseAggregators[id] = AggregatorV2V3Interface(_aggregator);
                                                          }
                                                        
                                                          function addPhase(
                                                            uint16 _phase,
                                                            uint64 _originalId
                                                          )
                                                            internal
                                                            view
                                                            returns (uint80)
                                                          {
                                                            return uint80(uint256(_phase) << PHASE_OFFSET | _originalId);
                                                          }
                                                        
                                                          function parseIds(
                                                            uint256 _roundId
                                                          )
                                                            internal
                                                            view
                                                            returns (uint16, uint64)
                                                          {
                                                            uint16 phaseId = uint16(_roundId >> PHASE_OFFSET);
                                                            uint64 aggregatorRoundId = uint64(_roundId);
                                                        
                                                            return (phaseId, aggregatorRoundId);
                                                          }
                                                        
                                                          function addPhaseIds(
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound,
                                                              uint16 phaseId
                                                          )
                                                            internal
                                                            view
                                                            returns (uint80, int256, uint256, uint256, uint80)
                                                          {
                                                            return (
                                                              addPhase(phaseId, uint64(roundId)),
                                                              answer,
                                                              startedAt,
                                                              updatedAt,
                                                              addPhase(phaseId, uint64(answeredInRound))
                                                            );
                                                          }
                                                        
                                                          /*
                                                           * Modifiers
                                                           */
                                                        
                                                          modifier hasProposal() {
                                                            require(address(proposedAggregator) != address(0), "No proposed aggregator present");
                                                            _;
                                                          }
                                                        
                                                        }
                                                        
                                                        interface AccessControllerInterface {
                                                          function hasAccess(address user, bytes calldata data) external view returns (bool);
                                                        }
                                                        
                                                        /**
                                                         * @title External Access Controlled Aggregator Proxy
                                                         * @notice A trusted proxy for updating where current answers are read from
                                                         * @notice This contract provides a consistent address for the
                                                         * Aggregator and AggregatorV3Interface but delegates where it reads from to the owner, who is
                                                         * trusted to update it.
                                                         * @notice Only access enabled addresses are allowed to access getters for
                                                         * aggregated answers and round information.
                                                         */
                                                        contract EACAggregatorProxy is AggregatorProxy {
                                                        
                                                          AccessControllerInterface public accessController;
                                                        
                                                          constructor(
                                                            address _aggregator,
                                                            address _accessController
                                                          )
                                                            public
                                                            AggregatorProxy(_aggregator)
                                                          {
                                                            setController(_accessController);
                                                          }
                                                        
                                                          /**
                                                           * @notice Allows the owner to update the accessController contract address.
                                                           * @param _accessController The new address for the accessController contract
                                                           */
                                                          function setController(address _accessController)
                                                            public
                                                            onlyOwner()
                                                          {
                                                            accessController = AccessControllerInterface(_accessController);
                                                          }
                                                        
                                                          /**
                                                           * @notice Reads the current answer from aggregator delegated to.
                                                           * @dev overridden function to add the checkAccess() modifier
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestAnswer()
                                                            public
                                                            view
                                                            override
                                                            checkAccess()
                                                            returns (int256)
                                                          {
                                                            return super.latestAnswer();
                                                          }
                                                        
                                                          /**
                                                           * @notice get the latest completed round where the answer was updated. This
                                                           * ID includes the proxy's phase, to make sure round IDs increase even when
                                                           * switching to a newly deployed aggregator.
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestTimestamp()
                                                            public
                                                            view
                                                            override
                                                            checkAccess()
                                                            returns (uint256)
                                                          {
                                                            return super.latestTimestamp();
                                                          }
                                                        
                                                          /**
                                                           * @notice get past rounds answers
                                                           * @param _roundId the answer number to retrieve the answer for
                                                           * @dev overridden function to add the checkAccess() modifier
                                                           *
                                                           * @dev #[deprecated] Use getRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended getRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function getAnswer(uint256 _roundId)
                                                            public
                                                            view
                                                            override
                                                            checkAccess()
                                                            returns (int256)
                                                          {
                                                            return super.getAnswer(_roundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get block timestamp when an answer was last updated
                                                           * @param _roundId the answer number to retrieve the updated timestamp for
                                                           * @dev overridden function to add the checkAccess() modifier
                                                           *
                                                           * @dev #[deprecated] Use getRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended getRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function getTimestamp(uint256 _roundId)
                                                            public
                                                            view
                                                            override
                                                            checkAccess()
                                                            returns (uint256)
                                                          {
                                                            return super.getTimestamp(_roundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get the latest completed round where the answer was updated
                                                           * @dev overridden function to add the checkAccess() modifier
                                                           *
                                                           * @dev #[deprecated] Use latestRoundData instead. This does not error if no
                                                           * answer has been reached, it will simply return 0. Either wait to point to
                                                           * an already answered Aggregator or use the recommended latestRoundData
                                                           * instead which includes better verification information.
                                                           */
                                                          function latestRound()
                                                            public
                                                            view
                                                            override
                                                            checkAccess()
                                                            returns (uint256)
                                                          {
                                                            return super.latestRound();
                                                          }
                                                        
                                                          /**
                                                           * @notice get data about a round. Consumers are encouraged to check
                                                           * that they're receiving fresh data by inspecting the updatedAt and
                                                           * answeredInRound return values.
                                                           * Note that different underlying implementations of AggregatorV3Interface
                                                           * have slightly different semantics for some of the return values. Consumers
                                                           * should determine what implementations they expect to receive
                                                           * data from and validate that they can properly handle return data from all
                                                           * of them.
                                                           * @param _roundId the round ID to retrieve the round data for
                                                           * @return roundId is the round ID from the aggregator for which the data was
                                                           * retrieved combined with a phase to ensure that round IDs get larger as
                                                           * time moves forward.
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @dev Note that answer and updatedAt may change between queries.
                                                           */
                                                          function getRoundData(uint80 _roundId)
                                                            public
                                                            view
                                                            checkAccess()
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return super.getRoundData(_roundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice get data about the latest round. Consumers are encouraged to check
                                                           * that they're receiving fresh data by inspecting the updatedAt and
                                                           * answeredInRound return values.
                                                           * Note that different underlying implementations of AggregatorV3Interface
                                                           * have slightly different semantics for some of the return values. Consumers
                                                           * should determine what implementations they expect to receive
                                                           * data from and validate that they can properly handle return data from all
                                                           * of them.
                                                           * @return roundId is the round ID from the aggregator for which the data was
                                                           * retrieved combined with a phase to ensure that round IDs get larger as
                                                           * time moves forward.
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @dev Note that answer and updatedAt may change between queries.
                                                           */
                                                          function latestRoundData()
                                                            public
                                                            view
                                                            checkAccess()
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return super.latestRoundData();
                                                          }
                                                        
                                                          /**
                                                           * @notice Used if an aggregator contract has been proposed.
                                                           * @param _roundId the round ID to retrieve the round data for
                                                           * @return roundId is the round ID for which data was retrieved
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                          */
                                                          function proposedGetRoundData(uint80 _roundId)
                                                            public
                                                            view
                                                            checkAccess()
                                                            hasProposal()
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return super.proposedGetRoundData(_roundId);
                                                          }
                                                        
                                                          /**
                                                           * @notice Used if an aggregator contract has been proposed.
                                                           * @return roundId is the round ID for which data was retrieved
                                                           * @return answer is the answer for the given round
                                                           * @return startedAt is the timestamp when the round was started.
                                                           * (Only some AggregatorV3Interface implementations return meaningful values)
                                                           * @return updatedAt is the timestamp when the round last was updated (i.e.
                                                           * answer was last computed)
                                                           * @return answeredInRound is the round ID of the round in which the answer
                                                           * was computed.
                                                          */
                                                          function proposedLatestRoundData()
                                                            public
                                                            view
                                                            checkAccess()
                                                            hasProposal()
                                                            override
                                                            returns (
                                                              uint80 roundId,
                                                              int256 answer,
                                                              uint256 startedAt,
                                                              uint256 updatedAt,
                                                              uint80 answeredInRound
                                                            )
                                                          {
                                                            return super.proposedLatestRoundData();
                                                          }
                                                        
                                                          /**
                                                           * @dev reverts if the caller does not have access by the accessController
                                                           * contract or is the contract itself.
                                                           */
                                                          modifier checkAccess() {
                                                            AccessControllerInterface ac = accessController;
                                                            require(address(ac) == address(0) || ac.hasAccess(msg.sender, msg.data), "No access");
                                                            _;
                                                          }
                                                        }

                                                        File 24 of 30: 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 25 of 30: 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 {IDebtTokenBase} from '../../../interfaces/IDebtTokenBase.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,
                                                          IDebtTokenBase
                                                        {
                                                          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);
                                                            emit Initialized(
                                                              UNDERLYING_ASSET_ADDRESS,
                                                              address(POOL),
                                                              address(_incentivesController),
                                                              decimals,
                                                              name,
                                                              symbol,
                                                              ''
                                                            );
                                                          }
                                                          /**
                                                           * @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;
                                                        interface IDebtTokenBase {
                                                          /**
                                                           * @dev Emitted when a debt token is initialized
                                                           * @param underlyingAsset The address of the underlying asset
                                                           * @param pool The address of the associated lending pool
                                                           * @param incentivesController The address of the incentives controller for this aToken
                                                           * @param debtTokenDecimals the decimals of the debt token
                                                           * @param debtTokenName the name of the debt token
                                                           * @param debtTokenSymbol the symbol of the debt token
                                                           * @param params A set of encoded parameters for additional initialization
                                                           **/
                                                          event Initialized(
                                                            address indexed underlyingAsset,
                                                            address indexed pool,
                                                            address incentivesController,
                                                            uint8 debtTokenDecimals,
                                                            string debtTokenName,
                                                            string debtTokenSymbol,
                                                            bytes params
                                                          );
                                                        }
                                                        // 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 view virtual returns (address payable) {
                                                            return msg.sender;
                                                          }
                                                          function _msgData() internal view virtual 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 26 of 30: 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 27 of 30: 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';
                                                        import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.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 = 0x2;
                                                          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());
                                                          }
                                                          /**
                                                           * @dev Returns the address of the incentives controller contract
                                                           * @return incentives address
                                                           **/
                                                          function getIncentivesController() external view override returns (IAaveIncentivesController) {
                                                            return _incentivesController;
                                                          }
                                                        }
                                                        // SPDX-License-Identifier: agpl-3.0
                                                        pragma solidity 0.6.12;
                                                        import {IScaledBalanceToken} from './IScaledBalanceToken.sol';
                                                        import {IAaveIncentivesController} from './IAaveIncentivesController.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;
                                                          /**
                                                           * @dev Returns the address of the incentives controller contract
                                                           **/
                                                          function getIncentivesController() external view returns (IAaveIncentivesController);
                                                        }
                                                        // 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;
                                                        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 {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 {IDebtTokenBase} from '../../../interfaces/IDebtTokenBase.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,
                                                          IDebtTokenBase
                                                        {
                                                          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);
                                                            emit Initialized(
                                                              UNDERLYING_ASSET_ADDRESS,
                                                              address(POOL),
                                                              address(_incentivesController),
                                                              decimals,
                                                              name,
                                                              symbol,
                                                              ''
                                                            );
                                                          }
                                                          /**
                                                           * @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;
                                                        interface IDebtTokenBase {
                                                          /**
                                                           * @dev Emitted when a debt token is initialized
                                                           * @param underlyingAsset The address of the underlying asset
                                                           * @param pool The address of the associated lending pool
                                                           * @param incentivesController The address of the incentives controller for this aToken
                                                           * @param debtTokenDecimals the decimals of the debt token
                                                           * @param debtTokenName the name of the debt token
                                                           * @param debtTokenSymbol the symbol of the debt token
                                                           * @param params A set of encoded parameters for additional initialization
                                                           **/
                                                          event Initialized(
                                                            address indexed underlyingAsset,
                                                            address indexed pool,
                                                            address incentivesController,
                                                            uint8 debtTokenDecimals,
                                                            string debtTokenName,
                                                            string debtTokenSymbol,
                                                            bytes params
                                                          );
                                                        }
                                                        // 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 view virtual returns (address payable) {
                                                            return msg.sender;
                                                          }
                                                          function _msgData() internal view virtual 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;
                                                          }
                                                        }
                                                        

                                                        File 28 of 30: 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';
                                                        import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesController.sol';
                                                        /**
                                                         * @title Aave ERC20 AToken
                                                         * @dev Implementation of the interest bearing token for the Aave protocol
                                                         * @author Aave
                                                         */
                                                        contract AToken is VersionedInitializable, IncentivizedERC20, IAToken {
                                                          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 = 0x2;
                                                          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);
                                                            emit Initialized(
                                                              UNDERLYING_ASSET_ADDRESS,
                                                              address(POOL),
                                                              RESERVE_TREASURY_ADDRESS,
                                                              address(_incentivesController),
                                                              underlyingAssetDecimals,
                                                              tokenName,
                                                              tokenSymbol,
                                                              ''
                                                            );
                                                          }
                                                          /**
                                                           * @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 address of the incentives controller contract
                                                           **/
                                                          function getIncentivesController() external view override returns (IAaveIncentivesController) {
                                                            return _incentivesController;
                                                          }
                                                          /**
                                                           * @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';
                                                        import {IAaveIncentivesController} from './IAaveIncentivesController.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 Emitted when an aToken is initialized
                                                           * @param underlyingAsset The address of the underlying asset
                                                           * @param pool The address of the associated lending pool
                                                           * @param treasury The address of the treasury
                                                           * @param incentivesController The address of the incentives controller for this aToken
                                                           * @param aTokenDecimals the decimals of the underlying
                                                           * @param aTokenName the name of the aToken
                                                           * @param aTokenSymbol the symbol of the aToken
                                                           * @param params A set of encoded parameters for additional initialization
                                                           **/
                                                          event Initialized(
                                                            address indexed underlyingAsset,
                                                            address indexed pool,
                                                            address treasury,
                                                            address incentivesController,
                                                            uint8 aTokenDecimals,
                                                            string aTokenName,
                                                            string aTokenSymbol,
                                                            bytes params
                                                          );
                                                          /**
                                                           * @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);
                                                          /**
                                                           * @dev Returns the address of the incentives controller contract
                                                           **/
                                                          function getIncentivesController() external view returns (IAaveIncentivesController);
                                                        }
                                                        // 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;
                                                        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 {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 view virtual returns (address payable) {
                                                            return msg.sender;
                                                          }
                                                          function _msgData() internal view virtual 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);
                                                        }
                                                        

                                                        File 29 of 30: 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 30 of 30: 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);
                                                          }
                                                        }